summaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/.ahub/tcchecker-tca/config.yaml54
-rw-r--r--compiler/CMakeLists.txt28
-rw-r--r--compiler/adtidas/README.md1
-rw-r--r--compiler/adtidas/include/adtidas/SmallVector.h92
-rw-r--r--compiler/angkor/CMakeLists.txt2
-rw-r--r--compiler/angkor/include/nncc/core/ADT/Iterable.h53
-rw-r--r--compiler/angkor/include/nncc/core/ADT/tensor/Index.h4
-rw-r--r--compiler/angkor/include/nncc/core/ADT/tensor/Shape.h3
-rw-r--r--compiler/angkor/src/ADT/feature/Buffer.test.cpp10
-rw-r--r--compiler/angkor/src/ADT/feature/CHWLayout.test.cpp6
-rw-r--r--compiler/angkor/src/ADT/feature/HWCLayout.test.cpp6
-rw-r--r--compiler/angkor/src/ADT/feature/Layout.test.cpp10
-rw-r--r--compiler/angkor/src/ADT/feature/Overlay.test.cpp14
-rw-r--r--compiler/angkor/src/ADT/feature/Shape.test.cpp8
-rw-r--r--compiler/angkor/src/ADT/kernel/Buffer.test.cpp12
-rw-r--r--compiler/angkor/src/ADT/kernel/Layout.test.cpp10
-rw-r--r--compiler/angkor/src/ADT/kernel/NCHWLayout.test.cpp8
-rw-r--r--compiler/angkor/src/ADT/kernel/NHWCLayout.test.cpp8
-rw-r--r--compiler/angkor/src/ADT/kernel/Overlay.test.cpp16
-rw-r--r--compiler/angkor/src/ADT/kernel/Shape.test.cpp10
-rw-r--r--compiler/angkor/src/ADT/tensor/Buffer.test.cpp6
-rw-r--r--compiler/angkor/src/ADT/tensor/Index.cpp27
-rw-r--r--compiler/angkor/src/ADT/tensor/Index.test.cpp58
-rw-r--r--compiler/angkor/src/ADT/tensor/IndexEnumerator.test.cpp2
-rw-r--r--compiler/angkor/src/ADT/tensor/Layout.test.cpp10
-rw-r--r--compiler/angkor/src/ADT/tensor/LexicalLayout.test.cpp6
-rw-r--r--compiler/angkor/src/ADT/tensor/Overlay.test.cpp10
-rw-r--r--compiler/angkor/src/ADT/tensor/Shape.cpp5
-rw-r--r--compiler/angkor/src/ADT/tensor/Shape.test.cpp64
-rw-r--r--compiler/angkor/src/TensorIndex.test.cpp26
-rw-r--r--compiler/angkor/src/TensorShape.test.cpp20
-rw-r--r--compiler/ann-api/README.md1
-rw-r--r--compiler/ann-api/include/.FORMATDENY (renamed from compiler/adtidas/.FORMATDENY)0
-rw-r--r--compiler/ann-ref/CMakeLists.txt6
-rw-r--r--compiler/ann-ref/src/NeuralNetworks.cpp11
-rw-r--r--compiler/arser/CMakeLists.txt15
-rw-r--r--compiler/arser/README.md3
-rw-r--r--compiler/arser/include/arser/arser.h507
-rw-r--r--compiler/arser/tests/arser.test.cpp344
-rw-r--r--compiler/bcq-tools/CMakeLists.txt29
-rw-r--r--compiler/bcq-tools/README.md70
-rw-r--r--compiler/bcq-tools/generate_bcq_metadata219
-rw-r--r--compiler/bcq-tools/generate_bcq_metadata.py219
-rw-r--r--compiler/bcq-tools/generate_bcq_output_arrays267
-rw-r--r--compiler/bcq-tools/generate_bcq_output_arrays.py224
-rw-r--r--compiler/bino/CMakeLists.txt2
-rw-r--r--compiler/caffe2circle/CMakeLists.txt16
-rw-r--r--compiler/caffe2circle/README.md3
-rw-r--r--compiler/caffe2circle/requires.cmake3
-rw-r--r--compiler/caffe2circle/src/caffe2circle.cpp39
-rw-r--r--compiler/caffegen/CMakeLists.txt2
-rw-r--r--compiler/circle-inspect/CMakeLists.txt14
-rw-r--r--compiler/circle-inspect/README.md22
-rw-r--r--compiler/circle-inspect/driver/Driver.cpp85
-rw-r--r--compiler/circle-inspect/requires.cmake3
-rw-r--r--compiler/circle-inspect/src/Dump.cpp177
-rw-r--r--compiler/circle-inspect/src/Dump.h65
-rw-r--r--compiler/circle-inspect/src/Reader.cpp169
-rw-r--r--compiler/circle-inspect/src/Reader.h91
-rw-r--r--compiler/circle-quantizer/CMakeLists.txt16
-rw-r--r--compiler/circle-quantizer/README.md3
-rw-r--r--compiler/circle-quantizer/requires.cmake8
-rw-r--r--compiler/circle-quantizer/src/CircleQuantizer.cpp223
-rw-r--r--compiler/circle-tensordump/CMakeLists.txt23
-rw-r--r--compiler/circle-tensordump/README.md73
-rw-r--r--compiler/circle-tensordump/driver/Driver.cpp86
-rw-r--r--compiler/circle-tensordump/requires.cmake4
-rw-r--r--compiler/circle-tensordump/src/Dump.cpp347
-rw-r--r--compiler/circle-tensordump/src/Dump.h57
-rw-r--r--compiler/circle-tensordump/src/Reader.cpp169
-rw-r--r--compiler/circle-tensordump/src/Reader.h91
-rw-r--r--compiler/circle-verify/CMakeLists.txt13
-rw-r--r--compiler/circle-verify/README.md23
-rw-r--r--compiler/circle-verify/requires.cmake5
-rw-r--r--compiler/circle-verify/src/Driver.cpp59
-rw-r--r--compiler/circle-verify/src/VerifyFlatBuffers.cpp36
-rw-r--r--compiler/circle-verify/src/VerifyFlatBuffers.h32
-rw-r--r--compiler/circle2circle-dredd-recipe-test/CMakeLists.txt112
-rw-r--r--compiler/circle2circle-dredd-recipe-test/README.md21
-rw-r--r--compiler/circle2circle-dredd-recipe-test/requires.cmake5
-rw-r--r--compiler/circle2circle-dredd-recipe-test/test.lst28
-rwxr-xr-xcompiler/circle2circle-dredd-recipe-test/testall.sh99
-rw-r--r--compiler/circle2circle/CMakeLists.txt48
-rw-r--r--compiler/circle2circle/README.md3
-rw-r--r--compiler/circle2circle/include/Model.h43
-rw-r--r--compiler/circle2circle/requires.cmake12
-rw-r--r--compiler/circle2circle/src/Circle2Circle.cpp316
-rw-r--r--compiler/circle2circle/src/Circle2Circle.test.cpp29
-rw-r--r--compiler/circle2circle/src/TestHelper.h55
-rw-r--r--compiler/circlechef/CMakeLists.txt23
-rw-r--r--compiler/circlechef/README.md8
-rw-r--r--compiler/circlechef/circle/CMakeLists.txt10
-rw-r--r--compiler/circlechef/circle/include/circlechef/RecipeChef.h41
-rw-r--r--compiler/circlechef/circle/src/CircleImport.cpp145
-rw-r--r--compiler/circlechef/circle/src/CircleImport.h81
-rw-r--r--compiler/circlechef/circle/src/CircleOpChef.h44
-rw-r--r--compiler/circlechef/circle/src/CircleOpChefs.h26
-rw-r--r--compiler/circlechef/circle/src/CircleOpRegistry.h71
-rw-r--r--compiler/circlechef/circle/src/Convert.cpp78
-rw-r--r--compiler/circlechef/circle/src/Convert.h58
-rw-r--r--compiler/circlechef/circle/src/Op/BCQFullyConnected.cpp64
-rw-r--r--compiler/circlechef/circle/src/Op/BCQFullyConnected.h39
-rw-r--r--compiler/circlechef/circle/src/Op/BCQGather.cpp68
-rw-r--r--compiler/circlechef/circle/src/Op/BCQGather.h39
-rw-r--r--compiler/circlechef/circle/src/Op/BatchMatMul.cpp48
-rw-r--r--compiler/circlechef/circle/src/Op/BatchMatMul.h39
-rw-r--r--compiler/circlechef/circle/src/Op/InstanceNorm.cpp53
-rw-r--r--compiler/circlechef/circle/src/Op/InstanceNorm.h39
-rw-r--r--compiler/circlechef/circle/src/RecipeChef.cpp260
-rw-r--r--compiler/circlechef/core/CMakeLists.txt9
-rw-r--r--compiler/circlechef/core/include/circlechef/ModelChef.h56
-rw-r--r--compiler/circlechef/core/src/Arguments.h34
-rw-r--r--compiler/circlechef/core/src/Convert.cpp72
-rw-r--r--compiler/circlechef/core/src/Convert.h31
-rw-r--r--compiler/circlechef/core/src/ModelChef.cpp598
-rw-r--r--compiler/circlechef/core/src/Op/BCQFullyConnected.cpp40
-rw-r--r--compiler/circlechef/core/src/Op/BCQFullyConnected.h52
-rw-r--r--compiler/circlechef/core/src/Op/BCQGather.cpp36
-rw-r--r--compiler/circlechef/core/src/Op/BCQGather.h49
-rw-r--r--compiler/circlechef/core/src/Op/BatchMatMul.cpp37
-rw-r--r--compiler/circlechef/core/src/Op/BatchMatMul.h49
-rw-r--r--compiler/circlechef/core/src/Op/InstanceNorm.cpp39
-rw-r--r--compiler/circlechef/core/src/Op/InstanceNorm.h52
-rw-r--r--compiler/circlechef/core/src/OpChef.def10
-rw-r--r--compiler/circlechef/core/src/OpChef.h48
-rw-r--r--compiler/circlechef/core/src/OpChefs.h25
-rw-r--r--compiler/circlechef/log/CMakeLists.txt7
-rw-r--r--compiler/circlechef/log/include/Log.h75
-rw-r--r--compiler/circlechef/log/include/LoggingContext.h35
-rw-r--r--compiler/circlechef/log/src/Log.cpp87
-rw-r--r--compiler/circlechef/log/src/LoggingContext.cpp41
-rw-r--r--compiler/circlechef/proto/CMakeLists.txt5
-rw-r--r--compiler/circlechef/proto/circlechef.proto116
-rw-r--r--compiler/circlechef/requires.cmake9
-rw-r--r--compiler/circlechef/tests/CMakeLists.txt70
-rwxr-xr-xcompiler/circlechef/tests/runvalidate.sh56
-rw-r--r--compiler/circlechef/tools/CMakeLists.txt6
-rw-r--r--compiler/circlechef/tools/console/CMakeLists.txt3
-rw-r--r--compiler/circlechef/tools/console/Driver.cpp58
-rw-r--r--compiler/circlechef/tools/file/CMakeLists.txt4
-rw-r--r--compiler/circlechef/tools/file/Driver.cpp85
-rw-r--r--compiler/circlechef/tools/reverse/CMakeLists.txt5
-rw-r--r--compiler/circlechef/tools/reverse/Driver.cpp72
-rw-r--r--compiler/circledump/CMakeLists.txt14
-rw-r--r--compiler/circledump/README.md71
-rw-r--r--compiler/circledump/driver/Driver.cpp60
-rw-r--r--compiler/circledump/include/circledump/Dump.h32
-rw-r--r--compiler/circledump/include/circleread/Model.h43
-rw-r--r--compiler/circledump/requires.cmake3
-rw-r--r--compiler/circledump/src/Dump.cpp418
-rw-r--r--compiler/circledump/src/Load.cpp133
-rw-r--r--compiler/circledump/src/OpPrinter.cpp795
-rw-r--r--compiler/circledump/src/OpPrinter.h61
-rw-r--r--compiler/circledump/src/Read.cpp169
-rw-r--r--compiler/circledump/src/Read.h101
-rw-r--r--compiler/cli/CMakeLists.txt2
-rw-r--r--compiler/coco/core/CMakeLists.txt3
-rw-r--r--compiler/coco/core/src/IR/Conv2D.cpp4
-rw-r--r--compiler/coco/core/src/IR/Op.cpp4
-rw-r--r--compiler/coco/generic/CMakeLists.txt2
-rw-r--r--compiler/common-artifacts/CMakeLists.txt278
-rw-r--r--compiler/common-artifacts/README.md37
-rw-r--r--compiler/common-artifacts/exclude.lst186
-rw-r--r--compiler/common-artifacts/requires.cmake9
-rw-r--r--compiler/common-artifacts/src/TestDataGenerator.cpp258
-rw-r--r--compiler/cwrap/CMakeLists.txt2
-rw-r--r--compiler/cwrap/include/cwrap/Fildes.h2
-rw-r--r--compiler/cwrap/src/Fildes.cpp9
-rw-r--r--compiler/cwrap/src/Fildes.test.cpp35
-rw-r--r--compiler/dredd-rule-lib/CMakeLists.txt21
-rw-r--r--compiler/dredd-rule-lib/README.md112
-rwxr-xr-xcompiler/dredd-rule-lib/rule-lib.sh220
-rw-r--r--compiler/enco-intf/README.md1
-rw-r--r--compiler/enco/core/CMakeLists.txt2
-rw-r--r--compiler/enco/core/src/Backend.cpp2
-rw-r--r--compiler/enco/core/src/Support/Debugging.cpp16
-rw-r--r--compiler/enco/core/src/Transforms/Split.cpp3
-rw-r--r--compiler/enco/frontend/caffe/CMakeLists.txt6
-rw-r--r--compiler/enco/frontend/tflite/CMakeLists.txt4
-rw-r--r--compiler/encodump/CMakeLists.txt9
-rw-r--r--compiler/encodump/requires.cmake1
-rw-r--r--compiler/encodump/src/Driver.cpp25
-rw-r--r--compiler/encodump/src/Dump.h16
-rw-r--r--compiler/exo-tflite/CMakeLists.txt61
-rw-r--r--compiler/exo-tflite/README.md3
-rw-r--r--compiler/exo-tflite/requires.cmake5
-rw-r--r--compiler/exo-tflite/src/Check.h39
-rw-r--r--compiler/exo-tflite/src/Conversion/AvgPool2DConverter.cpp80
-rw-r--r--compiler/exo-tflite/src/Conversion/CanonicalNodeConverter.cpp70
-rw-r--r--compiler/exo-tflite/src/Conversion/CanonicalNodeConverter.h45
-rw-r--r--compiler/exo-tflite/src/Conversion/EltwiseAddConverter.cpp82
-rw-r--r--compiler/exo-tflite/src/Conversion/EltwiseMulConverter.cpp82
-rw-r--r--compiler/exo-tflite/src/Conversion/FeatureBiasAddConverter.cpp78
-rw-r--r--compiler/exo-tflite/src/Conversion/MaxPool2DConverter.cpp68
-rw-r--r--compiler/exo-tflite/src/Conversion/ReluConverter.cpp76
-rw-r--r--compiler/exo-tflite/src/Conversion/ReluConverter.test.cpp132
-rw-r--r--compiler/exo-tflite/src/Conversion/ShapeInferencePass.cpp49
-rw-r--r--compiler/exo-tflite/src/Conversion/ShapeInferencePass.h40
-rw-r--r--compiler/exo-tflite/src/Conversion/TensorBroadcastConverter.cpp153
-rw-r--r--compiler/exo-tflite/src/Conversion/TypeInferencePass.cpp47
-rw-r--r--compiler/exo-tflite/src/Conversion/TypeInferencePass.h41
-rw-r--r--compiler/exo-tflite/src/Conversions.h41
-rw-r--r--compiler/exo-tflite/src/Convert.cpp107
-rw-r--r--compiler/exo-tflite/src/Dialect/IR/TFLDialect.test.cpp31
-rw-r--r--compiler/exo-tflite/src/Dialect/IR/TFLNodeImpl.h69
-rw-r--r--compiler/exo-tflite/src/Dialect/IR/TFLNodeVisitor.h86
-rw-r--r--compiler/exo-tflite/src/Dialect/IR/TFLNodes.cpp65
-rw-r--r--compiler/exo-tflite/src/Dialect/IR/TFLNodes.h297
-rw-r--r--compiler/exo-tflite/src/Dialect/IR/TFLNodes.lst24
-rw-r--r--compiler/exo-tflite/src/Dialect/IR/TFLNodes.test.cpp98
-rw-r--r--compiler/exo-tflite/src/Dialect/Service/TFLShapeInferenceRule.cpp284
-rw-r--r--compiler/exo-tflite/src/Dialect/Service/TFLShapeInferenceRule.test.cpp249
-rw-r--r--compiler/exo-tflite/src/Dialect/Service/TFLTypeInference.test.cpp73
-rw-r--r--compiler/exo-tflite/src/Dialect/Service/TFLTypeInferenceRule.cpp109
-rw-r--r--compiler/exo-tflite/src/ExporterUtils.cpp203
-rw-r--r--compiler/exo-tflite/src/ExporterUtils.h117
-rw-r--r--compiler/exo-tflite/src/GraphBlock.cpp86
-rw-r--r--compiler/exo-tflite/src/GraphBlock.h39
-rw-r--r--compiler/exo-tflite/src/Knob.cpp106
-rw-r--r--compiler/exo-tflite/src/Knob.h44
-rw-r--r--compiler/exo-tflite/src/Knob.lst31
-rw-r--r--compiler/exo-tflite/src/Log.cpp102
-rw-r--r--compiler/exo-tflite/src/Log.h81
-rw-r--r--compiler/exo-tflite/src/LogHelper.cpp79
-rw-r--r--compiler/exo-tflite/src/OperationExporter.cpp776
-rw-r--r--compiler/exo-tflite/src/OperationExporter.h31
-rw-r--r--compiler/exo-tflite/src/ShapeInference.cpp59
-rw-r--r--compiler/exo-tflite/src/ShapeInference.h40
-rw-r--r--compiler/exo-tflite/src/TFLExporter.cpp44
-rw-r--r--compiler/exo-tflite/src/TFLExporterImpl.cpp174
-rw-r--r--compiler/exo-tflite/src/TFLExporterImpl.h71
-rw-r--r--compiler/exo-tflite/src/TFLExporterImpl.test.cpp257
-rw-r--r--compiler/exo-tflite/src/TFLFormattedGraph.cpp215
-rw-r--r--compiler/exo-tflite/src/TFLFormattedGraph.h56
-rw-r--r--compiler/exo-tflite/src/TensorExporter.cpp213
-rw-r--r--compiler/exo-tflite/src/TensorExporter.h34
-rw-r--r--compiler/exo-tflite/src/TestGraph.h195
-rw-r--r--compiler/exo-tflite/src/TestHelper.h110
-rw-r--r--compiler/exo-tflite/src/TypeInference.cpp90
-rw-r--r--compiler/exo-tflite/src/TypeInference.h41
-rw-r--r--compiler/exo-tflite/src/TypeInference.test.cpp114
-rw-r--r--compiler/exo/CMakeLists.txt73
-rw-r--r--compiler/exo/README.md12
-rw-r--r--compiler/exo/include/exo/CircleExporter.h62
-rw-r--r--compiler/exo/include/exo/LoggingContext.h35
-rw-r--r--compiler/exo/include/exo/TFLExporter.h (renamed from compiler/exo-tflite/include/exo/TFLExporter.h)0
-rw-r--r--compiler/exo/requires.cmake6
-rw-r--r--compiler/exo/src/Check.h37
-rw-r--r--compiler/exo/src/Circle/CircleExporter.cpp49
-rw-r--r--compiler/exo/src/Circle/CircleExporterImpl.cpp181
-rw-r--r--compiler/exo/src/Circle/CircleExporterImpl.h78
-rw-r--r--compiler/exo/src/Circle/CircleExporterUtils.cpp163
-rw-r--r--compiler/exo/src/Circle/CircleExporterUtils.h120
-rw-r--r--compiler/exo/src/Circle/CircleOperationExporter.cpp1228
-rw-r--r--compiler/exo/src/Circle/CircleOperationExporter.h39
-rw-r--r--compiler/exo/src/Circle/CircleTensorExporter.cpp261
-rw-r--r--compiler/exo/src/Circle/CircleTensorExporter.h42
-rw-r--r--compiler/exo/src/Circle/CircleTypeInference.cpp85
-rw-r--r--compiler/exo/src/Circle/CircleTypeInference.h45
-rw-r--r--compiler/exo/src/Conversion/AvgPool2DConverter.cpp79
-rw-r--r--compiler/exo/src/Conversion/AvgPool2DConverter.h (renamed from compiler/exo-tflite/src/Conversion/AvgPool2DConverter.h)0
-rw-r--r--compiler/exo/src/Conversion/CanonicalNodeConverter.cpp19
-rw-r--r--compiler/exo/src/Conversion/CanonicalNodeConverter.h71
-rw-r--r--compiler/exo/src/Conversion/ConstGenConverter.cpp60
-rw-r--r--compiler/exo/src/Conversion/ConstGenConverter.h38
-rw-r--r--compiler/exo/src/Conversion/ConstGenConverter.test.cpp65
-rw-r--r--compiler/exo/src/Conversion/Conv2DConverter.cpp97
-rw-r--r--compiler/exo/src/Conversion/Conv2DConverter.h41
-rw-r--r--compiler/exo/src/Conversion/DepthwiseConv2DConverter.cpp114
-rw-r--r--compiler/exo/src/Conversion/DepthwiseConv2DConverter.h61
-rw-r--r--compiler/exo/src/Conversion/EltwiseAddConverter.cpp29
-rw-r--r--compiler/exo/src/Conversion/EltwiseAddConverter.h (renamed from compiler/exo-tflite/src/Conversion/EltwiseAddConverter.h)0
-rw-r--r--compiler/exo/src/Conversion/EltwiseBinaryConverter.h110
-rw-r--r--compiler/exo/src/Conversion/EltwiseDivConverter.cpp29
-rw-r--r--compiler/exo/src/Conversion/EltwiseDivConverter.h41
-rw-r--r--compiler/exo/src/Conversion/EltwiseMaxConverter.cpp75
-rw-r--r--compiler/exo/src/Conversion/EltwiseMaxConverter.h41
-rw-r--r--compiler/exo/src/Conversion/EltwiseMulConverter.cpp29
-rw-r--r--compiler/exo/src/Conversion/EltwiseMulConverter.h (renamed from compiler/exo-tflite/src/Conversion/EltwiseMulConverter.h)0
-rw-r--r--compiler/exo/src/Conversion/EltwiseSqrtConverter.cpp68
-rw-r--r--compiler/exo/src/Conversion/EltwiseSqrtConverter.h41
-rw-r--r--compiler/exo/src/Conversion/EltwiseSubConverter.cpp29
-rw-r--r--compiler/exo/src/Conversion/EltwiseSubConverter.h41
-rw-r--r--compiler/exo/src/Conversion/FeatureBiasAddConverter.cpp91
-rw-r--r--compiler/exo/src/Conversion/FeatureBiasAddConverter.h (renamed from compiler/exo-tflite/src/Conversion/FeatureBiasAddConverter.h)0
-rw-r--r--compiler/exo/src/Conversion/FeatureBiasAddConverter.test.cpp102
-rw-r--r--compiler/exo/src/Conversion/MatMulConverter.cpp103
-rw-r--r--compiler/exo/src/Conversion/MatMulConverter.h41
-rw-r--r--compiler/exo/src/Conversion/MaxPool2DConverter.cpp67
-rw-r--r--compiler/exo/src/Conversion/MaxPool2DConverter.h (renamed from compiler/exo-tflite/src/Conversion/MaxPool2DConverter.h)0
-rw-r--r--compiler/exo/src/Conversion/Relu6Converter.cpp68
-rw-r--r--compiler/exo/src/Conversion/Relu6Converter.h41
-rw-r--r--compiler/exo/src/Conversion/ReluConverter.cpp68
-rw-r--r--compiler/exo/src/Conversion/ReluConverter.h (renamed from compiler/exo-tflite/src/Conversion/ReluConverter.h)0
-rw-r--r--compiler/exo/src/Conversion/ReluConverter.test.cpp97
-rw-r--r--compiler/exo/src/Conversion/TensorBroadcastConverter.cpp189
-rw-r--r--compiler/exo/src/Conversion/TensorBroadcastConverter.h (renamed from compiler/exo-tflite/src/Conversion/TensorBroadcastConverter.h)0
-rw-r--r--compiler/exo/src/Conversion/TensorConcatConverter.cpp66
-rw-r--r--compiler/exo/src/Conversion/TensorConcatConverter.h41
-rw-r--r--compiler/exo/src/Conversion/TensorReduceConverter.cpp95
-rw-r--r--compiler/exo/src/Conversion/TensorReduceConverter.h46
-rw-r--r--compiler/exo/src/Conversion/TensorTransposeConverter.cpp102
-rw-r--r--compiler/exo/src/Conversion/TensorTransposeConverter.h41
-rw-r--r--compiler/exo/src/Conversion/TransposedConv2DConverter.cpp92
-rw-r--r--compiler/exo/src/Conversion/TransposedConv2DConverter.h62
-rw-r--r--compiler/exo/src/Conversions.h46
-rw-r--r--compiler/exo/src/Convert.cpp97
-rw-r--r--compiler/exo/src/Convert.h (renamed from compiler/exo-tflite/src/Convert.h)0
-rw-r--r--compiler/exo/src/Dialect/IR/CircleDialect.cpp28
-rw-r--r--compiler/exo/src/Dialect/IR/CircleDialect.h40
-rw-r--r--compiler/exo/src/Dialect/IR/CircleDialect.test.cpp31
-rw-r--r--compiler/exo/src/Dialect/IR/CircleNode.cpp26
-rw-r--r--compiler/exo/src/Dialect/IR/CircleNode.h23
-rw-r--r--compiler/exo/src/Dialect/IR/CircleNodeDecl.h50
-rw-r--r--compiler/exo/src/Dialect/IR/CircleNodeImpl.h69
-rw-r--r--compiler/exo/src/Dialect/IR/CircleNodeVisitor.forward.h30
-rw-r--r--compiler/exo/src/Dialect/IR/CircleNodeVisitor.h85
-rw-r--r--compiler/exo/src/Dialect/IR/CircleNodes.cpp18
-rw-r--r--compiler/exo/src/Dialect/IR/CircleNodes.h79
-rw-r--r--compiler/exo/src/Dialect/IR/CircleNodes.lst8
-rw-r--r--compiler/exo/src/Dialect/IR/CircleNodes.test.cpp36
-rw-r--r--compiler/exo/src/Dialect/IR/CircleOpcode.h32
-rw-r--r--compiler/exo/src/Dialect/IR/FusedActFunc.h35
-rw-r--r--compiler/exo/src/Dialect/IR/NodeMixins.cpp18
-rw-r--r--compiler/exo/src/Dialect/IR/NodeMixins.h66
-rw-r--r--compiler/exo/src/Dialect/IR/TFLDialect.cpp (renamed from compiler/exo-tflite/src/Dialect/IR/TFLDialect.cpp)0
-rw-r--r--compiler/exo/src/Dialect/IR/TFLDialect.h (renamed from compiler/exo-tflite/src/Dialect/IR/TFLDialect.h)0
-rw-r--r--compiler/exo/src/Dialect/IR/TFLDialect.test.cpp31
-rw-r--r--compiler/exo/src/Dialect/IR/TFLNode.cpp (renamed from compiler/exo-tflite/src/Dialect/IR/TFLNode.cpp)0
-rw-r--r--compiler/exo/src/Dialect/IR/TFLNode.h (renamed from compiler/exo-tflite/src/Dialect/IR/TFLNode.h)0
-rw-r--r--compiler/exo/src/Dialect/IR/TFLNodeDecl.h (renamed from compiler/exo-tflite/src/Dialect/IR/TFLNodeDecl.h)0
-rw-r--r--compiler/exo/src/Dialect/IR/TFLNodeImpl.h69
-rw-r--r--compiler/exo/src/Dialect/IR/TFLNodeVisitor.forward.h (renamed from compiler/exo-tflite/src/Dialect/IR/TFLNodeVisitor.forward.h)0
-rw-r--r--compiler/exo/src/Dialect/IR/TFLNodeVisitor.h85
-rw-r--r--compiler/exo/src/Dialect/IR/TFLNodes.cpp91
-rw-r--r--compiler/exo/src/Dialect/IR/TFLNodes.h551
-rw-r--r--compiler/exo/src/Dialect/IR/TFLNodes.lst30
-rw-r--r--compiler/exo/src/Dialect/IR/TFLNodes.test.cpp159
-rw-r--r--compiler/exo/src/Dialect/IR/TFLOpcode.h (renamed from compiler/exo-tflite/src/Dialect/IR/TFLOpcode.h)0
-rw-r--r--compiler/exo/src/Dialect/Service/CircleShapeInferenceRule.cpp67
-rw-r--r--compiler/exo/src/Dialect/Service/CircleShapeInferenceRule.h33
-rw-r--r--compiler/exo/src/Dialect/Service/CircleTypeInferenceRule.cpp58
-rw-r--r--compiler/exo/src/Dialect/Service/CircleTypeInferenceRule.h36
-rw-r--r--compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.cpp627
-rw-r--r--compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.h (renamed from compiler/exo-tflite/src/Dialect/Service/TFLShapeInferenceRule.h)0
-rw-r--r--compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.test.cpp277
-rw-r--r--compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.cpp141
-rw-r--r--compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.h (renamed from compiler/exo-tflite/src/Dialect/Service/TFLTypeInferenceRule.h)0
-rw-r--r--compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.test.cpp57
-rw-r--r--compiler/exo/src/ExoFormattedGraph.cpp525
-rw-r--r--compiler/exo/src/ExoFormattedGraph.h56
-rw-r--r--compiler/exo/src/ExoOptimize.cpp74
-rw-r--r--compiler/exo/src/ExoOptimize.h34
-rw-r--r--compiler/exo/src/ExporterUtils.cpp139
-rw-r--r--compiler/exo/src/ExporterUtils.h57
-rw-r--r--compiler/exo/src/GraphBlock.cpp243
-rw-r--r--compiler/exo/src/GraphBlock.h199
-rw-r--r--compiler/exo/src/Knob.cpp122
-rw-r--r--compiler/exo/src/Knob.h51
-rw-r--r--compiler/exo/src/Knob.lst11
-rw-r--r--compiler/exo/src/Log.cpp84
-rw-r--r--compiler/exo/src/Log.h75
-rw-r--r--compiler/exo/src/LogHelper.cpp79
-rw-r--r--compiler/exo/src/LogHelper.h (renamed from compiler/exo-tflite/src/LogHelper.h)0
-rw-r--r--compiler/exo/src/LoggingContext.cpp40
-rw-r--r--compiler/exo/src/Pass/FoldReshapeOfConstPass.cpp116
-rw-r--r--compiler/exo/src/Pass/FoldReshapeOfConstPass.h46
-rw-r--r--compiler/exo/src/Pass/FoldTransposeOfConstPass.cpp154
-rw-r--r--compiler/exo/src/Pass/FoldTransposeOfConstPass.h46
-rw-r--r--compiler/exo/src/Pass/FuseBiasAddPass.cpp362
-rw-r--r--compiler/exo/src/Pass/FuseBiasAddPass.h61
-rw-r--r--compiler/exo/src/Pass/FuseBiasAddPass.test.cpp361
-rw-r--r--compiler/exo/src/Pass/FuseInstanceNormPass.cpp402
-rw-r--r--compiler/exo/src/Pass/FuseInstanceNormPass.h40
-rw-r--r--compiler/exo/src/Pass/FuseReluPass.cpp115
-rw-r--r--compiler/exo/src/Pass/FuseReluPass.h40
-rw-r--r--compiler/exo/src/Pass/FuseReluPass.test.cpp115
-rw-r--r--compiler/exo/src/Pass/FuseRsqrtPass.cpp95
-rw-r--r--compiler/exo/src/Pass/FuseRsqrtPass.h47
-rw-r--r--compiler/exo/src/Pass/FuseSquaredDifferencePass.cpp86
-rw-r--r--compiler/exo/src/Pass/FuseSquaredDifferencePass.h49
-rw-r--r--compiler/exo/src/Pass/MergeConcatNodesPass.cpp191
-rw-r--r--compiler/exo/src/Pass/MergeConcatNodesPass.h41
-rw-r--r--compiler/exo/src/Pass/ShapeInferencePass.cpp59
-rw-r--r--compiler/exo/src/Pass/ShapeInferencePass.h40
-rw-r--r--compiler/exo/src/Pass/TypeInferencePass.cpp57
-rw-r--r--compiler/exo/src/Pass/TypeInferencePass.h42
-rw-r--r--compiler/exo/src/Passes.cpp19
-rw-r--r--compiler/exo/src/Passes.h38
-rw-r--r--compiler/exo/src/ProgressReporter.cpp (renamed from compiler/exo-tflite/src/ProgressReporter.cpp)0
-rw-r--r--compiler/exo/src/ProgressReporter.h (renamed from compiler/exo-tflite/src/ProgressReporter.h)0
-rw-r--r--compiler/exo/src/ShapeInference.cpp44
-rw-r--r--compiler/exo/src/ShapeInference.h41
-rw-r--r--compiler/exo/src/TFLite/TFLExporter.cpp49
-rw-r--r--compiler/exo/src/TFLite/TFLExporterImpl.cpp179
-rw-r--r--compiler/exo/src/TFLite/TFLExporterImpl.h78
-rw-r--r--compiler/exo/src/TFLite/TFLExporterImpl.test.cpp413
-rw-r--r--compiler/exo/src/TFLite/TFLExporterUtils.cpp160
-rw-r--r--compiler/exo/src/TFLite/TFLExporterUtils.h118
-rw-r--r--compiler/exo/src/TFLite/TFLExporterUtils.test.cpp108
-rw-r--r--compiler/exo/src/TFLite/TFLOperationExporter.cpp1199
-rw-r--r--compiler/exo/src/TFLite/TFLOperationExporter.h39
-rw-r--r--compiler/exo/src/TFLite/TFLTensorExporter.cpp252
-rw-r--r--compiler/exo/src/TFLite/TFLTensorExporter.h42
-rw-r--r--compiler/exo/src/TFLite/TFLTypeInference.cpp82
-rw-r--r--compiler/exo/src/TFLite/TFLTypeInference.h42
-rw-r--r--compiler/exo/src/TFLite/TFLTypeInference.test.cpp118
-rw-r--r--compiler/exo/src/TestGraph.h315
-rw-r--r--compiler/exo/src/TestHelper.h110
-rw-r--r--compiler/fipe/CMakeLists.txt2
-rw-r--r--compiler/fipe/README.md1
-rw-r--r--compiler/foder/CMakeLists.txt2
-rw-r--r--compiler/foder/README.md13
-rw-r--r--compiler/foder/include/foder/FileLoader.h69
-rw-r--r--compiler/hermes-std/CMakeLists.txt2
-rw-r--r--compiler/hermes/CMakeLists.txt2
-rw-r--r--compiler/hermes/src/hermes.test.cpp25
-rw-r--r--compiler/i5diff/CMakeLists.txt2
-rw-r--r--compiler/kuma/CMakeLists.txt19
-rw-r--r--compiler/kuma/README.md7
-rw-r--r--compiler/kuma/include/kuma.h98
-rw-r--r--compiler/kuma/src/IntervalSet.cpp92
-rw-r--r--compiler/kuma/src/IntervalSet.h78
-rw-r--r--compiler/kuma/src/IntervalSet.test.cpp31
-rw-r--r--compiler/kuma/src/kuma.cpp93
-rw-r--r--compiler/kuma/src/kuma.test.cpp89
-rw-r--r--compiler/loco/CMakeLists.txt9
-rw-r--r--compiler/loco/include/loco/IR/CanonicalNodes.lst9
-rw-r--r--compiler/loco/include/loco/IR/CastHelpers.h42
-rw-r--r--compiler/loco/include/loco/IR/DataType.h4
-rw-r--r--compiler/loco/include/loco/IR/DataTypeTraits.h69
-rw-r--r--compiler/loco/include/loco/IR/DepthwiseFilterCodec.h21
-rw-r--r--compiler/loco/include/loco/IR/Dimension.h12
-rw-r--r--compiler/loco/include/loco/IR/FeatureCodec.h25
-rw-r--r--compiler/loco/include/loco/IR/FilterCodec.h28
-rw-r--r--compiler/loco/include/loco/IR/Graph.h31
-rw-r--r--compiler/loco/include/loco/IR/Node.h8
-rw-r--r--compiler/loco/include/loco/IR/NodeMixins.h2
-rw-r--r--compiler/loco/include/loco/IR/Nodes.h239
-rw-r--r--compiler/loco/include/loco/IR/PaddingND.h56
-rw-r--r--compiler/loco/include/loco/IR/PermutingCodec.h66
-rw-r--r--compiler/loco/include/loco/IR/TensorShape.h22
-rw-r--r--compiler/loco/src/ADT/AnnotatedItem.test.cpp6
-rw-r--r--compiler/loco/src/IR/Algorithm.test.cpp16
-rw-r--r--compiler/loco/src/IR/CanonicalDialect.cpp9
-rw-r--r--compiler/loco/src/IR/CanonicalDialect.test.cpp2
-rw-r--r--compiler/loco/src/IR/CanonicalNode.test.cpp8
-rw-r--r--compiler/loco/src/IR/DataTypeTraits.test.cpp2
-rw-r--r--compiler/loco/src/IR/DepthwiseFilterIndex.test.cpp40
-rw-r--r--compiler/loco/src/IR/DepthwiseFilterShape.test.cpp20
-rw-r--r--compiler/loco/src/IR/Dialect.test.cpp2
-rw-r--r--compiler/loco/src/IR/Dimension.test.cpp4
-rw-r--r--compiler/loco/src/IR/FeatureIndex.test.cpp40
-rw-r--r--compiler/loco/src/IR/FeatureShape.test.cpp20
-rw-r--r--compiler/loco/src/IR/FilterIndex.test.cpp40
-rw-r--r--compiler/loco/src/IR/FilterShape.test.cpp20
-rw-r--r--compiler/loco/src/IR/Graph.cpp32
-rw-r--r--compiler/loco/src/IR/Graph.test.cpp57
-rw-r--r--compiler/loco/src/IR/MockupNode.h17
-rw-r--r--compiler/loco/src/IR/Node.test.cpp34
-rw-r--r--compiler/loco/src/IR/NodeShape.test.cpp52
-rw-r--r--compiler/loco/src/IR/Nodes.cpp7
-rw-r--r--compiler/loco/src/IR/Nodes.test.cpp403
-rw-r--r--compiler/loco/src/IR/Pad.test.cpp29
-rw-r--r--compiler/loco/src/IR/Padding2D.test.cpp29
-rw-r--r--compiler/loco/src/IR/PaddingND.test.cpp32
-rw-r--r--compiler/loco/src/IR/PermutingCodec.cpp69
-rw-r--r--compiler/loco/src/IR/PermutingCodec.test.cpp219
-rw-r--r--compiler/loco/src/IR/Stride.test.cpp12
-rw-r--r--compiler/loco/src/IR/TensorShape.cpp56
-rw-r--r--compiler/loco/src/IR/TensorShape.test.cpp64
-rw-r--r--compiler/loco/src/IR/Use.test.cpp8
-rw-r--r--compiler/loco/src/IR/Verifier.test.cpp6
-rw-r--r--compiler/loco/src/IR/Window.test.cpp12
-rw-r--r--compiler/loco/src/Service/CanonicalShapeInferenceRule.cpp134
-rw-r--r--compiler/loco/src/Service/CanonicalShapeInferenceRule.test.cpp172
-rw-r--r--compiler/loco/src/Service/GraphBuilder.test.cpp6
-rw-r--r--compiler/loco/src/Service/GraphTestcase.h47
-rw-r--r--compiler/loco/src/Service/MultiDialectShapeInferenceRule.test.cpp16
-rw-r--r--compiler/loco/src/Service/ShapeInference.test.cpp12
-rw-r--r--compiler/loco/src/Service/TypeInference.cpp17
-rw-r--r--compiler/loco/src/Service/TypeInference.test.cpp18
-rw-r--r--compiler/loco/src/loco.test.cpp12
-rw-r--r--compiler/locoex-customop/CMakeLists.txt5
-rw-r--r--compiler/locoex-customop/README.md4
-rw-r--r--compiler/locoex-customop/requires.cmake1
-rw-r--r--compiler/locoex-customop/src/Service/COpShapeInferenceRule.cpp2
-rw-r--r--compiler/locomotiv/CMakeLists.txt5
-rw-r--r--compiler/locomotiv/README.md2
-rw-r--r--compiler/locomotiv/src/Node.lst6
-rw-r--r--compiler/locomotiv/src/Node/AvgPool2D.cpp16
-rw-r--r--compiler/locomotiv/src/Node/AvgPool2D.test.cpp4
-rw-r--r--compiler/locomotiv/src/Node/BiasAdd.cpp29
-rw-r--r--compiler/locomotiv/src/Node/BiasAdd.test.cpp16
-rw-r--r--compiler/locomotiv/src/Node/BiasEncode.cpp13
-rw-r--r--compiler/locomotiv/src/Node/BiasEncode.test.cpp22
-rw-r--r--compiler/locomotiv/src/Node/ConstGen.cpp13
-rw-r--r--compiler/locomotiv/src/Node/ConstGen.test.cpp40
-rw-r--r--compiler/locomotiv/src/Node/Conv2D.cpp13
-rw-r--r--compiler/locomotiv/src/Node/Conv2D.test.cpp4
-rw-r--r--compiler/locomotiv/src/Node/DepthwiseConv2D.cpp13
-rw-r--r--compiler/locomotiv/src/Node/DepthwiseConv2D.test.cpp4
-rw-r--r--compiler/locomotiv/src/Node/DepthwiseFilterEncode.cpp13
-rw-r--r--compiler/locomotiv/src/Node/DepthwiseFilterEncode.test.cpp8
-rw-r--r--compiler/locomotiv/src/Node/EltwiseAdd.test.cpp8
-rw-r--r--compiler/locomotiv/src/Node/EltwiseDiv.test.cpp8
-rw-r--r--compiler/locomotiv/src/Node/EltwiseMax.cpp36
-rw-r--r--compiler/locomotiv/src/Node/EltwiseMax.test.cpp121
-rw-r--r--compiler/locomotiv/src/Node/EltwiseMul.test.cpp8
-rw-r--r--compiler/locomotiv/src/Node/EltwiseSqrt.test.cpp12
-rw-r--r--compiler/locomotiv/src/Node/EltwiseSub.test.cpp8
-rw-r--r--compiler/locomotiv/src/Node/FeatureCodec.test.cpp32
-rw-r--r--compiler/locomotiv/src/Node/FeatureDecode.cpp13
-rw-r--r--compiler/locomotiv/src/Node/FilterEncode.cpp13
-rw-r--r--compiler/locomotiv/src/Node/FilterEncode.test.cpp16
-rw-r--r--compiler/locomotiv/src/Node/Forward.cpp13
-rw-r--r--compiler/locomotiv/src/Node/Forward.test.cpp16
-rw-r--r--compiler/locomotiv/src/Node/MatMul.cpp142
-rw-r--r--compiler/locomotiv/src/Node/MatMul.test.cpp192
-rw-r--r--compiler/locomotiv/src/Node/MatrixCodec.test.cpp207
-rw-r--r--compiler/locomotiv/src/Node/MatrixDecode.cpp118
-rw-r--r--compiler/locomotiv/src/Node/MatrixEncode.cpp112
-rw-r--r--compiler/locomotiv/src/Node/MaxPool2D.cpp13
-rw-r--r--compiler/locomotiv/src/Node/MaxPool2D.test.cpp4
-rw-r--r--compiler/locomotiv/src/Node/Pull.cpp13
-rw-r--r--compiler/locomotiv/src/Node/Push.cpp13
-rw-r--r--compiler/locomotiv/src/Node/Push.test.cpp16
-rw-r--r--compiler/locomotiv/src/Node/ReLU.test.cpp10
-rw-r--r--compiler/locomotiv/src/Node/ReLU6.test.cpp14
-rw-r--r--compiler/locomotiv/src/Node/Reshape.cpp19
-rw-r--r--compiler/locomotiv/src/Node/Reshape.test.cpp14
-rw-r--r--compiler/locomotiv/src/Node/Softmax.cpp13
-rw-r--r--compiler/locomotiv/src/Node/Softmax.test.cpp14
-rw-r--r--compiler/locomotiv/src/Node/Tanh.test.cpp12
-rw-r--r--compiler/locomotiv/src/Node/TensorBroadcast.cpp16
-rw-r--r--compiler/locomotiv/src/Node/TensorBroadcast.test.cpp10
-rw-r--r--compiler/locomotiv/src/Node/TensorConcat.cpp15
-rw-r--r--compiler/locomotiv/src/Node/TensorConcat.test.cpp40
-rw-r--r--compiler/locomotiv/src/Node/TensorConstantPad.cpp124
-rw-r--r--compiler/locomotiv/src/Node/TensorConstantPad.test.cpp218
-rw-r--r--compiler/locomotiv/src/Node/TensorReduce.cpp161
-rw-r--r--compiler/locomotiv/src/Node/TensorReduce.test.cpp104
-rw-r--r--compiler/locomotiv/src/Node/TransposedConv2D.cpp13
-rw-r--r--compiler/locomotiv/src/Node/TransposedConv2D.test.cpp4
-rw-r--r--compiler/locomotiv/src/NodeData.test.cpp12
-rw-r--r--compiler/locomotiv/src/NodeDataImpl.test.cpp10
-rw-r--r--compiler/locomotiv/src/NodeDomain.test.cpp6
-rw-r--r--compiler/locomotiv/src/NodeExecution.cpp2
-rw-r--r--compiler/locomotiv/src/Session.test.cpp74
-rw-r--r--compiler/locop/CMakeLists.txt2
-rw-r--r--compiler/locop/requires.cmake1
-rw-r--r--compiler/locop/src/CanonicalNodeSummaryBuilder.cpp20
-rw-r--r--compiler/locop/src/FormattedGraph.cpp43
-rw-r--r--compiler/locop/src/FormattedGraph.test.cpp2
-rw-r--r--compiler/locop/src/FormattedTensorShape.test.cpp2
-rw-r--r--compiler/logo-core/CMakeLists.txt19
-rw-r--r--compiler/logo-core/README.md3
-rw-r--r--compiler/logo-core/include/logo/Pass.h (renamed from compiler/logo/include/logo/Pass.h)0
-rw-r--r--compiler/logo-core/include/logo/Phase.h (renamed from compiler/logo/include/logo/Phase.h)0
-rw-r--r--compiler/logo-core/requires.cmake1
-rw-r--r--compiler/logo-core/src/Pass.cpp (renamed from compiler/logo/src/Pass.cpp)0
-rw-r--r--compiler/logo-core/src/Pass.test.cpp (renamed from compiler/logo/src/Pass.test.cpp)0
-rw-r--r--compiler/logo-core/src/Phase.cpp (renamed from compiler/logo/src/Phase.cpp)0
-rw-r--r--compiler/logo/CMakeLists.txt4
-rw-r--r--compiler/logo/README.md2
-rw-r--r--compiler/logo/include/logo/DeadNodeQueryService.h35
-rw-r--r--compiler/logo/include/logo/RemoveDeadNodeWithQueryPass.h34
-rw-r--r--compiler/logo/include/logo/SimplifyDomainConversionPass.h5
-rw-r--r--compiler/logo/requires.cmake1
-rw-r--r--compiler/logo/src/Passes/ConstantFoldingPass.cpp4
-rw-r--r--compiler/logo/src/Passes/ConstantFoldingPass.test.cpp4
-rw-r--r--compiler/logo/src/Passes/RemoveDeadNodeWithQueryPass.cpp70
-rw-r--r--compiler/logo/src/Passes/RemoveForwardNodePass.cpp2
-rw-r--r--compiler/logo/src/Passes/ReorderDecodePass.cpp96
-rw-r--r--compiler/logo/src/Passes/ResolveDuplicateReshapePass.cpp4
-rw-r--r--compiler/logo/src/Passes/SimplifyDomainConversionPass.cpp289
-rw-r--r--compiler/logo/src/Passes/SimplifyDomainConversionPass.test.cpp234
-rw-r--r--compiler/luci-interpreter/CMakeLists.txt4
-rw-r--r--compiler/luci-interpreter/include/luci_interpreter/Interpreter.h78
-rw-r--r--compiler/luci-interpreter/include/luci_interpreter/core/DataType.h36
-rw-r--r--compiler/luci-interpreter/include/luci_interpreter/core/Tensor.h151
-rw-r--r--compiler/luci-interpreter/requires.cmake1
-rw-r--r--compiler/luci-interpreter/src/CMakeLists.txt41
-rw-r--r--compiler/luci-interpreter/src/Interpreter.cpp126
-rw-r--r--compiler/luci-interpreter/src/core/CMakeLists.txt17
-rw-r--r--compiler/luci-interpreter/src/core/EventNotifier.h36
-rw-r--r--compiler/luci-interpreter/src/core/Kernel.h75
-rw-r--r--compiler/luci-interpreter/src/core/KernelParams.h184
-rw-r--r--compiler/luci-interpreter/src/core/RuntimeGraph.cpp181
-rw-r--r--compiler/luci-interpreter/src/core/RuntimeGraph.h67
-rw-r--r--compiler/luci-interpreter/src/core/RuntimeModule.h59
-rw-r--r--compiler/luci-interpreter/src/core/Tensor.cpp77
-rw-r--r--compiler/luci-interpreter/src/kernels/Add.cpp195
-rw-r--r--compiler/luci-interpreter/src/kernels/Add.h49
-rw-r--r--compiler/luci-interpreter/src/kernels/Add.test.cpp246
-rw-r--r--compiler/luci-interpreter/src/kernels/ArgMax.cpp140
-rw-r--r--compiler/luci-interpreter/src/kernels/ArgMax.h44
-rw-r--r--compiler/luci-interpreter/src/kernels/ArgMax.test.cpp110
-rw-r--r--compiler/luci-interpreter/src/kernels/AveragePool2D.cpp157
-rw-r--r--compiler/luci-interpreter/src/kernels/AveragePool2D.h52
-rw-r--r--compiler/luci-interpreter/src/kernels/AveragePool2D.test.cpp220
-rw-r--r--compiler/luci-interpreter/src/kernels/BinaryOpCommon.h73
-rw-r--r--compiler/luci-interpreter/src/kernels/CMakeLists.txt201
-rw-r--r--compiler/luci-interpreter/src/kernels/Concatenation.cpp136
-rw-r--r--compiler/luci-interpreter/src/kernels/Concatenation.h48
-rw-r--r--compiler/luci-interpreter/src/kernels/Concatenation.test.cpp169
-rw-r--r--compiler/luci-interpreter/src/kernels/Conv2D.cpp308
-rw-r--r--compiler/luci-interpreter/src/kernels/Conv2D.h58
-rw-r--r--compiler/luci-interpreter/src/kernels/Conv2D.test.cpp416
-rw-r--r--compiler/luci-interpreter/src/kernels/DepthToSpace.cpp80
-rw-r--r--compiler/luci-interpreter/src/kernels/DepthToSpace.h45
-rw-r--r--compiler/luci-interpreter/src/kernels/DepthToSpace.test.cpp105
-rw-r--r--compiler/luci-interpreter/src/kernels/DepthwiseConv2D.cpp275
-rw-r--r--compiler/luci-interpreter/src/kernels/DepthwiseConv2D.h55
-rw-r--r--compiler/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp405
-rw-r--r--compiler/luci-interpreter/src/kernels/Div.cpp128
-rw-r--r--compiler/luci-interpreter/src/kernels/Div.h48
-rw-r--r--compiler/luci-interpreter/src/kernels/Div.test.cpp150
-rw-r--r--compiler/luci-interpreter/src/kernels/Elu.cpp52
-rw-r--r--compiler/luci-interpreter/src/kernels/Elu.h43
-rw-r--r--compiler/luci-interpreter/src/kernels/Elu.test.cpp75
-rw-r--r--compiler/luci-interpreter/src/kernels/Equal.cpp113
-rw-r--r--compiler/luci-interpreter/src/kernels/Equal.h53
-rw-r--r--compiler/luci-interpreter/src/kernels/Equal.test.cpp187
-rw-r--r--compiler/luci-interpreter/src/kernels/Exp.cpp56
-rw-r--r--compiler/luci-interpreter/src/kernels/Exp.h46
-rw-r--r--compiler/luci-interpreter/src/kernels/Exp.test.cpp51
-rw-r--r--compiler/luci-interpreter/src/kernels/Floor.cpp57
-rw-r--r--compiler/luci-interpreter/src/kernels/Floor.h45
-rw-r--r--compiler/luci-interpreter/src/kernels/Floor.test.cpp65
-rw-r--r--compiler/luci-interpreter/src/kernels/FloorDiv.cpp85
-rw-r--r--compiler/luci-interpreter/src/kernels/FloorDiv.h46
-rw-r--r--compiler/luci-interpreter/src/kernels/FloorDiv.test.cpp135
-rw-r--r--compiler/luci-interpreter/src/kernels/FullyConnected.cpp139
-rw-r--r--compiler/luci-interpreter/src/kernels/FullyConnected.h50
-rw-r--r--compiler/luci-interpreter/src/kernels/FullyConnected.test.cpp196
-rw-r--r--compiler/luci-interpreter/src/kernels/Greater.cpp113
-rw-r--r--compiler/luci-interpreter/src/kernels/Greater.h53
-rw-r--r--compiler/luci-interpreter/src/kernels/Greater.test.cpp214
-rw-r--r--compiler/luci-interpreter/src/kernels/GreaterEqual.cpp116
-rw-r--r--compiler/luci-interpreter/src/kernels/GreaterEqual.h53
-rw-r--r--compiler/luci-interpreter/src/kernels/GreaterEqual.test.cpp214
-rw-r--r--compiler/luci-interpreter/src/kernels/If.cpp90
-rw-r--r--compiler/luci-interpreter/src/kernels/If.h49
-rw-r--r--compiler/luci-interpreter/src/kernels/If.test.cpp141
-rw-r--r--compiler/luci-interpreter/src/kernels/InstanceNorm.cpp111
-rw-r--r--compiler/luci-interpreter/src/kernels/InstanceNorm.h49
-rw-r--r--compiler/luci-interpreter/src/kernels/InstanceNorm.test.cpp48
-rw-r--r--compiler/luci-interpreter/src/kernels/L2Normalize.cpp75
-rw-r--r--compiler/luci-interpreter/src/kernels/L2Normalize.h46
-rw-r--r--compiler/luci-interpreter/src/kernels/L2Normalize.test.cpp116
-rw-r--r--compiler/luci-interpreter/src/kernels/L2Pool2D.cpp88
-rw-r--r--compiler/luci-interpreter/src/kernels/L2Pool2D.h49
-rw-r--r--compiler/luci-interpreter/src/kernels/L2Pool2D.test.cpp265
-rw-r--r--compiler/luci-interpreter/src/kernels/LeakyRelu.cpp90
-rw-r--r--compiler/luci-interpreter/src/kernels/LeakyRelu.h53
-rw-r--r--compiler/luci-interpreter/src/kernels/LeakyRelu.test.cpp118
-rw-r--r--compiler/luci-interpreter/src/kernels/Less.cpp113
-rw-r--r--compiler/luci-interpreter/src/kernels/Less.h53
-rw-r--r--compiler/luci-interpreter/src/kernels/Less.test.cpp214
-rw-r--r--compiler/luci-interpreter/src/kernels/LessEqual.cpp113
-rw-r--r--compiler/luci-interpreter/src/kernels/LessEqual.h53
-rw-r--r--compiler/luci-interpreter/src/kernels/LessEqual.test.cpp214
-rw-r--r--compiler/luci-interpreter/src/kernels/LocalResponseNormalization.cpp65
-rw-r--r--compiler/luci-interpreter/src/kernels/LocalResponseNormalization.h44
-rw-r--r--compiler/luci-interpreter/src/kernels/LocalResponseNormalization.test.cpp144
-rw-r--r--compiler/luci-interpreter/src/kernels/LogSoftmax.cpp91
-rw-r--r--compiler/luci-interpreter/src/kernels/LogSoftmax.h48
-rw-r--r--compiler/luci-interpreter/src/kernels/LogSoftmax.test.cpp111
-rw-r--r--compiler/luci-interpreter/src/kernels/LogicalAnd.cpp62
-rw-r--r--compiler/luci-interpreter/src/kernels/LogicalAnd.h47
-rw-r--r--compiler/luci-interpreter/src/kernels/LogicalAnd.test.cpp83
-rw-r--r--compiler/luci-interpreter/src/kernels/LogicalNot.cpp60
-rw-r--r--compiler/luci-interpreter/src/kernels/LogicalNot.h46
-rw-r--r--compiler/luci-interpreter/src/kernels/LogicalNot.test.cpp65
-rw-r--r--compiler/luci-interpreter/src/kernels/LogicalOr.cpp51
-rw-r--r--compiler/luci-interpreter/src/kernels/LogicalOr.h44
-rw-r--r--compiler/luci-interpreter/src/kernels/LogicalOr.test.cpp86
-rw-r--r--compiler/luci-interpreter/src/kernels/Logistic.cpp94
-rw-r--r--compiler/luci-interpreter/src/kernels/Logistic.h52
-rw-r--r--compiler/luci-interpreter/src/kernels/Logistic.test.cpp134
-rw-r--r--compiler/luci-interpreter/src/kernels/MaxPool2D.cpp150
-rw-r--r--compiler/luci-interpreter/src/kernels/MaxPool2D.h52
-rw-r--r--compiler/luci-interpreter/src/kernels/MaxPool2D.test.cpp125
-rw-r--r--compiler/luci-interpreter/src/kernels/Maximum.cpp65
-rw-r--r--compiler/luci-interpreter/src/kernels/Maximum.h47
-rw-r--r--compiler/luci-interpreter/src/kernels/Maximum.test.cpp67
-rw-r--r--compiler/luci-interpreter/src/kernels/Mean.cpp332
-rw-r--r--compiler/luci-interpreter/src/kernels/Mean.h56
-rw-r--r--compiler/luci-interpreter/src/kernels/Mean.test.cpp179
-rw-r--r--compiler/luci-interpreter/src/kernels/Minimum.cpp65
-rw-r--r--compiler/luci-interpreter/src/kernels/Minimum.h47
-rw-r--r--compiler/luci-interpreter/src/kernels/Minimum.test.cpp67
-rw-r--r--compiler/luci-interpreter/src/kernels/Mul.cpp123
-rw-r--r--compiler/luci-interpreter/src/kernels/Mul.h51
-rw-r--r--compiler/luci-interpreter/src/kernels/Mul.test.cpp147
-rw-r--r--compiler/luci-interpreter/src/kernels/NotEqual.cpp113
-rw-r--r--compiler/luci-interpreter/src/kernels/NotEqual.h53
-rw-r--r--compiler/luci-interpreter/src/kernels/NotEqual.test.cpp187
-rw-r--r--compiler/luci-interpreter/src/kernels/Pad.cpp102
-rw-r--r--compiler/luci-interpreter/src/kernels/Pad.h43
-rw-r--r--compiler/luci-interpreter/src/kernels/Pad.test.cpp75
-rw-r--r--compiler/luci-interpreter/src/kernels/Pow.cpp79
-rw-r--r--compiler/luci-interpreter/src/kernels/Pow.h46
-rw-r--r--compiler/luci-interpreter/src/kernels/Pow.test.cpp122
-rw-r--r--compiler/luci-interpreter/src/kernels/Prelu.cpp153
-rw-r--r--compiler/luci-interpreter/src/kernels/Prelu.h54
-rw-r--r--compiler/luci-interpreter/src/kernels/Prelu.test.cpp246
-rw-r--r--compiler/luci-interpreter/src/kernels/Relu.cpp114
-rw-r--r--compiler/luci-interpreter/src/kernels/Relu.h51
-rw-r--r--compiler/luci-interpreter/src/kernels/Relu.test.cpp152
-rw-r--r--compiler/luci-interpreter/src/kernels/Relu6.cpp88
-rw-r--r--compiler/luci-interpreter/src/kernels/Relu6.h50
-rw-r--r--compiler/luci-interpreter/src/kernels/Relu6.test.cpp135
-rw-r--r--compiler/luci-interpreter/src/kernels/Reshape.cpp90
-rw-r--r--compiler/luci-interpreter/src/kernels/Reshape.h43
-rw-r--r--compiler/luci-interpreter/src/kernels/Reshape.test.cpp67
-rw-r--r--compiler/luci-interpreter/src/kernels/ResizeBilinear.cpp75
-rw-r--r--compiler/luci-interpreter/src/kernels/ResizeBilinear.h45
-rw-r--r--compiler/luci-interpreter/src/kernels/ResizeBilinear.test.cpp231
-rw-r--r--compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.cpp76
-rw-r--r--compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.h45
-rw-r--r--compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.test.cpp211
-rw-r--r--compiler/luci-interpreter/src/kernels/Reverse.cpp81
-rw-r--r--compiler/luci-interpreter/src/kernels/Reverse.h43
-rw-r--r--compiler/luci-interpreter/src/kernels/Reverse.test.cpp66
-rw-r--r--compiler/luci-interpreter/src/kernels/Rsqrt.cpp66
-rw-r--r--compiler/luci-interpreter/src/kernels/Rsqrt.h46
-rw-r--r--compiler/luci-interpreter/src/kernels/Rsqrt.test.cpp80
-rw-r--r--compiler/luci-interpreter/src/kernels/Slice.cpp149
-rw-r--r--compiler/luci-interpreter/src/kernels/Slice.h44
-rw-r--r--compiler/luci-interpreter/src/kernels/Slice.test.cpp64
-rw-r--r--compiler/luci-interpreter/src/kernels/Softmax.cpp90
-rw-r--r--compiler/luci-interpreter/src/kernels/Softmax.h49
-rw-r--r--compiler/luci-interpreter/src/kernels/Softmax.test.cpp102
-rw-r--r--compiler/luci-interpreter/src/kernels/SpaceToDepth.cpp79
-rw-r--r--compiler/luci-interpreter/src/kernels/SpaceToDepth.h45
-rw-r--r--compiler/luci-interpreter/src/kernels/SpaceToDepth.test.cpp60
-rw-r--r--compiler/luci-interpreter/src/kernels/Split.cpp81
-rw-r--r--compiler/luci-interpreter/src/kernels/Split.h47
-rw-r--r--compiler/luci-interpreter/src/kernels/Split.test.cpp117
-rw-r--r--compiler/luci-interpreter/src/kernels/Sqrt.cpp66
-rw-r--r--compiler/luci-interpreter/src/kernels/Sqrt.h46
-rw-r--r--compiler/luci-interpreter/src/kernels/Sqrt.test.cpp80
-rw-r--r--compiler/luci-interpreter/src/kernels/Squeeze.cpp86
-rw-r--r--compiler/luci-interpreter/src/kernels/Squeeze.h44
-rw-r--r--compiler/luci-interpreter/src/kernels/Squeeze.test.cpp69
-rw-r--r--compiler/luci-interpreter/src/kernels/StridedSlice.cpp145
-rw-r--r--compiler/luci-interpreter/src/kernels/StridedSlice.h47
-rw-r--r--compiler/luci-interpreter/src/kernels/StridedSlice.test.cpp99
-rw-r--r--compiler/luci-interpreter/src/kernels/Sub.cpp139
-rw-r--r--compiler/luci-interpreter/src/kernels/Sub.h48
-rw-r--r--compiler/luci-interpreter/src/kernels/Sub.test.cpp180
-rw-r--r--compiler/luci-interpreter/src/kernels/Tanh.cpp93
-rw-r--r--compiler/luci-interpreter/src/kernels/Tanh.h52
-rw-r--r--compiler/luci-interpreter/src/kernels/Tanh.test.cpp148
-rw-r--r--compiler/luci-interpreter/src/kernels/TestUtils.cpp123
-rw-r--r--compiler/luci-interpreter/src/kernels/TestUtils.h277
-rw-r--r--compiler/luci-interpreter/src/kernels/Transpose.cpp84
-rw-r--r--compiler/luci-interpreter/src/kernels/Transpose.h44
-rw-r--r--compiler/luci-interpreter/src/kernels/Transpose.test.cpp110
-rw-r--r--compiler/luci-interpreter/src/kernels/TransposeConv.cpp240
-rw-r--r--compiler/luci-interpreter/src/kernels/TransposeConv.h65
-rw-r--r--compiler/luci-interpreter/src/kernels/TransposeConv.test.cpp240
-rw-r--r--compiler/luci-interpreter/src/kernels/Unpack.cpp84
-rw-r--r--compiler/luci-interpreter/src/kernels/Unpack.h46
-rw-r--r--compiler/luci-interpreter/src/kernels/Unpack.test.cpp141
-rw-r--r--compiler/luci-interpreter/src/kernels/Utils.cpp185
-rw-r--r--compiler/luci-interpreter/src/kernels/Utils.h259
-rw-r--r--compiler/luci-interpreter/src/loader/CMakeLists.txt22
-rw-r--r--compiler/luci-interpreter/src/loader/GraphLoader.cpp201
-rw-r--r--compiler/luci-interpreter/src/loader/GraphLoader.h52
-rw-r--r--compiler/luci-interpreter/src/loader/KernelBuilder.cpp906
-rw-r--r--compiler/luci-interpreter/src/loader/KernelBuilder.h121
-rw-r--r--compiler/luci-interpreter/src/loader/KernelBuilder.test.cpp1227
-rw-r--r--compiler/luci-interpreter/src/loader/ModuleLoader.cpp52
-rw-r--r--compiler/luci-interpreter/src/loader/ModuleLoader.h49
-rw-r--r--compiler/luci-interpreter/src/loader/RuntimeToIR.h38
-rw-r--r--compiler/luci-value-test/CMakeLists.txt25
-rw-r--r--compiler/luci-value-test/README.md15
-rwxr-xr-xcompiler/luci-value-test/evalverify.sh63
-rwxr-xr-xcompiler/luci-value-test/luci_eval_verifier.py110
-rw-r--r--compiler/luci-value-test/requires.cmake6
-rw-r--r--compiler/luci-value-test/test.lst183
-rw-r--r--compiler/luci-value-test/tester/CMakeLists.txt13
-rw-r--r--compiler/luci-value-test/tester/src/EvalTester.cpp177
-rw-r--r--compiler/luci/CMakeLists.txt11
-rw-r--r--compiler/luci/README.md3
-rw-r--r--compiler/luci/env/CMakeLists.txt18
-rw-r--r--compiler/luci/env/README.md3
-rw-r--r--compiler/luci/env/include/luci/UserSettings.h45
-rw-r--r--compiler/luci/env/src/UserSettings.cpp77
-rw-r--r--compiler/luci/env/src/UserSettings.test.cpp68
-rw-r--r--compiler/luci/export/CMakeLists.txt30
-rw-r--r--compiler/luci/export/README.md3
-rw-r--r--compiler/luci/export/include/luci/CircleExporter.h64
-rw-r--r--compiler/luci/export/include/luci/CircleFileExpContract.h65
-rw-r--r--compiler/luci/export/src/Check.h35
-rw-r--r--compiler/luci/export/src/CircleExporter.cpp64
-rw-r--r--compiler/luci/export/src/CircleExporterImpl.cpp283
-rw-r--r--compiler/luci/export/src/CircleExporterImpl.h82
-rw-r--r--compiler/luci/export/src/CircleExporterUtils.cpp251
-rw-r--r--compiler/luci/export/src/CircleExporterUtils.h56
-rw-r--r--compiler/luci/export/src/CircleOperationExporter.cpp1464
-rw-r--r--compiler/luci/export/src/CircleOperationExporter.h37
-rw-r--r--compiler/luci/export/src/CircleTensorExporter.cpp502
-rw-r--r--compiler/luci/export/src/CircleTensorExporter.h44
-rw-r--r--compiler/luci/export/src/Optimize.cpp48
-rw-r--r--compiler/luci/export/src/Optimize.h33
-rw-r--r--compiler/luci/export/src/ProgressReporter.cpp84
-rw-r--r--compiler/luci/export/src/ProgressReporter.h53
-rw-r--r--compiler/luci/export/src/SerializedData.h114
-rw-r--r--compiler/luci/export/src/TypeBridge.cpp105
-rw-r--r--compiler/luci/export/src/TypeBridge.h44
-rw-r--r--compiler/luci/import/CMakeLists.txt27
-rw-r--r--compiler/luci/import/README.md3
-rw-r--r--compiler/luci/import/include/luci/Import/CircleReader.h102
-rw-r--r--compiler/luci/import/include/luci/Import/GraphBuilder.h46
-rw-r--r--compiler/luci/import/include/luci/Import/GraphBuilderBase.h48
-rw-r--r--compiler/luci/import/include/luci/Import/GraphBuilderContext.h98
-rw-r--r--compiler/luci/import/include/luci/Import/GraphBuilderRegistry.h85
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes.h134
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleAbs.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleAdd.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleAddN.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleArgMax.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleArgMin.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleAveragePool2D.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleBCQFullyConnected.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleBCQGather.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleBatchMatMul.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleBatchToSpaceND.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleCast.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleCeil.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleConcatenation.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleConst.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleConv2D.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleCos.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleCustom.h35
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleDepthToSpace.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleDepthwiseConv2D.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleDequantize.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleDiv.h36
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleElu.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleEqual.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleExp.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleExpandDims.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleFill.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleFloor.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleFloorDiv.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleFloorMod.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleFullyConnected.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleGather.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleGatherNd.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleGreater.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleGreaterEqual.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleIf.h35
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleInstanceNorm.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleL2Normalize.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleL2Pool2D.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleLeakyRelu.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleLess.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleLessEqual.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleLocalResponseNormalization.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleLog.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleLogSoftmax.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleLogicalAnd.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleLogicalNot.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleLogicalOr.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleLogistic.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleMatrixDiag.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleMatrixSetDiag.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleMaxPool2D.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleMaximum.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleMean.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleMinimum.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleMirrorPad.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleMul.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleNeg.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleNonMaxSuppressionV4.h35
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleNonMaxSuppressionV5.h35
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleNotEqual.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleOneHot.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CirclePRelu.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CirclePack.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CirclePad.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CirclePadV2.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CirclePow.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleRange.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleRank.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleReduceAny.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleReduceMax.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleReduceMin.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleReduceProd.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleRelu.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleRelu6.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleReluN1To1.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleReshape.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleResizeBilinear.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleResizeNearestNeighbor.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleReverseSequence.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleReverseV2.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleRound.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleRsqrt.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleScatterNd.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSegmentSum.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSelect.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSelectV2.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleShape.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSin.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSlice.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSoftmax.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSpaceToBatchND.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSpaceToDepth.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSparseToDense.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSplit.h35
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSplitV.h35
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSqrt.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSquare.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSquaredDifference.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSqueeze.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleStridedSlice.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSub.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSum.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleTanh.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleTile.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleTopKV2.h35
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleTranspose.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleTransposeConv.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleUnidirectionalSequenceLSTM.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleUnique.h35
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleUnpack.h35
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleWhere.h36
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleWhile.h35
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleZerosLike.h37
-rw-r--r--compiler/luci/import/include/luci/Importer.h54
-rw-r--r--compiler/luci/import/src/CircleReader.cpp333
-rw-r--r--compiler/luci/import/src/GraphBuilder.cpp78
-rw-r--r--compiler/luci/import/src/GraphBuilderContext.cpp62
-rw-r--r--compiler/luci/import/src/GraphBuilderRegistry.cpp166
-rw-r--r--compiler/luci/import/src/Importer.cpp294
-rw-r--r--compiler/luci/import/src/Importer.test.cpp28
-rw-r--r--compiler/luci/import/src/Nodes/CircleAbs.cpp44
-rw-r--r--compiler/luci/import/src/Nodes/CircleAdd.cpp48
-rw-r--r--compiler/luci/import/src/Nodes/CircleAddN.cpp50
-rw-r--r--compiler/luci/import/src/Nodes/CircleArgMax.cpp48
-rw-r--r--compiler/luci/import/src/Nodes/CircleArgMin.cpp48
-rw-r--r--compiler/luci/import/src/Nodes/CircleAveragePool2D.cpp50
-rw-r--r--compiler/luci/import/src/Nodes/CircleBCQFullyConnected.cpp62
-rw-r--r--compiler/luci/import/src/Nodes/CircleBCQGather.cpp52
-rw-r--r--compiler/luci/import/src/Nodes/CircleBatchMatMul.cpp47
-rw-r--r--compiler/luci/import/src/Nodes/CircleBatchToSpaceND.cpp47
-rw-r--r--compiler/luci/import/src/Nodes/CircleCast.cpp99
-rw-r--r--compiler/luci/import/src/Nodes/CircleCeil.cpp50
-rw-r--r--compiler/luci/import/src/Nodes/CircleConcatenation.cpp52
-rw-r--r--compiler/luci/import/src/Nodes/CircleConst.cpp153
-rw-r--r--compiler/luci/import/src/Nodes/CircleConv2D.cpp59
-rw-r--r--compiler/luci/import/src/Nodes/CircleCos.cpp46
-rw-r--r--compiler/luci/import/src/Nodes/CircleCustom.cpp88
-rw-r--r--compiler/luci/import/src/Nodes/CircleDepthToSpace.cpp67
-rw-r--r--compiler/luci/import/src/Nodes/CircleDepthwiseConv2D.cpp61
-rw-r--r--compiler/luci/import/src/Nodes/CircleDequantize.cpp46
-rw-r--r--compiler/luci/import/src/Nodes/CircleDiv.cpp49
-rw-r--r--compiler/luci/import/src/Nodes/CircleElu.cpp64
-rw-r--r--compiler/luci/import/src/Nodes/CircleEqual.cpp51
-rw-r--r--compiler/luci/import/src/Nodes/CircleExp.cpp59
-rw-r--r--compiler/luci/import/src/Nodes/CircleExpandDims.cpp51
-rw-r--r--compiler/luci/import/src/Nodes/CircleFill.cpp49
-rw-r--r--compiler/luci/import/src/Nodes/CircleFloor.cpp50
-rw-r--r--compiler/luci/import/src/Nodes/CircleFloorDiv.cpp68
-rw-r--r--compiler/luci/import/src/Nodes/CircleFloorMod.cpp57
-rw-r--r--compiler/luci/import/src/Nodes/CircleFullyConnected.cpp66
-rw-r--r--compiler/luci/import/src/Nodes/CircleGather.cpp68
-rw-r--r--compiler/luci/import/src/Nodes/CircleGatherNd.cpp64
-rw-r--r--compiler/luci/import/src/Nodes/CircleGreater.cpp76
-rw-r--r--compiler/luci/import/src/Nodes/CircleGreaterEqual.cpp62
-rw-r--r--compiler/luci/import/src/Nodes/CircleIf.cpp138
-rw-r--r--compiler/luci/import/src/Nodes/CircleInstanceNorm.cpp52
-rw-r--r--compiler/luci/import/src/Nodes/CircleL2Normalize.cpp56
-rw-r--r--compiler/luci/import/src/Nodes/CircleL2Pool2D.cpp54
-rw-r--r--compiler/luci/import/src/Nodes/CircleLeakyRelu.cpp50
-rw-r--r--compiler/luci/import/src/Nodes/CircleLess.cpp78
-rw-r--r--compiler/luci/import/src/Nodes/CircleLessEqual.cpp62
-rw-r--r--compiler/luci/import/src/Nodes/CircleLocalResponseNormalization.cpp51
-rw-r--r--compiler/luci/import/src/Nodes/CircleLog.cpp65
-rw-r--r--compiler/luci/import/src/Nodes/CircleLogSoftmax.cpp46
-rw-r--r--compiler/luci/import/src/Nodes/CircleLogicalAnd.cpp55
-rw-r--r--compiler/luci/import/src/Nodes/CircleLogicalNot.cpp51
-rw-r--r--compiler/luci/import/src/Nodes/CircleLogicalOr.cpp55
-rw-r--r--compiler/luci/import/src/Nodes/CircleLogistic.cpp52
-rw-r--r--compiler/luci/import/src/Nodes/CircleMatrixDiag.cpp56
-rw-r--r--compiler/luci/import/src/Nodes/CircleMatrixSetDiag.cpp57
-rw-r--r--compiler/luci/import/src/Nodes/CircleMaxPool2D.cpp52
-rw-r--r--compiler/luci/import/src/Nodes/CircleMaximum.cpp44
-rw-r--r--compiler/luci/import/src/Nodes/CircleMean.cpp46
-rw-r--r--compiler/luci/import/src/Nodes/CircleMinimum.cpp44
-rw-r--r--compiler/luci/import/src/Nodes/CircleMirrorPad.cpp50
-rw-r--r--compiler/luci/import/src/Nodes/CircleMul.cpp49
-rw-r--r--compiler/luci/import/src/Nodes/CircleNeg.cpp44
-rw-r--r--compiler/luci/import/src/Nodes/CircleNonMaxSuppressionV4.cpp123
-rw-r--r--compiler/luci/import/src/Nodes/CircleNonMaxSuppressionV5.cpp126
-rw-r--r--compiler/luci/import/src/Nodes/CircleNotEqual.cpp62
-rw-r--r--compiler/luci/import/src/Nodes/CircleOneHot.cpp77
-rw-r--r--compiler/luci/import/src/Nodes/CirclePRelu.cpp50
-rw-r--r--compiler/luci/import/src/Nodes/CirclePack.cpp61
-rw-r--r--compiler/luci/import/src/Nodes/CirclePad.cpp50
-rw-r--r--compiler/luci/import/src/Nodes/CirclePadV2.cpp52
-rw-r--r--compiler/luci/import/src/Nodes/CirclePow.cpp50
-rw-r--r--compiler/luci/import/src/Nodes/CircleRange.cpp46
-rw-r--r--compiler/luci/import/src/Nodes/CircleRank.cpp46
-rw-r--r--compiler/luci/import/src/Nodes/CircleReduceAny.cpp69
-rw-r--r--compiler/luci/import/src/Nodes/CircleReduceMax.cpp45
-rw-r--r--compiler/luci/import/src/Nodes/CircleReduceMin.cpp45
-rw-r--r--compiler/luci/import/src/Nodes/CircleReduceProd.cpp64
-rw-r--r--compiler/luci/import/src/Nodes/CircleRelu.cpp47
-rw-r--r--compiler/luci/import/src/Nodes/CircleRelu6.cpp47
-rw-r--r--compiler/luci/import/src/Nodes/CircleReluN1To1.cpp49
-rw-r--r--compiler/luci/import/src/Nodes/CircleReshape.cpp90
-rw-r--r--compiler/luci/import/src/Nodes/CircleResizeBilinear.cpp51
-rw-r--r--compiler/luci/import/src/Nodes/CircleResizeNearestNeighbor.cpp49
-rw-r--r--compiler/luci/import/src/Nodes/CircleReverseSequence.cpp71
-rw-r--r--compiler/luci/import/src/Nodes/CircleReverseV2.cpp67
-rw-r--r--compiler/luci/import/src/Nodes/CircleRound.cpp71
-rw-r--r--compiler/luci/import/src/Nodes/CircleRsqrt.cpp60
-rw-r--r--compiler/luci/import/src/Nodes/CircleScatterNd.cpp58
-rw-r--r--compiler/luci/import/src/Nodes/CircleSegmentSum.cpp68
-rw-r--r--compiler/luci/import/src/Nodes/CircleSelect.cpp56
-rw-r--r--compiler/luci/import/src/Nodes/CircleSelectV2.cpp60
-rw-r--r--compiler/luci/import/src/Nodes/CircleShape.cpp53
-rw-r--r--compiler/luci/import/src/Nodes/CircleSin.cpp63
-rw-r--r--compiler/luci/import/src/Nodes/CircleSlice.cpp52
-rw-r--r--compiler/luci/import/src/Nodes/CircleSoftmax.cpp49
-rw-r--r--compiler/luci/import/src/Nodes/CircleSpaceToBatchND.cpp47
-rw-r--r--compiler/luci/import/src/Nodes/CircleSpaceToDepth.cpp52
-rw-r--r--compiler/luci/import/src/Nodes/CircleSparseToDense.cpp51
-rw-r--r--compiler/luci/import/src/Nodes/CircleSplit.cpp119
-rw-r--r--compiler/luci/import/src/Nodes/CircleSplitV.cpp121
-rw-r--r--compiler/luci/import/src/Nodes/CircleSqrt.cpp44
-rw-r--r--compiler/luci/import/src/Nodes/CircleSquare.cpp63
-rw-r--r--compiler/luci/import/src/Nodes/CircleSquaredDifference.cpp77
-rw-r--r--compiler/luci/import/src/Nodes/CircleSqueeze.cpp51
-rw-r--r--compiler/luci/import/src/Nodes/CircleStridedSlice.cpp60
-rw-r--r--compiler/luci/import/src/Nodes/CircleSub.cpp51
-rw-r--r--compiler/luci/import/src/Nodes/CircleSum.cpp46
-rw-r--r--compiler/luci/import/src/Nodes/CircleTanh.cpp52
-rw-r--r--compiler/luci/import/src/Nodes/CircleTile.cpp68
-rw-r--r--compiler/luci/import/src/Nodes/CircleTopKV2.cpp117
-rw-r--r--compiler/luci/import/src/Nodes/CircleTranspose.cpp51
-rw-r--r--compiler/luci/import/src/Nodes/CircleTransposeConv.cpp83
-rw-r--r--compiler/luci/import/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp83
-rw-r--r--compiler/luci/import/src/Nodes/CircleUnique.cpp89
-rw-r--r--compiler/luci/import/src/Nodes/CircleUnpack.cpp151
-rw-r--r--compiler/luci/import/src/Nodes/CircleWhere.cpp60
-rw-r--r--compiler/luci/import/src/Nodes/CircleWhile.cpp123
-rw-r--r--compiler/luci/import/src/Nodes/CircleZerosLike.cpp49
-rw-r--r--compiler/luci/import/src/PostImport.cpp354
-rw-r--r--compiler/luci/import/src/PostImport.h34
-rw-r--r--compiler/luci/import/src/ValidateHelpers.cpp122
-rw-r--r--compiler/luci/import/src/ValidateHelpers.h35
-rw-r--r--compiler/luci/lang/CMakeLists.txt24
-rw-r--r--compiler/luci/lang/README.md3
-rw-r--r--compiler/luci/lang/include/luci/IR/AttrDilation.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/AttrFilter.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/AttrFusedActFunc.h38
-rw-r--r--compiler/luci/lang/include/luci/IR/AttrMirrorPadMode.h33
-rw-r--r--compiler/luci/lang/include/luci/IR/AttrPadding.h33
-rw-r--r--compiler/luci/lang/include/luci/IR/AttrStride.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleDialect.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleNode.h23
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleNodeDecl.h93
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleNodeImpl.h69
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleNodeVisitor.forward.h30
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleNodeVisitor.h87
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleNodes.h176
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleNodes.lst142
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleOpcode.h32
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleQuantParam.h37
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleShapeSignature.h51
-rw-r--r--compiler/luci/lang/include/luci/IR/LuciNodeMixins.h107
-rw-r--r--compiler/luci/lang/include/luci/IR/Module.h70
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleAbs.h40
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleAdd.h45
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleAddN.h46
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleArgMax.h50
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleArgMin.h50
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleAveragePool2D.h63
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleBCQFullyConnected.h66
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleBCQGather.h60
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleBatchMatMul.h54
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleBatchToSpaceND.h47
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleCast.h51
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleCeil.h40
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleConcatenation.h72
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleConst.h54
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleConv2D.h67
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleCos.h40
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleCustom.h61
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleCustomOut.h51
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleDepthToSpace.h48
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleDepthwiseConv2D.h73
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleDequantize.h40
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleDiv.h51
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleElu.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleEqual.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleExp.h40
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleExpandDims.h46
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleFill.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleFloor.h40
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleFloorDiv.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleFloorMod.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleFullyConnected.h50
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleGather.h50
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleGatherNd.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleGreater.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleGreaterEqual.h44
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleIf.h79
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleIfOut.h50
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleInput.h52
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleInstanceNorm.h56
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleL2Normalize.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleL2Pool2D.h62
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleLeakyRelu.h49
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleLess.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleLessEqual.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleLocalResponseNormalization.h60
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleLog.h40
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleLogSoftmax.h40
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalAnd.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalNot.h40
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalOr.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleLogistic.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleMatrixDiag.h40
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleMatrixSetDiag.h44
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleMaxPool2D.h62
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleMaximum.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleMean.h50
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleMinimum.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleMirrorPad.h54
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleMul.h45
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleNeg.h40
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV4.h53
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV4Out.h51
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV5.h56
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV5Out.h51
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleNotEqual.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleOneHot.h56
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleOutput.h75
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CirclePRelu.h46
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CirclePack.h67
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CirclePad.h46
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CirclePadV2.h49
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CirclePow.h46
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleRange.h46
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleRank.h40
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleReduceAny.h50
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleReduceMax.h50
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleReduceMin.h50
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleReduceProd.h50
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleRelu.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleRelu6.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleReluN1To1.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleReshape.h68
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleResizeBilinear.h57
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleResizeNearestNeighbor.h53
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleReverseSequence.h58
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleReverseV2.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleRound.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleRsqrt.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleScatterNd.h46
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSegmentSum.h46
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSelect.h49
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSelectV2.h49
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleShape.h50
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSin.h40
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSlice.h46
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSoftmax.h47
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSpaceToBatchND.h47
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSpaceToDepth.h48
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSparseToDense.h57
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSplit.h51
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSplitOut.h50
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSplitV.h54
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSplitVOut.h51
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSqrt.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSquare.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSquaredDifference.h47
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSqueeze.h50
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleStridedSlice.h73
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSub.h48
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSum.h50
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleTanh.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleTile.h46
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleTopKV2.h46
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleTopKV2Out.h51
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleTranspose.h49
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h79
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleUnidirectionalSequenceLSTM.h115
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleUnique.h47
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleUniqueOut.h51
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleUnpack.h54
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleUnpackOut.h51
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleWhere.h45
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleWhile.h79
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleWhileOut.h50
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleZerosLike.h46
-rw-r--r--compiler/luci/lang/include/luci/IR/PropertyShapeStatus.h38
-rw-r--r--compiler/luci/lang/include/luci/IR/SparsityParam.h233
-rw-r--r--compiler/luci/lang/include/luci/IR/VariadicArityNode.h69
-rw-r--r--compiler/luci/lang/src/Check.h35
-rw-r--r--compiler/luci/lang/src/CircleDialect.cpp89
-rw-r--r--compiler/luci/lang/src/CircleDialect.test.cpp87
-rw-r--r--compiler/luci/lang/src/CircleNode.cpp25
-rw-r--r--compiler/luci/lang/src/CircleNodeShapeDtype.test.cpp52
-rw-r--r--compiler/luci/lang/src/CircleNodes.cpp85
-rw-r--r--compiler/luci/lang/src/DeadNodeQueryService.cpp74
-rw-r--r--compiler/luci/lang/src/DeadNodeQueryService.h34
-rw-r--r--compiler/luci/lang/src/LuciNodeMixins.cpp18
-rw-r--r--compiler/luci/lang/src/Module.cpp46
-rw-r--r--compiler/luci/lang/src/Module.test.cpp73
-rw-r--r--compiler/luci/lang/src/Nodes/CircleAbs.test.cpp94
-rw-r--r--compiler/luci/lang/src/Nodes/CircleAdd.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleAddN.test.cpp91
-rw-r--r--compiler/luci/lang/src/Nodes/CircleArgMax.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleArgMin.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleAveragePool2D.test.cpp90
-rw-r--r--compiler/luci/lang/src/Nodes/CircleBCQFullyConnected.test.cpp38
-rw-r--r--compiler/luci/lang/src/Nodes/CircleBCQGather.test.cpp37
-rw-r--r--compiler/luci/lang/src/Nodes/CircleBatchMatMul.test.cpp84
-rw-r--r--compiler/luci/lang/src/Nodes/CircleBatchToSpaceND.test.cpp86
-rw-r--r--compiler/luci/lang/src/Nodes/CircleCast.test.cpp78
-rw-r--r--compiler/luci/lang/src/Nodes/CircleCeil.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleConcatenation.test.cpp84
-rw-r--r--compiler/luci/lang/src/Nodes/CircleConst.cpp83
-rw-r--r--compiler/luci/lang/src/Nodes/CircleConv2D.test.cpp108
-rw-r--r--compiler/luci/lang/src/Nodes/CircleCos.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleCustom.test.cpp50
-rw-r--r--compiler/luci/lang/src/Nodes/CircleCustomOut.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleDepthToSpace.test.cpp80
-rw-r--r--compiler/luci/lang/src/Nodes/CircleDepthwiseConv2D.test.cpp109
-rw-r--r--compiler/luci/lang/src/Nodes/CircleDequantize.test.cpp94
-rw-r--r--compiler/luci/lang/src/Nodes/CircleDiv.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleElu.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleEqual.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleExp.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleExpandDims.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleFill.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleFloor.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleFloorDiv.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleFloorMod.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleFullyConnected.test.cpp90
-rw-r--r--compiler/luci/lang/src/Nodes/CircleGather.test.cpp85
-rw-r--r--compiler/luci/lang/src/Nodes/CircleGatherNd.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleGreater.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleGreaterEqual.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleIf.test.cpp91
-rw-r--r--compiler/luci/lang/src/Nodes/CircleIfOut.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleInput.cpp38
-rw-r--r--compiler/luci/lang/src/Nodes/CircleInstanceNorm.test.cpp91
-rw-r--r--compiler/luci/lang/src/Nodes/CircleL2Pool2D.test.cpp94
-rw-r--r--compiler/luci/lang/src/Nodes/CircleLeakyRelu.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleLess.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleLessEqual.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleLocalResponseNormalization.test.cpp90
-rw-r--r--compiler/luci/lang/src/Nodes/CircleLog.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleLogSoftmax.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleLogicalAnd.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleLogicalNot.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleLogicalOr.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleLogistic.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleMatrixDiag.test.cpp78
-rw-r--r--compiler/luci/lang/src/Nodes/CircleMatrixSetDiag.test.cpp84
-rw-r--r--compiler/luci/lang/src/Nodes/CircleMaxPool2D.test.cpp90
-rw-r--r--compiler/luci/lang/src/Nodes/CircleMaximum.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleMean.test.cpp86
-rw-r--r--compiler/luci/lang/src/Nodes/CircleMinimum.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleMirrorPad.test.cpp86
-rw-r--r--compiler/luci/lang/src/Nodes/CircleMul.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleNeg.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleNonMaxSuppressionV4.test.cpp96
-rw-r--r--compiler/luci/lang/src/Nodes/CircleNonMaxSuppressionV4Out.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleNonMaxSuppressionV5.test.cpp101
-rw-r--r--compiler/luci/lang/src/Nodes/CircleNonMaxSuppressionV5Out.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleNotEqual.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleOneHot.test.cpp95
-rw-r--r--compiler/luci/lang/src/Nodes/CircleOutput.cpp38
-rw-r--r--compiler/luci/lang/src/Nodes/CirclePRelu.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CirclePack.test.cpp84
-rw-r--r--compiler/luci/lang/src/Nodes/CirclePad.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CirclePadV2.test.cpp86
-rw-r--r--compiler/luci/lang/src/Nodes/CirclePow.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleRange.test.cpp86
-rw-r--r--compiler/luci/lang/src/Nodes/CircleRank.test.cpp31
-rw-r--r--compiler/luci/lang/src/Nodes/CircleReduceAny.test.cpp86
-rw-r--r--compiler/luci/lang/src/Nodes/CircleReduceMax.test.cpp86
-rw-r--r--compiler/luci/lang/src/Nodes/CircleReduceMin.test.cpp86
-rw-r--r--compiler/luci/lang/src/Nodes/CircleReduceProd.test.cpp86
-rw-r--r--compiler/luci/lang/src/Nodes/CircleRelu.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleRelu6.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleReluN1To1.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleReshape.test.cpp97
-rw-r--r--compiler/luci/lang/src/Nodes/CircleResizeBilinear.test.cpp88
-rw-r--r--compiler/luci/lang/src/Nodes/CircleResizeNearestNeighbor.test.cpp85
-rw-r--r--compiler/luci/lang/src/Nodes/CircleReverseSequence.test.cpp35
-rw-r--r--compiler/luci/lang/src/Nodes/CircleReverseV2.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleRound.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleRsqrt.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleScatterNd.test.cpp86
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSegmentSum.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSelect.test.cpp86
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSelectV2.test.cpp86
-rw-r--r--compiler/luci/lang/src/Nodes/CircleShape.test.cpp80
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSin.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSlice.test.cpp86
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSoftmax.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSpaceToBatchND.test.cpp86
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSpaceToDepth.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSparseToDense.test.cpp93
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSplit.test.cpp85
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSplitOut.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSplitV.test.cpp90
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSplitVOut.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSqrt.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSquare.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSquaredDifference.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSqueeze.test.cpp87
-rw-r--r--compiler/luci/lang/src/Nodes/CircleStridedSlice.test.cpp108
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSub.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSum.test.cpp85
-rw-r--r--compiler/luci/lang/src/Nodes/CircleTanh.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleTile.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleTopKV2.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleTopKV2Out.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleTranspose.test.cpp81
-rw-r--r--compiler/luci/lang/src/Nodes/CircleTransposeConv.test.cpp98
-rw-r--r--compiler/luci/lang/src/Nodes/CircleUnidirectionalSequenceLSTM.test.cpp100
-rw-r--r--compiler/luci/lang/src/Nodes/CircleUnique.test.cpp76
-rw-r--r--compiler/luci/lang/src/Nodes/CircleUnpack.test.cpp83
-rw-r--r--compiler/luci/lang/src/Nodes/CircleUnpackOut.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleWhere.test.cpp33
-rw-r--r--compiler/luci/lang/src/Nodes/CircleWhile.test.cpp91
-rw-r--r--compiler/luci/lang/src/Nodes/CircleWhileOut.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleZerosLike.test.cpp76
-rw-r--r--compiler/luci/log/CMakeLists.txt10
-rw-r--r--compiler/luci/log/README.md3
-rw-r--r--compiler/luci/log/include/luci/Log.h79
-rw-r--r--compiler/luci/log/include/luci/LoggingContext.h35
-rw-r--r--compiler/luci/log/src/Log.cpp111
-rw-r--r--compiler/luci/log/src/LoggingContext.cpp41
-rw-r--r--compiler/luci/logex/CMakeLists.txt13
-rw-r--r--compiler/luci/logex/README.md3
-rw-r--r--compiler/luci/logex/include/luci/FormattedGraph.h56
-rw-r--r--compiler/luci/logex/include/luci/LogHelper.h36
-rw-r--r--compiler/luci/logex/src/FormattedGraph.cpp1891
-rw-r--r--compiler/luci/logex/src/LogHelper.cpp29
-rw-r--r--compiler/luci/pass/CMakeLists.txt29
-rw-r--r--compiler/luci/pass/README.md3
-rw-r--r--compiler/luci/pass/include/luci/CircleOptimizer.h92
-rw-r--r--compiler/luci/pass/include/luci/Pass/FoldDequantizePass.h38
-rw-r--r--compiler/luci/pass/include/luci/Pass/FuseActivationFunctionPass.h37
-rw-r--r--compiler/luci/pass/include/luci/Pass/FuseAddWithTConvPass.h37
-rw-r--r--compiler/luci/pass/include/luci/Pass/FuseBCQPass.h38
-rw-r--r--compiler/luci/pass/include/luci/Pass/FuseBatchNormWithTConv.h37
-rw-r--r--compiler/luci/pass/include/luci/Pass/FuseInstanceNormPass.h40
-rw-r--r--compiler/luci/pass/include/luci/Pass/FusePreActivationBatchNormPass.h42
-rw-r--r--compiler/luci/pass/include/luci/Pass/MakeBatchNormGammaPositivePass.h39
-rw-r--r--compiler/luci/pass/include/luci/Pass/QuantizationParameters.h31
-rw-r--r--compiler/luci/pass/include/luci/Pass/QuantizeDequantizeWeightsPass.h54
-rw-r--r--compiler/luci/pass/include/luci/Pass/QuantizeWithMinMaxPass.h54
-rw-r--r--compiler/luci/pass/include/luci/Pass/RequantizePass.h52
-rw-r--r--compiler/luci/pass/include/luci/Pass/ResolveCustomOpAddPass.h37
-rw-r--r--compiler/luci/pass/include/luci/Pass/ResolveCustomOpBatchMatMulPass.h37
-rw-r--r--compiler/luci/pass/include/luci/Pass/ResolveCustomOpMatMulPass.h37
-rw-r--r--compiler/luci/pass/include/luci/Pass/ShapeInferencePass.h41
-rw-r--r--compiler/luci/pass/include/luci/Pass/SparsifyTensorPass.h69
-rw-r--r--compiler/luci/pass/include/luci/Pass/TypeInferencePass.h42
-rw-r--r--compiler/luci/pass/src/CircleOptimizer.cpp333
-rw-r--r--compiler/luci/pass/src/CircleOptimizerUtils.cpp89
-rw-r--r--compiler/luci/pass/src/CircleOptimizerUtils.h42
-rw-r--r--compiler/luci/pass/src/FoldDequantizePass.cpp206
-rw-r--r--compiler/luci/pass/src/FuseActivationFunctionPass.cpp104
-rw-r--r--compiler/luci/pass/src/FuseActivationFunctionPass.test.cpp100
-rw-r--r--compiler/luci/pass/src/FuseActivationFunctionPassInternal.h31
-rw-r--r--compiler/luci/pass/src/FuseAddWithTConvPass.cpp120
-rw-r--r--compiler/luci/pass/src/FuseBCQPass.cpp506
-rw-r--r--compiler/luci/pass/src/FuseBatchNormWithTConv.cpp159
-rw-r--r--compiler/luci/pass/src/FuseInstanceNormPass.cpp727
-rw-r--r--compiler/luci/pass/src/FuseInstanceNormPass.test.cpp64
-rw-r--r--compiler/luci/pass/src/FuseInstanceNormPassInternal.h28
-rw-r--r--compiler/luci/pass/src/FusePreActivationBatchNormPass.cpp681
-rw-r--r--compiler/luci/pass/src/FusePreActivationBatchNormPass.test.cpp381
-rw-r--r--compiler/luci/pass/src/FusePreActivationBatchNormPassInternal.h44
-rw-r--r--compiler/luci/pass/src/MakeBatchNormGammaPositivePass.cpp130
-rw-r--r--compiler/luci/pass/src/ProgressReporter.cpp84
-rw-r--r--compiler/luci/pass/src/ProgressReporter.h53
-rw-r--r--compiler/luci/pass/src/PropagateConcatenationQparam.test.cpp372
-rw-r--r--compiler/luci/pass/src/QuantizationUtils.cpp312
-rw-r--r--compiler/luci/pass/src/QuantizationUtils.h52
-rw-r--r--compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp426
-rw-r--r--compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp855
-rw-r--r--compiler/luci/pass/src/RequantizePass.cpp243
-rw-r--r--compiler/luci/pass/src/ResolveCustomOpAddPass.cpp124
-rw-r--r--compiler/luci/pass/src/ResolveCustomOpBatchMatMulPass.cpp69
-rw-r--r--compiler/luci/pass/src/ResolveCustomOpMatMulPass.cpp185
-rw-r--r--compiler/luci/pass/src/ShapeInferencePass.cpp44
-rw-r--r--compiler/luci/pass/src/Sparsifier.cpp229
-rw-r--r--compiler/luci/pass/src/Sparsifier.h87
-rw-r--r--compiler/luci/pass/src/Sparsifier.test.cpp195
-rw-r--r--compiler/luci/pass/src/SparsifyTensorPass.cpp123
-rw-r--r--compiler/luci/pass/src/TypeInferencePass.cpp42
-rw-r--r--compiler/luci/requires.cmake11
-rw-r--r--compiler/luci/service/CMakeLists.txt25
-rw-r--r--compiler/luci/service/README.md3
-rw-r--r--compiler/luci/service/include/luci/Service/CircleShapeInference.h41
-rw-r--r--compiler/luci/service/include/luci/Service/CircleShapeInferenceRule.h33
-rw-r--r--compiler/luci/service/include/luci/Service/CircleShapeSignatureInferenceRule.h173
-rw-r--r--compiler/luci/service/include/luci/Service/CircleTypeInference.h42
-rw-r--r--compiler/luci/service/include/luci/Service/CircleTypeInferenceRule.h36
-rw-r--r--compiler/luci/service/include/luci/Service/ShapeDescription.h59
-rw-r--r--compiler/luci/service/include/luci/Service/Validate.h29
-rw-r--r--compiler/luci/service/src/Check.h35
-rw-r--r--compiler/luci/service/src/CircleShapeInference.cpp34
-rw-r--r--compiler/luci/service/src/CircleShapeInferenceRule.cpp2516
-rw-r--r--compiler/luci/service/src/CircleShapeInferenceRule.test.cpp626
-rw-r--r--compiler/luci/service/src/CircleShapeSignatureInferenceRule.cpp60
-rw-r--r--compiler/luci/service/src/CircleTypeInference.cpp72
-rw-r--r--compiler/luci/service/src/CircleTypeInferenceRule.cpp707
-rw-r--r--compiler/luci/service/src/CircleTypeInferenceRule.test.cpp63
-rw-r--r--compiler/luci/service/src/ShapeDescription.cpp139
-rw-r--r--compiler/luci/service/src/ShapeInfer_StridedSlice.cpp298
-rw-r--r--compiler/luci/service/src/ShapeInfer_StridedSlice.h31
-rw-r--r--compiler/luci/service/src/TestGraph.h215
-rw-r--r--compiler/luci/service/src/TestGraph.test.cpp101
-rw-r--r--compiler/luci/service/src/Validate.cpp127
-rw-r--r--compiler/luci/tester/CMakeLists.txt30
-rw-r--r--compiler/luci/tester/src/ReadTester.cpp103
-rw-r--r--compiler/luci/tester/src/WriteTester.cpp153
-rw-r--r--compiler/luci/tests/.gitignore (renamed from compiler/mocotest-tf/.gitignore)0
-rw-r--r--compiler/luci/tests/CMakeLists.txt126
-rwxr-xr-xcompiler/luci/tests/readverify.sh56
-rw-r--r--compiler/luci/tests/test.lst432
-rwxr-xr-xcompiler/luci/tests/writeverify.sh53
-rw-r--r--compiler/mio-circle/CMakeLists.txt28
-rw-r--r--compiler/mio-circle/README.md3
-rw-r--r--compiler/mio-circle/example.cpp41
-rw-r--r--compiler/mio-tf/CMakeLists.txt48
-rw-r--r--compiler/mio-tf/README.md3
-rw-r--r--compiler/mio-tf/src/mio_tf.test.cpp27
-rw-r--r--compiler/mio-tflite/CMakeLists.txt38
-rw-r--r--compiler/mio-tflite/README.md3
-rw-r--r--compiler/mio-tflite/example.cpp41
-rw-r--r--compiler/mir-caffe-importer/CMakeLists.txt17
-rw-r--r--compiler/mir-caffe-importer/caffe_importer.cpp349
-rw-r--r--compiler/mir-caffe-importer/caffe_importer.h91
-rw-r--r--compiler/mir-caffe-importer/caffe_op_creator.cpp910
-rw-r--r--compiler/mir-caffe-importer/caffe_op_creator.h148
-rw-r--r--compiler/mir-caffe2-importer/CMakeLists.txt29
-rw-r--r--compiler/mir-caffe2-importer/caffe2_importer.cpp274
-rw-r--r--compiler/mir-caffe2-importer/caffe2_importer.h86
-rw-r--r--compiler/mir-caffe2-importer/caffe2_op_creator.cpp633
-rw-r--r--compiler/mir-caffe2-importer/caffe2_proto_helper.cpp62
-rw-r--r--compiler/mir-caffe2-importer/caffe2_proto_helper.h40
-rw-r--r--compiler/mir-caffe2-importer/requires.cmake1
-rw-r--r--compiler/mir-interpreter/CMakeLists.txt4
-rw-r--r--compiler/mir-interpreter/README.md1
-rw-r--r--compiler/mir-interpreter/include/MirInterpreter.h101
-rw-r--r--compiler/mir-interpreter/requires.cmake (renamed from compiler/mir-caffe-importer/requires.cmake)0
-rw-r--r--compiler/mir-interpreter/src/MirInterpreter.cpp420
-rw-r--r--compiler/mir-interpreter/src/ops/Abs.cpp55
-rw-r--r--compiler/mir-interpreter/src/ops/Abs.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Add.cpp130
-rw-r--r--compiler/mir-interpreter/src/ops/Add.h29
-rw-r--r--compiler/mir-interpreter/src/ops/AvgPool2D.cpp173
-rw-r--r--compiler/mir-interpreter/src/ops/AvgPool2D.h31
-rw-r--r--compiler/mir-interpreter/src/ops/CappedReLU.cpp82
-rw-r--r--compiler/mir-interpreter/src/ops/CappedReLU.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Common.cpp37
-rw-r--r--compiler/mir-interpreter/src/ops/Common.h65
-rw-r--r--compiler/mir-interpreter/src/ops/Concat.cpp172
-rw-r--r--compiler/mir-interpreter/src/ops/Concat.h30
-rw-r--r--compiler/mir-interpreter/src/ops/Conv2D.cpp261
-rw-r--r--compiler/mir-interpreter/src/ops/Conv2D.h32
-rw-r--r--compiler/mir-interpreter/src/ops/DeConv2D.cpp122
-rw-r--r--compiler/mir-interpreter/src/ops/DeConv2D.h41
-rw-r--r--compiler/mir-interpreter/src/ops/DepthwiseConv2D.cpp225
-rw-r--r--compiler/mir-interpreter/src/ops/DepthwiseConv2D.h32
-rw-r--r--compiler/mir-interpreter/src/ops/Div.cpp62
-rw-r--r--compiler/mir-interpreter/src/ops/Div.h29
-rw-r--r--compiler/mir-interpreter/src/ops/ELU.cpp51
-rw-r--r--compiler/mir-interpreter/src/ops/ELU.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Equal.cpp58
-rw-r--r--compiler/mir-interpreter/src/ops/Equal.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Fill.h48
-rw-r--r--compiler/mir-interpreter/src/ops/FullyConnected.cpp214
-rw-r--r--compiler/mir-interpreter/src/ops/FullyConnected.h32
-rw-r--r--compiler/mir-interpreter/src/ops/Gather.cpp92
-rw-r--r--compiler/mir-interpreter/src/ops/Gather.h31
-rw-r--r--compiler/mir-interpreter/src/ops/Greater.cpp57
-rw-r--r--compiler/mir-interpreter/src/ops/Greater.h29
-rw-r--r--compiler/mir-interpreter/src/ops/HardSwish.cpp54
-rw-r--r--compiler/mir-interpreter/src/ops/HardSwish.h29
-rw-r--r--compiler/mir-interpreter/src/ops/LeakyReLU.cpp49
-rw-r--r--compiler/mir-interpreter/src/ops/LeakyReLU.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Less.cpp57
-rw-r--r--compiler/mir-interpreter/src/ops/Less.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Max.cpp66
-rw-r--r--compiler/mir-interpreter/src/ops/Max.h29
-rw-r--r--compiler/mir-interpreter/src/ops/MaxPool2D.cpp157
-rw-r--r--compiler/mir-interpreter/src/ops/MaxPool2D.h31
-rw-r--r--compiler/mir-interpreter/src/ops/Mul.cpp61
-rw-r--r--compiler/mir-interpreter/src/ops/Mul.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Pad.cpp84
-rw-r--r--compiler/mir-interpreter/src/ops/Pad.h36
-rw-r--r--compiler/mir-interpreter/src/ops/Quantization.cpp71
-rw-r--r--compiler/mir-interpreter/src/ops/Quantization.h32
-rw-r--r--compiler/mir-interpreter/src/ops/QuantizationHelpers.h126
-rw-r--r--compiler/mir-interpreter/src/ops/ReLU.cpp58
-rw-r--r--compiler/mir-interpreter/src/ops/ReLU.h29
-rw-r--r--compiler/mir-interpreter/src/ops/ReduceMean.cpp98
-rw-r--r--compiler/mir-interpreter/src/ops/ReduceMean.h30
-rw-r--r--compiler/mir-interpreter/src/ops/Reshape.cpp38
-rw-r--r--compiler/mir-interpreter/src/ops/Reshape.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Sigmoid.cpp58
-rw-r--r--compiler/mir-interpreter/src/ops/Sigmoid.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Slice.cpp52
-rw-r--r--compiler/mir-interpreter/src/ops/Slice.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Softmax.cpp155
-rw-r--r--compiler/mir-interpreter/src/ops/Softmax.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Sqrt.cpp58
-rw-r--r--compiler/mir-interpreter/src/ops/Sqrt.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Sub.cpp61
-rw-r--r--compiler/mir-interpreter/src/ops/Sub.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Tanh.cpp58
-rw-r--r--compiler/mir-interpreter/src/ops/Tanh.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Transpose.cpp64
-rw-r--r--compiler/mir-interpreter/src/ops/Transpose.h31
-rw-r--r--compiler/mir-onnx-importer/AttributeHelpers.h105
-rw-r--r--compiler/mir-onnx-importer/CMakeLists.txt90
-rw-r--r--compiler/mir-onnx-importer/ConvPoolHelpers.cpp74
-rw-r--r--compiler/mir-onnx-importer/ConvPoolHelpers.h38
-rw-r--r--compiler/mir-onnx-importer/ONNXHelpers.cpp130
-rw-r--r--compiler/mir-onnx-importer/ONNXHelpers.h40
-rw-r--r--compiler/mir-onnx-importer/ONNXImporterImpl.cpp186
-rw-r--r--compiler/mir-onnx-importer/ONNXImporterImpl.h52
-rw-r--r--compiler/mir-onnx-importer/ONNXNodeConverterRegistry.h150
-rw-r--r--compiler/mir-onnx-importer/ONNXOpRegistration.h89
-rw-r--r--compiler/mir-onnx-importer/Op/Add.cpp66
-rw-r--r--compiler/mir-onnx-importer/Op/Add.h38
-rw-r--r--compiler/mir-onnx-importer/Op/AveragePool.cpp111
-rw-r--r--compiler/mir-onnx-importer/Op/AveragePool.h38
-rw-r--r--compiler/mir-onnx-importer/Op/BatchNormalization.cpp135
-rw-r--r--compiler/mir-onnx-importer/Op/BatchNormalization.h39
-rw-r--r--compiler/mir-onnx-importer/Op/Concat.cpp67
-rw-r--r--compiler/mir-onnx-importer/Op/Concat.h37
-rw-r--r--compiler/mir-onnx-importer/Op/Constant.cpp78
-rw-r--r--compiler/mir-onnx-importer/Op/Constant.h38
-rw-r--r--compiler/mir-onnx-importer/Op/Conv.cpp138
-rw-r--r--compiler/mir-onnx-importer/Op/Conv.h36
-rw-r--r--compiler/mir-onnx-importer/Op/Dropout.cpp74
-rw-r--r--compiler/mir-onnx-importer/Op/Dropout.h39
-rw-r--r--compiler/mir-onnx-importer/Op/Flatten.cpp72
-rw-r--r--compiler/mir-onnx-importer/Op/Flatten.h37
-rw-r--r--compiler/mir-onnx-importer/Op/Gather.cpp40
-rw-r--r--compiler/mir-onnx-importer/Op/Gather.h33
-rw-r--r--compiler/mir-onnx-importer/Op/Gemm.cpp95
-rw-r--r--compiler/mir-onnx-importer/Op/Gemm.h33
-rw-r--r--compiler/mir-onnx-importer/Op/GlobalAveragePool.cpp53
-rw-r--r--compiler/mir-onnx-importer/Op/GlobalAveragePool.h33
-rw-r--r--compiler/mir-onnx-importer/Op/Identity.cpp42
-rw-r--r--compiler/mir-onnx-importer/Op/Identity.h36
-rw-r--r--compiler/mir-onnx-importer/Op/MatMul.cpp63
-rw-r--r--compiler/mir-onnx-importer/Op/MatMul.h37
-rw-r--r--compiler/mir-onnx-importer/Op/Max.cpp35
-rw-r--r--compiler/mir-onnx-importer/Op/Max.h33
-rw-r--r--compiler/mir-onnx-importer/Op/MaxPool.cpp120
-rw-r--r--compiler/mir-onnx-importer/Op/MaxPool.h38
-rw-r--r--compiler/mir-onnx-importer/Op/Mul.cpp35
-rw-r--r--compiler/mir-onnx-importer/Op/Mul.h33
-rw-r--r--compiler/mir-onnx-importer/Op/Pad.cpp72
-rw-r--r--compiler/mir-onnx-importer/Op/Pad.h37
-rw-r--r--compiler/mir-onnx-importer/Op/ReduceMean.cpp70
-rw-r--r--compiler/mir-onnx-importer/Op/ReduceMean.h36
-rw-r--r--compiler/mir-onnx-importer/Op/Relu.cpp36
-rw-r--r--compiler/mir-onnx-importer/Op/Relu.h33
-rw-r--r--compiler/mir-onnx-importer/Op/Reshape.cpp111
-rw-r--r--compiler/mir-onnx-importer/Op/Reshape.h37
-rw-r--r--compiler/mir-onnx-importer/Op/Shape.cpp46
-rw-r--r--compiler/mir-onnx-importer/Op/Shape.h33
-rw-r--r--compiler/mir-onnx-importer/Op/Sigmoid.cpp37
-rw-r--r--compiler/mir-onnx-importer/Op/Sigmoid.h33
-rw-r--r--compiler/mir-onnx-importer/Op/Softmax.cpp41
-rw-r--r--compiler/mir-onnx-importer/Op/Softmax.h33
-rw-r--r--compiler/mir-onnx-importer/Op/Sum.cpp41
-rw-r--r--compiler/mir-onnx-importer/Op/Sum.h33
-rw-r--r--compiler/mir-onnx-importer/Op/Transpose.cpp69
-rw-r--r--compiler/mir-onnx-importer/Op/Transpose.h36
-rw-r--r--compiler/mir-onnx-importer/Op/Unsqueeze.cpp57
-rw-r--r--compiler/mir-onnx-importer/Op/Unsqueeze.h33
-rw-r--r--compiler/mir-onnx-importer/Op/Upsample.cpp149
-rw-r--r--compiler/mir-onnx-importer/Op/Upsample.h39
-rw-r--r--compiler/mir-onnx-importer/requires.cmake1
-rw-r--r--compiler/mir-tflite-importer/CMakeLists.txt22
-rw-r--r--compiler/mir-tflite-importer/requires.cmake1
-rw-r--r--compiler/mir-tflite-importer/schema/schema.fbs794
-rw-r--r--compiler/mir-tflite-importer/schema/schema.meta2
-rw-r--r--compiler/mir-tflite-importer/tflite_importer.cpp367
-rw-r--r--compiler/mir-tflite-importer/tflite_importer.h98
-rw-r--r--compiler/mir-tflite-importer/tflite_op_creator.cpp676
-rw-r--r--compiler/mir-tflite-importer/tflite_op_creator.h156
-rw-r--r--compiler/mir/CMakeLists.txt6
-rw-r--r--compiler/mir/Readme.md2
-rw-r--r--compiler/mir/include/mir/Attributes.h83
-rw-r--r--compiler/mir/include/mir/Common.h9
-rw-r--r--compiler/mir/include/mir/DataType.h26
-rw-r--r--compiler/mir/include/mir/Graph.h20
-rw-r--r--compiler/mir/include/mir/OpDefs.h8
-rw-r--r--compiler/mir/include/mir/Operation.h114
-rw-r--r--compiler/mir/include/mir/Operations.inc8
-rw-r--r--compiler/mir/include/mir/Quantization.h47
-rw-r--r--compiler/mir/include/mir/Shape.h2
-rw-r--r--compiler/mir/include/mir/Tensor.h12
-rw-r--r--compiler/mir/include/mir/TensorType.h56
-rw-r--r--compiler/mir/include/mir/TensorUtil.h15
-rw-r--r--compiler/mir/include/mir/TensorVariant.h39
-rw-r--r--compiler/mir/include/mir/ops/AbsOp.h45
-rw-r--r--compiler/mir/include/mir/ops/AvgPool2DOp.h42
-rw-r--r--compiler/mir/include/mir/ops/BinaryElementwiseOp.h14
-rw-r--r--compiler/mir/include/mir/ops/BroadcastOp.h47
-rw-r--r--compiler/mir/include/mir/ops/CappedReluOp.h2
-rw-r--r--compiler/mir/include/mir/ops/ConcatOp.h4
-rw-r--r--compiler/mir/include/mir/ops/ConstantOp.h2
-rw-r--r--compiler/mir/include/mir/ops/Conv2DOp.h42
-rw-r--r--compiler/mir/include/mir/ops/Deconv2DOp.h48
-rw-r--r--compiler/mir/include/mir/ops/DepthwiseConv2DOp.h41
-rw-r--r--compiler/mir/include/mir/ops/DequantizeOp.h46
-rw-r--r--compiler/mir/include/mir/ops/EluOp.h2
-rw-r--r--compiler/mir/include/mir/ops/EqualOp.h44
-rw-r--r--compiler/mir/include/mir/ops/FullyConnectedOp.h18
-rw-r--r--compiler/mir/include/mir/ops/GatherOp.h4
-rw-r--r--compiler/mir/include/mir/ops/GreaterOp.h44
-rw-r--r--compiler/mir/include/mir/ops/HardSwishOp.h44
-rw-r--r--compiler/mir/include/mir/ops/InputOp.h8
-rw-r--r--compiler/mir/include/mir/ops/LeakyReluOp.h2
-rw-r--r--compiler/mir/include/mir/ops/LessOp.h44
-rw-r--r--compiler/mir/include/mir/ops/MaxPool2DOp.h35
-rw-r--r--compiler/mir/include/mir/ops/PadOp.h29
-rw-r--r--compiler/mir/include/mir/ops/QuantizeOp.h44
-rw-r--r--compiler/mir/include/mir/ops/ReduceOp.h4
-rw-r--r--compiler/mir/include/mir/ops/ReluOp.h2
-rw-r--r--compiler/mir/include/mir/ops/ReshapeOp.h2
-rw-r--r--compiler/mir/include/mir/ops/ResizeOp.h4
-rw-r--r--compiler/mir/include/mir/ops/SigmoidOp.h2
-rw-r--r--compiler/mir/include/mir/ops/SliceOp.h4
-rw-r--r--compiler/mir/include/mir/ops/SoftmaxOp.h2
-rw-r--r--compiler/mir/include/mir/ops/SqrtOp.h2
-rw-r--r--compiler/mir/include/mir/ops/SqueezeOp.h4
-rw-r--r--compiler/mir/include/mir/ops/TanhOp.h2
-rw-r--r--compiler/mir/include/mir/ops/TransposeOp.h2
-rw-r--r--compiler/mir/include/mir_caffe2_importer/caffe2_importer.h34
-rw-r--r--compiler/mir/include/mir_caffe_importer/caffe_importer.h35
-rw-r--r--compiler/mir/include/mir_onnx_importer/ONNXImporterImpl.h35
-rw-r--r--compiler/mir/include/mir_tflite_importer/tflite_importer.h32
-rw-r--r--compiler/mir/src/DotNodeBuilder.cpp2
-rw-r--r--compiler/mir/src/Graph.cpp104
-rw-r--r--compiler/mir/src/GraphPatternMatcher.cpp17
-rw-r--r--compiler/mir/src/IrDotDumper.cpp4
-rw-r--r--compiler/mir/src/Operation.cpp21
-rw-r--r--compiler/mir/src/Shape.cpp25
-rw-r--r--compiler/mir/src/TensorVariant.cpp50
-rw-r--r--compiler/mir/src/mir_caffe2_importer/CMakeLists.txt28
-rw-r--r--compiler/mir/src/mir_caffe2_importer/caffe2_importer.cpp343
-rw-r--r--compiler/mir/src/mir_caffe2_importer/caffe2_op_creator.cpp551
-rw-r--r--compiler/mir/src/mir_caffe2_importer/caffe2_op_creator.h (renamed from compiler/mir-caffe2-importer/caffe2_op_creator.h)0
-rw-r--r--compiler/mir/src/mir_caffe2_importer/caffe2_op_types.h (renamed from compiler/mir-caffe2-importer/caffe2_op_types.h)0
-rw-r--r--compiler/mir/src/mir_caffe2_importer/caffe2_proto_helper.cpp62
-rw-r--r--compiler/mir/src/mir_caffe2_importer/caffe2_proto_helper.h40
-rw-r--r--compiler/mir/src/mir_caffe_importer/CMakeLists.txt16
-rw-r--r--compiler/mir/src/mir_caffe_importer/caffe_importer.cpp439
-rw-r--r--compiler/mir/src/mir_caffe_importer/caffe_op_creator.cpp835
-rw-r--r--compiler/mir/src/mir_caffe_importer/caffe_op_creator.h146
-rw-r--r--compiler/mir/src/mir_caffe_importer/caffe_op_types.h (renamed from compiler/mir-caffe-importer/caffe_op_types.h)0
-rw-r--r--compiler/mir/src/mir_onnx_importer/AttributeHelpers.h105
-rw-r--r--compiler/mir/src/mir_onnx_importer/CMakeLists.txt119
-rw-r--r--compiler/mir/src/mir_onnx_importer/ConvPoolHelpers.cpp113
-rw-r--r--compiler/mir/src/mir_onnx_importer/ConvPoolHelpers.h44
-rw-r--r--compiler/mir/src/mir_onnx_importer/ONNXHelpers.cpp188
-rw-r--r--compiler/mir/src/mir_onnx_importer/ONNXHelpers.h50
-rw-r--r--compiler/mir/src/mir_onnx_importer/ONNXImporterImpl.cpp240
-rw-r--r--compiler/mir/src/mir_onnx_importer/ONNXNodeConverterRegistry.cpp142
-rw-r--r--compiler/mir/src/mir_onnx_importer/ONNXNodeConverterRegistry.h80
-rw-r--r--compiler/mir/src/mir_onnx_importer/ONNXNodeConverterRegistry.test.cpp64
-rw-r--r--compiler/mir/src/mir_onnx_importer/ONNXOpRegistration.h256
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Abs.cpp47
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Abs.h30
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Add.cpp53
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Add.h31
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/AveragePool.cpp99
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/AveragePool.h31
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/BatchNormalization.cpp119
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/BatchNormalization.h32
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Concat.cpp54
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Concat.h30
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Constant.cpp61
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Constant.h31
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Conv.cpp156
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Conv.h29
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/ConvTranspose.cpp138
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/ConvTranspose.h29
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Div.cpp38
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Div.h29
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Dropout.cpp54
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Dropout.h32
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Equal.cpp43
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Equal.h30
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Expand.cpp43
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Expand.h29
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Flatten.cpp58
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Flatten.h30
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Gather.cpp40
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Gather.h29
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Gemm.cpp120
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Gemm.h33
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/GlobalAveragePool.cpp50
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/GlobalAveragePool.h29
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Greater.cpp47
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Greater.h30
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Identity.cpp30
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Identity.h29
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Less.cpp47
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Less.h30
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/MatMul.cpp50
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/MatMul.h30
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Max.cpp54
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Max.h31
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/MaxPool.cpp107
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/MaxPool.h31
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Mul.cpp35
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Mul.h29
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Pad.cpp70
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Pad.h30
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Reciprocal.cpp53
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Reciprocal.h30
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/ReduceMean.cpp60
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/ReduceMean.h29
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Relu.cpp46
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Relu.h30
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Reshape.cpp97
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Reshape.h30
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Shape.cpp46
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Shape.h29
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Sigmoid.cpp46
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Sigmoid.h30
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Softmax.cpp40
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Softmax.h29
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Sqrt.cpp46
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Sqrt.h30
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Sub.cpp53
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Sub.h31
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Sum.cpp41
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Sum.h29
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Tanh.cpp46
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Tanh.h30
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Transpose.cpp57
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Transpose.h29
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Unsqueeze.cpp56
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Unsqueeze.h29
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Upsample.cpp127
-rw-r--r--compiler/mir/src/mir_onnx_importer/Op/Upsample.h31
-rw-r--r--compiler/mir/src/mir_tflite_importer/CMakeLists.txt21
-rw-r--r--compiler/mir/src/mir_tflite_importer/schema/schema.fbs937
-rw-r--r--compiler/mir/src/mir_tflite_importer/schema/schema.meta2
-rw-r--r--compiler/mir/src/mir_tflite_importer/schema/schema_v0.fbs (renamed from compiler/mir-tflite-importer/schema/schema_v0.fbs)0
-rw-r--r--compiler/mir/src/mir_tflite_importer/schema/schema_v0.meta (renamed from compiler/mir-tflite-importer/schema/schema_v0.meta)0
-rw-r--r--compiler/mir/src/mir_tflite_importer/schema/schema_v1.fbs (renamed from compiler/mir-tflite-importer/schema/schema_v1.fbs)0
-rw-r--r--compiler/mir/src/mir_tflite_importer/schema/schema_v1.meta (renamed from compiler/mir-tflite-importer/schema/schema_v1.meta)0
-rw-r--r--compiler/mir/src/mir_tflite_importer/schema/schema_v2.fbs (renamed from compiler/mir-tflite-importer/schema/schema_v2.fbs)0
-rw-r--r--compiler/mir/src/mir_tflite_importer/schema/schema_v2.meta (renamed from compiler/mir-tflite-importer/schema/schema_v2.meta)0
-rw-r--r--compiler/mir/src/mir_tflite_importer/schema/schema_v3.fbs (renamed from compiler/mir-tflite-importer/schema/schema_v3.fbs)0
-rw-r--r--compiler/mir/src/mir_tflite_importer/schema/schema_v3.meta (renamed from compiler/mir-tflite-importer/schema/schema_v3.meta)0
-rw-r--r--compiler/mir/src/mir_tflite_importer/tflite_importer.cpp428
-rw-r--r--compiler/mir/src/mir_tflite_importer/tflite_op_creator.cpp652
-rw-r--r--compiler/mir/src/mir_tflite_importer/tflite_op_creator.h163
-rw-r--r--compiler/mir/src/ops/AvgPool2DOp.cpp26
-rw-r--r--compiler/mir/src/ops/BinaryElementwiseOp.cpp48
-rw-r--r--compiler/mir/src/ops/BroadcastOp.cpp35
-rw-r--r--compiler/mir/src/ops/ConcatOp.cpp10
-rw-r--r--compiler/mir/src/ops/Conv2DOp.cpp41
-rw-r--r--compiler/mir/src/ops/DeConv2DOp.cpp37
-rw-r--r--compiler/mir/src/ops/DepthwiseConv2DOp.cpp23
-rw-r--r--compiler/mir/src/ops/FullyConnectedOp.cpp5
-rw-r--r--compiler/mir/src/ops/GatherOp.cpp4
-rw-r--r--compiler/mir/src/ops/MaxPool2DOp.cpp26
-rw-r--r--compiler/mir/src/ops/PadOp.cpp7
-rw-r--r--compiler/mir/src/ops/ReduceOp.cpp4
-rw-r--r--compiler/mir/src/ops/SliceOp.cpp6
-rw-r--r--compiler/mir/src/ops/SqueezeOp.cpp7
-rw-r--r--compiler/mir/src/ops/TransposeOp.cpp7
-rw-r--r--compiler/mir/unittests/CMakeLists.txt5
-rw-r--r--compiler/mir/unittests/Graph.cpp84
-rw-r--r--compiler/mir/unittests/NodeReplacer.cpp3
-rw-r--r--compiler/mir/unittests/Operation.cpp14
-rw-r--r--compiler/mir/unittests/ShapeInference.cpp51
-rw-r--r--compiler/mir2loco/CMakeLists.txt3
-rw-r--r--compiler/mir2loco/README.md1
-rw-r--r--compiler/mir2loco/include/mir2loco.h7
-rw-r--r--compiler/mir2loco/src/mir2loco.cpp669
-rw-r--r--compiler/mir2loco/src/mir2loco.test.cpp462
-rw-r--r--compiler/moco-log/CMakeLists.txt1
-rw-r--r--compiler/moco-onnx/CMakeLists.txt36
-rw-r--r--compiler/moco-tf/CMakeLists.txt26
-rw-r--r--compiler/moco-tf/include/moco/tf/Frontend.h66
-rw-r--r--compiler/moco-tf/include/moco/tf/Names.h93
-rw-r--r--compiler/moco-tf/proto/CMakeLists.txt22
-rw-r--r--compiler/moco-tf/requires.cmake5
-rw-r--r--compiler/moco-tf/src/Annotations/ConcatData.h44
-rw-r--r--compiler/moco-tf/src/Annotations/PadData.h51
-rw-r--r--compiler/moco-tf/src/Annotations/PaddingData.h49
-rw-r--r--compiler/moco-tf/src/Annotations/ShapeInferenceData.cpp264
-rw-r--r--compiler/moco-tf/src/Annotations/ShapeInferenceData.h75
-rw-r--r--compiler/moco-tf/src/Annotations/ShapeInferenceData.test.cpp174
-rw-r--r--compiler/moco-tf/src/Annotations/StrideData.h48
-rw-r--r--compiler/moco-tf/src/Annotations/WindowData.h46
-rw-r--r--compiler/moco-tf/src/CanonicalEltwiseInputConnector.cpp1
-rw-r--r--compiler/moco-tf/src/Canonicalization/AddCanonicalizer.cpp24
-rw-r--r--compiler/moco-tf/src/Canonicalization/AddCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.cpp116
-rw-r--r--compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.cpp29
-rw-r--r--compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.cpp86
-rw-r--r--compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.cpp59
-rw-r--r--compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.cpp371
-rw-r--r--compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.h45
-rw-r--r--compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.cpp110
-rw-r--r--compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp105
-rw-r--r--compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.cpp29
-rw-r--r--compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.cpp114
-rw-r--r--compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.cpp34
-rw-r--r--compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.cpp31
-rw-r--r--compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/MulCanonicalizer.cpp23
-rw-r--r--compiler/moco-tf/src/Canonicalization/MulCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/PadCanonicalizer.cpp100
-rw-r--r--compiler/moco-tf/src/Canonicalization/PadCanonicalizer.h45
-rw-r--r--compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.cpp102
-rw-r--r--compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.cpp23
-rw-r--r--compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.cpp27
-rw-r--r--compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.cpp27
-rw-r--r--compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.cpp47
-rw-r--r--compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.cpp62
-rw-r--r--compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.cpp37
-rw-r--r--compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.cpp27
-rw-r--r--compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/SquaredDifferenceCanonicalizer.cpp115
-rw-r--r--compiler/moco-tf/src/Canonicalization/SquaredDifferenceCanonicalizer.h42
-rw-r--r--compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.cpp36
-rw-r--r--compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.cpp27
-rw-r--r--compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/SubCanonicalizer.cpp23
-rw-r--r--compiler/moco-tf/src/Canonicalization/SubCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.cpp74
-rw-r--r--compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.cpp27
-rw-r--r--compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.h7
-rw-r--r--compiler/moco-tf/src/Canonicalizer.cpp23
-rw-r--r--compiler/moco-tf/src/CodecHelper.h74
-rw-r--r--compiler/moco-tf/src/Dialect/TFDialect.cpp31
-rw-r--r--compiler/moco-tf/src/Dialect/TFDialect.h46
-rw-r--r--compiler/moco-tf/src/Dialect/TFDialect.test.cpp29
-rw-r--r--compiler/moco-tf/src/Dialect/TFNode.cpp28
-rw-r--r--compiler/moco-tf/src/Dialect/TFNode.h23
-rw-r--r--compiler/moco-tf/src/Dialect/TFNodeDecl.h96
-rw-r--r--compiler/moco-tf/src/Dialect/TFNodeImpl.h67
-rw-r--r--compiler/moco-tf/src/Dialect/TFNodeVisitor.forward.h33
-rw-r--r--compiler/moco-tf/src/Dialect/TFNodeVisitor.h82
-rw-r--r--compiler/moco-tf/src/Dialect/TFNodes.h46
-rw-r--r--compiler/moco-tf/src/Dialect/TFNodes.lst34
-rw-r--r--compiler/moco-tf/src/Dialect/TFOpcode.h38
-rw-r--r--compiler/moco-tf/src/Dialect/TFShapeInferenceRule.cpp58
-rw-r--r--compiler/moco-tf/src/Dialect/TFShapeInferenceRule.h39
-rw-r--r--compiler/moco-tf/src/Dialect/TFTypeInferenceRule.cpp101
-rw-r--r--compiler/moco-tf/src/Dialect/TFTypeInferenceRule.h40
-rw-r--r--compiler/moco-tf/src/Dialect/VariadicArityNode.h80
-rw-r--r--compiler/moco-tf/src/Dialect/VariadicArityNode.test.cpp55
-rw-r--r--compiler/moco-tf/src/Frontend.cpp184
-rw-r--r--compiler/moco-tf/src/Frontend.test.cpp7
-rw-r--r--compiler/moco-tf/src/GraphBuilder.h43
-rw-r--r--compiler/moco-tf/src/GraphBuilderContext.cpp82
-rw-r--r--compiler/moco-tf/src/GraphBuilderContext.h147
-rw-r--r--compiler/moco-tf/src/GraphBuilderContext.test.cpp75
-rw-r--r--compiler/moco-tf/src/GraphBuilderRegistry.h102
-rw-r--r--compiler/moco-tf/src/IR/TFAdd.h59
-rw-r--r--compiler/moco-tf/src/IR/TFAdd.test.cpp32
-rw-r--r--compiler/moco-tf/src/IR/TFAvgPool.h104
-rw-r--r--compiler/moco-tf/src/IR/TFAvgPool.test.cpp35
-rw-r--r--compiler/moco-tf/src/IR/TFBiasAdd.h71
-rw-r--r--compiler/moco-tf/src/IR/TFBiasAdd.test.cpp33
-rw-r--r--compiler/moco-tf/src/IR/TFConcatV2.h94
-rw-r--r--compiler/moco-tf/src/IR/TFConcatV2.test.cpp35
-rw-r--r--compiler/moco-tf/src/IR/TFConst.cpp66
-rw-r--r--compiler/moco-tf/src/IR/TFConst.h86
-rw-r--r--compiler/moco-tf/src/IR/TFConst.test.cpp65
-rw-r--r--compiler/moco-tf/src/IR/TFConv2D.h58
-rw-r--r--compiler/moco-tf/src/IR/TFConv2D.test.cpp35
-rw-r--r--compiler/moco-tf/src/IR/TFConv2DBackpropInput.h105
-rw-r--r--compiler/moco-tf/src/IR/TFDepthwiseConv2dNative.h65
-rw-r--r--compiler/moco-tf/src/IR/TFDepthwiseConv2dNative.test.cpp35
-rw-r--r--compiler/moco-tf/src/IR/TFFusedBatchNorm.h58
-rw-r--r--compiler/moco-tf/src/IR/TFFusedBatchNorm.test.cpp36
-rw-r--r--compiler/moco-tf/src/IR/TFIdentity.h55
-rw-r--r--compiler/moco-tf/src/IR/TFIdentity.test.cpp31
-rw-r--r--compiler/moco-tf/src/IR/TFMaxPool.h104
-rw-r--r--compiler/moco-tf/src/IR/TFMaxPool.test.cpp35
-rw-r--r--compiler/moco-tf/src/IR/TFMean.h52
-rw-r--r--compiler/moco-tf/src/IR/TFMean.test.cpp33
-rw-r--r--compiler/moco-tf/src/IR/TFMul.h59
-rw-r--r--compiler/moco-tf/src/IR/TFMul.test.cpp32
-rw-r--r--compiler/moco-tf/src/IR/TFRealDiv.h59
-rw-r--r--compiler/moco-tf/src/IR/TFRealDiv.test.cpp32
-rw-r--r--compiler/moco-tf/src/IR/TFRelu.h40
-rw-r--r--compiler/moco-tf/src/IR/TFRelu.test.cpp31
-rw-r--r--compiler/moco-tf/src/IR/TFRelu6.h40
-rw-r--r--compiler/moco-tf/src/IR/TFRelu6.test.cpp31
-rw-r--r--compiler/moco-tf/src/IR/TFReshape.h57
-rw-r--r--compiler/moco-tf/src/IR/TFReshape.test.cpp32
-rw-r--r--compiler/moco-tf/src/IR/TFRsqrt.h55
-rw-r--r--compiler/moco-tf/src/IR/TFRsqrt.test.cpp31
-rw-r--r--compiler/moco-tf/src/IR/TFShape.h63
-rw-r--r--compiler/moco-tf/src/IR/TFShape.test.cpp32
-rw-r--r--compiler/moco-tf/src/IR/TFSoftmax.h40
-rw-r--r--compiler/moco-tf/src/IR/TFSoftmax.test.cpp31
-rw-r--r--compiler/moco-tf/src/IR/TFSqrt.h55
-rw-r--r--compiler/moco-tf/src/IR/TFSqrt.test.cpp31
-rw-r--r--compiler/moco-tf/src/IR/TFSquaredDifference.h59
-rw-r--r--compiler/moco-tf/src/IR/TFSquaredDifference.test.cpp32
-rw-r--r--compiler/moco-tf/src/IR/TFSqueeze.h74
-rw-r--r--compiler/moco-tf/src/IR/TFSqueeze.test.cpp32
-rw-r--r--compiler/moco-tf/src/IR/TFStopGradient.h55
-rw-r--r--compiler/moco-tf/src/IR/TFStopGradient.test.cpp31
-rw-r--r--compiler/moco-tf/src/IR/TFSub.h59
-rw-r--r--compiler/moco-tf/src/IR/TFSub.test.cpp32
-rw-r--r--compiler/moco-tf/src/IR/TFTanh.h40
-rw-r--r--compiler/moco-tf/src/IR/TFTanh.test.cpp31
-rw-r--r--compiler/moco-tf/src/ImportTarget.h26
-rw-r--r--compiler/moco-tf/src/Importer.cpp290
-rw-r--r--compiler/moco-tf/src/Importer.h57
-rw-r--r--compiler/moco-tf/src/Importer.test.cpp148
-rw-r--r--compiler/moco-tf/src/Knob.lst23
-rw-r--r--compiler/moco-tf/src/LogHelper.cpp16
-rw-r--r--compiler/moco-tf/src/LogHelper.h15
-rw-r--r--compiler/moco-tf/src/Op/Add.cpp107
-rw-r--r--compiler/moco-tf/src/Op/Add.test.cpp136
-rw-r--r--compiler/moco-tf/src/Op/AvgPool.cpp325
-rw-r--r--compiler/moco-tf/src/Op/AvgPool.h52
-rw-r--r--compiler/moco-tf/src/Op/AvgPool.test.cpp211
-rw-r--r--compiler/moco-tf/src/Op/BiasAdd.cpp240
-rw-r--r--compiler/moco-tf/src/Op/BiasAdd.h52
-rw-r--r--compiler/moco-tf/src/Op/BiasAdd.test.cpp301
-rw-r--r--compiler/moco-tf/src/Op/COpCall.cpp19
-rw-r--r--compiler/moco-tf/src/Op/COpCall.h5
-rw-r--r--compiler/moco-tf/src/Op/COpCall.test.cpp14
-rw-r--r--compiler/moco-tf/src/Op/Concat.cpp276
-rw-r--r--compiler/moco-tf/src/Op/Concat.h52
-rw-r--r--compiler/moco-tf/src/Op/Concat.test.cpp449
-rw-r--r--compiler/moco-tf/src/Op/Const.cpp359
-rw-r--r--compiler/moco-tf/src/Op/Const.h52
-rw-r--r--compiler/moco-tf/src/Op/Const.test.cpp464
-rw-r--r--compiler/moco-tf/src/Op/Conv2D.cpp322
-rw-r--r--compiler/moco-tf/src/Op/Conv2D.h52
-rw-r--r--compiler/moco-tf/src/Op/Conv2D.test.cpp513
-rw-r--r--compiler/moco-tf/src/Op/DepthwiseConv2dNative.cpp155
-rw-r--r--compiler/moco-tf/src/Op/DepthwiseConv2dNative.test.cpp219
-rw-r--r--compiler/moco-tf/src/Op/FusedBatchNorm.cpp121
-rw-r--r--compiler/moco-tf/src/Op/FusedBatchNorm.test.cpp223
-rw-r--r--compiler/moco-tf/src/Op/Identity.cpp185
-rw-r--r--compiler/moco-tf/src/Op/Identity.h52
-rw-r--r--compiler/moco-tf/src/Op/MaxPool.cpp297
-rw-r--r--compiler/moco-tf/src/Op/MaxPool.h52
-rw-r--r--compiler/moco-tf/src/Op/MaxPool.test.cpp299
-rw-r--r--compiler/moco-tf/src/Op/Mul.cpp107
-rw-r--r--compiler/moco-tf/src/Op/Mul.test.cpp136
-rw-r--r--compiler/moco-tf/src/Op/Placeholder.cpp100
-rw-r--r--compiler/moco-tf/src/Op/Placeholder.test.cpp88
-rw-r--r--compiler/moco-tf/src/Op/RealDiv.cpp109
-rw-r--r--compiler/moco-tf/src/Op/RealDiv.test.cpp136
-rw-r--r--compiler/moco-tf/src/Op/Relu.cpp159
-rw-r--r--compiler/moco-tf/src/Op/Relu.h51
-rw-r--r--compiler/moco-tf/src/Op/Relu.test.cpp133
-rw-r--r--compiler/moco-tf/src/Op/Relu6.cpp149
-rw-r--r--compiler/moco-tf/src/Op/Relu6.h53
-rw-r--r--compiler/moco-tf/src/Op/Relu6.test.cpp133
-rw-r--r--compiler/moco-tf/src/Op/Reshape.cpp119
-rw-r--r--compiler/moco-tf/src/Op/Reshape.test.cpp108
-rw-r--r--compiler/moco-tf/src/Op/Rsqrt.cpp103
-rw-r--r--compiler/moco-tf/src/Op/Rsqrt.test.cpp103
-rw-r--r--compiler/moco-tf/src/Op/Shape.cpp118
-rw-r--r--compiler/moco-tf/src/Op/Shape.test.cpp94
-rw-r--r--compiler/moco-tf/src/Op/Softmax.cpp104
-rw-r--r--compiler/moco-tf/src/Op/Softmax.test.cpp94
-rw-r--r--compiler/moco-tf/src/Op/Sqrt.cpp102
-rw-r--r--compiler/moco-tf/src/Op/Sqrt.test.cpp103
-rw-r--r--compiler/moco-tf/src/Op/SquaredDifference.cpp114
-rw-r--r--compiler/moco-tf/src/Op/SquaredDifference.test.cpp136
-rw-r--r--compiler/moco-tf/src/Op/Squeeze.cpp121
-rw-r--r--compiler/moco-tf/src/Op/Squeeze.test.cpp162
-rw-r--r--compiler/moco-tf/src/Op/StopGradient.cpp105
-rw-r--r--compiler/moco-tf/src/Op/StopGradient.test.cpp100
-rw-r--r--compiler/moco-tf/src/Op/Sub.cpp107
-rw-r--r--compiler/moco-tf/src/Op/Sub.test.cpp136
-rw-r--r--compiler/moco-tf/src/Op/Tanh.cpp102
-rw-r--r--compiler/moco-tf/src/Op/Tanh.test.cpp103
-rw-r--r--compiler/moco-tf/src/Phase.cpp107
-rw-r--r--compiler/moco-tf/src/Phase.h78
-rw-r--r--compiler/moco-tf/src/SimpleNodeTransform.h64
-rw-r--r--compiler/moco-tf/src/SimpleNodeTransform.test.cpp56
-rw-r--r--compiler/moco-tf/src/TFEltwiseBinaryCanonicalzeHelper.h60
-rw-r--r--compiler/moco-tf/src/TFFormattedGraph.cpp93
-rw-r--r--compiler/moco-tf/src/TFOptimizer.cpp22
-rw-r--r--compiler/moco-tf/src/TFReduceCanonicalzeHelper.h118
-rw-r--r--compiler/moco-tf/src/TestHelper.cpp47
-rw-r--r--compiler/moco-tf/src/TestHelper.h39
-rw-r--r--compiler/moco-tf/src/TestHelper.test.cpp121
-rw-r--r--compiler/moco-tf/src/Transforms.h8
-rw-r--r--compiler/moco-tf/src/Transforms/ClearAnnotTransform.cpp63
-rw-r--r--compiler/moco-tf/src/Transforms/ClearAnnotTransform.h44
-rw-r--r--compiler/moco-tf/src/Transforms/ClearAnnotTransform.test.cpp29
-rw-r--r--compiler/moco-tf/src/Transforms/FixShapeTransform.cpp1539
-rw-r--r--compiler/moco-tf/src/Transforms/FixShapeTransform.h44
-rw-r--r--compiler/moco-tf/src/Transforms/FixShapeTransform.test.cpp227
-rw-r--r--compiler/moco-tf/src/Transforms/FuseBinaryIntoPreceding.cpp547
-rw-r--r--compiler/moco-tf/src/Transforms/FuseBinaryIntoPreceding.h44
-rw-r--r--compiler/moco-tf/src/Transforms/RemoveTFIdentityNodeTransform.cpp67
-rw-r--r--compiler/moco-tf/src/Transforms/RemoveTFIdentityNodeTransform.h50
-rw-r--r--compiler/moco-tf/src/Transforms/ResolveConstantShape.cpp126
-rw-r--r--compiler/moco-tf/src/Transforms/ResolveConstantShape.h44
-rw-r--r--compiler/moco-tf/src/Transforms/ResolveFusedBatchNorm.cpp259
-rw-r--r--compiler/moco-tf/src/Transforms/ResolveFusedBatchNorm.h44
-rw-r--r--compiler/moco-tf/src/Transforms/ResolveFusedBatchNorm.test.cpp232
-rw-r--r--compiler/moco-tf/src/Transforms/ResolveReshapeWildcardDim.cpp157
-rw-r--r--compiler/moco-tf/src/Transforms/ResolveReshapeWildcardDim.h45
-rw-r--r--compiler/moco-tf/src/Transforms/ShapeInferencePass.cpp7
-rw-r--r--compiler/moco-tf/src/Transforms/TypeInferencePass.cpp7
-rw-r--r--compiler/moco-value-pbtxt-test/.gitignore1
-rw-r--r--compiler/moco-value-pbtxt-test/CMakeLists.txt135
-rw-r--r--compiler/moco-value-pbtxt-test/README.md1
-rw-r--r--compiler/moco-value-pbtxt-test/requires.cmake (renamed from compiler/mocotest-tf/requires.cmake)0
-rwxr-xr-xcompiler/moco-value-pbtxt-test/runall.sh96
-rw-r--r--compiler/moco-value-pbtxt-test/test.lst103
-rw-r--r--compiler/moco/CMakeLists.txt5
-rw-r--r--compiler/moco/README.md3
-rw-r--r--compiler/moco/import/CMakeLists.txt26
-rw-r--r--compiler/moco/import/README.md3
-rw-r--r--compiler/moco/import/include/moco/GraphHelper.h59
-rw-r--r--compiler/moco/import/include/moco/Import/GraphBuilder.h40
-rw-r--r--compiler/moco/import/include/moco/Import/GraphBuilderContext.h144
-rw-r--r--compiler/moco/import/include/moco/Import/GraphBuilderRegistry.h87
-rw-r--r--compiler/moco/import/include/moco/Import/ModelSignature.h80
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes.h53
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Add.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/AvgPool.h34
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/BiasAdd.h34
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Concat.h34
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Const.h34
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Conv2D.h34
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Conv2DBackpropInput.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/DepthwiseConv2dNative.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/FakeQuantWithMinMaxVars.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/FusedBatchNorm.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Identity.h34
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/MaxPool.h34
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Maximum.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Mean.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Mul.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Pack.h34
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Pad.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Placeholder.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/RealDiv.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Relu.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Relu6.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Reshape.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Rsqrt.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Shape.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Softmax.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Sqrt.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/SquaredDifference.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Squeeze.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/StopGradient.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/StridedSlice.h34
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Sub.h37
-rw-r--r--compiler/moco/import/include/moco/Import/Nodes/Tanh.h37
-rw-r--r--compiler/moco/import/include/moco/Importer.h54
-rw-r--r--compiler/moco/import/src/Convert.cpp34
-rw-r--r--compiler/moco/import/src/Convert.h31
-rw-r--r--compiler/moco/import/src/GraphBuilderContext.cpp80
-rw-r--r--compiler/moco/import/src/GraphBuilderContext.test.cpp77
-rw-r--r--compiler/moco/import/src/GraphBuilderRegistry.cpp63
-rw-r--r--compiler/moco/import/src/Importer.cpp196
-rw-r--r--compiler/moco/import/src/Importer.test.cpp223
-rw-r--r--compiler/moco/import/src/ModelSignature.cpp66
-rw-r--r--compiler/moco/import/src/Nodes/Add.cpp85
-rw-r--r--compiler/moco/import/src/Nodes/Add.test.cpp58
-rw-r--r--compiler/moco/import/src/Nodes/AvgPool.cpp140
-rw-r--r--compiler/moco/import/src/Nodes/AvgPool.test.cpp99
-rw-r--r--compiler/moco/import/src/Nodes/BiasAdd.cpp122
-rw-r--r--compiler/moco/import/src/Nodes/BiasAdd.test.cpp112
-rw-r--r--compiler/moco/import/src/Nodes/Concat.cpp109
-rw-r--r--compiler/moco/import/src/Nodes/Concat.test.cpp134
-rw-r--r--compiler/moco/import/src/Nodes/Const.cpp242
-rw-r--r--compiler/moco/import/src/Nodes/Const.test.cpp465
-rw-r--r--compiler/moco/import/src/Nodes/Conv2D.cpp139
-rw-r--r--compiler/moco/import/src/Nodes/Conv2D.test.cpp119
-rw-r--r--compiler/moco/import/src/Nodes/Conv2DBackpropInput.cpp140
-rw-r--r--compiler/moco/import/src/Nodes/Conv2DBackpropInput.test.cpp98
-rw-r--r--compiler/moco/import/src/Nodes/DepthwiseConv2dNative.cpp148
-rw-r--r--compiler/moco/import/src/Nodes/DepthwiseConv2dNative.test.cpp97
-rw-r--r--compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.cpp123
-rw-r--r--compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.test.cpp65
-rw-r--r--compiler/moco/import/src/Nodes/FusedBatchNorm.cpp102
-rw-r--r--compiler/moco/import/src/Nodes/FusedBatchNorm.test.cpp88
-rw-r--r--compiler/moco/import/src/Nodes/Identity.cpp95
-rw-r--r--compiler/moco/import/src/Nodes/MaxPool.cpp145
-rw-r--r--compiler/moco/import/src/Nodes/MaxPool.test.cpp98
-rw-r--r--compiler/moco/import/src/Nodes/Maximum.cpp87
-rw-r--r--compiler/moco/import/src/Nodes/Maximum.test.cpp58
-rw-r--r--compiler/moco/import/src/Nodes/Mean.cpp99
-rw-r--r--compiler/moco/import/src/Nodes/Mean.test.cpp120
-rw-r--r--compiler/moco/import/src/Nodes/Mul.cpp85
-rw-r--r--compiler/moco/import/src/Nodes/Mul.test.cpp58
-rw-r--r--compiler/moco/import/src/Nodes/Pack.cpp102
-rw-r--r--compiler/moco/import/src/Nodes/Pack.test.cpp84
-rw-r--r--compiler/moco/import/src/Nodes/Pad.cpp91
-rw-r--r--compiler/moco/import/src/Nodes/Pad.test.cpp65
-rw-r--r--compiler/moco/import/src/Nodes/Placeholder.cpp90
-rw-r--r--compiler/moco/import/src/Nodes/Placeholder.test.cpp71
-rw-r--r--compiler/moco/import/src/Nodes/RealDiv.cpp86
-rw-r--r--compiler/moco/import/src/Nodes/RealDiv.test.cpp58
-rw-r--r--compiler/moco/import/src/Nodes/Relu.cpp86
-rw-r--r--compiler/moco/import/src/Nodes/Relu.test.cpp58
-rw-r--r--compiler/moco/import/src/Nodes/Relu6.cpp80
-rw-r--r--compiler/moco/import/src/Nodes/Relu6.test.cpp58
-rw-r--r--compiler/moco/import/src/Nodes/Reshape.cpp102
-rw-r--r--compiler/moco/import/src/Nodes/Reshape.test.cpp61
-rw-r--r--compiler/moco/import/src/Nodes/Rsqrt.cpp82
-rw-r--r--compiler/moco/import/src/Nodes/Rsqrt.test.cpp57
-rw-r--r--compiler/moco/import/src/Nodes/Shape.cpp100
-rw-r--r--compiler/moco/import/src/Nodes/Shape.test.cpp65
-rw-r--r--compiler/moco/import/src/Nodes/Softmax.cpp86
-rw-r--r--compiler/moco/import/src/Nodes/Softmax.test.cpp58
-rw-r--r--compiler/moco/import/src/Nodes/Sqrt.cpp81
-rw-r--r--compiler/moco/import/src/Nodes/Sqrt.test.cpp57
-rw-r--r--compiler/moco/import/src/Nodes/SquaredDifference.cpp92
-rw-r--r--compiler/moco/import/src/Nodes/SquaredDifference.test.cpp59
-rw-r--r--compiler/moco/import/src/Nodes/Squeeze.cpp112
-rw-r--r--compiler/moco/import/src/Nodes/Squeeze.test.cpp109
-rw-r--r--compiler/moco/import/src/Nodes/StopGradient.cpp87
-rw-r--r--compiler/moco/import/src/Nodes/StopGradient.test.cpp57
-rw-r--r--compiler/moco/import/src/Nodes/StridedSlice.cpp187
-rw-r--r--compiler/moco/import/src/Nodes/StridedSlice.test.cpp107
-rw-r--r--compiler/moco/import/src/Nodes/Sub.cpp85
-rw-r--r--compiler/moco/import/src/Nodes/Sub.test.cpp58
-rw-r--r--compiler/moco/import/src/Nodes/Tanh.cpp81
-rw-r--r--compiler/moco/import/src/Nodes/Tanh.test.cpp57
-rw-r--r--compiler/moco/import/src/TestHelper.h83
-rw-r--r--compiler/moco/import/src/TestHelper.test.cpp101
-rw-r--r--compiler/moco/lang/CMakeLists.txt21
-rw-r--r--compiler/moco/lang/README.md3
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFAdd.h56
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFAvgPool.h101
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFBiasAdd.h68
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFConcatV2.h90
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFConst.h94
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFConv2D.h55
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFConv2DBackpropInput.h102
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFDepthwiseConv2dNative.h56
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFFakeQuantWithMinMaxVars.h54
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFFusedBatchNorm.h55
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFIdentity.h52
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFMaxPool.h101
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFMaximum.h56
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFMean.h71
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFMul.h56
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFPack.h86
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFPad.h61
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFPlaceholder.h90
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFPush.h84
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFRealDiv.h56
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFRelu.h52
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFRelu6.h50
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFReshape.h54
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFRsqrt.h52
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFShape.h60
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFSoftmax.h37
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFSqrt.h52
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFSquaredDifference.h56
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFSqueeze.h71
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFStopGradient.h52
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFStridedSlice.h123
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFSub.h56
-rw-r--r--compiler/moco/lang/include/moco/IR/Nodes/TFTanh.h37
-rw-r--r--compiler/moco/lang/include/moco/IR/TFDataLayout.h29
-rw-r--r--compiler/moco/lang/include/moco/IR/TFDialect.h43
-rw-r--r--compiler/moco/lang/include/moco/IR/TFNode.h23
-rw-r--r--compiler/moco/lang/include/moco/IR/TFNodeDecl.h104
-rw-r--r--compiler/moco/lang/include/moco/IR/TFNodeImpl.h68
-rw-r--r--compiler/moco/lang/include/moco/IR/TFNodeVisitor.forward.h30
-rw-r--r--compiler/moco/lang/include/moco/IR/TFNodeVisitor.h83
-rw-r--r--compiler/moco/lang/include/moco/IR/TFNodes.h55
-rw-r--r--compiler/moco/lang/include/moco/IR/TFNodes.lst48
-rw-r--r--compiler/moco/lang/include/moco/IR/TFOpcode.h35
-rw-r--r--compiler/moco/lang/include/moco/IR/TFPadding.h29
-rw-r--r--compiler/moco/lang/include/moco/IR/VariadicArityNode.h77
-rw-r--r--compiler/moco/lang/include/moco/Names.h96
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFAdd.test.cpp31
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFAvgPool.test.cpp34
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFBiasAdd.test.cpp32
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFConcatV2.test.cpp34
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFConst.cpp113
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFConst.test.cpp95
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFConv2D.test.cpp34
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFConv2DBackpropInput.test.cpp35
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFDepthwiseConv2dNative.test.cpp34
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFFakeQuantWithMinMaxVars.test.cpp34
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFFusedBatchNorm.test.cpp35
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFIdentity.test.cpp30
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFMaxPool.test.cpp34
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFMaximum.test.cpp31
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFMean.test.cpp32
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFMul.test.cpp31
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFPack.test.cpp34
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFPad.test.cpp31
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFPlaceholder.test.cpp46
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFRealDiv.test.cpp31
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFRelu.test.cpp30
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFRelu6.test.cpp30
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFReshape.test.cpp31
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFRsqrt.test.cpp30
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFShape.test.cpp31
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFSoftmax.test.cpp30
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFSqrt.test.cpp30
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFSquaredDifference.test.cpp31
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFSqueeze.test.cpp31
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFStopGradient.test.cpp30
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFStridedSlice.test.cpp38
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFSub.test.cpp31
-rw-r--r--compiler/moco/lang/src/IR/Nodes/TFTanh.test.cpp30
-rw-r--r--compiler/moco/lang/src/IR/TFDialect.cpp91
-rw-r--r--compiler/moco/lang/src/IR/TFDialect.test.cpp29
-rw-r--r--compiler/moco/lang/src/IR/TFNode.cpp137
-rw-r--r--compiler/moco/lang/src/IR/TFNode.test.cpp44
-rw-r--r--compiler/moco/lang/src/IR/VariadicArityNode.test.cpp55
-rw-r--r--compiler/moco/pass/CMakeLists.txt26
-rw-r--r--compiler/moco/pass/README.md3
-rw-r--r--compiler/moco/pass/include/moco/Pass/Passes.h32
-rw-r--r--compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldAdd.h41
-rw-r--r--compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldMul.h41
-rw-r--r--compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldPack.h43
-rw-r--r--compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldStridedSlice.h41
-rw-r--r--compiler/moco/pass/include/moco/Pass/Passes/FuseBinaryIntoPreceding.h41
-rw-r--r--compiler/moco/pass/include/moco/Pass/Passes/RemoveTFIdentityNode.h49
-rw-r--r--compiler/moco/pass/include/moco/Pass/Passes/ResolveConstantShape.h41
-rw-r--r--compiler/moco/pass/include/moco/Pass/Passes/ResolveFusedBatchNorm.h41
-rw-r--r--compiler/moco/pass/include/moco/Pass/Passes/ResolveReshapeWildcardDim.h42
-rw-r--r--compiler/moco/pass/include/moco/Pass/Passes/ResolveSquaredDifference.h41
-rw-r--r--compiler/moco/pass/include/moco/Pass/Passes/SqueezeReduceNode.h42
-rw-r--r--compiler/moco/pass/src/ConstantFoldAdd.test.cpp109
-rw-r--r--compiler/moco/pass/src/ConstantFoldHelper.cpp238
-rw-r--r--compiler/moco/pass/src/ConstantFoldHelper.h64
-rw-r--r--compiler/moco/pass/src/ConstantFoldMul.test.cpp109
-rw-r--r--compiler/moco/pass/src/ConstantFoldPack.test.cpp90
-rw-r--r--compiler/moco/pass/src/ConstantFoldStridedSlice.test.cpp268
-rw-r--r--compiler/moco/pass/src/Passes/ConstantFoldAdd.cpp116
-rw-r--r--compiler/moco/pass/src/Passes/ConstantFoldMul.cpp116
-rw-r--r--compiler/moco/pass/src/Passes/ConstantFoldPack.cpp180
-rw-r--r--compiler/moco/pass/src/Passes/ConstantFoldStridedSlice.cpp291
-rw-r--r--compiler/moco/pass/src/Passes/FuseBinaryIntoPreceding.cpp538
-rw-r--r--compiler/moco/pass/src/Passes/RemoveTFIdentityNode.cpp66
-rw-r--r--compiler/moco/pass/src/Passes/ResolveConstantShape.cpp129
-rw-r--r--compiler/moco/pass/src/Passes/ResolveFusedBatchNorm.cpp254
-rw-r--r--compiler/moco/pass/src/Passes/ResolveReshapeWildcardDim.cpp153
-rw-r--r--compiler/moco/pass/src/Passes/ResolveSquaredDifference.cpp97
-rw-r--r--compiler/moco/pass/src/Passes/SqueezeReduceNode.cpp105
-rw-r--r--compiler/moco/pass/src/TensorPackEnumerator.cpp134
-rw-r--r--compiler/moco/pass/src/TensorPackEnumerator.h66
-rw-r--r--compiler/moco/pass/src/TensorSliceEnumerator.cpp84
-rw-r--r--compiler/moco/pass/src/TensorSliceEnumerator.h62
-rw-r--r--compiler/moco/pass/src/TensorSliceEnumerator.test.cpp83
-rw-r--r--compiler/moco/pass/src/TestHelper.h67
-rw-r--r--compiler/moco/pass/src/TestHelper.test.cpp38
-rw-r--r--compiler/moco/requires.cmake8
-rw-r--r--compiler/moco/service/CMakeLists.txt24
-rw-r--r--compiler/moco/service/README.md3
-rw-r--r--compiler/moco/service/include/moco/Service/TFShapeInferenceRule.h38
-rw-r--r--compiler/moco/service/include/moco/Service/TFTypeInferenceRule.h36
-rw-r--r--compiler/moco/service/src/Service/TFShapeInferenceRule.cpp890
-rw-r--r--compiler/moco/service/src/Service/TFShapeInferenceRule.test.cpp500
-rw-r--r--compiler/moco/service/src/Service/TFTypeInferenceRule.cpp113
-rw-r--r--compiler/moco/service/src/TestHelper.h69
-rw-r--r--compiler/moco/service/src/TestHelper.test.cpp38
-rw-r--r--compiler/moco/support/CMakeLists.txt9
-rw-r--r--compiler/moco/support/README.md3
-rw-r--r--compiler/moco/support/include/moco/Support/NodeAs.h29
-rw-r--r--compiler/moco/support/include/moco/Support/TFShapeInferenceHelper.h221
-rw-r--r--compiler/moco/support/src/TFShapeInferenceHelper.cpp354
-rw-r--r--compiler/mocotest-onnx/CMakeLists.txt53
-rw-r--r--compiler/mocotest-tf/CMakeLists.txt99
-rw-r--r--compiler/mocotest-tf/README.md1
-rwxr-xr-xcompiler/mocotest-tf/runall.sh90
-rw-r--r--compiler/mocotest-tf/test.lst71
-rw-r--r--compiler/morph/CMakeLists.txt2
-rw-r--r--compiler/nest/core/CMakeLists.txt2
-rw-r--r--compiler/nest/core/src/Block.test.cpp6
-rw-r--r--compiler/nest/core/src/Bound.test.cpp4
-rw-r--r--compiler/nest/core/src/Closure.test.cpp4
-rw-r--r--compiler/nest/core/src/Domain.test.cpp4
-rw-r--r--compiler/nest/core/src/DomainContext.test.cpp22
-rw-r--r--compiler/nest/core/src/DomainID.test.cpp2
-rw-r--r--compiler/nest/core/src/DomainInfo.test.cpp10
-rw-r--r--compiler/nest/core/src/Expr.test.cpp8
-rw-r--r--compiler/nest/core/src/FV.test.cpp8
-rw-r--r--compiler/nest/core/src/Level.test.cpp2
-rw-r--r--compiler/nest/core/src/Module.test.cpp12
-rw-r--r--compiler/nest/core/src/Ret.test.cpp12
-rw-r--r--compiler/nest/core/src/Schedule.test.cpp4
-rw-r--r--compiler/nest/core/src/Var.test.cpp4
-rw-r--r--compiler/nest/core/src/VarContext.test.cpp30
-rw-r--r--compiler/nest/core/src/VarID.test.cpp2
-rw-r--r--compiler/nest/core/src/expr/AddNode.test.cpp6
-rw-r--r--compiler/nest/core/src/expr/DerefNode.test.cpp2
-rw-r--r--compiler/nest/core/src/expr/MulNode.test.cpp6
-rw-r--r--compiler/nest/core/src/expr/Subscript.test.cpp6
-rw-r--r--compiler/nest/core/src/expr/VarNode.test.cpp4
-rw-r--r--compiler/nest/core/src/stmt/PushNode.test.cpp2
-rw-r--r--compiler/nike/CMakeLists.txt6
-rw-r--r--compiler/nnc/CMakeLists.txt2
-rw-r--r--compiler/nnc/backends/acl_soft_backend/AclCppGenerator.cpp22
-rw-r--r--compiler/nnc/backends/acl_soft_backend/AclCppOpGenerator.cpp46
-rw-r--r--compiler/nnc/backends/acl_soft_backend/CMakeLists.txt7
-rw-r--r--compiler/nnc/backends/interpreter/CMakeLists.txt4
-rw-r--r--compiler/nnc/backends/interpreter/Interpreter.cpp332
-rw-r--r--compiler/nnc/backends/interpreter/InterpreterBackend.cpp68
-rw-r--r--compiler/nnc/backends/interpreter/ops/Add.h47
-rw-r--r--compiler/nnc/backends/interpreter/ops/AvgPool2D.cpp82
-rw-r--r--compiler/nnc/backends/interpreter/ops/AvgPool2D.h44
-rw-r--r--compiler/nnc/backends/interpreter/ops/Concat.h64
-rw-r--r--compiler/nnc/backends/interpreter/ops/Conv2D.cpp90
-rw-r--r--compiler/nnc/backends/interpreter/ops/Conv2D.h43
-rw-r--r--compiler/nnc/backends/interpreter/ops/DeConv2D.cpp128
-rw-r--r--compiler/nnc/backends/interpreter/ops/DeConv2D.h52
-rw-r--r--compiler/nnc/backends/interpreter/ops/DepthwiseConv2D.cpp79
-rw-r--r--compiler/nnc/backends/interpreter/ops/DepthwiseConv2D.h43
-rw-r--r--compiler/nnc/backends/interpreter/ops/Div.h47
-rw-r--r--compiler/nnc/backends/interpreter/ops/Fill.h56
-rw-r--r--compiler/nnc/backends/interpreter/ops/FullyConnected.cpp67
-rw-r--r--compiler/nnc/backends/interpreter/ops/FullyConnected.h44
-rw-r--r--compiler/nnc/backends/interpreter/ops/Gather.cpp70
-rw-r--r--compiler/nnc/backends/interpreter/ops/Gather.h43
-rw-r--r--compiler/nnc/backends/interpreter/ops/Max.h49
-rw-r--r--compiler/nnc/backends/interpreter/ops/MaxPool2D.cpp77
-rw-r--r--compiler/nnc/backends/interpreter/ops/MaxPool2D.h44
-rw-r--r--compiler/nnc/backends/interpreter/ops/Mul.h47
-rw-r--r--compiler/nnc/backends/interpreter/ops/OperationImpl.h44
-rw-r--r--compiler/nnc/backends/interpreter/ops/Pad.cpp72
-rw-r--r--compiler/nnc/backends/interpreter/ops/Pad.h55
-rw-r--r--compiler/nnc/backends/interpreter/ops/ReduceMean.h95
-rw-r--r--compiler/nnc/backends/interpreter/ops/Reshape.h57
-rw-r--r--compiler/nnc/backends/interpreter/ops/Softmax.h75
-rw-r--r--compiler/nnc/backends/interpreter/ops/Sub.h47
-rw-r--r--compiler/nnc/backends/interpreter/ops/Transpose.cpp54
-rw-r--r--compiler/nnc/backends/interpreter/ops/Transpose.h41
-rw-r--r--compiler/nnc/backends/interpreter/ops/common.cpp37
-rw-r--r--compiler/nnc/backends/interpreter/ops/common.h33
-rw-r--r--compiler/nnc/backends/soft_backend/CMakeLists.txt7
-rw-r--r--compiler/nnc/backends/soft_backend/CPPGenerator.cpp27
-rw-r--r--compiler/nnc/backends/soft_backend/ModelAnalyzer.cpp21
-rw-r--r--compiler/nnc/backends/soft_backend/ModelAnalyzer.h2
-rw-r--r--compiler/nnc/backends/soft_backend/SBSerializer.cpp19
-rw-r--r--compiler/nnc/backends/soft_backend/SBSerializer.h2
-rw-r--r--compiler/nnc/backends/soft_backend/code_snippets/cpp_broadcast.def73
-rw-r--r--compiler/nnc/backends/soft_backend/code_snippets/cpp_conv_transpose.def2
-rw-r--r--compiler/nnc/backends/soft_backend/code_snippets/cpp_elementwise.def53
-rw-r--r--compiler/nnc/backends/soft_backend/code_snippets/cpp_operations.def67
-rw-r--r--compiler/nnc/backends/soft_backend/code_snippets/cpp_reduce.def2
-rw-r--r--compiler/nnc/cmake/config.cmake5
-rw-r--r--compiler/nnc/doc/COC_for_nnc_developer.rst20
-rw-r--r--compiler/nnc/doc/codestyle.rst223
-rw-r--r--compiler/nnc/doc/nnc_debug.rst76
-rw-r--r--compiler/nnc/doc/nnc_interpreter_testing.rst114
-rw-r--r--compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_DLD.rst1006
-rw-r--r--compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_HLD.rst572
-rw-r--r--compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_SDD.rst884
-rw-r--r--compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_SRS.rst559
-rw-r--r--compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_STD.rst1696
-rw-r--r--compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_UTR.rst767
-rw-r--r--compiler/nnc/doc/project/images/nncc_components.pngbin45359 -> 0 bytes
-rw-r--r--compiler/nnc/doc/project/images/nncc_idef0_a0.pngbin50434 -> 0 bytes
-rw-r--r--compiler/nnc/doc/project/images/nncc_idef0_a1.pngbin86576 -> 0 bytes
-rw-r--r--compiler/nnc/doc/project/images/nncc_idef0_a12.pngbin42778 -> 0 bytes
-rw-r--r--compiler/nnc/driver/Driver.cpp44
-rw-r--r--compiler/nnc/driver/Options.cpp7
-rw-r--r--compiler/nnc/driver/Options.h (renamed from compiler/nnc/include/option/Options.h)0
-rw-r--r--compiler/nnc/include/backends/acl_soft_backend/AclCppGenerator.h8
-rw-r--r--compiler/nnc/include/backends/interpreter/Interpreter.h93
-rw-r--r--compiler/nnc/include/backends/interpreter/InterpreterBackend.h8
-rw-r--r--compiler/nnc/include/backends/soft_backend/CPPGenerator.h4
-rw-r--r--compiler/nnc/include/passes/optimizations/DeadCodeElimination.h40
-rw-r--r--compiler/nnc/include/passes/optimizations/RemoveDeadEnds.h40
-rw-r--r--compiler/nnc/include/passes/transformations/LowerConv2D.h41
-rw-r--r--compiler/nnc/passes/dot_dumper/CMakeLists.txt2
-rw-r--r--compiler/nnc/passes/optimizations/CMakeLists.txt17
-rw-r--r--compiler/nnc/passes/optimizations/CombineTransposes.cpp8
-rw-r--r--compiler/nnc/passes/optimizations/ConstantFoldTranspose.cpp26
-rw-r--r--compiler/nnc/passes/optimizations/DeadCodeElimination.cpp48
-rw-r--r--compiler/nnc/passes/optimizations/FuseArithmeticOps.cpp12
-rw-r--r--compiler/nnc/passes/optimizations/OptimizationUtils.cpp8
-rw-r--r--compiler/nnc/passes/optimizations/RemoveDeadEnds.cpp34
-rw-r--r--compiler/nnc/passes/optimizations/SinkRelu.cpp2
-rw-r--r--compiler/nnc/passes/optimizations/SinkTranspose.cpp2
-rw-r--r--compiler/nnc/passes/transformations/CMakeLists.txt5
-rw-r--r--compiler/nnc/passes/transformations/DataFormatSwitcher.cpp126
-rw-r--r--compiler/nnc/passes/transformations/LowerConv2D.cpp75
-rw-r--r--compiler/nnc/requires.cmake6
-rw-r--r--compiler/nnc/support/CLOptionChecker.cpp4
-rw-r--r--compiler/nnc/support/CommandLine.cpp2
-rw-r--r--compiler/nnc/tests/acl_soft_backend/AclCppOperations.cpp16
-rw-r--r--compiler/nnc/tests/acl_soft_backend/BuildInfo.h.in16
-rw-r--r--compiler/nnc/tests/acl_soft_backend/CMakeLists.txt5
-rw-r--r--compiler/nnc/tests/acl_soft_backend/artifact_cmake/CMakeLists.txt3
-rw-r--r--compiler/nnc/tests/acl_soft_backend/artifact_cmake/main.cpp16
-rw-r--r--compiler/nnc/tests/import/CMakeLists.txt4
-rw-r--r--compiler/nnc/tests/import/caffe.cpp10
-rw-r--r--compiler/nnc/tests/import/tflite.cpp12
-rw-r--r--compiler/nnc/tests/soft_backend/CMakeLists.txt6
-rw-r--r--compiler/nnc/tests/soft_backend/CompileCPP.cpp15
-rw-r--r--compiler/nnc/tests/soft_backend/test_main.def2
-rw-r--r--compiler/nnc/unittests/CMakeLists.txt9
-rw-r--r--compiler/nnc/unittests/acl_backend/CMakeLists.txt4
-rw-r--r--compiler/nnc/unittests/acl_backend/MIRToDOM.cpp28
-rw-r--r--compiler/nnc/unittests/caffe2_frontend/CMakeLists.txt14
-rwxr-xr-xcompiler/nnc/unittests/caffe2_frontend/test_data/gen_model.py27
-rw-r--r--compiler/nnc/unittests/caffe2_frontend/unsupported_caffe2_model.cpp45
-rw-r--r--compiler/nnc/unittests/caffe_frontend/CMakeLists.txt5
-rw-r--r--compiler/nnc/unittests/caffe_frontend/unsupported_caffe_model.cpp45
-rw-r--r--compiler/nnc/unittests/optimizations/CMakeLists.txt11
-rw-r--r--compiler/nnc/unittests/optimizations/CombineTransposes.cpp30
-rw-r--r--compiler/nnc/unittests/optimizations/DeadCodeElimination.cpp89
-rw-r--r--compiler/nnc/unittests/optimizations/FuseArithmeticOps.cpp9
-rw-r--r--compiler/nnc/unittests/optimizations/RemoveDeadEnds.cpp48
-rw-r--r--compiler/nnc/unittests/optimizations/SinkTest.cpp35
-rw-r--r--compiler/nnc/unittests/pass/CMakeLists.txt4
-rw-r--r--compiler/nnc/unittests/pass/PassExceptionTest.cpp2
-rw-r--r--compiler/nnc/unittests/pass/PassManagerTest.cpp1
-rw-r--r--compiler/nnc/unittests/soft_backend/CMakeLists.txt21
-rw-r--r--compiler/nnc/unittests/soft_backend/CPPOperations.cpp151
-rw-r--r--compiler/nnc/unittests/soft_backend/Generator.cpp10
-rw-r--r--compiler/nnc/unittests/soft_backend/ModelAnalyzer.cpp20
-rw-r--r--compiler/nnc/unittests/tflite_frontend/CMakeLists.txt15
-rwxr-xr-xcompiler/nnc/unittests/tflite_frontend/test_data/gen_test.py41
-rw-r--r--compiler/nnc/unittests/tflite_frontend/unsupported_tflite_model.cpp44
-rw-r--r--compiler/nnc/unittests/transformations/CMakeLists.txt2
-rw-r--r--compiler/nnc/unittests/transformations/Switcher.cpp122
-rw-r--r--compiler/nnc/utils/CMakeLists.txt6
-rw-r--r--compiler/nnc/utils/caffe2_dot_dumper/CMakeLists.txt2
-rw-r--r--compiler/nnc/utils/caffe2_dot_dumper/model_dump.cpp12
-rw-r--r--compiler/nnc/utils/caffe_dot_dumper/CMakeLists.txt2
-rw-r--r--compiler/nnc/utils/caffe_dot_dumper/model_dump.cpp7
-rwxr-xr-xcompiler/nnc/utils/caffe_model_maker/AllFill.sh48
-rwxr-xr-xcompiler/nnc/utils/caffe_model_maker/Filler.sh28
-rw-r--r--compiler/nnc/utils/caffe_model_maker/GenerateCaffeModels.py722
-rw-r--r--compiler/nnc/utils/caffe_model_maker/Pyloss.py83
-rw-r--r--compiler/nnc/utils/caffe_model_maker/README.md22
-rw-r--r--compiler/nnc/utils/caffe_model_maker/in.jpgbin15556 -> 0 bytes
-rw-r--r--compiler/nnc/utils/infer_tests/README.md2
-rwxr-xr-x[-rw-r--r--]compiler/nnc/utils/infer_tests/infer_testcases.py0
-rwxr-xr-x[-rw-r--r--]compiler/nnc/utils/infer_tests/res2bin.py0
-rw-r--r--compiler/nnc/utils/input_gen/tensor_gen.cpp10
-rwxr-xr-x[-rw-r--r--]compiler/nnc/utils/model_runner/common_place.py0
-rwxr-xr-x[-rw-r--r--]compiler/nnc/utils/model_runner/model_runner_caffe.py0
-rwxr-xr-x[-rw-r--r--]compiler/nnc/utils/model_runner/model_runner_caffe2.py0
-rwxr-xr-x[-rw-r--r--]compiler/nnc/utils/model_runner/model_runner_onnx.py0
-rwxr-xr-x[-rw-r--r--]compiler/nnc/utils/model_runner/model_runner_tflite.py0
-rw-r--r--compiler/nnc/utils/prepare_inputs/README.md2
-rwxr-xr-x[-rw-r--r--]compiler/nnc/utils/prepare_inputs/jpeg2hdf5.py0
-rw-r--r--compiler/nnc/utils/tflite_dot_dumper/CMakeLists.txt2
-rw-r--r--compiler/nnc/utils/tflite_dot_dumper/model_dump.cpp7
-rw-r--r--compiler/nnc/utils/tflite_model_generator/CMakeLists.txt26
-rw-r--r--compiler/nnc/utils/tflite_model_generator/ModelGenerator.cpp41
-rw-r--r--compiler/nnc/utils/tflite_model_generator/Operator.def128
-rw-r--r--compiler/nnc/utils/tflite_model_generator/RandomModelBuilder.h135
-rw-r--r--compiler/nnc/utils/tflite_model_generator/TFLiteRandomModelBuilder.cpp359
-rw-r--r--compiler/nnc/utils/tflite_model_generator/TFLiteRandomModelBuilder.h118
-rw-r--r--compiler/nnc/utils/tflite_model_generator/Tree.cpp389
-rw-r--r--compiler/nnc/utils/tflite_model_generator/Tree.h145
-rw-r--r--compiler/nnkit-caffe/CMakeLists.txt2
-rw-r--r--compiler/nnkit-caffe/README.md1
-rw-r--r--compiler/nnkit-mocotf/README.md1
-rw-r--r--compiler/nnkit-mocotf/support/include/nnkit/support/moco/tf/Backend.h3
-rw-r--r--compiler/nnkit-mocotf/support/src/Backend.cpp58
-rw-r--r--compiler/nnkit-onnxrt/CMakeLists.txt2
-rw-r--r--compiler/nnkit-onnxrt/README.md1
-rw-r--r--compiler/nnkit-tf/CMakeLists.txt2
-rw-r--r--compiler/nnkit-tf/README.md1
-rw-r--r--compiler/nnkit-tf/support/CMakeLists.txt12
-rw-r--r--compiler/nnkit-tf/support/include/nnkit/support/tf/Runner.h36
-rw-r--r--compiler/nnkit-tf/support/src/Backend.cpp25
-rw-r--r--compiler/nnkit-tf/support/src/Runner.cpp78
-rw-r--r--compiler/nnkit-tflite/CMakeLists.txt2
-rw-r--r--compiler/nnkit-tflite/README.md1
-rw-r--r--compiler/nnkit-tflite/backend/Backend.cpp4
-rw-r--r--compiler/nnkit-tflite/support/CMakeLists.txt14
-rw-r--r--compiler/nnkit-tflite/support/include/nnkit/support/tflite/AbstractBackend.h2
-rw-r--r--compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorSet.h2
-rw-r--r--compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorSets.h2
-rw-r--r--compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorUtils.h2
-rw-r--r--compiler/nnkit/actions/HDF5/CMakeLists.txt2
-rw-r--r--compiler/nnkit/tools/benchmark/CMakeLists.txt8
-rw-r--r--compiler/nnkit/tools/run/CMakeLists.txt16
-rw-r--r--compiler/nnop/CMakeLists.txt2
-rw-r--r--compiler/nnop/README.md1
-rw-r--r--compiler/nnsuite/README.md1
-rw-r--r--compiler/nnsuite/conv/nnkit-caffe/CMakeLists.txt2
-rw-r--r--compiler/nnsuite/conv/nnkit-tflite/CMakeLists.txt2
-rw-r--r--compiler/one-cmds/CMakeLists.txt81
-rw-r--r--compiler/one-cmds/README.md3
-rw-r--r--compiler/one-cmds/how-to-create-hdf5-dataset.txt97
-rw-r--r--compiler/one-cmds/how-to-prepare-virtualenv.txt54
-rw-r--r--compiler/one-cmds/how-to-use-one-commands.txt210
-rw-r--r--compiler/one-cmds/one-build134
-rw-r--r--compiler/one-cmds/one-build.template.cfg24
-rw-r--r--compiler/one-cmds/one-codegen111
-rw-r--r--compiler/one-cmds/one-import101
-rw-r--r--compiler/one-cmds/one-import-bcq222
-rw-r--r--compiler/one-cmds/one-import-tf195
-rw-r--r--compiler/one-cmds/one-import-tflite111
-rw-r--r--compiler/one-cmds/one-optimize155
-rw-r--r--compiler/one-cmds/one-pack120
-rw-r--r--compiler/one-cmds/one-prepare-venv56
-rw-r--r--compiler/one-cmds/one-quantize232
-rw-r--r--compiler/one-cmds/requires.cmake8
-rw-r--r--compiler/one-cmds/tests/CMakeLists.txt62
-rw-r--r--compiler/one-cmds/tests/README.txt27
-rw-r--r--compiler/one-cmds/tests/one-build_001.cfg20
-rw-r--r--compiler/one-cmds/tests/one-build_001.test38
-rw-r--r--compiler/one-cmds/tests/one-build_002.cfg24
-rw-r--r--compiler/one-cmds/tests/one-build_002.test38
-rw-r--r--compiler/one-cmds/tests/one-build_neg_001.test41
-rw-r--r--compiler/one-cmds/tests/one-build_neg_002.cfg20
-rw-r--r--compiler/one-cmds/tests/one-build_neg_002.test41
-rw-r--r--compiler/one-cmds/tests/one-build_neg_003.cfg15
-rw-r--r--compiler/one-cmds/tests/one-build_neg_003.test41
-rw-r--r--compiler/one-cmds/tests/one-build_neg_004.cfg24
-rw-r--r--compiler/one-cmds/tests/one-build_neg_004.test41
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_001.test45
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_neg_001.test49
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_neg_002.test49
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_neg_003.test49
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_neg_004.test49
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_neg_005.test48
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_neg_006.test48
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_neg_007.test48
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_neg_008.test48
-rw-r--r--compiler/one-cmds/tests/one-import-bcq_neg_009.test48
-rw-r--r--compiler/one-cmds/tests/one-import_001.test44
-rw-r--r--compiler/one-cmds/tests/one-import_002.cfg16
-rw-r--r--compiler/one-cmds/tests/one-import_002.test41
-rw-r--r--compiler/one-cmds/tests/one-import_neg_001.test49
-rw-r--r--compiler/one-cmds/tests/one-import_neg_002.test49
-rw-r--r--compiler/one-cmds/tests/one-import_neg_003.test49
-rw-r--r--compiler/one-cmds/tests/one-import_neg_004.test49
-rw-r--r--compiler/one-cmds/tests/one-import_neg_005.test49
-rw-r--r--compiler/one-cmds/tests/one-import_neg_006.test49
-rw-r--r--compiler/one-cmds/tests/one-import_neg_007.test49
-rw-r--r--compiler/one-cmds/tests/one-import_neg_008.test49
-rw-r--r--compiler/one-cmds/tests/one-import_neg_009.test48
-rw-r--r--compiler/one-cmds/tests/one-import_neg_010.test49
-rw-r--r--compiler/one-cmds/tests/one-optimize_001.test51
-rw-r--r--compiler/one-cmds/tests/one-optimize_neg_001.test47
-rw-r--r--compiler/one-cmds/tests/one-optimize_neg_002.test47
-rw-r--r--compiler/one-cmds/tests/one-optimize_neg_003.test51
-rw-r--r--compiler/one-cmds/tests/one-pack_001.test51
-rw-r--r--compiler/one-cmds/tests/one-pack_neg_001.test43
-rw-r--r--compiler/one-cmds/tests/one-pack_neg_002.test47
-rw-r--r--compiler/one-cmds/tests/one-pack_neg_003.test45
-rw-r--r--compiler/one-cmds/tests/one-quantize_001.test54
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_001.test61
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_002.test60
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_003.test59
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_004.test59
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_005.test50
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_006.test50
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_007.test59
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_008.test60
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_009.test60
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_010.test60
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_011.test60
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_012.test60
-rw-r--r--compiler/one-cmds/tests/one-quantize_neg_013.test60
-rw-r--r--compiler/one-cmds/tests/prepare_test_materials.sh78
-rw-r--r--compiler/one-cmds/tests/preprocess_images.py35
-rw-r--r--compiler/one-cmds/tests/rawdata2hdf5_001.test41
-rw-r--r--compiler/one-cmds/tests/rawdata2hdf5_neg_001.test47
-rw-r--r--compiler/one-cmds/tests/rawdata2hdf5_neg_002.test42
-rw-r--r--compiler/one-cmds/tests/rawdata2hdf5_neg_003.test40
-rw-r--r--compiler/one-cmds/tests/rawdata2hdf5_neg_004.test41
-rw-r--r--compiler/one-cmds/utils.py147
-rw-r--r--compiler/oneco-value-pbtxt-test/CMakeLists.txt53
-rw-r--r--compiler/oneco-value-pbtxt-test/Const_000/test.pbtxt (renamed from compiler/mocotest-onnx/Const_000/test.pbtxt)0
-rw-r--r--compiler/oneco-value-pbtxt-test/Identity_000/test.pbtxt (renamed from compiler/mocotest-onnx/Identity_000/test.pbtxt)0
-rw-r--r--compiler/oneco-value-pbtxt-test/README.md1
-rw-r--r--compiler/oneco-value-pbtxt-test/requires.cmake (renamed from compiler/mocotest-onnx/requires.cmake)0
-rw-r--r--compiler/oneco/CMakeLists.txt36
-rw-r--r--compiler/oneco/README.md1
-rw-r--r--compiler/oneco/include/moco/onnx/Frontend.h (renamed from compiler/moco-onnx/include/moco/onnx/Frontend.h)0
-rw-r--r--compiler/oneco/proto/CMakeLists.txt (renamed from compiler/moco-onnx/proto/CMakeLists.txt)0
-rw-r--r--compiler/oneco/requires.cmake (renamed from compiler/moco-onnx/requires.cmake)0
-rw-r--r--compiler/oneco/src/Convert.cpp (renamed from compiler/moco-onnx/src/Convert.cpp)0
-rw-r--r--compiler/oneco/src/Convert.h (renamed from compiler/moco-onnx/src/Convert.h)0
-rw-r--r--compiler/oneco/src/Frontend.cpp (renamed from compiler/moco-onnx/src/Frontend.cpp)0
-rw-r--r--compiler/oneco/src/Frontend.test.cpp (renamed from compiler/moco-onnx/src/Frontend.test.cpp)0
-rw-r--r--compiler/oneco/src/GraphBuilder.h (renamed from compiler/moco-onnx/src/GraphBuilder.h)0
-rw-r--r--compiler/oneco/src/GraphBuilderContext.cpp (renamed from compiler/moco-onnx/src/GraphBuilderContext.cpp)0
-rw-r--r--compiler/oneco/src/GraphBuilderContext.h (renamed from compiler/moco-onnx/src/GraphBuilderContext.h)0
-rw-r--r--compiler/oneco/src/GraphBuilderRegistry.h (renamed from compiler/moco-onnx/src/GraphBuilderRegistry.h)0
-rw-r--r--compiler/oneco/src/Onnxutil.cpp (renamed from compiler/moco-onnx/src/Onnxutil.cpp)0
-rw-r--r--compiler/oneco/src/Onnxutil.h (renamed from compiler/moco-onnx/src/Onnxutil.h)0
-rw-r--r--compiler/oneco/src/Op/Constant.cpp (renamed from compiler/moco-onnx/src/Op/Constant.cpp)0
-rw-r--r--compiler/oneco/src/Op/Constant.h (renamed from compiler/moco-onnx/src/Op/Constant.h)0
-rw-r--r--compiler/oneco/src/Op/Constant_V1.cpp (renamed from compiler/moco-onnx/src/Op/Constant_V1.cpp)0
-rw-r--r--compiler/oneco/src/Op/Constant_V9.cpp (renamed from compiler/moco-onnx/src/Op/Constant_V9.cpp)0
-rw-r--r--compiler/oneco/src/Op/Identity.cpp (renamed from compiler/moco-onnx/src/Op/Identity.cpp)0
-rw-r--r--compiler/oneco/src/Op/Identity.h (renamed from compiler/moco-onnx/src/Op/Identity.h)0
-rw-r--r--compiler/oneco/src/Op/Identity_V1.cpp (renamed from compiler/moco-onnx/src/Op/Identity_V1.cpp)0
-rw-r--r--compiler/onnx-tools/CMakeLists.txt21
-rw-r--r--compiler/onnx-tools/README.md65
-rw-r--r--compiler/onnx-tools/onnx-dump.py146
-rw-r--r--compiler/onnx-tools/onnx-ops.py45
-rw-r--r--compiler/onnx2circle/CMakeLists.txt27
-rw-r--r--compiler/onnx2circle/README.md3
-rw-r--r--compiler/onnx2circle/requires.cmake9
-rw-r--r--compiler/onnx2circle/src/onnx2circle.cpp111
-rw-r--r--compiler/onnx2tflite-integration-test/CMakeLists.txt120
-rw-r--r--compiler/onnx2tflite-integration-test/README.md1
-rw-r--r--compiler/onnx2tflite-integration-test/requires.cmake6
-rw-r--r--compiler/onnx2tflite-integration-test/test.lst5
-rwxr-xr-xcompiler/onnx2tflite-integration-test/testall.sh112
-rw-r--r--compiler/onnx2tflite/CMakeLists.txt8
-rw-r--r--compiler/onnx2tflite/requires.cmake3
-rw-r--r--compiler/onnx2tflite/src/Driver.cpp83
-rw-r--r--compiler/onnxkit/CMakeLists.txt4
-rw-r--r--compiler/oops/CMakeLists.txt12
-rw-r--r--compiler/oops/README.md1
-rw-r--r--compiler/oops/include/oops/InternalExn.h80
-rw-r--r--compiler/oops/include/oops/UserExn.h89
-rw-r--r--compiler/oops/test.cpp94
-rw-r--r--compiler/pepper-assert/CMakeLists.txt2
-rw-r--r--compiler/pepper-assert/README.md1
-rw-r--r--compiler/pepper-assert/include/pepper/assert.h38
-rw-r--r--compiler/pepper-env/CMakeLists.txt19
-rw-r--r--compiler/pepper-env/README.md3
-rw-r--r--compiler/pepper-env/include/pepper/env.h101
-rw-r--r--compiler/pepper-env/src/env.cpp47
-rw-r--r--compiler/pepper-env/src/env.test.cpp40
-rw-r--r--compiler/pepper-str/CMakeLists.txt2
-rw-r--r--compiler/pepper-strcast/CMakeLists.txt2
-rw-r--r--compiler/plier-tf/CMakeLists.txt21
-rw-r--r--compiler/plier-tf/include/plier/tf/TestHelper.h21
-rw-r--r--compiler/plier-tf/proto/CMakeLists.txt22
-rw-r--r--compiler/plier-tf/requires.cmake1
-rw-r--r--compiler/plier-tf/src/Convert.cpp4
-rw-r--r--compiler/plier-tf/src/Convert.test.cpp10
-rw-r--r--compiler/plier-tf/src/TestHelper.cpp30
-rw-r--r--compiler/pota-quantization-value-test/CMakeLists.txt69
-rw-r--r--compiler/pota-quantization-value-test/README.md55
-rwxr-xr-xcompiler/pota-quantization-value-test/compare_tensors.py117
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ifm2.json32
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/record_minmax/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ifm2.json32
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/record_minmax/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ifm2.json28
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/record_minmax/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ifm2.json28
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/record_minmax/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/fake_quantization/ker.json48
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/bias.json10
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ker.json61
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/fake_quantization/ker.json48
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/quantization/bias.json10
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/quantization/ker.json64
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/fake_quantization/ker.json48
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/quantization/bias.json7
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/quantization/ker.json52
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/fake_quantization/ker.json34
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/bias.json14
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ker.json53
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/fake_quantization/ker.json34
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/quantization/bias.json14
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/quantization/ker.json58
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/fake_quantization/ker.json34
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/quantization/bias.json9
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/quantization/ker.json38
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/fake_quantization/weight.json76
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/bias.json14
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/in.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/out.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/weight.json95
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/record_minmax/in.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/record_minmax/out.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/fake_quantization/weight.json76
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/quantization/bias.json14
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/quantization/in.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/quantization/out.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/quantization/weight.json100
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/record_minmax/in.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/record_minmax/out.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/fake_quantization/weight.json76
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/quantization/bias.json9
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/quantization/in.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/quantization/out.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/quantization/weight.json80
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/record_minmax/in.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/record_minmax/out.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/reduction_indices.json5
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ifm2.json32
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/record_minmax/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ifm2.json32
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/record_minmax/ifm1.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/alpha.json13
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/alpha.json13
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/fake_quantization/ker.json48
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/.json5
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ker.json58
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/fake_quantization/ker.json48
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/quantization/ker.json60
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/record_minmax/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/fake_quantization/ker.json48
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/quantization/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/quantization/ker.json52
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/quantization/ofm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/record_minmax/ifm.json4
-rw-r--r--compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/record_minmax/ofm.json4
-rwxr-xr-xcompiler/pota-quantization-value-test/gen_h5_explicit_inputs.py59
-rw-r--r--compiler/pota-quantization-value-test/requires.cmake4
-rw-r--r--compiler/pota-quantization-value-test/test.lst28
-rwxr-xr-xcompiler/pota-quantization-value-test/test_fake_wquant.sh87
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/4.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/0.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/1.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/2.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/3.txt1
-rw-r--r--compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/4.txt1
-rwxr-xr-xcompiler/pota-quantization-value-test/test_quantization.sh87
-rwxr-xr-xcompiler/pota-quantization-value-test/test_record_minmax.sh100
-rw-r--r--compiler/pp/CMakeLists.txt9
-rw-r--r--compiler/pp/src/LinearDocument.cpp4
-rw-r--r--compiler/rawdata2hdf5/CMakeLists.txt17
-rw-r--r--compiler/rawdata2hdf5/README.md24
-rw-r--r--compiler/rawdata2hdf5/rawdata2hdf5116
-rw-r--r--compiler/record-minmax-conversion-test/CMakeLists.txt42
-rw-r--r--compiler/record-minmax-conversion-test/README.md5
-rwxr-xr-xcompiler/record-minmax-conversion-test/gen_h5_random_inputs.py47
-rw-r--r--compiler/record-minmax-conversion-test/requires.cmake2
-rw-r--r--compiler/record-minmax-conversion-test/test.lst16
-rwxr-xr-xcompiler/record-minmax-conversion-test/testall.sh81
-rw-r--r--compiler/record-minmax/CMakeLists.txt32
-rw-r--r--compiler/record-minmax/README.md18
-rw-r--r--compiler/record-minmax/driver/Driver.cpp119
-rw-r--r--compiler/record-minmax/include/MinMaxObserver.h74
-rw-r--r--compiler/record-minmax/include/RecordFunction.h102
-rw-r--r--compiler/record-minmax/include/RecordMinMax.h52
-rw-r--r--compiler/record-minmax/requires.cmake4
-rw-r--r--compiler/record-minmax/src/HDF5Importer.cpp133
-rw-r--r--compiler/record-minmax/src/HDF5Importer.h87
-rw-r--r--compiler/record-minmax/src/MinMaxObserver.cpp69
-rw-r--r--compiler/record-minmax/src/RecordMinMax.cpp214
-rw-r--r--compiler/record-minmax/tests/RecordFunction.test.cpp118
-rw-r--r--compiler/safemain/README.md1
-rw-r--r--compiler/souschef/CMakeLists.txt13
-rw-r--r--compiler/souschef/README.md3
-rw-r--r--compiler/souschef/include/souschef/Arguments.h39
-rw-r--r--compiler/souschef/include/souschef/Data/Constant.h67
-rw-r--r--compiler/souschef/include/souschef/Data/Explicit.h80
-rw-r--r--compiler/souschef/include/souschef/Data/Gaussian.h93
-rw-r--r--compiler/souschef/include/souschef/DataChef.def19
-rw-r--r--compiler/souschef/include/souschef/DataChef.h61
-rw-r--r--compiler/souschef/include/souschef/DataChefs.h24
-rw-r--r--compiler/souschef/include/souschef/Dataset.h79
-rw-r--r--compiler/souschef/include/souschef/Dims.h49
-rw-r--r--compiler/souschef/include/souschef/LexicalCast.h37
-rw-r--r--compiler/souschef/include/souschef/RangedArguments.h53
-rw-r--r--compiler/souschef/include/souschef/Registry.h43
-rw-r--r--compiler/souschef/include/souschef/TensorFiller.h96
-rw-r--r--compiler/souschef/src/Dims.cpp19
-rw-r--r--compiler/souschef/src/Gaussian.cpp140
-rw-r--r--compiler/souschef/src/LexicalCast.cpp42
-rw-r--r--compiler/stdex/CMakeLists.txt9
-rw-r--r--compiler/stdex/include/stdex/Memory.h6
-rw-r--r--compiler/tf2circle-conversion-test/.gitignore1
-rw-r--r--compiler/tf2circle-conversion-test/CMakeLists.txt138
-rw-r--r--compiler/tf2circle-conversion-test/README.md3
-rw-r--r--compiler/tf2circle-conversion-test/requires.cmake2
-rw-r--r--compiler/tf2circle-conversion-test/test.lst103
-rwxr-xr-xcompiler/tf2circle-conversion-test/testall.sh90
-rw-r--r--compiler/tf2circle-dredd-pb-test/.gitignore1
-rw-r--r--compiler/tf2circle-dredd-pb-test/CMakeLists.txt141
-rw-r--r--compiler/tf2circle-dredd-pb-test/README.md3
-rw-r--r--compiler/tf2circle-dredd-pb-test/contrib/.gitignore3
-rw-r--r--compiler/tf2circle-dredd-pb-test/requires.cmake4
-rwxr-xr-xcompiler/tf2circle-dredd-pb-test/runner.sh121
-rw-r--r--compiler/tf2circle-dredd-pbtxt-test/.gitignore1
-rw-r--r--compiler/tf2circle-dredd-pbtxt-test/CMakeLists.txt184
-rw-r--r--compiler/tf2circle-dredd-pbtxt-test/README.md3
-rw-r--r--compiler/tf2circle-dredd-pbtxt-test/requires.cmake5
-rwxr-xr-xcompiler/tf2circle-dredd-pbtxt-test/runner.sh121
-rw-r--r--compiler/tf2circle-dredd-pbtxt-test/test.lst4
-rw-r--r--compiler/tf2circle-model-test/.gitignore1
-rw-r--r--compiler/tf2circle-model-test/CMakeLists.txt110
-rw-r--r--compiler/tf2circle-model-test/README.md1
-rw-r--r--compiler/tf2circle-model-test/contrib/.gitignore3
-rw-r--r--compiler/tf2circle-model-test/requires.cmake2
-rwxr-xr-xcompiler/tf2circle-model-test/runner.sh83
-rw-r--r--compiler/tf2circle-ui-check/.gitignore1
-rw-r--r--compiler/tf2circle-ui-check/CMakeLists.txt44
-rw-r--r--compiler/tf2circle-ui-check/README.md21
-rwxr-xr-xcompiler/tf2circle-ui-check/checkall.sh57
-rw-r--r--compiler/tf2circle-ui-check/requires.cmake2
-rw-r--r--compiler/tf2circle-value-pbtxt-remote-test/.gitignore1
-rw-r--r--compiler/tf2circle-value-pbtxt-remote-test/CMakeLists.txt169
-rw-r--r--compiler/tf2circle-value-pbtxt-remote-test/README.md138
-rw-r--r--compiler/tf2circle-value-pbtxt-remote-test/requires.cmake3
-rwxr-xr-xcompiler/tf2circle-value-pbtxt-remote-test/testall.sh152
-rw-r--r--compiler/tf2circle/CMakeLists.txt47
-rw-r--r--compiler/tf2circle/README.md3
-rw-r--r--compiler/tf2circle/proto/CustomOpInfo.proto57
-rw-r--r--compiler/tf2circle/requires.cmake8
-rw-r--r--compiler/tf2circle/src/CustomopConfLoader.cpp138
-rw-r--r--compiler/tf2circle/src/CustomopConfLoader.h32
-rw-r--r--compiler/tf2circle/src/tf2circle.cpp225
-rw-r--r--compiler/tf2nnpackage-value-remote-test/CMakeLists.txt93
-rw-r--r--compiler/tf2nnpackage-value-remote-test/README.md60
-rw-r--r--compiler/tf2nnpackage-value-remote-test/requires.cmake1
-rw-r--r--compiler/tf2nnpackage-value-remote-test/test.lst3
-rwxr-xr-xcompiler/tf2nnpackage-value-remote-test/testall.sh110
-rw-r--r--compiler/tf2nnpkg/CMakeLists.txt35
-rw-r--r--compiler/tf2nnpkg/README.md1
-rw-r--r--compiler/tf2nnpkg/requires.cmake8
-rw-r--r--compiler/tf2nnpkg/src/filesystem.h43
-rw-r--r--compiler/tf2nnpkg/src/filesystem_common.cpp44
-rw-r--r--compiler/tf2nnpkg/src/filesystem_linux.cpp46
-rw-r--r--compiler/tf2nnpkg/src/filesystem_windows.cpp55
-rw-r--r--compiler/tf2nnpkg/src/tf2nnpkg.cpp300
-rw-r--r--compiler/tf2tflite-dredd-pb-test/.gitignore1
-rw-r--r--compiler/tf2tflite-dredd-pb-test/CMakeLists.txt141
-rw-r--r--compiler/tf2tflite-dredd-pb-test/README.md6
-rw-r--r--compiler/tf2tflite-dredd-pb-test/contrib/.gitignore3
-rw-r--r--compiler/tf2tflite-dredd-pb-test/requires.cmake4
-rwxr-xr-xcompiler/tf2tflite-dredd-pb-test/runner.sh121
-rw-r--r--compiler/tf2tflite-dredd-pbtxt-test/.gitignore1
-rw-r--r--compiler/tf2tflite-dredd-pbtxt-test/CMakeLists.txt184
-rw-r--r--compiler/tf2tflite-dredd-pbtxt-test/README.md1
-rw-r--r--compiler/tf2tflite-dredd-pbtxt-test/requires.cmake5
-rwxr-xr-xcompiler/tf2tflite-dredd-pbtxt-test/runner.sh121
-rw-r--r--compiler/tf2tflite-dredd-pbtxt-test/test.lst1
-rw-r--r--compiler/tf2tflite-value-pb-test/.gitignore1
-rw-r--r--compiler/tf2tflite-value-pb-test/CMakeLists.txt131
-rw-r--r--compiler/tf2tflite-value-pb-test/README.md1
-rw-r--r--compiler/tf2tflite-value-pb-test/contrib/.gitignore3
-rw-r--r--compiler/tf2tflite-value-pb-test/requires.cmake6
-rwxr-xr-xcompiler/tf2tflite-value-pb-test/runner.sh112
-rw-r--r--compiler/tf2tflite-value-pbtxt-test/.gitignore1
-rw-r--r--compiler/tf2tflite-value-pbtxt-test/CMakeLists.txt159
-rw-r--r--compiler/tf2tflite-value-pbtxt-test/README.md3
-rw-r--r--compiler/tf2tflite-value-pbtxt-test/requires.cmake4
-rw-r--r--compiler/tf2tflite-value-pbtxt-test/test.lst101
-rwxr-xr-xcompiler/tf2tflite-value-pbtxt-test/testall.sh (renamed from compiler/tf2tflite/testall.sh)0
-rw-r--r--compiler/tf2tflite/CMakeLists.txt177
-rw-r--r--compiler/tf2tflite/requires.cmake6
-rw-r--r--compiler/tf2tflite/src/CustomopConfLoader.cpp4
-rw-r--r--compiler/tf2tflite/src/CustomopConfLoader.h2
-rw-r--r--compiler/tf2tflite/src/Driver.cpp13
-rw-r--r--compiler/tf2tflite/test.lst60
-rw-r--r--compiler/tf2tfliteV2-conversion-test/CMakeLists.txt109
-rw-r--r--compiler/tf2tfliteV2-conversion-test/README.md2
-rw-r--r--compiler/tf2tfliteV2-conversion-test/requires.cmake2
-rw-r--r--compiler/tf2tfliteV2-conversion-test/test.lst124
-rwxr-xr-xcompiler/tf2tfliteV2-conversion-test/testall.sh81
-rw-r--r--compiler/tf2tfliteV2/CMakeLists.txt13
-rw-r--r--compiler/tf2tfliteV2/README.md65
-rwxr-xr-xcompiler/tf2tfliteV2/tf2tfliteV2.py246
-rw-r--r--compiler/tfgraph-xform/CMakeLists.txt328
-rw-r--r--compiler/tfgraph-xform/README.md5
-rw-r--r--compiler/tfinfo-v2/CMakeLists.txt36
-rw-r--r--compiler/tfinfo-v2/README.md1
-rw-r--r--compiler/tfinfo-v2/include/tfinfo-v2/TensorInfoLoader.h43
-rw-r--r--compiler/tfinfo-v2/include/tfinfo-v2/TensorSignature.h124
-rw-r--r--compiler/tfinfo-v2/proto/tfinfo-v2.proto46
-rw-r--r--compiler/tfinfo-v2/requires.cmake2
-rw-r--r--compiler/tfinfo-v2/src/TFInfo_v2.test.cpp246
-rw-r--r--compiler/tfinfo-v2/src/TensorInfoLoader.cpp179
-rw-r--r--compiler/tfinfo-v2/src/TensorSignature.cpp17
-rw-r--r--compiler/tfinfo/CMakeLists.txt4
-rw-r--r--compiler/tfinfo/include/nnkit/support/tftestinfo/ParsedTensor.h10
-rw-r--r--compiler/tfinfo/requires.cmake3
-rw-r--r--compiler/tfinfo/src/TensorInfoParser.cpp54
-rw-r--r--compiler/tfinfo/src/TensorInfoParser.test.cpp13
-rw-r--r--compiler/tfkit/CMakeLists.txt36
-rw-r--r--compiler/tfkit/src/PackCommand.cpp21
-rw-r--r--compiler/tfkit/src/UnpackCommand.cpp95
-rw-r--r--compiler/tfl-inspect/CMakeLists.txt14
-rw-r--r--compiler/tfl-inspect/README.md51
-rw-r--r--compiler/tfl-inspect/driver/Driver.cpp85
-rw-r--r--compiler/tfl-inspect/requires.cmake4
-rw-r--r--compiler/tfl-inspect/src/Dump.cpp181
-rw-r--r--compiler/tfl-inspect/src/Dump.h65
-rw-r--r--compiler/tfl-inspect/src/Reader.cpp169
-rw-r--r--compiler/tfl-inspect/src/Reader.h91
-rw-r--r--compiler/tfl-verify/CMakeLists.txt13
-rw-r--r--compiler/tfl-verify/README.md23
-rw-r--r--compiler/tfl-verify/requires.cmake5
-rw-r--r--compiler/tfl-verify/src/Driver.cpp59
-rw-r--r--compiler/tfl-verify/src/VerifyFlatBuffers.cpp36
-rw-r--r--compiler/tfl-verify/src/VerifyFlatBuffers.h32
-rw-r--r--compiler/tflchef/CMakeLists.txt15
-rw-r--r--compiler/tflchef/core/CMakeLists.txt9
-rw-r--r--compiler/tflchef/core/schema/tflite.fbs698
-rw-r--r--compiler/tflchef/core/schema/tflite.meta2
-rw-r--r--compiler/tflchef/core/src/Arguments.h34
-rw-r--r--compiler/tflchef/core/src/Convert.cpp96
-rw-r--r--compiler/tflchef/core/src/Convert.h8
-rw-r--r--compiler/tflchef/core/src/CustomOp/AddV2.cpp63
-rw-r--r--compiler/tflchef/core/src/CustomOp/AddV2.h49
-rw-r--r--compiler/tflchef/core/src/CustomOp/All.cpp61
-rw-r--r--compiler/tflchef/core/src/CustomOp/All.h49
-rw-r--r--compiler/tflchef/core/src/CustomOp/BatchMatMulV2.cpp65
-rw-r--r--compiler/tflchef/core/src/CustomOp/BatchMatMulV2.h49
-rw-r--r--compiler/tflchef/core/src/CustomOp/MatMul.cpp63
-rw-r--r--compiler/tflchef/core/src/CustomOp/MatMul.h49
-rw-r--r--compiler/tflchef/core/src/CustomOp/MatrixBandPart.cpp62
-rw-r--r--compiler/tflchef/core/src/CustomOp/MatrixBandPart.h49
-rw-r--r--compiler/tflchef/core/src/CustomOp/MaxPoolWithArgMax.cpp86
-rw-r--r--compiler/tflchef/core/src/CustomOp/MaxPoolWithArgMax.h49
-rw-r--r--compiler/tflchef/core/src/Data/Constant.h62
-rw-r--r--compiler/tflchef/core/src/Data/Explicit.h82
-rw-r--r--compiler/tflchef/core/src/Data/Gaussian.cpp61
-rw-r--r--compiler/tflchef/core/src/Data/Gaussian.h46
-rw-r--r--compiler/tflchef/core/src/DataChef.def9
-rw-r--r--compiler/tflchef/core/src/DataChef.h56
-rw-r--r--compiler/tflchef/core/src/DataChefs.h24
-rw-r--r--compiler/tflchef/core/src/Dataset.h57
-rw-r--r--compiler/tflchef/core/src/LexicalCast.cpp20
-rw-r--r--compiler/tflchef/core/src/LexicalCast.h32
-rw-r--r--compiler/tflchef/core/src/ModelChef.cpp510
-rw-r--r--compiler/tflchef/core/src/Op/Abs.cpp30
-rw-r--r--compiler/tflchef/core/src/Op/Abs.h46
-rw-r--r--compiler/tflchef/core/src/Op/Add.cpp39
-rw-r--r--compiler/tflchef/core/src/Op/Add.h46
-rw-r--r--compiler/tflchef/core/src/Op/AddN.cpp32
-rw-r--r--compiler/tflchef/core/src/Op/AddN.h46
-rw-r--r--compiler/tflchef/core/src/Op/ArgMax.cpp39
-rw-r--r--compiler/tflchef/core/src/Op/ArgMax.h46
-rw-r--r--compiler/tflchef/core/src/Op/ArgMin.cpp39
-rw-r--r--compiler/tflchef/core/src/Op/ArgMin.h46
-rw-r--r--compiler/tflchef/core/src/Op/BatchMatMul.cpp35
-rw-r--r--compiler/tflchef/core/src/Op/BatchMatMul.h49
-rw-r--r--compiler/tflchef/core/src/Op/BatchToSpaceND.cpp31
-rw-r--r--compiler/tflchef/core/src/Op/BatchToSpaceND.h52
-rw-r--r--compiler/tflchef/core/src/Op/Cast.cpp42
-rw-r--r--compiler/tflchef/core/src/Op/Cast.h46
-rw-r--r--compiler/tflchef/core/src/Op/Ceil.cpp28
-rw-r--r--compiler/tflchef/core/src/Op/Ceil.h46
-rw-r--r--compiler/tflchef/core/src/Op/Conv2D.cpp12
-rw-r--r--compiler/tflchef/core/src/Op/Cos.cpp29
-rw-r--r--compiler/tflchef/core/src/Op/Cos.h46
-rw-r--r--compiler/tflchef/core/src/Op/DepthToSpace.cpp42
-rw-r--r--compiler/tflchef/core/src/Op/DepthToSpace.h52
-rw-r--r--compiler/tflchef/core/src/Op/DepthwiseConv2D.cpp2
-rw-r--r--compiler/tflchef/core/src/Op/Dequantize.cpp27
-rw-r--r--compiler/tflchef/core/src/Op/Dequantize.h46
-rw-r--r--compiler/tflchef/core/src/Op/ELU.cpp27
-rw-r--r--compiler/tflchef/core/src/Op/ELU.h46
-rw-r--r--compiler/tflchef/core/src/Op/Equal.cpp29
-rw-r--r--compiler/tflchef/core/src/Op/Equal.h46
-rw-r--r--compiler/tflchef/core/src/Op/Exp.cpp30
-rw-r--r--compiler/tflchef/core/src/Op/Exp.h46
-rw-r--r--compiler/tflchef/core/src/Op/ExpandDims.cpp30
-rw-r--r--compiler/tflchef/core/src/Op/ExpandDims.h49
-rw-r--r--compiler/tflchef/core/src/Op/Fill.cpp31
-rw-r--r--compiler/tflchef/core/src/Op/Fill.h46
-rw-r--r--compiler/tflchef/core/src/Op/Floor.cpp28
-rw-r--r--compiler/tflchef/core/src/Op/Floor.h46
-rw-r--r--compiler/tflchef/core/src/Op/FloorDiv.cpp30
-rw-r--r--compiler/tflchef/core/src/Op/FloorDiv.h49
-rw-r--r--compiler/tflchef/core/src/Op/FloorMod.cpp28
-rw-r--r--compiler/tflchef/core/src/Op/FloorMod.h49
-rw-r--r--compiler/tflchef/core/src/Op/Gather.cpp39
-rw-r--r--compiler/tflchef/core/src/Op/Gather.h46
-rw-r--r--compiler/tflchef/core/src/Op/GatherNd.cpp31
-rw-r--r--compiler/tflchef/core/src/Op/GatherNd.h49
-rw-r--r--compiler/tflchef/core/src/Op/Greater.cpp29
-rw-r--r--compiler/tflchef/core/src/Op/Greater.h46
-rw-r--r--compiler/tflchef/core/src/Op/GreaterEqual.cpp29
-rw-r--r--compiler/tflchef/core/src/Op/GreaterEqual.h52
-rw-r--r--compiler/tflchef/core/src/Op/If.cpp35
-rw-r--r--compiler/tflchef/core/src/Op/If.h46
-rw-r--r--compiler/tflchef/core/src/Op/L2Normalize.cpp33
-rw-r--r--compiler/tflchef/core/src/Op/L2Normalize.h49
-rw-r--r--compiler/tflchef/core/src/Op/L2Pool2D.cpp47
-rw-r--r--compiler/tflchef/core/src/Op/L2Pool2D.h46
-rw-r--r--compiler/tflchef/core/src/Op/LeakyRelu.cpp34
-rw-r--r--compiler/tflchef/core/src/Op/LeakyRelu.h49
-rw-r--r--compiler/tflchef/core/src/Op/Less.cpp29
-rw-r--r--compiler/tflchef/core/src/Op/Less.h46
-rw-r--r--compiler/tflchef/core/src/Op/LessEqual.cpp29
-rw-r--r--compiler/tflchef/core/src/Op/LessEqual.h49
-rw-r--r--compiler/tflchef/core/src/Op/LocalResponseNormalization.cpp50
-rw-r--r--compiler/tflchef/core/src/Op/LocalResponseNormalization.h53
-rw-r--r--compiler/tflchef/core/src/Op/Log.cpp28
-rw-r--r--compiler/tflchef/core/src/Op/Log.h46
-rw-r--r--compiler/tflchef/core/src/Op/LogSoftmax.cpp32
-rw-r--r--compiler/tflchef/core/src/Op/LogSoftmax.h49
-rw-r--r--compiler/tflchef/core/src/Op/LogicalAnd.cpp29
-rw-r--r--compiler/tflchef/core/src/Op/LogicalAnd.h49
-rw-r--r--compiler/tflchef/core/src/Op/LogicalNot.cpp29
-rw-r--r--compiler/tflchef/core/src/Op/LogicalNot.h49
-rw-r--r--compiler/tflchef/core/src/Op/LogicalOr.cpp29
-rw-r--r--compiler/tflchef/core/src/Op/LogicalOr.h49
-rw-r--r--compiler/tflchef/core/src/Op/Logistic.cpp28
-rw-r--r--compiler/tflchef/core/src/Op/Logistic.h46
-rw-r--r--compiler/tflchef/core/src/Op/MatrixDiag.cpp28
-rw-r--r--compiler/tflchef/core/src/Op/MatrixDiag.h49
-rw-r--r--compiler/tflchef/core/src/Op/MatrixSetDiag.cpp28
-rw-r--r--compiler/tflchef/core/src/Op/MatrixSetDiag.h52
-rw-r--r--compiler/tflchef/core/src/Op/Maximum.cpp31
-rw-r--r--compiler/tflchef/core/src/Op/Maximum.h49
-rw-r--r--compiler/tflchef/core/src/Op/Mean.cpp39
-rw-r--r--compiler/tflchef/core/src/Op/Mean.h46
-rw-r--r--compiler/tflchef/core/src/Op/Minimum.cpp31
-rw-r--r--compiler/tflchef/core/src/Op/Minimum.h49
-rw-r--r--compiler/tflchef/core/src/Op/MirrorPad.cpp41
-rw-r--r--compiler/tflchef/core/src/Op/MirrorPad.h49
-rw-r--r--compiler/tflchef/core/src/Op/Mul.cpp39
-rw-r--r--compiler/tflchef/core/src/Op/Mul.h46
-rw-r--r--compiler/tflchef/core/src/Op/Neg.cpp30
-rw-r--r--compiler/tflchef/core/src/Op/Neg.h46
-rw-r--r--compiler/tflchef/core/src/Op/NonMaxSuppressionV4.cpp30
-rw-r--r--compiler/tflchef/core/src/Op/NonMaxSuppressionV4.h52
-rw-r--r--compiler/tflchef/core/src/Op/NonMaxSuppressionV5.cpp30
-rw-r--r--compiler/tflchef/core/src/Op/NonMaxSuppressionV5.h52
-rw-r--r--compiler/tflchef/core/src/Op/NotEqual.cpp29
-rw-r--r--compiler/tflchef/core/src/Op/NotEqual.h49
-rw-r--r--compiler/tflchef/core/src/Op/OneHot.cpp39
-rw-r--r--compiler/tflchef/core/src/Op/OneHot.h46
-rw-r--r--compiler/tflchef/core/src/Op/PRelu.cpp31
-rw-r--r--compiler/tflchef/core/src/Op/PRelu.h47
-rw-r--r--compiler/tflchef/core/src/Op/Pack.cpp38
-rw-r--r--compiler/tflchef/core/src/Op/Pack.h46
-rw-r--r--compiler/tflchef/core/src/Op/Pad.cpp28
-rw-r--r--compiler/tflchef/core/src/Op/Pad.h46
-rw-r--r--compiler/tflchef/core/src/Op/PadV2.cpp28
-rw-r--r--compiler/tflchef/core/src/Op/PadV2.h46
-rw-r--r--compiler/tflchef/core/src/Op/Pow.cpp31
-rw-r--r--compiler/tflchef/core/src/Op/Pow.h46
-rw-r--r--compiler/tflchef/core/src/Op/Range.cpp31
-rw-r--r--compiler/tflchef/core/src/Op/Range.h46
-rw-r--r--compiler/tflchef/core/src/Op/Rank.cpp29
-rw-r--r--compiler/tflchef/core/src/Op/Rank.h46
-rw-r--r--compiler/tflchef/core/src/Op/ReLUN1To1.cpp27
-rw-r--r--compiler/tflchef/core/src/Op/ReLUN1To1.h46
-rw-r--r--compiler/tflchef/core/src/Op/ReduceAny.cpp39
-rw-r--r--compiler/tflchef/core/src/Op/ReduceAny.h46
-rw-r--r--compiler/tflchef/core/src/Op/ReduceMax.cpp39
-rw-r--r--compiler/tflchef/core/src/Op/ReduceMax.h46
-rw-r--r--compiler/tflchef/core/src/Op/ReduceMin.cpp39
-rw-r--r--compiler/tflchef/core/src/Op/ReduceMin.h46
-rw-r--r--compiler/tflchef/core/src/Op/ReduceProd.cpp39
-rw-r--r--compiler/tflchef/core/src/Op/ReduceProd.h46
-rw-r--r--compiler/tflchef/core/src/Op/Reshape.cpp3
-rw-r--r--compiler/tflchef/core/src/Op/ResizeBilinear.cpp42
-rw-r--r--compiler/tflchef/core/src/Op/ResizeBilinear.h52
-rw-r--r--compiler/tflchef/core/src/Op/ResizeNearestNeighbor.cpp43
-rw-r--r--compiler/tflchef/core/src/Op/ResizeNearestNeighbor.h52
-rw-r--r--compiler/tflchef/core/src/Op/ReverseSequence.cpp42
-rw-r--r--compiler/tflchef/core/src/Op/ReverseSequence.h52
-rw-r--r--compiler/tflchef/core/src/Op/ReverseV2.cpp29
-rw-r--r--compiler/tflchef/core/src/Op/ReverseV2.h49
-rw-r--r--compiler/tflchef/core/src/Op/Round.cpp28
-rw-r--r--compiler/tflchef/core/src/Op/Round.h46
-rw-r--r--compiler/tflchef/core/src/Op/Rsqrt.cpp28
-rw-r--r--compiler/tflchef/core/src/Op/Rsqrt.h46
-rw-r--r--compiler/tflchef/core/src/Op/ScatterNd.cpp32
-rw-r--r--compiler/tflchef/core/src/Op/ScatterNd.h49
-rw-r--r--compiler/tflchef/core/src/Op/SegmentSum.cpp29
-rw-r--r--compiler/tflchef/core/src/Op/SegmentSum.h49
-rw-r--r--compiler/tflchef/core/src/Op/Select.cpp29
-rw-r--r--compiler/tflchef/core/src/Op/Select.h46
-rw-r--r--compiler/tflchef/core/src/Op/SelectV2.cpp29
-rw-r--r--compiler/tflchef/core/src/Op/SelectV2.h49
-rw-r--r--compiler/tflchef/core/src/Op/Shape.cpp39
-rw-r--r--compiler/tflchef/core/src/Op/Shape.h46
-rw-r--r--compiler/tflchef/core/src/Op/Sin.cpp28
-rw-r--r--compiler/tflchef/core/src/Op/Sin.h46
-rw-r--r--compiler/tflchef/core/src/Op/Slice.cpp31
-rw-r--r--compiler/tflchef/core/src/Op/Slice.h46
-rw-r--r--compiler/tflchef/core/src/Op/Softmax.cpp39
-rw-r--r--compiler/tflchef/core/src/Op/Softmax.h46
-rw-r--r--compiler/tflchef/core/src/Op/SpaceToBatchND.cpp31
-rw-r--r--compiler/tflchef/core/src/Op/SpaceToBatchND.h52
-rw-r--r--compiler/tflchef/core/src/Op/SpaceToDepth.cpp38
-rw-r--r--compiler/tflchef/core/src/Op/SpaceToDepth.h52
-rw-r--r--compiler/tflchef/core/src/Op/SparseToDense.cpp39
-rw-r--r--compiler/tflchef/core/src/Op/SparseToDense.h52
-rw-r--r--compiler/tflchef/core/src/Op/Split.cpp39
-rw-r--r--compiler/tflchef/core/src/Op/Split.h46
-rw-r--r--compiler/tflchef/core/src/Op/SplitV.cpp39
-rw-r--r--compiler/tflchef/core/src/Op/SplitV.h46
-rw-r--r--compiler/tflchef/core/src/Op/Square.cpp29
-rw-r--r--compiler/tflchef/core/src/Op/Square.h46
-rw-r--r--compiler/tflchef/core/src/Op/SquaredDifference.cpp30
-rw-r--r--compiler/tflchef/core/src/Op/SquaredDifference.h52
-rw-r--r--compiler/tflchef/core/src/Op/Squeeze.cpp41
-rw-r--r--compiler/tflchef/core/src/Op/Squeeze.h46
-rw-r--r--compiler/tflchef/core/src/Op/StridedSlice.cpp44
-rw-r--r--compiler/tflchef/core/src/Op/StridedSlice.h52
-rw-r--r--compiler/tflchef/core/src/Op/Sum.cpp39
-rw-r--r--compiler/tflchef/core/src/Op/Sum.h46
-rw-r--r--compiler/tflchef/core/src/Op/Tanh.cpp28
-rw-r--r--compiler/tflchef/core/src/Op/Tanh.h46
-rw-r--r--compiler/tflchef/core/src/Op/Tile.cpp29
-rw-r--r--compiler/tflchef/core/src/Op/Tile.h46
-rw-r--r--compiler/tflchef/core/src/Op/TopKV2.cpp29
-rw-r--r--compiler/tflchef/core/src/Op/TopKV2.h46
-rw-r--r--compiler/tflchef/core/src/Op/Transpose.cpp32
-rw-r--r--compiler/tflchef/core/src/Op/Transpose.h49
-rw-r--r--compiler/tflchef/core/src/Op/TransposeConv.cpp43
-rw-r--r--compiler/tflchef/core/src/Op/TransposeConv.h52
-rw-r--r--compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.cpp45
-rw-r--r--compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.h53
-rw-r--r--compiler/tflchef/core/src/Op/Unique.cpp39
-rw-r--r--compiler/tflchef/core/src/Op/Unique.h46
-rw-r--r--compiler/tflchef/core/src/Op/Unpack.cpp35
-rw-r--r--compiler/tflchef/core/src/Op/Unpack.h46
-rw-r--r--compiler/tflchef/core/src/Op/Where.cpp28
-rw-r--r--compiler/tflchef/core/src/Op/Where.h46
-rw-r--r--compiler/tflchef/core/src/Op/While.cpp35
-rw-r--r--compiler/tflchef/core/src/Op/While.h46
-rw-r--r--compiler/tflchef/core/src/Op/ZerosLike.cpp28
-rw-r--r--compiler/tflchef/core/src/Op/ZerosLike.h49
-rw-r--r--compiler/tflchef/core/src/OpChef.def104
-rw-r--r--compiler/tflchef/core/src/OpChef.h9
-rw-r--r--compiler/tflchef/core/src/OpChefs.h103
-rw-r--r--compiler/tflchef/log/CMakeLists.txt7
-rw-r--r--compiler/tflchef/log/include/Log.h75
-rw-r--r--compiler/tflchef/log/include/LoggingContext.h35
-rw-r--r--compiler/tflchef/log/src/Log.cpp87
-rw-r--r--compiler/tflchef/log/src/LoggingContext.cpp41
-rw-r--r--compiler/tflchef/proto/tflchef.proto533
-rw-r--r--compiler/tflchef/requires.cmake7
-rw-r--r--compiler/tflchef/tests/CMakeLists.txt109
-rw-r--r--compiler/tflchef/tests/averagepool2d/test.recipe24
-rw-r--r--compiler/tflchef/tests/concatenation/test.recipe28
-rw-r--r--compiler/tflchef/tests/conv2d/test.recipe44
-rw-r--r--compiler/tflchef/tests/conv2d/test.reverse0
-rw-r--r--compiler/tflchef/tests/depthwiseconv2d/test.recipe41
-rw-r--r--compiler/tflchef/tests/depthwiseconv2d/test.reverse0
-rw-r--r--compiler/tflchef/tests/div/test.recipe27
-rw-r--r--compiler/tflchef/tests/div/test.reverse0
-rw-r--r--compiler/tflchef/tests/fullyconnected/test.recipe34
-rw-r--r--compiler/tflchef/tests/fullyconnected/test.reverse0
-rw-r--r--compiler/tflchef/tests/fullyconnected2/test.recipe34
-rw-r--r--compiler/tflchef/tests/fullyconnected2/test.reverse0
-rw-r--r--compiler/tflchef/tests/maxpool2d/test.recipe24
-rw-r--r--compiler/tflchef/tests/maxpool2d/test.reverse0
-rw-r--r--compiler/tflchef/tests/multisubgraph/test.recipe72
-rw-r--r--compiler/tflchef/tests/no_shape/test.recipe43
-rw-r--r--compiler/tflchef/tests/no_shape/test.reverse (renamed from compiler/tflchef/tests/averagepool2d/test.reverse)0
-rw-r--r--compiler/tflchef/tests/quantization/test.recipe46
-rw-r--r--compiler/tflchef/tests/quantization/test.reverse0
-rw-r--r--compiler/tflchef/tests/relu/test.recipe17
-rw-r--r--compiler/tflchef/tests/relu/test.reverse0
-rw-r--r--compiler/tflchef/tests/relu6/test.recipe17
-rw-r--r--compiler/tflchef/tests/relu6/test.reverse0
-rw-r--r--compiler/tflchef/tests/reshape/test.recipe20
-rw-r--r--compiler/tflchef/tests/reshape/test.reverse0
-rwxr-xr-xcompiler/tflchef/tests/runvalidate.sh56
-rw-r--r--compiler/tflchef/tests/shape_signature/test.recipe19
-rw-r--r--compiler/tflchef/tests/shape_signature/test.reverse (renamed from compiler/tflchef/tests/concatenation/test.reverse)0
-rw-r--r--compiler/tflchef/tests/sqrt/test.recipe18
-rw-r--r--compiler/tflchef/tests/sub/test.recipe27
-rw-r--r--compiler/tflchef/tests/sub/test.reverse0
-rw-r--r--compiler/tflchef/tflite/CMakeLists.txt3
-rw-r--r--compiler/tflchef/tflite/include/tflchef/RawModel.h41
-rw-r--r--compiler/tflchef/tflite/include/tflchef/RecipeChef.h2
-rw-r--r--compiler/tflchef/tflite/src/Convert.cpp62
-rw-r--r--compiler/tflchef/tflite/src/Convert.h18
-rw-r--r--compiler/tflchef/tflite/src/FillerHelper.cpp50
-rw-r--r--compiler/tflchef/tflite/src/FillerHelper.h31
-rw-r--r--compiler/tflchef/tflite/src/Op/Abs.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/Abs.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Add.cpp54
-rw-r--r--compiler/tflchef/tflite/src/Op/Add.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/AddN.cpp46
-rw-r--r--compiler/tflchef/tflite/src/Op/AddN.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/ArgMax.cpp54
-rw-r--r--compiler/tflchef/tflite/src/Op/ArgMax.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/ArgMin.cpp54
-rw-r--r--compiler/tflchef/tflite/src/Op/ArgMin.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/BatchMatMul.cpp48
-rw-r--r--compiler/tflchef/tflite/src/Op/BatchMatMul.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/BatchToSpaceND.cpp53
-rw-r--r--compiler/tflchef/tflite/src/Op/BatchToSpaceND.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Cast.cpp48
-rw-r--r--compiler/tflchef/tflite/src/Op/Cast.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Ceil.cpp39
-rw-r--r--compiler/tflchef/tflite/src/Op/Ceil.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Conv2D.cpp3
-rw-r--r--compiler/tflchef/tflite/src/Op/Cos.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/Cos.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/DepthToSpace.cpp47
-rw-r--r--compiler/tflchef/tflite/src/Op/DepthToSpace.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/DepthwiseConv2D.cpp5
-rw-r--r--compiler/tflchef/tflite/src/Op/Dequantize.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/Dequantize.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/ELU.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/ELU.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Equal.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/Equal.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Exp.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/Exp.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/ExpandDims.cpp47
-rw-r--r--compiler/tflchef/tflite/src/Op/ExpandDims.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Fill.cpp47
-rw-r--r--compiler/tflchef/tflite/src/Op/Fill.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Floor.cpp39
-rw-r--r--compiler/tflchef/tflite/src/Op/Floor.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/FloorDiv.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/FloorDiv.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/FloorMod.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/FloorMod.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/FullyConnected.cpp10
-rw-r--r--compiler/tflchef/tflite/src/Op/Gather.cpp59
-rw-r--r--compiler/tflchef/tflite/src/Op/Gather.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/GatherNd.cpp50
-rw-r--r--compiler/tflchef/tflite/src/Op/GatherNd.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Greater.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/Greater.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/GreaterEqual.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/GreaterEqual.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/L2Normalize.cpp45
-rw-r--r--compiler/tflchef/tflite/src/Op/L2Normalize.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/L2Pool2D.cpp52
-rw-r--r--compiler/tflchef/tflite/src/Op/L2Pool2D.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/LeakyRelu.cpp46
-rw-r--r--compiler/tflchef/tflite/src/Op/LeakyRelu.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Less.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/Less.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/LessEqual.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/LessEqual.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/LocalResponseNormalization.cpp51
-rw-r--r--compiler/tflchef/tflite/src/Op/LocalResponseNormalization.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Log.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/Log.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/LogSoftmax.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/LogSoftmax.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/LogicalAnd.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/LogicalAnd.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/LogicalNot.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/LogicalNot.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/LogicalOr.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/LogicalOr.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Logistic.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/Logistic.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/MatrixDiag.cpp38
-rw-r--r--compiler/tflchef/tflite/src/Op/MatrixDiag.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/MatrixSetDiag.cpp38
-rw-r--r--compiler/tflchef/tflite/src/Op/MatrixSetDiag.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Maximum.cpp38
-rw-r--r--compiler/tflchef/tflite/src/Op/Maximum.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Mean.cpp54
-rw-r--r--compiler/tflchef/tflite/src/Op/Mean.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Minimum.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/Minimum.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/MirrorPad.cpp53
-rw-r--r--compiler/tflchef/tflite/src/Op/MirrorPad.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Mul.cpp53
-rw-r--r--compiler/tflchef/tflite/src/Op/Mul.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Neg.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/Neg.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/NonMaxSuppressionV4.cpp56
-rw-r--r--compiler/tflchef/tflite/src/Op/NonMaxSuppressionV4.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/NonMaxSuppressionV5.cpp59
-rw-r--r--compiler/tflchef/tflite/src/Op/NonMaxSuppressionV5.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/NotEqual.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/NotEqual.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/OneHot.cpp87
-rw-r--r--compiler/tflchef/tflite/src/Op/OneHot.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/PRelu.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/PRelu.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Pack.cpp48
-rw-r--r--compiler/tflchef/tflite/src/Op/Pack.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Pad.cpp47
-rw-r--r--compiler/tflchef/tflite/src/Op/Pad.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/PadV2.cpp42
-rw-r--r--compiler/tflchef/tflite/src/Op/PadV2.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Pow.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/Pow.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Range.cpp61
-rw-r--r--compiler/tflchef/tflite/src/Op/Range.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Rank.cpp38
-rw-r--r--compiler/tflchef/tflite/src/Op/Rank.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/ReLUN1To1.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/ReLUN1To1.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/ReduceAny.cpp52
-rw-r--r--compiler/tflchef/tflite/src/Op/ReduceAny.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/ReduceMax.cpp54
-rw-r--r--compiler/tflchef/tflite/src/Op/ReduceMax.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/ReduceMin.cpp54
-rw-r--r--compiler/tflchef/tflite/src/Op/ReduceMin.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/ReduceProd.cpp52
-rw-r--r--compiler/tflchef/tflite/src/Op/ReduceProd.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Reshape.cpp25
-rw-r--r--compiler/tflchef/tflite/src/Op/ResizeBilinear.cpp59
-rw-r--r--compiler/tflchef/tflite/src/Op/ResizeBilinear.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/ResizeNearestNeighbor.cpp59
-rw-r--r--compiler/tflchef/tflite/src/Op/ResizeNearestNeighbor.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/ReverseSequence.cpp53
-rw-r--r--compiler/tflchef/tflite/src/Op/ReverseSequence.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/ReverseV2.cpp44
-rw-r--r--compiler/tflchef/tflite/src/Op/ReverseV2.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Round.cpp37
-rw-r--r--compiler/tflchef/tflite/src/Op/Round.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Rsqrt.cpp39
-rw-r--r--compiler/tflchef/tflite/src/Op/Rsqrt.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/ScatterNd.cpp43
-rw-r--r--compiler/tflchef/tflite/src/Op/ScatterNd.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/SegmentSum.cpp41
-rw-r--r--compiler/tflchef/tflite/src/Op/SegmentSum.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Select.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/Select.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/SelectV2.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/SelectV2.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Shape.cpp45
-rw-r--r--compiler/tflchef/tflite/src/Op/Shape.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Sin.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/Sin.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Slice.cpp50
-rw-r--r--compiler/tflchef/tflite/src/Op/Slice.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Softmax.cpp47
-rw-r--r--compiler/tflchef/tflite/src/Op/Softmax.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/SpaceToBatchND.cpp53
-rw-r--r--compiler/tflchef/tflite/src/Op/SpaceToBatchND.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/SpaceToDepth.cpp47
-rw-r--r--compiler/tflchef/tflite/src/Op/SpaceToDepth.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/SparseToDense.cpp54
-rw-r--r--compiler/tflchef/tflite/src/Op/SparseToDense.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Split.cpp54
-rw-r--r--compiler/tflchef/tflite/src/Op/Split.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/SplitV.cpp56
-rw-r--r--compiler/tflchef/tflite/src/Op/SplitV.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Sqrt.cpp13
-rw-r--r--compiler/tflchef/tflite/src/Op/Square.cpp40
-rw-r--r--compiler/tflchef/tflite/src/Op/Square.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/SquaredDifference.cpp41
-rw-r--r--compiler/tflchef/tflite/src/Op/SquaredDifference.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Squeeze.cpp52
-rw-r--r--compiler/tflchef/tflite/src/Op/Squeeze.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/StridedSlice.cpp60
-rw-r--r--compiler/tflchef/tflite/src/Op/StridedSlice.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Sub.cpp9
-rw-r--r--compiler/tflchef/tflite/src/Op/Sum.cpp54
-rw-r--r--compiler/tflchef/tflite/src/Op/Sum.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Tanh.cpp39
-rw-r--r--compiler/tflchef/tflite/src/Op/Tanh.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Tile.cpp48
-rw-r--r--compiler/tflchef/tflite/src/Op/Tile.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/TopKV2.cpp50
-rw-r--r--compiler/tflchef/tflite/src/Op/TopKV2.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Transpose.cpp48
-rw-r--r--compiler/tflchef/tflite/src/Op/Transpose.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/TransposeConv.cpp64
-rw-r--r--compiler/tflchef/tflite/src/Op/TransposeConv.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.cpp66
-rw-r--r--compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Unique.cpp47
-rw-r--r--compiler/tflchef/tflite/src/Op/Unique.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Unpack.cpp45
-rw-r--r--compiler/tflchef/tflite/src/Op/Unpack.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/Where.cpp36
-rw-r--r--compiler/tflchef/tflite/src/Op/Where.h39
-rw-r--r--compiler/tflchef/tflite/src/Op/ZerosLike.cpp42
-rw-r--r--compiler/tflchef/tflite/src/Op/ZerosLike.h39
-rw-r--r--compiler/tflchef/tflite/src/RawModelLoader.cpp94
-rw-r--r--compiler/tflchef/tflite/src/RecipeChef.cpp132
-rw-r--r--compiler/tflchef/tflite/src/TFliteImport.h66
-rw-r--r--compiler/tflchef/tflite/src/TFliteOpChef.h2
-rw-r--r--compiler/tflchef/tflite/src/TFliteOpChefs.h94
-rw-r--r--compiler/tflchef/tflite/src/TFliteOpRegistry.h128
-rw-r--r--compiler/tflchef/tools/console/Driver.cpp15
-rw-r--r--compiler/tflchef/tools/file/CMakeLists.txt1
-rw-r--r--compiler/tflchef/tools/file/Driver.cpp41
-rw-r--r--compiler/tflchef/tools/reverse/CMakeLists.txt2
-rw-r--r--compiler/tflchef/tools/reverse/Driver.cpp38
-rw-r--r--compiler/tfldump/CMakeLists.txt16
-rw-r--r--compiler/tfldump/driver/Driver.cpp24
-rw-r--r--compiler/tfldump/include/tfldump/Dump.h2
-rw-r--r--compiler/tfldump/include/tflread/Model.h2
-rw-r--r--compiler/tfldump/requires.cmake3
-rw-r--r--compiler/tfldump/schema/schema.fbs698
-rw-r--r--compiler/tfldump/schema/schema.meta2
-rw-r--r--compiler/tfldump/src/Dump.cpp283
-rw-r--r--compiler/tfldump/src/OpPrinter.cpp572
-rw-r--r--compiler/tfldump/src/OpPrinter.h2
-rw-r--r--compiler/tfldump/src/Read.cpp10
-rw-r--r--compiler/tfldump/src/Read.h10
-rw-r--r--compiler/tflite2circle-conversion-test/CMakeLists.txt68
-rw-r--r--compiler/tflite2circle-conversion-test/README.md3
-rw-r--r--compiler/tflite2circle-conversion-test/requires.cmake2
-rwxr-xr-xcompiler/tflite2circle-conversion-test/testall.sh77
-rw-r--r--compiler/tflite2circle/CMakeLists.txt19
-rw-r--r--compiler/tflite2circle/README.md11
-rw-r--r--compiler/tflite2circle/driver/Driver.cpp91
-rw-r--r--compiler/tflite2circle/include/CircleModel.h101
-rw-r--r--compiler/tflite2circle/include/TFLModel.h55
-rw-r--r--compiler/tflite2circle/requires.cmake5
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions.h114
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/AbsOptions.cpp32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/AbsOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/AddNOptions.cpp32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/AddNOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/AddOptions.cpp36
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/AddOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ArgMaxOptions.cpp36
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ArgMaxOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ArgMinOptions.cpp36
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ArgMinOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/BatchMatMulOptions.cpp36
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/BatchMatMulOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/BatchToSpaceNDOptions.cpp32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/BatchToSpaceNDOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/CastOptions.cpp40
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/CastOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ConcatenationOptions.cpp37
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ConcatenationOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/Conv2DOptions.cpp41
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/Conv2DOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/CosOptions.cpp32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/CosOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/DepthToSpaceOptions.cpp35
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/DepthToSpaceOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/DepthwiseConv2DOptions.cpp42
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/DepthwiseConv2DOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/DivOptions.cpp36
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/DivOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/EqualOptions.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/EqualOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ExpOptions.cpp32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ExpOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ExpandDimsOptions.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ExpandDimsOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/FillOptions.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/FillOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/FloorDivOptions.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/FloorDivOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/FloorModOptions.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/FloorModOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/FullyConnectedOptions.cpp43
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/FullyConnectedOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/GatherNdOptions.cpp32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/GatherNdOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/GatherOptions.cpp35
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/GatherOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/GreaterEqualOptions.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/GreaterEqualOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/GreaterOptions.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/GreaterOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/IfOptions.cpp36
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/IfOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/L2NormalizeOptions.cpp36
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/L2NormalizeOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/LeakyReluOptions.cpp32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/LeakyReluOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/LessEqualOptions.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/LessEqualOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/LessOptions.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/LessOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/LocalResponseNormalizationOptions.cpp39
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/LocalResponseNormalizationOptions.h32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/LogSoftmaxOptions.cpp32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/LogSoftmaxOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/LogicalAndOptions.cpp32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/LogicalAndOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/LogicalNotOptions.cpp32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/LogicalNotOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/LogicalOrOptions.cpp32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/LogicalOrOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/MatrixDiagOptions.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/MatrixDiagOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/MatrixSetDiagOptions.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/MatrixSetDiagOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/MaximumMinimumOptions.cpp34
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/MaximumMinimumOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/MirrorPadOptions.cpp35
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/MirrorPadOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/MulOptions.cpp36
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/MulOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/NegOptions.cpp32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/NegOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/NonMaxSuppressionV4Options.cpp30
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/NonMaxSuppressionV4Options.h32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/NonMaxSuppressionV5Options.cpp30
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/NonMaxSuppressionV5Options.h32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/NotEqualOptions.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/NotEqualOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/OneHotOptions.cpp35
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/OneHotOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/PackOptions.cpp35
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/PackOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/PadOptions.cpp31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/PadOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/PadV2Options.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/PadV2Options.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/Pool2DOptions.cpp41
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/Pool2DOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/PowOptions.cpp32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/PowOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/RangeOptions.cpp32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/RangeOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/RankOptions.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/RankOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ReducerOptions.cpp34
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ReducerOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ReshapeOptions.cpp39
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ReshapeOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ResizeBilinearOptions.cpp36
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ResizeBilinearOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ResizeNearestNeighborOptions.cpp36
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ResizeNearestNeighborOptions.h32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ReverseSequenceOptions.cpp33
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ReverseSequenceOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ReverseV2Options.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ReverseV2Options.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ScatterNdOptions.cpp32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ScatterNdOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SegmentSumOptions.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SegmentSumOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SelectOptions.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SelectOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SelectV2Options.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SelectV2Options.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ShapeOptions.cpp35
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ShapeOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SliceOptions.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SliceOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SoftmaxOptions.cpp35
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SoftmaxOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SpaceToBatchNDOptions.cpp32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SpaceToBatchNDOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SpaceToDepthOptions.cpp35
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SpaceToDepthOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SparseToDenseOptions.cpp35
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SparseToDenseOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SplitOptions.cpp34
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SplitOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SplitVOptions.cpp34
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SplitVOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SquareOptions.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SquareOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SquaredDifferenceOptions.cpp30
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SquaredDifferenceOptions.h32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SqueezeOptions.cpp37
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SqueezeOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/StridedSliceOptions.cpp39
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/StridedSliceOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SubOptions.cpp36
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/SubOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/TileOptions.cpp31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/TileOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/TopKV2Options.cpp31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/TopKV2Options.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/TransposeConvOptions.cpp37
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/TransposeConvOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/TransposeOptions.cpp31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/TransposeOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.cpp41
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.h32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/UniqueOptions.cpp36
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/UniqueOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/UnpackOptions.cpp36
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/UnpackOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/WhereOptions.cpp29
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/WhereOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/WhileOptions.cpp36
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/WhileOptions.h31
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ZerosLikeOptions.cpp32
-rw-r--r--compiler/tflite2circle/src/BuildBuiltinOptions/ZerosLikeOptions.h31
-rw-r--r--compiler/tflite2circle/src/CircleModel.cpp342
-rw-r--r--compiler/tflite2circle/src/DataLookup.cpp200
-rw-r--r--compiler/tflite2circle/src/DataLookup.h99
-rw-r--r--compiler/tflite2circle/src/TFLActivationFunctionType.lst12
-rw-r--r--compiler/tflite2circle/src/TFLBuiltinOptions.lst108
-rw-r--r--compiler/tflite2circle/src/TFLModel.cpp43
-rw-r--r--compiler/tflite2circle/src/TFLOperator.lst133
-rw-r--r--compiler/tflite2circle/src/TFLTensorType.lst16
-rw-r--r--compiler/tfts/CMakeLists.txt5
-rw-r--r--compiler/v4tf/README.md16
-rw-r--r--compiler/vconone/CMakeLists.txt31
-rw-r--r--compiler/vconone/README.md14
-rw-r--r--compiler/vconone/driver/driver.cpp36
-rw-r--r--compiler/vconone/include/vconone/vconone.h61
-rw-r--r--compiler/vconone/src/version.cpp63
-rw-r--r--compiler/vconone/src/version.test.cpp49
-rw-r--r--compiler/vconone/version_cfg.h.in22
3846 files changed, 192958 insertions, 51944 deletions
diff --git a/compiler/.ahub/tcchecker-tca/config.yaml b/compiler/.ahub/tcchecker-tca/config.yaml
new file mode 100644
index 000000000..ef681de1a
--- /dev/null
+++ b/compiler/.ahub/tcchecker-tca/config.yaml
@@ -0,0 +1,54 @@
+version: 2
+test:
+ - name: NN Compiler
+ testCaseLanguage: CPP
+ testFW: GTEST
+ testCaseFolder:
+ - ./angkor
+ - ./arser
+ - ./circle2circle
+ - ./circle-quantizer
+ - ./cwrap
+ - ./foder
+ - ./hermes
+ - ./hermes-std
+ - ./loco
+ - ./locomotiv
+ - ./locop
+ - ./logo
+ - ./logo-core
+ - ./luci
+ - ./luci-interpreter
+ - ./luci-value-test
+ - ./mio-circle
+ - ./mio-tflite
+ - ./oops
+ - ./pepper-assert
+ - ./pepper-str
+ - ./pepper-strcast
+ - ./pp
+ - ./record-minmax
+ - ./safemain
+ - ./souschef
+ - ./stdex
+ - ./tflite2circle
+
+ testFile:
+ - extension: .test.cpp
+ any: true
+
+ testCase:
+ - condition:
+ - functionName:
+ starts:
+ - TEST
+
+ negativeTestCase:
+ - condition:
+ - testName:
+ ends:
+ - _NEG
+
+ positiveTestCase:
+ - condition:
+ - inverse: negativeTestCase
diff --git a/compiler/CMakeLists.txt b/compiler/CMakeLists.txt
index b009a6ec6..7cf12f164 100644
--- a/compiler/CMakeLists.txt
+++ b/compiler/CMakeLists.txt
@@ -14,10 +14,10 @@ function(get_project_build_order VAR)
set(REQUIRES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_DIR}/requires.cmake")
macro(require PRED)
- file(APPEND "${DEPS_FILE}" "${PRED} ${SUCC}\n")
+ file(APPEND "${DEPS_FILE}" "${PRED} ${SUCC} ")
endmacro(require)
- file(APPEND "${DEPS_FILE}" "${SUCC} ${SUCC}\n")
+ file(APPEND "${DEPS_FILE}" "${SUCC} ${SUCC} ")
if(EXISTS "${REQUIRES_FILE}")
include(${REQUIRES_FILE})
endif(EXISTS "${REQUIRES_FILE}")
@@ -41,10 +41,30 @@ function(add_compiler_directory DIR)
string(TOUPPER ${DIR} PREFIX)
option(BUILD_COMPILER_${PREFIX} "Build compiler/${dir}" ON)
+ set(BUILD_WHITELIST "" CACHE STRING "Set modules to be built")
- if(BUILD_COMPILER_${PREFIX})
+ if(NOT BUILD_WHITELIST STREQUAL "")
+ set(ENABLE OFF)
+ set(CURRENT_DIR ${DIR})
+ foreach(ACCEPTED_DIR IN ITEMS ${BUILD_WHITELIST})
+ if(ACCEPTED_DIR STREQUAL CURRENT_DIR)
+ set(ENABLE ON)
+ endif()
+ endforeach(ACCEPTED_DIR)
+ else()
+ set(ENABLE ${BUILD_COMPILER_${PREFIX}})
+ endif()
+
+ # This line prevents some errors in this CMakeLists.txt
+ if(NOT DEFINED ENABLE)
+ message(FATAL_ERROR "Undefined ENABLE! Please check CMakeLists.txt")
+ endif()
+
+ if(ENABLE)
+ message(STATUS "Configure ${PREFIX}")
add_subdirectory(${DIR})
- endif(BUILD_COMPILER_${PREFIX})
+ message(STATUS "Configure ${PREFIX} - Done")
+ endif(ENABLE)
endfunction(add_compiler_directory)
function(add_compiler_directories)
diff --git a/compiler/adtidas/README.md b/compiler/adtidas/README.md
new file mode 100644
index 000000000..df427d335
--- /dev/null
+++ b/compiler/adtidas/README.md
@@ -0,0 +1 @@
+# adtidas
diff --git a/compiler/adtidas/include/adtidas/SmallVector.h b/compiler/adtidas/include/adtidas/SmallVector.h
index 1ba48da74..1ad630c63 100644
--- a/compiler/adtidas/include/adtidas/SmallVector.h
+++ b/compiler/adtidas/include/adtidas/SmallVector.h
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-#ifndef _NNC_CORE_SMALL_VECTOR_H
-#define _NNC_CORE_SMALL_VECTOR_H
+#ifndef _ADTIDAS_SMALL_VECTOR_H_
+#define _ADTIDAS_SMALL_VECTOR_H_
#include <cassert>
#include <iterator>
#include <initializer_list>
-namespace adt {
+namespace adt
+{
/**
* @brief vector with cheap memory allocation
@@ -29,28 +30,33 @@ namespace adt {
* @tparam Capacity maximum number of elements
* @note much like std::array, but tracks number of used elements. Stored in stack
*/
-template <typename T, size_t Capacity>
-class small_vector {
+template <typename T, size_t Capacity> class small_vector
+{
public:
using value_type = T;
- using reference = T&;
- using iterator = T*;
+ using reference = T &;
+ using iterator = T *;
+ using const_iterator = const T *;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using size_type = size_t;
- template <typename It>
- small_vector(It begin, It end) : _size(std::distance(begin, end)) {
+ template <typename It> small_vector(It begin, It end) : _size(std::distance(begin, end))
+ {
assert(_size <= Capacity);
std::copy(begin, end, this->begin());
}
- explicit small_vector(size_t size, value_type initializer = value_type()) : _size(size) {
+ explicit small_vector(size_t size, value_type initializer = value_type()) : _size(size)
+ {
assert(_size <= Capacity);
std::fill(begin(), end(), initializer);
}
explicit small_vector() : _size(0) {}
- small_vector(std::initializer_list<value_type>&& l) : _size(l.size()) {
+ small_vector(std::initializer_list<value_type> l) : _size(l.size())
+ {
assert(_size <= Capacity);
std::copy(std::begin(l), std::end(l), begin());
}
@@ -58,24 +64,22 @@ public:
/**
* @return current size
*/
- inline size_t size() const noexcept {
- return _size;
- }
+ inline size_t size() const noexcept { return _size; }
/**
* @return maximum number of elements this vector can hold
*/
- constexpr size_t capacity() const {
- return Capacity;
- }
+ constexpr size_t capacity() const { return Capacity; }
/**
- * @brief resize to given new size
- * @note if new size is greater than current size, new elements are default-initialized
- */
- void resize(size_t new_size) noexcept {
+ * @brief resize to given new size
+ * @note if new size is greater than current size, new elements are default-initialized
+ */
+ void resize(size_t new_size) noexcept
+ {
assert(new_size <= Capacity);
- if (new_size > _size) {
+ if (new_size > _size)
+ {
std::fill(_storage + _size, _storage + new_size, T());
}
_size = new_size;
@@ -84,7 +88,8 @@ public:
/**
* @return reference to the element at position idx
*/
- inline reference operator[](size_t idx) noexcept {
+ inline reference operator[](size_t idx) noexcept
+ {
assert(idx < _size);
return _storage[idx];
}
@@ -92,43 +97,54 @@ public:
/**
* @return value of element at position idx
*/
- inline constexpr value_type operator[](size_t idx) const noexcept {
- //assert on the same line since c++11 does not allow multi-line constexpr functions
+ inline constexpr value_type operator[](size_t idx) const noexcept
+ {
+ // assert on the same line since c++11 does not allow multi-line constexpr functions
return assert(idx < _size), _storage[idx];
}
- inline iterator begin() noexcept {
- return std::begin(_storage);
- }
+ inline iterator begin() noexcept { return std::begin(_storage); }
+ inline iterator end() noexcept { return _storage + _size; }
- inline iterator end() noexcept {
- return _storage + _size;
- }
+ inline reverse_iterator rbegin() noexcept { return reverse_iterator{end()}; }
+ inline reverse_iterator rend() noexcept { return reverse_iterator{begin()}; }
+
+ // const overloads
+ inline const_iterator begin() const noexcept { return std::begin(_storage); }
+ inline const_iterator end() const noexcept { return _storage + _size; }
+
+ inline const_reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
+ inline const_reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
- inline void push_back(const value_type& e) noexcept {
+ inline void push_back(const value_type &e) noexcept
+ {
assert(_size < Capacity);
_storage[_size++] = e;
}
- inline void push_back(value_type&& e) noexcept {
+ inline void push_back(value_type &&e) noexcept
+ {
assert(_size < Capacity);
_storage[_size++] = std::move(e);
}
private:
size_t _size;
- value_type _storage[Capacity];
+ value_type _storage[Capacity]{};
};
template <typename T, size_t LCapacity, size_t RCapacity>
-bool operator==(const small_vector<T, LCapacity>& lhs, const small_vector<T, RCapacity>& rhs) {
- if (lhs.size() != rhs.size()) {
+bool operator==(const small_vector<T, LCapacity> &lhs, const small_vector<T, RCapacity> &rhs)
+{
+ if (lhs.size() != rhs.size())
+ {
return false;
}
bool equal = true;
size_t end = lhs.size();
- for (size_t i = 0; i < end; ++i) {
+ for (size_t i = 0; i < end; ++i)
+ {
equal &= (lhs[i] == rhs[i]);
}
@@ -137,4 +153,4 @@ bool operator==(const small_vector<T, LCapacity>& lhs, const small_vector<T, RCa
} // namespace adt
-#endif //_NNC_CORE_SMALL_VECTOR_H
+#endif //_ADTIDAS_SMALL_VECTOR_H_
diff --git a/compiler/angkor/CMakeLists.txt b/compiler/angkor/CMakeLists.txt
index 4f12b07c4..44b5e9058 100644
--- a/compiler/angkor/CMakeLists.txt
+++ b/compiler/angkor/CMakeLists.txt
@@ -16,7 +16,7 @@ if(NOT ENABLE_TEST)
endif(NOT ENABLE_TEST)
# Google Test is mandatory for test
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
GTest_AddTest(angkor_test ${TESTS})
target_link_libraries(angkor_test angkor)
diff --git a/compiler/angkor/include/nncc/core/ADT/Iterable.h b/compiler/angkor/include/nncc/core/ADT/Iterable.h
deleted file mode 100644
index f76d44d2b..000000000
--- a/compiler/angkor/include/nncc/core/ADT/Iterable.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __NNCC_CORE_ADT_ITERABLE_H__
-#define __NNCC_CORE_ADT_ITERABLE_H__
-
-namespace nncc
-{
-namespace core
-{
-namespace ADT
-{
-
-template <typename T> class Iterable
-{
-public:
- Iterable(const T *ptr) : _ptr{ptr}
- {
- // DO NOTHING
- }
-
-public:
- const T &get(void) const { return *_ptr; }
-
-private:
- const T *const _ptr;
-};
-
-template <typename T, typename Callable>
-const Iterable<T> &operator<<(const Iterable<T> &it, Callable cb)
-{
- it.get().iterate(cb);
- return it;
-}
-
-} // namespace ADT
-} // namespace core
-} // namespace nncc
-
-#endif // __NNCC_CORE_ADT_ITERABLE_H__
diff --git a/compiler/angkor/include/nncc/core/ADT/tensor/Index.h b/compiler/angkor/include/nncc/core/ADT/tensor/Index.h
index 99d748fab..19beafafc 100644
--- a/compiler/angkor/include/nncc/core/ADT/tensor/Index.h
+++ b/compiler/angkor/include/nncc/core/ADT/tensor/Index.h
@@ -53,6 +53,10 @@ private:
std::vector<uint32_t> _indices;
};
+// It throws an exception when rank of inputs does not match.
+Index operator+(const Index &lhs, const Index &rhs);
+bool operator==(const Index &lhs, const Index &rhs);
+
} // namespace tensor
} // namespace ADT
} // namespace core
diff --git a/compiler/angkor/include/nncc/core/ADT/tensor/Shape.h b/compiler/angkor/include/nncc/core/ADT/tensor/Shape.h
index 4cc0882e3..3eaab0e54 100644
--- a/compiler/angkor/include/nncc/core/ADT/tensor/Shape.h
+++ b/compiler/angkor/include/nncc/core/ADT/tensor/Shape.h
@@ -53,6 +53,9 @@ private:
std::vector<uint32_t> _dims;
};
+/**
+ * NOTE num_elements returns 1 for rank-0 tensors
+ */
uint64_t num_elements(const Shape &);
Shape squeeze(const Shape &);
diff --git a/compiler/angkor/src/ADT/feature/Buffer.test.cpp b/compiler/angkor/src/ADT/feature/Buffer.test.cpp
index 1e4430251..f61b7244b 100644
--- a/compiler/angkor/src/ADT/feature/Buffer.test.cpp
+++ b/compiler/angkor/src/ADT/feature/Buffer.test.cpp
@@ -30,9 +30,9 @@ TEST(ADT_FEATURE_BUFFER, ctor)
const Shape shape{4, 6, 3};
auto buffer = make_buffer<int, CHWLayout>(shape);
- ASSERT_EQ(buffer.shape().depth(), shape.depth());
- ASSERT_EQ(buffer.shape().height(), shape.height());
- ASSERT_EQ(buffer.shape().width(), shape.width());
+ ASSERT_EQ(shape.depth(), buffer.shape().depth());
+ ASSERT_EQ(shape.height(), buffer.shape().height());
+ ASSERT_EQ(shape.width(), buffer.shape().width());
}
TEST(ADT_FEATURE_BUFFER, access)
@@ -40,9 +40,9 @@ TEST(ADT_FEATURE_BUFFER, access)
const Shape shape{4, 6, 3};
auto buffer = make_buffer<int, CHWLayout>(shape);
- ASSERT_EQ(buffer.at(3, 5, 2), 0);
+ ASSERT_EQ(0, buffer.at(3, 5, 2));
buffer.at(3, 5, 2) = 4;
// Casting is introduced to use 'const T &at(...) const' method
- ASSERT_EQ(static_cast<const Buffer<int> &>(buffer).at(3, 5, 2), 4);
+ ASSERT_EQ(4, static_cast<const Buffer<int> &>(buffer).at(3, 5, 2));
}
diff --git a/compiler/angkor/src/ADT/feature/CHWLayout.test.cpp b/compiler/angkor/src/ADT/feature/CHWLayout.test.cpp
index 5610df8f3..72aef22d1 100644
--- a/compiler/angkor/src/ADT/feature/CHWLayout.test.cpp
+++ b/compiler/angkor/src/ADT/feature/CHWLayout.test.cpp
@@ -25,7 +25,7 @@ TEST(ADT_FEATURE_CHW_LAYOUT, col_increase)
const Shape shape{4, 3, 6};
const CHWLayout l;
- ASSERT_EQ(l.offset(shape, 1, 2, 1) + 1, l.offset(shape, 1, 2, 2));
+ ASSERT_EQ(l.offset(shape, 1, 2, 2), l.offset(shape, 1, 2, 1) + 1);
}
TEST(ADT_FEATURE_CHW_LAYOUT, row_increase)
@@ -33,7 +33,7 @@ TEST(ADT_FEATURE_CHW_LAYOUT, row_increase)
const Shape shape{4, 3, 6};
const CHWLayout l;
- ASSERT_EQ(l.offset(shape, 1, 1, 1) + 6, l.offset(shape, 1, 2, 1));
+ ASSERT_EQ(l.offset(shape, 1, 2, 1), l.offset(shape, 1, 1, 1) + 6);
}
TEST(ADT_FEATURE_CHW_LAYOUT, ch_increase)
@@ -41,5 +41,5 @@ TEST(ADT_FEATURE_CHW_LAYOUT, ch_increase)
const Shape shape{4, 3, 6};
const CHWLayout l;
- ASSERT_EQ(l.offset(shape, 1, 1, 1) + 6 * 3, l.offset(shape, 2, 1, 1));
+ ASSERT_EQ(l.offset(shape, 2, 1, 1), l.offset(shape, 1, 1, 1) + 6 * 3);
}
diff --git a/compiler/angkor/src/ADT/feature/HWCLayout.test.cpp b/compiler/angkor/src/ADT/feature/HWCLayout.test.cpp
index d1f359753..1cfb29c6f 100644
--- a/compiler/angkor/src/ADT/feature/HWCLayout.test.cpp
+++ b/compiler/angkor/src/ADT/feature/HWCLayout.test.cpp
@@ -29,7 +29,7 @@ TEST(ADT_FEATURE_HWC_LAYOUT, C_increase)
const Shape shape{C, H, W};
const HWCLayout l;
- ASSERT_EQ(l.offset(shape, 1, 1, 1) + 1, l.offset(shape, 2, 1, 1));
+ ASSERT_EQ(l.offset(shape, 2, 1, 1), l.offset(shape, 1, 1, 1) + 1);
}
TEST(ADT_FEATURE_HWC_LAYOUT, W_increase)
@@ -41,7 +41,7 @@ TEST(ADT_FEATURE_HWC_LAYOUT, W_increase)
const Shape shape{C, H, W};
const HWCLayout l;
- ASSERT_EQ(l.offset(shape, 1, 2, 1) + C, l.offset(shape, 1, 2, 2));
+ ASSERT_EQ(l.offset(shape, 1, 2, 2), l.offset(shape, 1, 2, 1) + C);
}
TEST(ADT_FEATURE_HWC_LAYOUT, H_increase)
@@ -53,5 +53,5 @@ TEST(ADT_FEATURE_HWC_LAYOUT, H_increase)
const Shape shape{C, H, W};
const HWCLayout l;
- ASSERT_EQ(l.offset(shape, 1, 1, 1) + W * C, l.offset(shape, 1, 2, 1));
+ ASSERT_EQ(l.offset(shape, 1, 2, 1), l.offset(shape, 1, 1, 1) + W * C);
}
diff --git a/compiler/angkor/src/ADT/feature/Layout.test.cpp b/compiler/angkor/src/ADT/feature/Layout.test.cpp
index 023594e16..de91cb3cd 100644
--- a/compiler/angkor/src/ADT/feature/Layout.test.cpp
+++ b/compiler/angkor/src/ADT/feature/Layout.test.cpp
@@ -28,7 +28,7 @@ TEST(ADT_FEATURE_LAYOUT, ctor)
{
Layout l{offset_0};
- ASSERT_EQ(l.offset(Shape{4, 3, 6}, 1, 1, 1), 0);
+ ASSERT_EQ(0, l.offset(Shape{4, 3, 6}, 1, 1, 1));
}
TEST(ADT_FEATURE_LAYOUT, copy)
@@ -36,11 +36,11 @@ TEST(ADT_FEATURE_LAYOUT, copy)
Layout orig{offset_0};
Layout copy{offset_1};
- ASSERT_EQ(copy.offset(Shape{4, 3, 6}, 1, 1, 1), 1);
+ ASSERT_EQ(1, copy.offset(Shape{4, 3, 6}, 1, 1, 1));
copy = orig;
- ASSERT_EQ(copy.offset(Shape{4, 3, 6}, 1, 1, 1), 0);
+ ASSERT_EQ(0, copy.offset(Shape{4, 3, 6}, 1, 1, 1));
}
TEST(ADT_FEATURE_LAYOUT, move)
@@ -48,9 +48,9 @@ TEST(ADT_FEATURE_LAYOUT, move)
Layout orig{offset_0};
Layout move{offset_1};
- ASSERT_EQ(move.offset(Shape{4, 3, 6}, 1, 1, 1), 1);
+ ASSERT_EQ(1, move.offset(Shape{4, 3, 6}, 1, 1, 1));
move = std::move(orig);
- ASSERT_EQ(move.offset(Shape{4, 3, 6}, 1, 1, 1), 0);
+ ASSERT_EQ(0, move.offset(Shape{4, 3, 6}, 1, 1, 1));
}
diff --git a/compiler/angkor/src/ADT/feature/Overlay.test.cpp b/compiler/angkor/src/ADT/feature/Overlay.test.cpp
index c8e2943f8..8ba28bf5a 100644
--- a/compiler/angkor/src/ADT/feature/Overlay.test.cpp
+++ b/compiler/angkor/src/ADT/feature/Overlay.test.cpp
@@ -34,9 +34,9 @@ TEST(ADT_FEATURE_OVERLAY, ctor)
};
auto overlay = make_overlay<int, CHWLayout>(shape, data);
- ASSERT_EQ(overlay.shape().depth(), shape.depth());
- ASSERT_EQ(overlay.shape().height(), shape.height());
- ASSERT_EQ(overlay.shape().width(), shape.width());
+ ASSERT_EQ(shape.depth(), overlay.shape().depth());
+ ASSERT_EQ(shape.height(), overlay.shape().height());
+ ASSERT_EQ(shape.width(), overlay.shape().width());
}
TEST(ADT_FEATURE_OVERLAY, read)
@@ -50,9 +50,9 @@ TEST(ADT_FEATURE_OVERLAY, read)
CHWLayout layout{};
- ASSERT_EQ(data[layout.offset(shape, 3, 5, 2)], 0);
+ ASSERT_EQ(0, data[layout.offset(shape, 3, 5, 2)]);
data[layout.offset(shape, 3, 5, 2)] = 2;
- ASSERT_EQ(overlay.at(3, 5, 2), 2);
+ ASSERT_EQ(2, overlay.at(3, 5, 2));
}
TEST(ADT_FEATURE_OVERLAY, access)
@@ -66,7 +66,7 @@ TEST(ADT_FEATURE_OVERLAY, access)
CHWLayout layout{};
- ASSERT_EQ(data[layout.offset(shape, 3, 5, 2)], 0);
+ ASSERT_EQ(0, data[layout.offset(shape, 3, 5, 2)]);
overlay.at(3, 5, 2) = 4;
- ASSERT_EQ(data[layout.offset(shape, 3, 5, 2)], 4);
+ ASSERT_EQ(4, data[layout.offset(shape, 3, 5, 2)]);
}
diff --git a/compiler/angkor/src/ADT/feature/Shape.test.cpp b/compiler/angkor/src/ADT/feature/Shape.test.cpp
index 9216182f0..460561bc3 100644
--- a/compiler/angkor/src/ADT/feature/Shape.test.cpp
+++ b/compiler/angkor/src/ADT/feature/Shape.test.cpp
@@ -26,9 +26,9 @@ TEST(ADT_FEATURE_SHAPE, ctor)
nncc::core::ADT::feature::Shape shape{C, H, W};
- ASSERT_EQ(shape.depth(), C);
- ASSERT_EQ(shape.height(), H);
- ASSERT_EQ(shape.width(), W);
+ ASSERT_EQ(C, shape.depth());
+ ASSERT_EQ(H, shape.height());
+ ASSERT_EQ(W, shape.width());
}
TEST(ADT_FEATURE_SHAPE, num_elements)
@@ -40,7 +40,7 @@ TEST(ADT_FEATURE_SHAPE, num_elements)
using nncc::core::ADT::feature::Shape;
using nncc::core::ADT::feature::num_elements;
- ASSERT_EQ(num_elements(Shape{C, H, W}), C * H * W);
+ ASSERT_EQ(C * H * W, num_elements(Shape{C, H, W}));
}
TEST(ADT_FEATURE_SHAPE, operator_eq)
diff --git a/compiler/angkor/src/ADT/kernel/Buffer.test.cpp b/compiler/angkor/src/ADT/kernel/Buffer.test.cpp
index da344593e..a3d92a6f2 100644
--- a/compiler/angkor/src/ADT/kernel/Buffer.test.cpp
+++ b/compiler/angkor/src/ADT/kernel/Buffer.test.cpp
@@ -30,10 +30,10 @@ TEST(ADT_KERNEL_BUFFER, ctor)
const Shape shape{2, 4, 6, 3};
auto buffer = make_buffer<int, NCHWLayout>(shape);
- ASSERT_EQ(buffer.shape().count(), shape.count());
- ASSERT_EQ(buffer.shape().depth(), shape.depth());
- ASSERT_EQ(buffer.shape().height(), shape.height());
- ASSERT_EQ(buffer.shape().width(), shape.width());
+ ASSERT_EQ(shape.count(), buffer.shape().count());
+ ASSERT_EQ(shape.depth(), buffer.shape().depth());
+ ASSERT_EQ(shape.height(), buffer.shape().height());
+ ASSERT_EQ(shape.width(), buffer.shape().width());
}
TEST(ADT_KERNEL_BUFFER, access)
@@ -41,9 +41,9 @@ TEST(ADT_KERNEL_BUFFER, access)
const Shape shape{2, 4, 6, 3};
auto buffer = make_buffer<int, NCHWLayout>(shape);
- ASSERT_EQ(buffer.at(1, 3, 5, 2), 0);
+ ASSERT_EQ(0, buffer.at(1, 3, 5, 2));
buffer.at(1, 3, 5, 2) = 4;
// Casting is introduced to use 'const T &at(...) const' method
- ASSERT_EQ(static_cast<const Buffer<int> &>(buffer).at(1, 3, 5, 2), 4);
+ ASSERT_EQ(4, static_cast<const Buffer<int> &>(buffer).at(1, 3, 5, 2));
}
diff --git a/compiler/angkor/src/ADT/kernel/Layout.test.cpp b/compiler/angkor/src/ADT/kernel/Layout.test.cpp
index 94885cd4e..36234d2a6 100644
--- a/compiler/angkor/src/ADT/kernel/Layout.test.cpp
+++ b/compiler/angkor/src/ADT/kernel/Layout.test.cpp
@@ -28,7 +28,7 @@ TEST(ADT_KERNEL_LAYOUT, ctor)
{
Layout l{offset_0};
- ASSERT_EQ(l.offset(Shape{4, 3, 6, 5}, 1, 1, 1, 1), 0);
+ ASSERT_EQ(0, l.offset(Shape{4, 3, 6, 5}, 1, 1, 1, 1));
}
TEST(ADT_KERNEL_LAYOUT, copy)
@@ -36,11 +36,11 @@ TEST(ADT_KERNEL_LAYOUT, copy)
Layout orig{offset_0};
Layout copy{offset_1};
- ASSERT_EQ(copy.offset(Shape{4, 3, 6, 5}, 1, 1, 1, 1), 1);
+ ASSERT_EQ(1, copy.offset(Shape{4, 3, 6, 5}, 1, 1, 1, 1));
copy = orig;
- ASSERT_EQ(copy.offset(Shape{4, 3, 6, 5}, 1, 1, 1, 1), 0);
+ ASSERT_EQ(0, copy.offset(Shape{4, 3, 6, 5}, 1, 1, 1, 1));
}
TEST(ADT_KERNEL_LAYOUT, move)
@@ -48,9 +48,9 @@ TEST(ADT_KERNEL_LAYOUT, move)
Layout orig{offset_0};
Layout move{offset_1};
- ASSERT_EQ(move.offset(Shape{4, 3, 6, 5}, 1, 1, 1, 1), 1);
+ ASSERT_EQ(1, move.offset(Shape{4, 3, 6, 5}, 1, 1, 1, 1));
move = std::move(orig);
- ASSERT_EQ(move.offset(Shape{4, 3, 6, 5}, 1, 1, 1, 1), 0);
+ ASSERT_EQ(0, move.offset(Shape{4, 3, 6, 5}, 1, 1, 1, 1));
}
diff --git a/compiler/angkor/src/ADT/kernel/NCHWLayout.test.cpp b/compiler/angkor/src/ADT/kernel/NCHWLayout.test.cpp
index ba03b7b04..578bc58c7 100644
--- a/compiler/angkor/src/ADT/kernel/NCHWLayout.test.cpp
+++ b/compiler/angkor/src/ADT/kernel/NCHWLayout.test.cpp
@@ -25,7 +25,7 @@ TEST(ADT_KERNEL_KERNEL_NCHW_LAYOUT, col_increment)
const Shape shape{4, 3, 6, 5};
const NCHWLayout l;
- ASSERT_EQ(l.offset(shape, 1, 1, 1, 1) + 1, l.offset(shape, 1, 1, 1, 2));
+ ASSERT_EQ(l.offset(shape, 1, 1, 1, 2), l.offset(shape, 1, 1, 1, 1) + 1);
}
TEST(ADT_KERNEL_KERNEL_NCHW_LAYOUT, row_increment)
@@ -33,7 +33,7 @@ TEST(ADT_KERNEL_KERNEL_NCHW_LAYOUT, row_increment)
const Shape shape{4, 3, 6, 5};
const NCHWLayout l;
- ASSERT_EQ(l.offset(shape, 1, 1, 1, 1) + 5, l.offset(shape, 1, 1, 2, 1));
+ ASSERT_EQ(l.offset(shape, 1, 1, 2, 1), l.offset(shape, 1, 1, 1, 1) + 5);
}
TEST(ADT_KERNEL_KERNEL_NCHW_LAYOUT, ch_increment)
@@ -41,7 +41,7 @@ TEST(ADT_KERNEL_KERNEL_NCHW_LAYOUT, ch_increment)
const Shape shape{4, 3, 6, 5};
const NCHWLayout l;
- ASSERT_EQ(l.offset(shape, 1, 1, 1, 1) + 6 * 5, l.offset(shape, 1, 2, 1, 1));
+ ASSERT_EQ(l.offset(shape, 1, 2, 1, 1), l.offset(shape, 1, 1, 1, 1) + 6 * 5);
}
TEST(ADT_KERNEL_KERNEL_NCHW_LAYOUT, n_increment)
@@ -49,5 +49,5 @@ TEST(ADT_KERNEL_KERNEL_NCHW_LAYOUT, n_increment)
const Shape shape{4, 3, 6, 5};
const NCHWLayout l;
- ASSERT_EQ(l.offset(shape, 1, 1, 1, 1) + 3 * 6 * 5, l.offset(shape, 2, 1, 1, 1));
+ ASSERT_EQ(l.offset(shape, 2, 1, 1, 1), l.offset(shape, 1, 1, 1, 1) + 3 * 6 * 5);
}
diff --git a/compiler/angkor/src/ADT/kernel/NHWCLayout.test.cpp b/compiler/angkor/src/ADT/kernel/NHWCLayout.test.cpp
index 2c5df7d89..184e10751 100644
--- a/compiler/angkor/src/ADT/kernel/NHWCLayout.test.cpp
+++ b/compiler/angkor/src/ADT/kernel/NHWCLayout.test.cpp
@@ -31,7 +31,7 @@ TEST(ADT_KERNEL_KERNEL_NHWC_LAYOUT, ch_increment)
const Shape shape{N, C, H, W};
const NHWCLayout l;
- ASSERT_EQ(l.offset(shape, 1, 1, 1, 1) + 1, l.offset(shape, 1, 2, 1, 1));
+ ASSERT_EQ(l.offset(shape, 1, 2, 1, 1), l.offset(shape, 1, 1, 1, 1) + 1);
}
TEST(ADT_KERNEL_KERNEL_NHWC_LAYOUT, col_increment)
@@ -44,7 +44,7 @@ TEST(ADT_KERNEL_KERNEL_NHWC_LAYOUT, col_increment)
const Shape shape{N, C, H, W};
const NHWCLayout l;
- ASSERT_EQ(l.offset(shape, 1, 1, 1, 1) + C, l.offset(shape, 1, 1, 1, 2));
+ ASSERT_EQ(l.offset(shape, 1, 1, 1, 2), l.offset(shape, 1, 1, 1, 1) + C);
}
TEST(ADT_KERNEL_KERNEL_NHWC_LAYOUT, row_increment)
@@ -57,7 +57,7 @@ TEST(ADT_KERNEL_KERNEL_NHWC_LAYOUT, row_increment)
const Shape shape{N, C, H, W};
const NHWCLayout l;
- ASSERT_EQ(l.offset(shape, 1, 1, 1, 1) + C * W, l.offset(shape, 1, 1, 2, 1));
+ ASSERT_EQ(l.offset(shape, 1, 1, 2, 1), l.offset(shape, 1, 1, 1, 1) + C * W);
}
TEST(ADT_KERNEL_KERNEL_NHWC_LAYOUT, n_increment)
@@ -70,5 +70,5 @@ TEST(ADT_KERNEL_KERNEL_NHWC_LAYOUT, n_increment)
const Shape shape{N, C, H, W};
const NHWCLayout l;
- ASSERT_EQ(l.offset(shape, 1, 1, 1, 1) + H * W * C, l.offset(shape, 2, 1, 1, 1));
+ ASSERT_EQ(l.offset(shape, 2, 1, 1, 1), l.offset(shape, 1, 1, 1, 1) + H * W * C);
}
diff --git a/compiler/angkor/src/ADT/kernel/Overlay.test.cpp b/compiler/angkor/src/ADT/kernel/Overlay.test.cpp
index e80ebbc30..4e9bd8dbd 100644
--- a/compiler/angkor/src/ADT/kernel/Overlay.test.cpp
+++ b/compiler/angkor/src/ADT/kernel/Overlay.test.cpp
@@ -34,10 +34,10 @@ TEST(ADT_KERNEL_OVERLAY, ctor)
};
auto overlay = make_overlay<int, NCHWLayout>(shape, data);
- ASSERT_EQ(overlay.shape().count(), shape.count());
- ASSERT_EQ(overlay.shape().depth(), shape.depth());
- ASSERT_EQ(overlay.shape().height(), shape.height());
- ASSERT_EQ(overlay.shape().width(), shape.width());
+ ASSERT_EQ(shape.count(), overlay.shape().count());
+ ASSERT_EQ(shape.depth(), overlay.shape().depth());
+ ASSERT_EQ(shape.height(), overlay.shape().height());
+ ASSERT_EQ(shape.width(), overlay.shape().width());
}
TEST(ADT_KERNEL_OVERLAY, read)
@@ -51,9 +51,9 @@ TEST(ADT_KERNEL_OVERLAY, read)
NCHWLayout layout{};
- ASSERT_EQ(data[layout.offset(shape, 1, 3, 5, 2)], 0);
+ ASSERT_EQ(0, data[layout.offset(shape, 1, 3, 5, 2)]);
data[layout.offset(shape, 1, 3, 5, 2)] = 2;
- ASSERT_EQ(overlay.at(1, 3, 5, 2), 2);
+ ASSERT_EQ(2, overlay.at(1, 3, 5, 2));
}
TEST(ADT_KERNEL_OVERLAY, access)
@@ -67,7 +67,7 @@ TEST(ADT_KERNEL_OVERLAY, access)
NCHWLayout layout{};
- ASSERT_EQ(data[layout.offset(shape, 1, 3, 5, 2)], 0);
+ ASSERT_EQ(0, data[layout.offset(shape, 1, 3, 5, 2)]);
overlay.at(1, 3, 5, 2) = 4;
- ASSERT_EQ(data[layout.offset(shape, 1, 3, 5, 2)], 4);
+ ASSERT_EQ(4, data[layout.offset(shape, 1, 3, 5, 2)]);
}
diff --git a/compiler/angkor/src/ADT/kernel/Shape.test.cpp b/compiler/angkor/src/ADT/kernel/Shape.test.cpp
index da608fb7f..b2bd13c77 100644
--- a/compiler/angkor/src/ADT/kernel/Shape.test.cpp
+++ b/compiler/angkor/src/ADT/kernel/Shape.test.cpp
@@ -27,10 +27,10 @@ TEST(ADT_KERNEL_SHAPE, ctor)
nncc::core::ADT::kernel::Shape shape{N, C, H, W};
- ASSERT_EQ(shape.count(), N);
- ASSERT_EQ(shape.depth(), C);
- ASSERT_EQ(shape.height(), H);
- ASSERT_EQ(shape.width(), W);
+ ASSERT_EQ(N, shape.count());
+ ASSERT_EQ(C, shape.depth());
+ ASSERT_EQ(H, shape.height());
+ ASSERT_EQ(W, shape.width());
}
TEST(ADT_KERNEL_SHAPE, num_elements)
@@ -43,7 +43,7 @@ TEST(ADT_KERNEL_SHAPE, num_elements)
using nncc::core::ADT::kernel::Shape;
using nncc::core::ADT::kernel::num_elements;
- ASSERT_EQ(num_elements(Shape{N, C, H, W}), N * C * H * W);
+ ASSERT_EQ(N * C * H * W, num_elements(Shape{N, C, H, W}));
}
TEST(ADT_KERNEL_SHAPE, operator_eq)
diff --git a/compiler/angkor/src/ADT/tensor/Buffer.test.cpp b/compiler/angkor/src/ADT/tensor/Buffer.test.cpp
index c2b6a9983..39d0a8068 100644
--- a/compiler/angkor/src/ADT/tensor/Buffer.test.cpp
+++ b/compiler/angkor/src/ADT/tensor/Buffer.test.cpp
@@ -31,7 +31,7 @@ TEST(ADT_TENSOR_BUFFER, ctor)
const Shape shape{2, 3};
auto buffer = make_buffer<int, LexicalLayout>(shape);
- ASSERT_EQ(buffer.shape(), shape);
+ ASSERT_EQ(shape, buffer.shape());
}
TEST(ADT_TENSOR_BUFFER, access)
@@ -41,9 +41,9 @@ TEST(ADT_TENSOR_BUFFER, access)
const Index index{1, 2};
- ASSERT_EQ(buffer.at(index), 0);
+ ASSERT_EQ(0, buffer.at(index));
buffer.at(index) = 4;
// Casting is introduced to use 'const T &at(...) const' method
- ASSERT_EQ(static_cast<const Buffer<int> &>(buffer).at(index), 4);
+ ASSERT_EQ(4, static_cast<const Buffer<int> &>(buffer).at(index));
}
diff --git a/compiler/angkor/src/ADT/tensor/Index.cpp b/compiler/angkor/src/ADT/tensor/Index.cpp
index 89b07d15b..61f0a7106 100644
--- a/compiler/angkor/src/ADT/tensor/Index.cpp
+++ b/compiler/angkor/src/ADT/tensor/Index.cpp
@@ -16,6 +16,7 @@
#include "nncc/core/ADT/tensor/Index.h"
+#include <stdexcept>
#include <algorithm>
namespace nncc
@@ -48,6 +49,32 @@ Index &Index::fill(uint32_t index)
uint32_t &Index::at(uint32_t axis) { return _indices.at(axis); }
uint32_t Index::at(uint32_t axis) const { return _indices.at(axis); }
+Index operator+(const Index &lhs, const Index &rhs)
+{
+ if (lhs.rank() != rhs.rank())
+ throw std::runtime_error("Two tensors should have same rank");
+
+ Index ret;
+ ret.resize(lhs.rank());
+ for (uint32_t axis = 0; axis < lhs.rank(); axis++)
+ {
+ ret.at(axis) = lhs.at(axis) + rhs.at(axis);
+ }
+ return ret;
+}
+
+bool operator==(const Index &lhs, const Index &rhs)
+{
+ if (lhs.rank() != rhs.rank())
+ return false;
+ for (uint32_t axis = 0; axis < lhs.rank(); axis++)
+ {
+ if (lhs.at(axis) != rhs.at(axis))
+ return false;
+ }
+ return true;
+}
+
} // namespace tensor
} // namespace ADT
} // namespace core
diff --git a/compiler/angkor/src/ADT/tensor/Index.test.cpp b/compiler/angkor/src/ADT/tensor/Index.test.cpp
index ee0466281..53dbd41d4 100644
--- a/compiler/angkor/src/ADT/tensor/Index.test.cpp
+++ b/compiler/angkor/src/ADT/tensor/Index.test.cpp
@@ -22,19 +22,51 @@ TEST(ADT_TENSOR_INDEX, ctor)
{
nncc::core::ADT::tensor::Index index;
- ASSERT_EQ(index.rank(), 0);
+ ASSERT_EQ(0, index.rank());
}
TEST(ADT_TENSOR_INDEX, ctor_initializer_list)
{
const nncc::core::ADT::tensor::Index index{1, 3, 5, 7};
- ASSERT_EQ(index.rank(), 4);
+ ASSERT_EQ(4, index.rank());
- ASSERT_EQ(index.at(0), 1);
- ASSERT_EQ(index.at(1), 3);
- ASSERT_EQ(index.at(2), 5);
- ASSERT_EQ(index.at(3), 7);
+ ASSERT_EQ(1, index.at(0));
+ ASSERT_EQ(3, index.at(1));
+ ASSERT_EQ(5, index.at(2));
+ ASSERT_EQ(7, index.at(3));
+}
+
+TEST(ADT_TENSOR_INDEX, operator_add)
+{
+ nncc::core::ADT::tensor::Index index1{1, 2, 3, 4};
+ nncc::core::ADT::tensor::Index index2{5, 6, 7, 8};
+ nncc::core::ADT::tensor::Index result{index1 + index2};
+
+ ASSERT_EQ(6, result.at(0));
+ ASSERT_EQ(8, result.at(1));
+ ASSERT_EQ(10, result.at(2));
+ ASSERT_EQ(12, result.at(3));
+}
+
+TEST(ADT_TENSOR_INDEX, operator_eqaul)
+{
+ nncc::core::ADT::tensor::Index index1{1, 2, 3, 4};
+ nncc::core::ADT::tensor::Index index2{1, 2, 3, 4};
+ nncc::core::ADT::tensor::Index index3{5, 6, 7, 8};
+ nncc::core::ADT::tensor::Index index4{1, 2};
+
+ ASSERT_TRUE(index1 == index2);
+ ASSERT_FALSE(index1 == index3);
+ ASSERT_FALSE(index1 == index4);
+}
+
+TEST(ADT_TENSOR_INDEX, operator_add_different_size)
+{
+ nncc::core::ADT::tensor::Index index1{1, 2, 3, 4};
+ nncc::core::ADT::tensor::Index index2{5, 6};
+
+ EXPECT_THROW(index1 + index2, std::runtime_error);
}
TEST(ADT_TENSOR_INDEX, resize)
@@ -43,7 +75,7 @@ TEST(ADT_TENSOR_INDEX, resize)
index.resize(4);
- ASSERT_EQ(index.rank(), 4);
+ ASSERT_EQ(4, index.rank());
}
TEST(ADT_TENSOR_INDEX, at)
@@ -57,7 +89,7 @@ TEST(ADT_TENSOR_INDEX, at)
for (uint32_t axis = 0; axis < 4; ++axis)
{
index.at(axis) = indices[axis];
- ASSERT_EQ(index.at(axis), indices[axis]);
+ ASSERT_EQ(indices[axis], index.at(axis));
}
}
@@ -66,11 +98,11 @@ TEST(ADT_TENSOR_INDEX, copy)
const nncc::core::ADT::tensor::Index original{3, 5, 2, 7};
const nncc::core::ADT::tensor::Index copied{original};
- ASSERT_EQ(original.rank(), copied.rank());
+ ASSERT_EQ(copied.rank(), original.rank());
for (uint32_t axis = 0; axis < 4; ++axis)
{
- ASSERT_EQ(original.at(axis), copied.at(axis));
+ ASSERT_EQ(copied.at(axis), original.at(axis));
}
}
@@ -80,8 +112,8 @@ TEST(ADT_TENSOR_INDEX, fill)
index.fill(3);
- ASSERT_EQ(index.rank(), 2);
+ ASSERT_EQ(2, index.rank());
- ASSERT_EQ(index.at(0), 3);
- ASSERT_EQ(index.at(1), 3);
+ ASSERT_EQ(3, index.at(0));
+ ASSERT_EQ(3, index.at(1));
}
diff --git a/compiler/angkor/src/ADT/tensor/IndexEnumerator.test.cpp b/compiler/angkor/src/ADT/tensor/IndexEnumerator.test.cpp
index 204a8aa21..54cc2e9ad 100644
--- a/compiler/angkor/src/ADT/tensor/IndexEnumerator.test.cpp
+++ b/compiler/angkor/src/ADT/tensor/IndexEnumerator.test.cpp
@@ -40,7 +40,7 @@ TEST(ADT_TENSOR_INDEX_ENUMERATOR, iterate_full_range)
{
const auto &ind = e.current();
- ASSERT_EQ(ind.rank(), 2);
+ ASSERT_EQ(2, ind.rank());
count.at(ind.at(0) * W + ind.at(1)) += 1;
}
diff --git a/compiler/angkor/src/ADT/tensor/Layout.test.cpp b/compiler/angkor/src/ADT/tensor/Layout.test.cpp
index 145adfecc..6d5b3fe71 100644
--- a/compiler/angkor/src/ADT/tensor/Layout.test.cpp
+++ b/compiler/angkor/src/ADT/tensor/Layout.test.cpp
@@ -28,7 +28,7 @@ TEST(ADT_TENSOR_LAYOUT, ctor)
{
nncc::core::ADT::tensor::Layout l{offset_0};
- ASSERT_EQ(l.offset(Shape{4, 3, 6}, Index{1, 1, 1}), 0);
+ ASSERT_EQ(0, l.offset(Shape{4, 3, 6}, Index{1, 1, 1}));
}
TEST(ADT_TENSOR_LAYOUT, copy)
@@ -36,11 +36,11 @@ TEST(ADT_TENSOR_LAYOUT, copy)
nncc::core::ADT::tensor::Layout orig{offset_0};
nncc::core::ADT::tensor::Layout copy{offset_1};
- ASSERT_EQ(copy.offset(Shape{4, 3, 6}, Index{1, 1, 1}), 1);
+ ASSERT_EQ(1, copy.offset(Shape{4, 3, 6}, Index{1, 1, 1}));
copy = orig;
- ASSERT_EQ(copy.offset(Shape{4, 3, 6}, Index{1, 1, 1}), 0);
+ ASSERT_EQ(0, copy.offset(Shape{4, 3, 6}, Index{1, 1, 1}));
}
TEST(ADT_TENSOR_LAYOUT, move)
@@ -48,9 +48,9 @@ TEST(ADT_TENSOR_LAYOUT, move)
nncc::core::ADT::tensor::Layout orig{offset_0};
nncc::core::ADT::tensor::Layout move{offset_1};
- ASSERT_EQ(move.offset(Shape{4, 3, 6}, Index{1, 1, 1}), 1);
+ ASSERT_EQ(1, move.offset(Shape{4, 3, 6}, Index{1, 1, 1}));
move = std::move(orig);
- ASSERT_EQ(move.offset(Shape{4, 3, 6}, Index{1, 1, 1}), 0);
+ ASSERT_EQ(0, move.offset(Shape{4, 3, 6}, Index{1, 1, 1}));
}
diff --git a/compiler/angkor/src/ADT/tensor/LexicalLayout.test.cpp b/compiler/angkor/src/ADT/tensor/LexicalLayout.test.cpp
index 8f9b7296f..0acaa3a86 100644
--- a/compiler/angkor/src/ADT/tensor/LexicalLayout.test.cpp
+++ b/compiler/angkor/src/ADT/tensor/LexicalLayout.test.cpp
@@ -28,7 +28,7 @@ TEST(ADT_TENSOR_LEXICAL_LAYOUT, last)
const nncc::core::ADT::tensor::LexicalLayout l;
- ASSERT_EQ(l.offset(shape, curr) + 1, l.offset(shape, next));
+ ASSERT_EQ(l.offset(shape, next), l.offset(shape, curr) + 1);
}
TEST(ADT_TENSOR_LEXICAL_LAYOUT, lexical_middle)
@@ -39,7 +39,7 @@ TEST(ADT_TENSOR_LEXICAL_LAYOUT, lexical_middle)
const nncc::core::ADT::tensor::LexicalLayout l;
- ASSERT_EQ(l.offset(shape, curr) + 6, l.offset(shape, next));
+ ASSERT_EQ(l.offset(shape, next), l.offset(shape, curr) + 6);
}
TEST(ADT_TENSOR_LEXICAL_LAYOUT, lexical_first)
@@ -50,5 +50,5 @@ TEST(ADT_TENSOR_LEXICAL_LAYOUT, lexical_first)
const nncc::core::ADT::tensor::LexicalLayout l;
- ASSERT_EQ(l.offset(shape, curr) + 6 * 3, l.offset(shape, next));
+ ASSERT_EQ(l.offset(shape, next), l.offset(shape, curr) + 6 * 3);
}
diff --git a/compiler/angkor/src/ADT/tensor/Overlay.test.cpp b/compiler/angkor/src/ADT/tensor/Overlay.test.cpp
index aacb5a9a1..57cd1e6f9 100644
--- a/compiler/angkor/src/ADT/tensor/Overlay.test.cpp
+++ b/compiler/angkor/src/ADT/tensor/Overlay.test.cpp
@@ -35,7 +35,7 @@ TEST(ADT_TENSOR_OVERLAY, ctor)
};
auto view = make_overlay<int, LexicalLayout>(shape, data);
- ASSERT_EQ(view.shape(), shape);
+ ASSERT_EQ(shape, view.shape());
}
TEST(ADT_TENSOR_OVERLAY, read)
@@ -51,9 +51,9 @@ TEST(ADT_TENSOR_OVERLAY, read)
const Index index{1, 2};
- ASSERT_EQ(data[layout.offset(shape, index)], 0);
+ ASSERT_EQ(0, data[layout.offset(shape, index)]);
data[layout.offset(shape, index)] = 2;
- ASSERT_EQ(view.at(index), 2);
+ ASSERT_EQ(2, view.at(index));
}
TEST(ADT_TENSOR_OVERLAY, access)
@@ -69,7 +69,7 @@ TEST(ADT_TENSOR_OVERLAY, access)
const Index index{1, 2};
- ASSERT_EQ(data[layout.offset(shape, index)], 0);
+ ASSERT_EQ(0, data[layout.offset(shape, index)]);
view.at(index) = 4;
- ASSERT_EQ(data[layout.offset(shape, index)], 4);
+ ASSERT_EQ(4, data[layout.offset(shape, index)]);
}
diff --git a/compiler/angkor/src/ADT/tensor/Shape.cpp b/compiler/angkor/src/ADT/tensor/Shape.cpp
index 3e5bec29a..fb39ba192 100644
--- a/compiler/angkor/src/ADT/tensor/Shape.cpp
+++ b/compiler/angkor/src/ADT/tensor/Shape.cpp
@@ -50,11 +50,6 @@ Shape &Shape::squeeze(void)
uint64_t num_elements(const Shape &shape)
{
- if (shape.rank() == 0)
- {
- return 0;
- }
-
uint64_t res = 1;
for (uint32_t axis = 0; axis < shape.rank(); ++axis)
diff --git a/compiler/angkor/src/ADT/tensor/Shape.test.cpp b/compiler/angkor/src/ADT/tensor/Shape.test.cpp
index 7ce5f3e58..9915e7877 100644
--- a/compiler/angkor/src/ADT/tensor/Shape.test.cpp
+++ b/compiler/angkor/src/ADT/tensor/Shape.test.cpp
@@ -22,19 +22,19 @@ TEST(ADT_TENSOR_SHAPE, ctor)
{
nncc::core::ADT::tensor::Shape shape;
- ASSERT_EQ(shape.rank(), 0);
+ ASSERT_EQ(0, shape.rank());
}
TEST(ADT_TENSOR_SHAPE, ctor_initializer_list)
{
nncc::core::ADT::tensor::Shape shape{1, 3, 5, 7};
- ASSERT_EQ(shape.rank(), 4);
+ ASSERT_EQ(4, shape.rank());
- ASSERT_EQ(shape.dim(0), 1);
- ASSERT_EQ(shape.dim(1), 3);
- ASSERT_EQ(shape.dim(2), 5);
- ASSERT_EQ(shape.dim(3), 7);
+ ASSERT_EQ(1, shape.dim(0));
+ ASSERT_EQ(3, shape.dim(1));
+ ASSERT_EQ(5, shape.dim(2));
+ ASSERT_EQ(7, shape.dim(3));
}
TEST(ADT_TENSOR_SHAPE, resize)
@@ -43,7 +43,7 @@ TEST(ADT_TENSOR_SHAPE, resize)
shape.resize(4);
- ASSERT_EQ(shape.rank(), 4);
+ ASSERT_EQ(4, shape.rank());
}
TEST(ADT_TENSOR_SHAPE, dim)
@@ -57,7 +57,7 @@ TEST(ADT_TENSOR_SHAPE, dim)
for (uint32_t axis = 0; axis < 4; ++axis)
{
shape.dim(axis) = dims[axis];
- ASSERT_EQ(shape.dim(axis), dims[axis]);
+ ASSERT_EQ(dims[axis], shape.dim(axis));
}
}
@@ -66,20 +66,30 @@ TEST(ADT_TENSOR_SHAPE, copy)
const nncc::core::ADT::tensor::Shape original{3, 5, 2, 7};
const nncc::core::ADT::tensor::Shape copied{original};
- ASSERT_EQ(original.rank(), copied.rank());
+ ASSERT_EQ(copied.rank(), original.rank());
for (uint32_t axis = 0; axis < 4; ++axis)
{
- ASSERT_EQ(original.dim(axis), copied.dim(axis));
+ ASSERT_EQ(copied.dim(axis), original.dim(axis));
}
}
+TEST(ADT_TENSOR_SHAPE, num_elements_rank_0)
+{
+ using nncc::core::ADT::tensor::Shape;
+ using nncc::core::ADT::tensor::num_elements;
+
+ Shape rank_0_shape;
+
+ ASSERT_EQ(1, num_elements(rank_0_shape));
+}
+
TEST(ADT_TENSOR_SHAPE, num_elements_zero)
{
using nncc::core::ADT::tensor::Shape;
using nncc::core::ADT::tensor::num_elements;
- ASSERT_EQ(num_elements(Shape{0, 0, 0, 0}), 0);
+ ASSERT_EQ(0, num_elements(Shape{0, 0, 0, 0}));
}
TEST(ADT_TENSOR_SHAPE, num_elements_nonzero)
@@ -87,7 +97,7 @@ TEST(ADT_TENSOR_SHAPE, num_elements_nonzero)
using nncc::core::ADT::tensor::Shape;
using nncc::core::ADT::tensor::num_elements;
- ASSERT_EQ(num_elements(Shape{2, 3}), 6);
+ ASSERT_EQ(6, num_elements(Shape{2, 3}));
}
TEST(ADT_TENSOR_SHAPE, num_elements_nulldim)
@@ -95,7 +105,7 @@ TEST(ADT_TENSOR_SHAPE, num_elements_nulldim)
using nncc::core::ADT::tensor::Shape;
using nncc::core::ADT::tensor::num_elements;
- ASSERT_EQ(num_elements(Shape{2, 0, 3}), 0);
+ ASSERT_EQ(0, num_elements(Shape{2, 0, 3}));
}
TEST(ADT_TENSOR_SHAPE, squeeze_neg)
@@ -105,10 +115,10 @@ TEST(ADT_TENSOR_SHAPE, squeeze_neg)
auto squeezed = squeeze(Shape{3, 5, 2});
- ASSERT_EQ(squeezed.rank(), 3);
- ASSERT_EQ(squeezed.dim(0), 3);
- ASSERT_EQ(squeezed.dim(1), 5);
- ASSERT_EQ(squeezed.dim(2), 2);
+ ASSERT_EQ(3, squeezed.rank());
+ ASSERT_EQ(3, squeezed.dim(0));
+ ASSERT_EQ(5, squeezed.dim(1));
+ ASSERT_EQ(2, squeezed.dim(2));
}
TEST(ADT_TENSOR_SHAPE, squeeze_neg_0)
@@ -118,10 +128,10 @@ TEST(ADT_TENSOR_SHAPE, squeeze_neg_0)
auto squeezed = squeeze(Shape{3, 0, 2});
- ASSERT_EQ(squeezed.rank(), 3);
- ASSERT_EQ(squeezed.dim(0), 3);
- ASSERT_EQ(squeezed.dim(1), 0);
- ASSERT_EQ(squeezed.dim(2), 2);
+ ASSERT_EQ(3, squeezed.rank());
+ ASSERT_EQ(3, squeezed.dim(0));
+ ASSERT_EQ(0, squeezed.dim(1));
+ ASSERT_EQ(2, squeezed.dim(2));
}
TEST(ADT_TENSOR_SHAPE, squeeze_pos)
@@ -131,9 +141,9 @@ TEST(ADT_TENSOR_SHAPE, squeeze_pos)
auto squeezed = squeeze(Shape{3, 1, 2});
- ASSERT_EQ(squeezed.rank(), 2);
- ASSERT_EQ(squeezed.dim(0), 3);
- ASSERT_EQ(squeezed.dim(1), 2);
+ ASSERT_EQ(2, squeezed.rank());
+ ASSERT_EQ(3, squeezed.dim(0));
+ ASSERT_EQ(2, squeezed.dim(1));
}
TEST(ADT_TENSOR_SHAPE, squeeze_nested)
@@ -145,9 +155,9 @@ TEST(ADT_TENSOR_SHAPE, squeeze_nested)
shape.squeeze().squeeze();
- ASSERT_EQ(shape.rank(), 2);
- ASSERT_EQ(shape.dim(0), 3);
- ASSERT_EQ(shape.dim(1), 2);
+ ASSERT_EQ(2, shape.rank());
+ ASSERT_EQ(3, shape.dim(0));
+ ASSERT_EQ(2, shape.dim(1));
}
TEST(ADT_TENSOR_SHAPE, eq_negative_on_unmatched_rank)
diff --git a/compiler/angkor/src/TensorIndex.test.cpp b/compiler/angkor/src/TensorIndex.test.cpp
index 68cf3917a..dcfc4d39f 100644
--- a/compiler/angkor/src/TensorIndex.test.cpp
+++ b/compiler/angkor/src/TensorIndex.test.cpp
@@ -22,19 +22,19 @@ TEST(TensorIndexTest, ctor)
{
angkor::TensorIndex index;
- ASSERT_EQ(index.rank(), 0);
+ ASSERT_EQ(0, index.rank());
}
TEST(TensorIndexTest, ctor_initializer_list)
{
const angkor::TensorIndex index{1, 3, 5, 7};
- ASSERT_EQ(index.rank(), 4);
+ ASSERT_EQ(4, index.rank());
- ASSERT_EQ(index.at(0), 1);
- ASSERT_EQ(index.at(1), 3);
- ASSERT_EQ(index.at(2), 5);
- ASSERT_EQ(index.at(3), 7);
+ ASSERT_EQ(1, index.at(0));
+ ASSERT_EQ(3, index.at(1));
+ ASSERT_EQ(5, index.at(2));
+ ASSERT_EQ(7, index.at(3));
}
TEST(TensorIndexTest, resize)
@@ -43,7 +43,7 @@ TEST(TensorIndexTest, resize)
index.resize(4);
- ASSERT_EQ(index.rank(), 4);
+ ASSERT_EQ(4, index.rank());
}
TEST(TensorIndexTest, at)
@@ -57,7 +57,7 @@ TEST(TensorIndexTest, at)
for (uint32_t axis = 0; axis < 4; ++axis)
{
index.at(axis) = indices[axis];
- ASSERT_EQ(index.at(axis), indices[axis]);
+ ASSERT_EQ(indices[axis], index.at(axis));
}
}
@@ -66,11 +66,11 @@ TEST(TensorIndexTest, copy)
const angkor::TensorIndex original{3, 5, 2, 7};
const angkor::TensorIndex copied{original};
- ASSERT_EQ(original.rank(), copied.rank());
+ ASSERT_EQ(copied.rank(), original.rank());
for (uint32_t axis = 0; axis < 4; ++axis)
{
- ASSERT_EQ(original.at(axis), copied.at(axis));
+ ASSERT_EQ(copied.at(axis), original.at(axis));
}
}
@@ -80,8 +80,8 @@ TEST(TensorIndexTest, fill)
index.fill(3);
- ASSERT_EQ(index.rank(), 2);
+ ASSERT_EQ(2, index.rank());
- ASSERT_EQ(index.at(0), 3);
- ASSERT_EQ(index.at(1), 3);
+ ASSERT_EQ(3, index.at(0));
+ ASSERT_EQ(3, index.at(1));
}
diff --git a/compiler/angkor/src/TensorShape.test.cpp b/compiler/angkor/src/TensorShape.test.cpp
index 5e6766a96..3b96bb863 100644
--- a/compiler/angkor/src/TensorShape.test.cpp
+++ b/compiler/angkor/src/TensorShape.test.cpp
@@ -22,19 +22,19 @@ TEST(TensorShapeTest, ctor)
{
angkor::TensorShape shape;
- ASSERT_EQ(shape.rank(), 0);
+ ASSERT_EQ(0, shape.rank());
}
TEST(TensorShapeTest, ctor_initializer_list)
{
angkor::TensorShape shape{1, 3, 5, 7};
- ASSERT_EQ(shape.rank(), 4);
+ ASSERT_EQ(4, shape.rank());
- ASSERT_EQ(shape.dim(0), 1);
- ASSERT_EQ(shape.dim(1), 3);
- ASSERT_EQ(shape.dim(2), 5);
- ASSERT_EQ(shape.dim(3), 7);
+ ASSERT_EQ(1, shape.dim(0));
+ ASSERT_EQ(3, shape.dim(1));
+ ASSERT_EQ(5, shape.dim(2));
+ ASSERT_EQ(7, shape.dim(3));
}
TEST(TensorShapeTest, resize)
@@ -43,7 +43,7 @@ TEST(TensorShapeTest, resize)
shape.resize(4);
- ASSERT_EQ(shape.rank(), 4);
+ ASSERT_EQ(4, shape.rank());
}
TEST(TensorShapeTest, dim)
@@ -57,7 +57,7 @@ TEST(TensorShapeTest, dim)
for (uint32_t axis = 0; axis < 4; ++axis)
{
shape.dim(axis) = dims[axis];
- ASSERT_EQ(shape.dim(axis), dims[axis]);
+ ASSERT_EQ(dims[axis], shape.dim(axis));
}
}
@@ -66,11 +66,11 @@ TEST(TensorShapeTest, copy)
const angkor::TensorShape original{3, 5, 2, 7};
const angkor::TensorShape copied{original};
- ASSERT_EQ(original.rank(), copied.rank());
+ ASSERT_EQ(copied.rank(), original.rank());
for (uint32_t axis = 0; axis < 4; ++axis)
{
- ASSERT_EQ(original.dim(axis), copied.dim(axis));
+ ASSERT_EQ(copied.dim(axis), original.dim(axis));
}
}
diff --git a/compiler/ann-api/README.md b/compiler/ann-api/README.md
new file mode 100644
index 000000000..0c141168b
--- /dev/null
+++ b/compiler/ann-api/README.md
@@ -0,0 +1 @@
+# ann-api
diff --git a/compiler/adtidas/.FORMATDENY b/compiler/ann-api/include/.FORMATDENY
index e69de29bb..e69de29bb 100644
--- a/compiler/adtidas/.FORMATDENY
+++ b/compiler/ann-api/include/.FORMATDENY
diff --git a/compiler/ann-ref/CMakeLists.txt b/compiler/ann-ref/CMakeLists.txt
index d18a3c7c7..0f3822514 100644
--- a/compiler/ann-ref/CMakeLists.txt
+++ b/compiler/ann-ref/CMakeLists.txt
@@ -1,16 +1,16 @@
-nncc_find_package(Eigen QUIET)
+nnas_find_package(Eigen QUIET)
if(NOT Eigen_FOUND)
return()
endif(NOT Eigen_FOUND)
-nncc_find_package(GEMMLowp QUIET)
+nnas_find_package(GEMMLowp QUIET)
if(NOT GEMMLowp_FOUND)
return()
endif(NOT GEMMLowp_FOUND)
-nncc_include(TargetRequire)
+nnas_include(TargetRequire)
TargetRequire_Assert(ann_api eigen gemmlowp)
diff --git a/compiler/ann-ref/src/NeuralNetworks.cpp b/compiler/ann-ref/src/NeuralNetworks.cpp
index 38a7f2ffd..e43a82667 100644
--- a/compiler/ann-ref/src/NeuralNetworks.cpp
+++ b/compiler/ann-ref/src/NeuralNetworks.cpp
@@ -26,20 +26,11 @@
#include <memory>
-namespace
-{
-template <typename T, typename... Args> std::unique_ptr<T> make_unique(Args &&... args)
-{
- // NOTE std::make_unique is missing in C++11 standard
- return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-}
-} // namespace
-
int ANeuralNetworksMemory_createFromFd(size_t size, int prot, int fd, size_t offset,
ANeuralNetworksMemory **memory)
{
*memory = nullptr;
- auto m = make_unique<MappedMemory>();
+ auto m = std::make_unique<MappedMemory>();
if (m == nullptr)
{
return ANEURALNETWORKS_OUT_OF_MEMORY;
diff --git a/compiler/arser/CMakeLists.txt b/compiler/arser/CMakeLists.txt
new file mode 100644
index 000000000..63d19f538
--- /dev/null
+++ b/compiler/arser/CMakeLists.txt
@@ -0,0 +1,15 @@
+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_include_directories(arser INTERFACE include/)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+set(TESTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/arser.test.cpp")
+GTest_AddTest(arser_test ${TESTS})
+target_include_directories(arser_test PRIVATE include)
diff --git a/compiler/arser/README.md b/compiler/arser/README.md
new file mode 100644
index 000000000..e853e7eea
--- /dev/null
+++ b/compiler/arser/README.md
@@ -0,0 +1,3 @@
+# arser
+
+This is an Argument parser for c++. See [`arser.test.cpp`](tests/arser.test.cpp) for details on how to use
diff --git a/compiler/arser/include/arser/arser.h b/compiler/arser/include/arser/arser.h
new file mode 100644
index 000000000..64bb557c4
--- /dev/null
+++ b/compiler/arser/include/arser/arser.h
@@ -0,0 +1,507 @@
+/*
+ * 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 <iostream>
+#include <sstream>
+
+#include <iterator>
+#include <typeinfo>
+
+#include <algorithm>
+#include <functional>
+#include <list>
+#include <map>
+#include <string>
+#include <vector>
+
+#include <cstring>
+
+namespace
+{
+
+template <typename T> T lexical_cast(const std::string &str)
+{
+ std::istringstream ss;
+ ss.str(str);
+ T data;
+ ss >> data;
+ return data;
+}
+
+template <> bool lexical_cast(const std::string &str)
+{
+ bool data = true;
+ if (str == "false" || str == "False" || str == "FALSE" || str == "0")
+ data = false;
+ return data;
+}
+
+template <typename T> inline std::string to_string(const T value) { return std::to_string(value); }
+
+template <> inline std::string to_string(const char *value) { return std::string(value); }
+
+template <> inline std::string to_string(const bool value) { return value ? "true" : "false"; }
+
+} // namespace
+
+namespace arser
+{
+
+// TypeName declaration
+template <typename T> struct TypeName
+{
+ static const char *Get() { return typeid(T).name(); }
+};
+template <> struct TypeName<int>
+{
+ static const char *Get() { return "int"; }
+};
+template <> struct TypeName<std::vector<int>>
+{
+ static const char *Get() { return "vector<int>"; }
+};
+template <> struct TypeName<float>
+{
+ static const char *Get() { return "float"; }
+};
+template <> struct TypeName<std::vector<float>>
+{
+ static const char *Get() { return "vector<float>"; }
+};
+template <> struct TypeName<bool>
+{
+ static const char *Get() { return "bool"; }
+};
+template <> struct TypeName<std::string>
+{
+ static const char *Get() { return "string"; }
+};
+template <> struct TypeName<std::vector<std::string>>
+{
+ static const char *Get() { return "vector<string>"; }
+};
+template <> struct TypeName<const char *>
+{
+ static const char *Get() { return "string"; }
+};
+template <> struct TypeName<std::vector<const char *>>
+{
+ static const char *Get() { return "vector<string>"; }
+};
+
+// supported DataType
+enum class DataType
+{
+ INT32,
+ INT32_VEC,
+ FLOAT,
+ FLOAT_VEC,
+ BOOL,
+ STR,
+ STR_VEC,
+};
+
+class Arser;
+
+class Argument
+{
+public:
+ explicit Argument(const std::string &arg_name) : _name{arg_name} {}
+
+ Argument &nargs(uint32_t num)
+ {
+ if (num == 0)
+ {
+ _type = "bool";
+ }
+ _nargs = num;
+ return *this;
+ }
+
+ Argument &type(DataType type)
+ {
+ switch (type)
+ {
+ case DataType::INT32:
+ _type = "int";
+ break;
+ case DataType::INT32_VEC:
+ _type = "vector<int>";
+ break;
+ case DataType::FLOAT:
+ _type = "float";
+ break;
+ case DataType::FLOAT_VEC:
+ _type = "vector<float>";
+ break;
+ case DataType::BOOL:
+ _type = "bool";
+ break;
+ case DataType::STR:
+ _type = "string";
+ break;
+ case DataType::STR_VEC:
+ _type = "vector<string>";
+ break;
+ default:
+ throw std::runtime_error("NYI DataType");
+ }
+ return *this;
+ }
+
+ Argument &required(void)
+ {
+ _is_required = true;
+ return *this;
+ }
+
+ Argument &required(bool value)
+ {
+ _is_required = value;
+ return *this;
+ }
+
+ Argument &help(std::string help_message)
+ {
+ _help_message = help_message;
+ return *this;
+ }
+
+ Argument &exit_with(const std::function<void(void)> &func)
+ {
+ _func = func;
+ return *this;
+ }
+
+ template <typename T> Argument &default_value(const T value)
+ {
+ if ((_nargs <= 1 && TypeName<T>::Get() == _type) ||
+ (_nargs > 1 && TypeName<std::vector<T>>::Get() == _type))
+ _values.emplace_back(::to_string(value));
+ else
+ {
+ throw std::runtime_error("Type mismatch. "
+ "You called default_value() method with a type different "
+ "from the one you specified. "
+ "Please check the type of what you specified in "
+ "add_argument() method.");
+ }
+ return *this;
+ }
+
+ template <typename T, typename... Ts> Argument &default_value(const T value, const Ts... values)
+ {
+ if ((_nargs <= 1 && TypeName<T>::Get() == _type) ||
+ (_nargs > 1 && TypeName<std::vector<T>>::Get() == _type))
+ {
+ _values.emplace_back(::to_string(value));
+ default_value(values...);
+ }
+ else
+ {
+ throw std::runtime_error("Type mismatch. "
+ "You called default_value() method with a type different "
+ "from the one you specified. "
+ "Please check the type of what you specified in "
+ "add_argument() method.");
+ }
+ return *this;
+ }
+
+private:
+ std::string _name;
+ std::string _type;
+ std::string _help_message;
+ std::function<void(void)> _func;
+ uint32_t _nargs{1};
+ bool _is_required{false};
+ std::vector<std::string> _values;
+
+ friend class Arser;
+ friend std::ostream &operator<<(std::ostream &, const Arser &);
+};
+
+class Arser
+{
+public:
+ explicit Arser(const std::string &program_description = {})
+ : _program_description{program_description}
+ {
+ add_argument("--help").help("Show help message and exit").nargs(0);
+ }
+
+ Argument &add_argument(const std::string &arg_name)
+ {
+ if (arg_name.at(0) != '-')
+ {
+ _positional_arg_vec.emplace_back(arg_name);
+ _arg_map[arg_name] = &_positional_arg_vec.back();
+ }
+ else
+ {
+ _optional_arg_vec.emplace_back(arg_name);
+ _arg_map[arg_name] = &_optional_arg_vec.back();
+ }
+ return *_arg_map[arg_name];
+ }
+
+ void parse(int argc, char **argv)
+ {
+ _program_name = argv[0];
+ _program_name.erase(0, _program_name.find_last_of("/\\") + 1);
+ if (argc >= 2)
+ {
+ if (!std::strcmp(argv[1], "--help"))
+ {
+ std::cout << *this;
+ std::exit(0);
+ }
+ else
+ {
+ for (const auto &arg : _arg_map)
+ {
+ const auto &func = arg.second->_func;
+ if (func && !std::strcmp(argv[1], arg.second->_name.c_str()))
+ {
+ func();
+ std::exit(0);
+ }
+ }
+ }
+ }
+ /*
+ ** ./program_name [optional argument] [positional argument]
+ */
+ // get the number of positioanl argument
+ size_t parg_num = _positional_arg_vec.size();
+ // get the number of "required" optional argument
+ size_t required_oarg_num = 0;
+ for (auto arg : _optional_arg_vec)
+ {
+ if (arg._is_required)
+ required_oarg_num++;
+ }
+ // parse argument
+ for (int c = 1; c < argc;)
+ {
+ std::string arg_name{argv[c++]};
+ auto arg = _arg_map.find(arg_name);
+ // check whether arg is positional or not
+ if (arg == _arg_map.end())
+ {
+ if (parg_num)
+ {
+ auto it = _positional_arg_vec.begin();
+ std::advance(it, _positional_arg_vec.size() - parg_num);
+ (*it)._values.clear();
+ (*it)._values.emplace_back(arg_name);
+ parg_num--;
+ }
+ else
+ throw std::runtime_error("Invalid argument. "
+ "You've given more positional argument than necessary.");
+ }
+ else // optional argument
+ {
+ // check whether arg is required or not
+ if (arg->second->_is_required)
+ required_oarg_num--;
+ arg->second->_values.clear();
+ for (uint32_t n = 0; n < arg->second->_nargs; n++)
+ {
+ if (c >= argc)
+ throw std::runtime_error("Invalid argument. "
+ "You must have missed some argument.");
+ arg->second->_values.emplace_back(argv[c++]);
+ }
+ if (arg->second->_nargs == 0)
+ {
+ // TODO std::boolalpha for true or false
+ arg->second->_values.emplace_back("1");
+ }
+ }
+ }
+ if (parg_num || required_oarg_num)
+ throw std::runtime_error("Invalid argument. "
+ "You must have missed some argument.");
+ }
+
+ bool operator[](const std::string &arg_name)
+ {
+ auto arg = _arg_map.find(arg_name);
+ if (arg == _arg_map.end())
+ return false;
+
+ return arg->second->_values.size() > 0 ? true : false;
+ }
+
+ template <typename T> T get_impl(const std::string &arg_name, T *);
+
+ template <typename T> std::vector<T> get_impl(const std::string &arg_name, std::vector<T> *);
+
+ template <typename T> T get(const std::string &arg_name);
+
+private:
+ std::string _program_name;
+ std::string _program_description;
+ std::list<Argument> _positional_arg_vec;
+ std::list<Argument> _optional_arg_vec;
+ std::map<std::string, Argument *> _arg_map;
+
+ friend std::ostream &operator<<(std::ostream &, const Arser &);
+};
+
+template <typename T> T Arser::get_impl(const std::string &arg_name, T *)
+{
+ auto arg = _arg_map.find(arg_name);
+ if (arg == _arg_map.end())
+ throw std::runtime_error("Invalid argument. "
+ "There is no argument you are looking for.");
+
+ if (arg->second->_type != TypeName<T>::Get())
+ throw std::runtime_error("Type mismatch. "
+ "You called get() method with a type different "
+ "from the one you specified. "
+ "Please check the type of what you specified in "
+ "add_argument() method.");
+
+ if (arg->second->_values.size() == 0)
+ throw std::runtime_error("Wrong access. "
+ "You must make sure that the argument is given before accessing it. "
+ "You can do it by calling arser[\"argument\"].");
+
+ return ::lexical_cast<T>(arg->second->_values[0]);
+}
+
+template <typename T> std::vector<T> Arser::get_impl(const std::string &arg_name, std::vector<T> *)
+{
+ auto arg = _arg_map.find(arg_name);
+ if (arg == _arg_map.end())
+ throw std::runtime_error("Invalid argument. "
+ "There is no argument you are looking for.");
+
+ if (arg->second->_type != TypeName<std::vector<T>>::Get())
+ throw std::runtime_error("Type mismatch. "
+ "You called get using a type different from the one you specified.");
+
+ std::vector<T> data;
+ std::transform(arg->second->_values.begin(), arg->second->_values.end(), std::back_inserter(data),
+ [](std::string str) -> T { return ::lexical_cast<T>(str); });
+ return data;
+}
+
+template <typename T> T Arser::get(const std::string &arg_name)
+{
+ return get_impl(arg_name, static_cast<T *>(nullptr));
+}
+
+std::ostream &operator<<(std::ostream &stream, const Arser &parser)
+{
+ // print description
+ if (!parser._program_description.empty())
+ {
+ stream << "What " << parser._program_name << " does: " << parser._program_description << "\n\n";
+ }
+ /*
+ ** print usage
+ */
+ stream << "Usage: ./" << parser._program_name << " ";
+ // required optional argument
+ for (const auto &arg : parser._optional_arg_vec)
+ {
+ if (!arg._is_required)
+ continue;
+ stream << arg._name << " ";
+ std::string arg_name = arg._name.substr(2);
+ std::for_each(arg_name.begin(), arg_name.end(),
+ [&stream](const char &c) { stream << static_cast<char>(::toupper(c)); });
+ stream << " ";
+ }
+ // rest of the optional argument
+ for (const auto &arg : parser._optional_arg_vec)
+ {
+ if (arg._is_required)
+ continue;
+ stream << "[" << arg._name;
+ if (arg._nargs)
+ {
+ stream << " ";
+ std::string arg_name = arg._name.substr(2);
+ std::for_each(arg_name.begin(), arg_name.end(),
+ [&stream](const char &c) { stream << static_cast<char>(::toupper(c)); });
+ }
+ stream << "]"
+ << " ";
+ }
+ // positional arguement
+ for (const auto &arg : parser._positional_arg_vec)
+ {
+ stream << arg._name << " ";
+ }
+ stream << "\n\n";
+ /*
+ ** print argument list and its help message
+ */
+ // get the length of the longest argument
+ size_t length_of_longest_arg = 0;
+ for (const auto &arg : parser._positional_arg_vec)
+ {
+ length_of_longest_arg = std::max(length_of_longest_arg, arg._name.length());
+ }
+ for (const auto &arg : parser._optional_arg_vec)
+ {
+ length_of_longest_arg = std::max(length_of_longest_arg, arg._name.length());
+ }
+
+ const size_t message_width = 60;
+ // positional argument
+ if (!parser._positional_arg_vec.empty())
+ {
+ stream << "[Positional argument]" << std::endl;
+ for (const auto &arg : parser._positional_arg_vec)
+ {
+ stream.width(length_of_longest_arg);
+ stream << std::left << arg._name << "\t";
+ for (size_t i = 0; i < arg._help_message.length(); i += message_width)
+ {
+ if (i)
+ stream << std::string(length_of_longest_arg, ' ') << "\t";
+ stream << arg._help_message.substr(i, message_width) << std::endl;
+ }
+ }
+ std::cout << std::endl;
+ }
+ // optional argument
+ if (!parser._optional_arg_vec.empty())
+ {
+ stream << "[Optional argument]" << std::endl;
+ for (const auto &arg : parser._optional_arg_vec)
+ {
+ stream.width(length_of_longest_arg);
+ stream << std::left << arg._name << "\t";
+ for (size_t i = 0; i < arg._help_message.length(); i += message_width)
+ {
+ if (i)
+ stream << std::string(length_of_longest_arg, ' ') << "\t";
+ stream << arg._help_message.substr(i, message_width) << std::endl;
+ }
+ }
+ }
+
+ return stream;
+}
+
+} // namespace arser
diff --git a/compiler/arser/tests/arser.test.cpp b/compiler/arser/tests/arser.test.cpp
new file mode 100644
index 000000000..28bee4238
--- /dev/null
+++ b/compiler/arser/tests/arser.test.cpp
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "arser/arser.h"
+
+using namespace arser;
+
+class Prompt
+{
+public:
+ Prompt(const std::string &command)
+ {
+ std::istringstream iss(command);
+ std::vector<std::string> token(std::istream_iterator<std::string>{iss},
+ std::istream_iterator<std::string>());
+ _arg = std::move(token);
+ _argv.reserve(_arg.size());
+ for (const auto &t : _arg)
+ {
+ _argv.push_back(const_cast<char *>(t.data()));
+ }
+ }
+ int argc(void) const { return _argv.size(); }
+ char **argv(void) { return _argv.data(); }
+
+private:
+ std::vector<char *> _argv;
+ std::vector<std::string> _arg;
+};
+
+TEST(BasicTest, option)
+{
+ /* arrange */
+ Arser arser;
+
+ arser.add_argument("--verbose")
+ .nargs(0)
+ .help("It provides additional details as to what the executable is doing");
+
+ Prompt prompt("./executable --verbose");
+ /* act */
+ arser.parse(prompt.argc(), prompt.argv());
+ /* assert */
+ EXPECT_TRUE(arser["--verbose"]);
+ EXPECT_TRUE(arser.get<bool>("--verbose"));
+}
+
+TEST(BasicTest, OptionalArgument)
+{
+ /* arrange */
+ Arser arser;
+
+ arser.add_argument("--volume")
+ .nargs(1)
+ .type(arser::DataType::INT32)
+ .help("Set a volume as you provided.");
+ arser.add_argument("--frequency")
+ .nargs(1)
+ .type(arser::DataType::FLOAT)
+ .help("Set a frequency as you provided.");
+
+ Prompt prompt("./radio --volume 5 --frequency 128.5");
+ /* act */
+ arser.parse(prompt.argc(), prompt.argv());
+ /* assert */
+ EXPECT_TRUE(arser["--volume"]);
+ EXPECT_EQ(5, arser.get<int>("--volume"));
+
+ EXPECT_TRUE(arser["--frequency"]);
+ EXPECT_FLOAT_EQ(128.5, arser.get<float>("--frequency"));
+
+ EXPECT_FALSE(arser["--price"]);
+ EXPECT_THROW(arser.get<bool>("--volume"), std::runtime_error);
+}
+
+TEST(BasicTest, NonRequiredOptionalArgument)
+{
+ /* arrange */
+ Arser arser;
+
+ arser.add_argument("--weight")
+ .nargs(1)
+ .type(arser::DataType::INT32)
+ .help("Set a volume as you provided.");
+
+ Prompt prompt("./radio"); // empty argument
+ /* act */
+ arser.parse(prompt.argc(), prompt.argv());
+ /* assert */
+ EXPECT_FALSE(arser["--volume"]);
+ EXPECT_THROW(arser.get<int>("--weight"), std::runtime_error);
+}
+
+TEST(BasicTest, RequiredOptionalArgument)
+{
+ /* arrange */
+ Arser arser;
+
+ arser.add_argument("--volume")
+ .nargs(1)
+ .type(arser::DataType::INT32)
+ .required()
+ .help("Set a volume as you provided.");
+
+ Prompt prompt("./radio");
+ /* act */ /* assert */
+ EXPECT_THROW(arser.parse(prompt.argc(), prompt.argv()), std::runtime_error);
+}
+
+TEST(BasicTest, OptionalMultipleArgument)
+{
+ /* arrange */
+ Arser arser;
+
+ arser.add_argument("--add").nargs(2).type(arser::DataType::INT32_VEC).help("Add two numbers.");
+
+ Prompt prompt("./calculator --add 3 5");
+ /* act */
+ arser.parse(prompt.argc(), prompt.argv());
+ /* assert */
+ EXPECT_TRUE(arser["--add"]);
+ std::vector<int> values = arser.get<std::vector<int>>("--add");
+ EXPECT_EQ(3, values.at(0));
+ EXPECT_EQ(5, values.at(1));
+
+ EXPECT_THROW(arser.get<std::vector<float>>("--add"), std::runtime_error);
+}
+
+TEST(BasicTest, MultipleOptionalArgument)
+{
+ /* arrange */
+ Arser arser;
+
+ arser.add_argument("--input_path")
+ .nargs(1)
+ .type(arser::DataType::STR)
+ .help("input path of this program.")
+ .required();
+ arser.add_argument("--output_path")
+ .nargs(1)
+ .type(arser::DataType::STR)
+ .help("output path of this program.")
+ .required(true);
+ arser.add_argument("--training_data")
+ .nargs(5)
+ .type(arser::DataType::INT32_VEC)
+ .help("give traning data to this program.")
+ .required();
+
+ Prompt prompt("./ml --input_path /I/am/in.put --output_path I/am/out.put "
+ "--training_data 2 43 234 3 334");
+ /* act */
+ arser.parse(prompt.argc(), prompt.argv());
+ /* assert */
+ EXPECT_TRUE(arser["--input_path"]);
+ EXPECT_EQ("/I/am/in.put", arser.get<std::string>("--input_path"));
+ EXPECT_TRUE(arser["--output_path"]);
+ EXPECT_EQ("I/am/out.put", arser.get<std::string>("--output_path"));
+ EXPECT_TRUE(arser["--training_data"]);
+ std::vector<int32_t> data = arser.get<std::vector<int32_t>>("--training_data");
+ EXPECT_EQ(2, data.at(0));
+ EXPECT_EQ(43, data.at(1));
+ EXPECT_EQ(234, data.at(2));
+ EXPECT_EQ(3, data.at(3));
+ EXPECT_EQ(334, data.at(4));
+}
+
+TEST(BasicTest, MultipleFloatValue)
+{
+ /* arrange */
+ Arser arser;
+
+ arser.add_argument("--add_float")
+ .nargs(2)
+ .type(arser::DataType::FLOAT_VEC)
+ .help("Add two float numbers.");
+
+ Prompt prompt("./calculator --add_float 3.2 5.4");
+ /* act */
+ arser.parse(prompt.argc(), prompt.argv());
+ /* assert */
+ EXPECT_TRUE(arser["--add_float"]);
+ std::vector<float> values = arser.get<std::vector<float>>("--add_float");
+ EXPECT_FLOAT_EQ(3.2, values.at(0));
+ EXPECT_FLOAT_EQ(5.4, values.at(1));
+
+ EXPECT_THROW(arser.get<std::vector<int>>("--add_float"), std::runtime_error);
+}
+
+TEST(BasicTest, MultipleStringValue)
+{
+ /* arrange */
+ Arser arser;
+
+ arser.add_argument("--three_color")
+ .nargs(3)
+ .type(arser::DataType::STR_VEC)
+ .help("insert your three favorite color");
+
+ Prompt prompt("./color_factory --three_color red blue yellow");
+ /* act */
+ arser.parse(prompt.argc(), prompt.argv());
+ /* assert */
+ EXPECT_TRUE(arser["--three_color"]);
+ std::vector<std::string> values = arser.get<std::vector<std::string>>("--three_color");
+ EXPECT_EQ("red", values.at(0));
+ EXPECT_EQ("blue", values.at(1));
+ EXPECT_EQ("yellow", values.at(2));
+
+ EXPECT_THROW(arser.get<std::vector<std::string>>("--color"), std::runtime_error);
+}
+
+void printBiography(void) { std::cerr << "When I was young.." << std::endl; }
+
+TEST(BasicTest, ExitWithFunctionCall)
+{
+ /* arrange */
+ Arser arser;
+
+ arser.add_argument("--history").help("Show history and exit").exit_with(printBiography);
+
+ arser.add_argument("--name").nargs(1).type(arser::DataType::STR).help("Name your hero");
+
+ Prompt prompt("./hero --history");
+ /* act */ /* assert */
+ EXPECT_EXIT(arser.parse(prompt.argc(), prompt.argv()), testing::ExitedWithCode(0),
+ "When I was young..");
+}
+
+void printVersion(std::string version) { std::cerr << "arser version : " << version << std::endl; }
+
+TEST(BasicTest, ExitWithFunctionCallWithBind)
+{
+ /* arrange */
+ Arser arser;
+
+ arser.add_argument("--version")
+ .help("Show version and exit")
+ .exit_with(std::bind(printVersion, "1.2.0"));
+
+ Prompt prompt("./arser --version");
+ /* act */ /* assert */
+ EXPECT_EXIT(arser.parse(prompt.argc(), prompt.argv()), testing::ExitedWithCode(0),
+ "arser version : 1.2.0");
+}
+
+TEST(BasicTest, ExitWithFunctionCallWithLamda)
+{
+ /* arrange */
+ Arser arser;
+
+ arser.add_argument("--shutdown").help("Shut down your computer").exit_with([](void) {
+ std::cerr << "Good bye.." << std::endl;
+ });
+
+ arser.add_argument("OS").nargs(1).type(arser::DataType::STR).help("The OS you want to boot");
+
+ Prompt prompt("./computer --shutdown");
+ /* act */ /* assert */
+ EXPECT_EXIT(arser.parse(prompt.argc(), prompt.argv()), testing::ExitedWithCode(0), "Good bye..");
+}
+
+TEST(BasicTest, DefaultValue)
+{
+ /* arrange */
+ Arser arser;
+
+ arser.add_argument("--delivery")
+ .nargs(3)
+ .type(arser::DataType::STR_VEC)
+ .default_value("pizza", "chicken", "hamburger")
+ .help("Enter three foods that you want to deliver");
+ arser.add_argument("--assistant")
+ .type(arser::DataType::STR)
+ .default_value("Bixby")
+ .help("Enter name of your assistant");
+ arser.add_argument("--sound")
+ .type(arser::DataType::BOOL)
+ .nargs(1)
+ .default_value(true)
+ .help("Sound on/off");
+ arser.add_argument("--number")
+ .type(arser::DataType::INT32_VEC)
+ .nargs(4)
+ .default_value(1, 2, 3, 4)
+ .help("Enter the number that you want to call");
+ arser.add_argument("--time")
+ .type(arser::DataType::INT32_VEC)
+ .nargs(3)
+ .default_value(0, 0, 0)
+ .help("Current time(H/M/S)");
+ arser.add_argument("--name")
+ .type(arser::DataType::STR)
+ .nargs(1)
+ .default_value("no name")
+ .help("Enter your name");
+
+ Prompt prompt("/phone --time 1 52 34 --name arser");
+ /* act */
+ arser.parse(prompt.argc(), prompt.argv());
+ /* assert */
+ // 3 strings, no argument
+ std::vector<std::string> delivery = arser.get<std::vector<std::string>>("--delivery");
+ EXPECT_EQ("pizza", delivery.at(0));
+ EXPECT_EQ("chicken", delivery.at(1));
+ EXPECT_EQ("hamburger", delivery.at(2));
+ // 1 string, no argument
+ EXPECT_EQ("Bixby", arser.get<std::string>("--assistant"));
+ // 1 bool, no argument
+ EXPECT_EQ(true, arser.get<bool>("--sound"));
+ // 4 integer, no argument
+ std::vector<int> number = arser.get<std::vector<int>>("--number");
+ EXPECT_EQ(1, number.at(0));
+ EXPECT_EQ(2, number.at(1));
+ EXPECT_EQ(3, number.at(2));
+ EXPECT_EQ(4, number.at(3));
+ // 3 integer, 3 arguments
+ std::vector<int> time = arser.get<std::vector<int>>("--time");
+ EXPECT_EQ(1, time.at(0));
+ EXPECT_EQ(52, time.at(1));
+ EXPECT_EQ(34, time.at(2));
+ // 1 string, 1 argument
+ EXPECT_EQ("arser", arser.get<std::string>("--name"));
+}
diff --git a/compiler/bcq-tools/CMakeLists.txt b/compiler/bcq-tools/CMakeLists.txt
new file mode 100644
index 000000000..463f9a2e6
--- /dev/null
+++ b/compiler/bcq-tools/CMakeLists.txt
@@ -0,0 +1,29 @@
+set(BCQ_TOOLS_FILES
+ generate_bcq_metadata
+ generate_bcq_output_arrays
+ generate_bcq_metadata.py
+ generate_bcq_output_arrays.py
+)
+
+foreach(BCQ_TOOLS IN ITEMS ${BCQ_TOOLS_FILES})
+
+ set(BCQ_TOOLS_FILE ${BCQ_TOOLS})
+ set(BCQ_TOOLS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${BCQ_TOOLS_FILE}")
+ set(BCQ_TOOLS_BIN "${CMAKE_CURRENT_BINARY_DIR}/${BCQ_TOOLS_FILE}")
+ set(BCQ_TOOLS_TARGET "${BCQ_TOOLS}_target")
+
+ add_custom_command(OUTPUT ${BCQ_TOOLS_BIN}
+ COMMAND ${CMAKE_COMMAND} -E copy "${BCQ_TOOLS_SRC}" "${BCQ_TOOLS_BIN}"
+ DEPENDS ${BCQ_TOOLS_SRC}
+ COMMENT "Generate ${BCQ_TOOLS_BIN}"
+ )
+
+ add_custom_target(${BCQ_TOOLS_TARGET} ALL DEPENDS ${BCQ_TOOLS_BIN})
+
+ install(FILES ${BCQ_TOOLS_BIN}
+ PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE
+ GROUP_READ GROUP_EXECUTE
+ WORLD_READ WORLD_EXECUTE
+ DESTINATION bin)
+
+endforeach(BCQ_TOOLS)
diff --git a/compiler/bcq-tools/README.md b/compiler/bcq-tools/README.md
new file mode 100644
index 000000000..0acd0ba00
--- /dev/null
+++ b/compiler/bcq-tools/README.md
@@ -0,0 +1,70 @@
+# BCQ Tools
+
+This directory includes some tools related with BCQ.
+
+## generate_bcq_output_arrays
+
+### Purpose
+
+To apply BCQ, BCQ information nodes should be designated as model output so that they are alive even after TFLite conversion is finished.
+However, there are so many nodes to designate and sometimes we cannot copy and paste all of them because the string size is too big.
+`generate_bcq_output_arrays` is for generating output_arrays, which include BCQ information nodes.
+
+### How to use
+
+```bash
+generate_bcq_output_arrays \
+--input_path /path/to/original_model.pb \
+--output_path /path/to/output_arrays.txt
+```
+
+### How it works
+
+```
+[Original BCQ information nodes]
+const(value=[1, 2, 3, -1], name='const1')
+const(value=[1, 2, 3, -2], name='const2')
+const(value=[1, 2, 3, -3], name='const3')
+
+[Generated output_arrays]
+,const1,const2,const3
+```
+
+### Caution
+
+- Generated output_arrays will be start with comma.
+
+## generate_bcq_metadata
+
+### Purpose
+
+`generate_bcq_metadata` is for appending metadata as output of a model which includes BCQ information.
+The appended metadata is used for connecting BCQ related operations and constant nodes.
+
+### How to use
+
+```bash
+generate_bcq_metadata \
+--input_path /path/to/original_model.pb \
+--output_path /path/to/metadata_inserted_model.pb
+--output_arrays output1,output2,...,outputN
+```
+
+### How it works
+
+Metadata will be generated as following description.
+```
+< Generated Metadata in BCQ version 1 >
+[0] Starting magic number = {-2e9 + 27}
+[1] Version of BCQ = {1}
+[2] The number of original model outputs = {N | N > 0}
+[3] Bundle size = {7, 8}
+[4] Ending magic number = {2e9 - 27}
+```
+- BCQ version 1
+ - Two magic numbers, starting and ending magic number, are used for indicating that the model includes BCQ metadata. To decrease value duplication probability, prime number is used and the value is inserted not only at the beginning but also at the end.
+ - The word **bundle** means that a set of BCQ information and BCQ applicable operation. If six BCQ information nodes are used for one operation, the six information nodes and the other one operation are packaged as **bundle**. Then, in this case, the bundle size will be 6 + 1 = 7.
+
+### Caution
+
+- If there is no BCQ information in original model, any changes will be applied.
diff --git a/compiler/bcq-tools/generate_bcq_metadata b/compiler/bcq-tools/generate_bcq_metadata
new file mode 100644
index 000000000..8405556aa
--- /dev/null
+++ b/compiler/bcq-tools/generate_bcq_metadata
@@ -0,0 +1,219 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import numpy as np
+import tensorflow as tf
+
+import argparse
+import sys
+
+ONE_START_MAGICNUM = int(-2e9 + 27)
+ONE_END_MAGICNUM = int(2e9 - 27)
+
+
+def _get_parser():
+ """
+ Returns an ArgumentParser for generating BCQ metadata.
+ """
+ parser = argparse.ArgumentParser(
+ description=("Command line tool to generate metadata of BCQ nodes"))
+
+ # Input and output path.
+ parser.add_argument(
+ "-i",
+ "--input_path",
+ type=str,
+ help="Full filepath of the input file.",
+ required=True)
+ parser.add_argument(
+ "-o",
+ "--output_path",
+ type=str,
+ help="Full filepath of the output file.",
+ required=True)
+ parser.add_argument(
+ "-O",
+ "--output_arrays",
+ type=str,
+ help="Original model output arrays",
+ required=True)
+
+ return parser
+
+
+# This function is copied from
+# https://github.com/tensorflow/tensorflow/blob/r2.3/tensorflow/examples/label_image/label_image.py#L26
+def load_graph(model_file):
+ graph = tf.Graph()
+ graph_def = tf.compat.v1.GraphDef()
+
+ with open(model_file, "rb") as f:
+ graph_def.ParseFromString(f.read())
+ with graph.as_default():
+ tf.import_graph_def(graph_def, name="")
+
+ return graph
+
+
+def generate_metadata_header(original_graph, bcq_version, output_arrays):
+ # Generating metadata starts
+ metadata_values = np.array([ONE_START_MAGICNUM])
+
+ # Append BCQ version
+ metadata_values = np.append(metadata_values, bcq_version)
+
+ # Append original output count
+ output_cnt = output_arrays.count(',') + 1
+ metadata_values = np.append(metadata_values, output_cnt)
+
+ return metadata_values
+
+
+def generate_bcq_metadata_v1(flags):
+ """
+ BCQv1 contains following metadata.
+ - The number of each BCQ information set
+ """
+
+ is_valid = True
+ allowed_info_names = [
+ "bcqinfo_do_w_x", "bcqinfo_alpha", "bcqinfo_packed_binary_code",
+ "bcqinfo_number_of_clusters", "bcqinfo_size_of_clusters",
+ "bcqinfo_qbits_of_clusters", "bcqinfo_dequant_weight"
+ ]
+
+ original_graph = load_graph(flags.input_path)
+ original_graph_def = original_graph.as_graph_def()
+
+ prefix_infonames_dict = {}
+
+ for node in original_graph_def.node:
+ if node.op == "Const" and "/bcqinfo_" in node.name:
+ prefix_index = node.name.index("/bcqinfo_")
+ prefix = node.name[:prefix_index]
+ infoname = node.name[prefix_index + 1:]
+
+ if infoname not in allowed_info_names:
+ is_valid = False
+ break
+
+ if prefix not in prefix_infonames_dict:
+ prefix_infonames_dict[prefix] = set()
+
+ prefix_infonames_dict[prefix].add(infoname)
+
+ # All the number of BCQ information should be same
+ num_of_bcqinfo = -1
+ for key in prefix_infonames_dict:
+ infonames = prefix_infonames_dict[key]
+ if num_of_bcqinfo == -1:
+ num_of_bcqinfo = len(infonames)
+ elif num_of_bcqinfo != len(infonames):
+ is_valid = False
+
+ # The number of BCQv1 information should be 6 or 7
+ if num_of_bcqinfo != 6 and num_of_bcqinfo != 7:
+ is_valid = False
+
+ # If BCQ information is invalid, return original model
+ if is_valid == False:
+ return original_graph_def
+
+ new_graph_def = tf.compat.v1.GraphDef()
+ for node in original_graph_def.node:
+ new_node = new_graph_def.node.add()
+ new_node.CopyFrom(node)
+
+ # Generate metadata header
+ metadata_values = generate_metadata_header(original_graph, 1, flags.output_arrays)
+
+ # Append metadata of BCQv1
+ metadata_values = np.append(metadata_values, num_of_bcqinfo + 1)
+
+ # Finish generating metadata
+ metadata_values = np.append(metadata_values, ONE_END_MAGICNUM)
+
+ # Generate metadata tensor
+ metadata_tensor = tf.make_tensor_proto(metadata_values, tf.int32)
+
+ new_node = new_graph_def.node.add()
+ new_node.op = "Const"
+ new_node.name = "one_compiler/bcqinfo_one_metadata"
+ new_node.attr["dtype"].CopyFrom(
+ tf.core.framework.attr_value_pb2.AttrValue(type=tf.int32.as_datatype_enum))
+ new_node.attr["value"].tensor.CopyFrom(metadata_tensor)
+ return new_graph_def
+
+
+def determine_bcq_version(flags):
+ """
+ CAUTION : For now, BCQ has only one version and thus always returns 1 when BCQ
+ information nodes are included. If new BCQ version is introduced,
+ this function must be updated accordingly.
+
+ When BCQ information does not exist, -1 is returned.
+ """
+ bcq_version = -1
+
+ original_graph = load_graph(flags.input_path)
+ original_graph_def = original_graph.as_graph_def()
+
+ for node in original_graph_def.node:
+ if node.op == "Const" and "/bcqinfo_" in node.name:
+ bcq_version = 1
+ break
+
+ return bcq_version
+
+
+def generate_bcq_metadata(flags):
+ """
+ Basic format of metadata is as following.
+ - Magic number indicating start
+ - Version of BCQ Format
+ - The number of original outputs
+ - Metadata based on each BCQ format
+ - Magic number indicating end
+ """
+ program_version = 1
+ model_version = determine_bcq_version(flags)
+
+ if model_version == 1:
+ result_graph_def = generate_bcq_metadata_v1(flags)
+ elif model_version == -1:
+ # When there is no BCQ information, do nothing
+ result_graph_def = load_graph(flags.input_path)
+ else:
+ err_msg = "BCQ version of the model(v{}) ".format(model_version)
+ err_msg += "is higher than "
+ err_msg += "the version supported by this program(v{})".format(program_version)
+ raise SystemExit(err_msg)
+
+ tf.io.write_graph(result_graph_def, '.', flags.output_path, False)
+
+
+def main():
+ # Parse argument.
+ parser = _get_parser()
+ flags = parser.parse_known_args(args=sys.argv[1:])
+
+ # Generate a new pb file, which BCQ metadata is included.
+ generate_bcq_metadata(flags[0])
+
+
+if __name__ == "__main__":
+ main()
diff --git a/compiler/bcq-tools/generate_bcq_metadata.py b/compiler/bcq-tools/generate_bcq_metadata.py
new file mode 100644
index 000000000..8405556aa
--- /dev/null
+++ b/compiler/bcq-tools/generate_bcq_metadata.py
@@ -0,0 +1,219 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import numpy as np
+import tensorflow as tf
+
+import argparse
+import sys
+
+ONE_START_MAGICNUM = int(-2e9 + 27)
+ONE_END_MAGICNUM = int(2e9 - 27)
+
+
+def _get_parser():
+ """
+ Returns an ArgumentParser for generating BCQ metadata.
+ """
+ parser = argparse.ArgumentParser(
+ description=("Command line tool to generate metadata of BCQ nodes"))
+
+ # Input and output path.
+ parser.add_argument(
+ "-i",
+ "--input_path",
+ type=str,
+ help="Full filepath of the input file.",
+ required=True)
+ parser.add_argument(
+ "-o",
+ "--output_path",
+ type=str,
+ help="Full filepath of the output file.",
+ required=True)
+ parser.add_argument(
+ "-O",
+ "--output_arrays",
+ type=str,
+ help="Original model output arrays",
+ required=True)
+
+ return parser
+
+
+# This function is copied from
+# https://github.com/tensorflow/tensorflow/blob/r2.3/tensorflow/examples/label_image/label_image.py#L26
+def load_graph(model_file):
+ graph = tf.Graph()
+ graph_def = tf.compat.v1.GraphDef()
+
+ with open(model_file, "rb") as f:
+ graph_def.ParseFromString(f.read())
+ with graph.as_default():
+ tf.import_graph_def(graph_def, name="")
+
+ return graph
+
+
+def generate_metadata_header(original_graph, bcq_version, output_arrays):
+ # Generating metadata starts
+ metadata_values = np.array([ONE_START_MAGICNUM])
+
+ # Append BCQ version
+ metadata_values = np.append(metadata_values, bcq_version)
+
+ # Append original output count
+ output_cnt = output_arrays.count(',') + 1
+ metadata_values = np.append(metadata_values, output_cnt)
+
+ return metadata_values
+
+
+def generate_bcq_metadata_v1(flags):
+ """
+ BCQv1 contains following metadata.
+ - The number of each BCQ information set
+ """
+
+ is_valid = True
+ allowed_info_names = [
+ "bcqinfo_do_w_x", "bcqinfo_alpha", "bcqinfo_packed_binary_code",
+ "bcqinfo_number_of_clusters", "bcqinfo_size_of_clusters",
+ "bcqinfo_qbits_of_clusters", "bcqinfo_dequant_weight"
+ ]
+
+ original_graph = load_graph(flags.input_path)
+ original_graph_def = original_graph.as_graph_def()
+
+ prefix_infonames_dict = {}
+
+ for node in original_graph_def.node:
+ if node.op == "Const" and "/bcqinfo_" in node.name:
+ prefix_index = node.name.index("/bcqinfo_")
+ prefix = node.name[:prefix_index]
+ infoname = node.name[prefix_index + 1:]
+
+ if infoname not in allowed_info_names:
+ is_valid = False
+ break
+
+ if prefix not in prefix_infonames_dict:
+ prefix_infonames_dict[prefix] = set()
+
+ prefix_infonames_dict[prefix].add(infoname)
+
+ # All the number of BCQ information should be same
+ num_of_bcqinfo = -1
+ for key in prefix_infonames_dict:
+ infonames = prefix_infonames_dict[key]
+ if num_of_bcqinfo == -1:
+ num_of_bcqinfo = len(infonames)
+ elif num_of_bcqinfo != len(infonames):
+ is_valid = False
+
+ # The number of BCQv1 information should be 6 or 7
+ if num_of_bcqinfo != 6 and num_of_bcqinfo != 7:
+ is_valid = False
+
+ # If BCQ information is invalid, return original model
+ if is_valid == False:
+ return original_graph_def
+
+ new_graph_def = tf.compat.v1.GraphDef()
+ for node in original_graph_def.node:
+ new_node = new_graph_def.node.add()
+ new_node.CopyFrom(node)
+
+ # Generate metadata header
+ metadata_values = generate_metadata_header(original_graph, 1, flags.output_arrays)
+
+ # Append metadata of BCQv1
+ metadata_values = np.append(metadata_values, num_of_bcqinfo + 1)
+
+ # Finish generating metadata
+ metadata_values = np.append(metadata_values, ONE_END_MAGICNUM)
+
+ # Generate metadata tensor
+ metadata_tensor = tf.make_tensor_proto(metadata_values, tf.int32)
+
+ new_node = new_graph_def.node.add()
+ new_node.op = "Const"
+ new_node.name = "one_compiler/bcqinfo_one_metadata"
+ new_node.attr["dtype"].CopyFrom(
+ tf.core.framework.attr_value_pb2.AttrValue(type=tf.int32.as_datatype_enum))
+ new_node.attr["value"].tensor.CopyFrom(metadata_tensor)
+ return new_graph_def
+
+
+def determine_bcq_version(flags):
+ """
+ CAUTION : For now, BCQ has only one version and thus always returns 1 when BCQ
+ information nodes are included. If new BCQ version is introduced,
+ this function must be updated accordingly.
+
+ When BCQ information does not exist, -1 is returned.
+ """
+ bcq_version = -1
+
+ original_graph = load_graph(flags.input_path)
+ original_graph_def = original_graph.as_graph_def()
+
+ for node in original_graph_def.node:
+ if node.op == "Const" and "/bcqinfo_" in node.name:
+ bcq_version = 1
+ break
+
+ return bcq_version
+
+
+def generate_bcq_metadata(flags):
+ """
+ Basic format of metadata is as following.
+ - Magic number indicating start
+ - Version of BCQ Format
+ - The number of original outputs
+ - Metadata based on each BCQ format
+ - Magic number indicating end
+ """
+ program_version = 1
+ model_version = determine_bcq_version(flags)
+
+ if model_version == 1:
+ result_graph_def = generate_bcq_metadata_v1(flags)
+ elif model_version == -1:
+ # When there is no BCQ information, do nothing
+ result_graph_def = load_graph(flags.input_path)
+ else:
+ err_msg = "BCQ version of the model(v{}) ".format(model_version)
+ err_msg += "is higher than "
+ err_msg += "the version supported by this program(v{})".format(program_version)
+ raise SystemExit(err_msg)
+
+ tf.io.write_graph(result_graph_def, '.', flags.output_path, False)
+
+
+def main():
+ # Parse argument.
+ parser = _get_parser()
+ flags = parser.parse_known_args(args=sys.argv[1:])
+
+ # Generate a new pb file, which BCQ metadata is included.
+ generate_bcq_metadata(flags[0])
+
+
+if __name__ == "__main__":
+ main()
diff --git a/compiler/bcq-tools/generate_bcq_output_arrays b/compiler/bcq-tools/generate_bcq_output_arrays
new file mode 100644
index 000000000..b71a37410
--- /dev/null
+++ b/compiler/bcq-tools/generate_bcq_output_arrays
@@ -0,0 +1,267 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import tensorflow as tf
+
+import argparse
+import sys
+
+
+def _get_parser():
+ """
+ Returns an ArgumentParser for generating output_arrays.
+ """
+ parser = argparse.ArgumentParser(
+ description=("Command line tool to generated output_arrays of BCQ nodes"))
+
+ # Input and output path.
+ parser.add_argument(
+ "-i",
+ "--input_path",
+ type=str,
+ help="Full filepath of the input file.",
+ required=True)
+ parser.add_argument(
+ "-m",
+ "--metadata_path",
+ type=str,
+ help="Full filepath for the file that provides metadata.",
+ required=True)
+ parser.add_argument(
+ "-A",
+ "--output_arrays_path",
+ type=str,
+ help="Full filepath for the file that provides output arrays",
+ required=True)
+
+ return parser
+
+
+# This function is copied from
+# https://github.com/tensorflow/tensorflow/blob/r2.3/tensorflow/examples/label_image/label_image.py#L26
+def load_graph(model_file):
+ graph = tf.Graph()
+ graph_def = tf.compat.v1.GraphDef()
+
+ with open(model_file, "rb") as f:
+ graph_def.ParseFromString(f.read())
+ with graph.as_default():
+ tf.import_graph_def(graph_def, name="")
+
+ return graph
+
+
+def find_bcq_version(flags):
+ """
+ If BCQ metadata exists, BCQ version is in the second element.
+ Return -1 when the metadata is not found.
+ """
+ graph = load_graph(flags.input_path)
+ graph_def = graph.as_graph_def()
+ for node in graph_def.node:
+ if node.op == "Const" and "one_compiler/bcqinfo_one_metadata" in node.name:
+ metadata_tensor = tf.make_ndarray(node.attr["value"].tensor)
+ return metadata_tensor[1]
+ return -1
+
+
+def print_bcqinfo_output_arrays_v1(flags):
+ """
+ This function generates a file which includes output arrays of BCQ v1
+ information bundles. Each bundle is consisted with one of candidate
+ operations (BCQ may be applied) and BCQ constant nodes related with
+ the operation.
+ """
+ graph = load_graph(flags.input_path)
+ graph_def = graph.as_graph_def()
+ ops = graph.get_operations()
+
+ # If there is a constant node named PREFIX_1/bcqinfo_alpha,
+ # it is used for applying BCQ to constant node named PREFIX_1.
+ # Collected prefixes will be used for connecting
+ # bcqinfo nodes and user operations of prefix nodes.
+ prefix_set = set()
+ has_dequant_weight = False
+ for op in ops:
+ if op.type == "Const" and "/bcqinfo_" in op.outputs[0].name:
+ # Metadata do not have prefix
+ if "one_compiler/bcqinfo_one_metadata" in op.outputs[0].name:
+ continue
+
+ prefix_index = op.outputs[0].name.index("/bcqinfo_")
+ prefix = op.outputs[0].name[:prefix_index]
+ prefix_set.add(prefix)
+
+ # Usually, output name of op is like "outputname:0"
+ # -2 is for removing ":0"
+ infoname = op.outputs[0].name[prefix_index + 1:-2]
+ if infoname == "bcqinfo_dequant_weight":
+ has_dequant_weight = True
+
+ # Ideal situation is that the user nodes of BCQ applicable constant nodes
+ # are BCQ applicable operations such as MatMul, GatherV2, etc.
+ # However, operations which do not change original values such as
+ # Ideneity or Transpose can exist between them. In view of TensorFlow Lite,
+ # real user nodes of BCQ applicable constant nodes must be found first.
+ # This work is done by BFS search with queue.
+
+ prefix_node_dict = {} # key : prefix / value : list of candidates
+ matmul_node_prefix_dict = {} # key : Name of MatMul node / value : prefix
+
+ queue_prefix = list(prefix_set)
+ queue_nodename = [queue_prefix[idx] + ":0" for idx in range(len(queue_prefix))]
+
+ while len(queue_prefix) > 0:
+ prefix = queue_prefix.pop(0)
+ nodename = queue_nodename.pop(0)
+ if prefix not in prefix_node_dict.keys():
+ prefix_node_dict[prefix] = []
+
+ # Usually, output name of op is like "outputname:0"
+ # -2 is for removing ":0"
+ for op in ops:
+ if op.type == "MatMul" and (op.inputs[0].name == nodename
+ or op.inputs[1].name == nodename):
+ prefix_node_dict[prefix].append(op.outputs[0].name[:-2])
+ matmul_node_prefix_dict[op.outputs[0].name[:-2]] = prefix
+ elif op.type == "Einsum" and (op.inputs[0].name == nodename
+ or op.inputs[1].name == nodename):
+ prefix_node_dict[prefix].append(op.outputs[0].name[:-2])
+ elif op.type == "GatherV2" and op.inputs[0].name == nodename:
+ prefix_node_dict[prefix].append(op.outputs[0].name[:-2])
+ elif len(op.outputs) == 1:
+ for i in range(len(op.inputs)):
+ if op.inputs[i].name == nodename:
+ queue_prefix.append(prefix)
+ queue_nodename.append(op.outputs[0].name)
+ break
+
+ # When TensorFlow model is converted to TensorFlow Lite model,
+ # more than one operation can be fused as one.
+ # For example, MatMul + BiasAdd + ReLU in TensorFlow can be fused as
+ # one FullyConnected in TensorFlow Lite.
+ # It means that even real user nodes of BCQ applicable constant nodes
+ # in TensorFlow are found, they may be real user nodes in TensorFlow Lite.
+ # Therefore additional candidates of real user nodes should be found either.
+ # Finding additional candidates is done by BFS search with queue.
+
+ fuseop_prefix_dict = {} # key : Candidate operation / Value : prefix
+
+ # These ops can be candidate. However other candidates may exists after these ops.
+ mark_type = ["Add", "AddV2", "BiasAdd", "Reshape", "Transpose"]
+
+ # These ops can be candidate. And no more candidates will be found after these ops.
+ mark_and_stop_type = ["Relu", "Relu6", "Tanh"]
+
+ # These ops cannot be candidates but other candidates may exists after these ops.
+ # NOTE : Some of following ops may be removed from the list but not sure for now.
+ pass_type = [
+ "BatchToSpaceND", "Cast", "DepthToSpace", "ExpandDims", "ResizeBilinear",
+ "ResizeNearestNeighbor", "ScatterNd", "SpaceToBatchND", "SpaceToDepth", "Squeeze",
+ "Identity", "Pack", "Unpack", "Stack"
+ ]
+
+ queue_prefix = list(matmul_node_prefix_dict.values())
+ queue_nodename = [matmul + ":0" for matmul in matmul_node_prefix_dict.keys()]
+
+ visited_nodes = set(queue_nodename)
+ while len(queue_prefix) > 0:
+ prefix = queue_prefix.pop(0)
+ nodename = queue_nodename.pop(0)
+
+ # Usually, output name of op is like "outputname:0"
+ # -2 is for removing ":0"
+ for op in ops:
+ for i in range(len(op.inputs)):
+ if nodename == op.inputs[i].name:
+ if op.type in mark_type:
+ if op.outputs[0].name[:-2] not in fuseop_prefix_dict.keys():
+ fuseop_prefix_dict[op.outputs[0].name[:-2]] = set()
+ fuseop_prefix_dict[op.outputs[0].name[:-2]].add(prefix)
+ if op.outputs[0].name not in visited_nodes:
+ queue_prefix.append(prefix)
+ queue_nodename.append(op.outputs[0].name)
+ visited_nodes.add(op.outputs[0].name)
+ elif op.type in mark_and_stop_type:
+ if op.outputs[0].name[:-2] not in fuseop_prefix_dict.keys():
+ fuseop_prefix_dict[op.outputs[0].name[:-2]] = set()
+ fuseop_prefix_dict[op.outputs[0].name[:-2]].add(prefix)
+ elif op.type in pass_type and op.outputs[0].name not in visited_nodes:
+ queue_prefix.append(prefix)
+ queue_nodename.append(op.outputs[0].name)
+ visited_nodes.add(op.outputs[0].name)
+
+ # Write the name of metadata node
+ with open(flags.metadata_path, 'w') as f_metadata:
+ f_metadata.write("one_compiler/bcqinfo_one_metadata,")
+
+ # Write all pairs of candidate operations and related BCQ information nodes.
+ with open(flags.output_arrays_path, 'w') as f_arrays:
+ for prefix in prefix_set:
+ for fusable_op in prefix_node_dict[prefix]:
+ f_arrays.write("," + prefix + "/bcqinfo_do_w_x")
+ f_arrays.write("," + prefix + "/bcqinfo_alpha")
+ f_arrays.write("," + prefix + "/bcqinfo_packed_binary_code")
+ f_arrays.write("," + prefix + "/bcqinfo_number_of_clusters")
+ f_arrays.write("," + prefix + "/bcqinfo_size_of_clusters")
+ f_arrays.write("," + prefix + "/bcqinfo_qbits_of_clusters")
+ f_arrays.write("," + fusable_op)
+ if has_dequant_weight:
+ f_arrays.write("," + prefix + "/bcqinfo_dequant_weight")
+ for fuseop in fuseop_prefix_dict.keys():
+ if len(fuseop_prefix_dict[fuseop]) == 1:
+ prefix = fuseop_prefix_dict[fuseop].pop()
+ f_arrays.write("," + prefix + "/bcqinfo_do_w_x")
+ f_arrays.write("," + prefix + "/bcqinfo_alpha")
+ f_arrays.write("," + prefix + "/bcqinfo_packed_binary_code")
+ f_arrays.write("," + prefix + "/bcqinfo_number_of_clusters")
+ f_arrays.write("," + prefix + "/bcqinfo_size_of_clusters")
+ f_arrays.write("," + prefix + "/bcqinfo_qbits_of_clusters")
+ f_arrays.write("," + fuseop)
+ if has_dequant_weight:
+ f_arrays.write("," + prefix + "/bcqinfo_dequant_weight")
+
+
+def print_bcq_output_arrays(flags):
+ program_version = 1
+ model_version = find_bcq_version(flags)
+
+ if model_version == 1:
+ print_bcqinfo_output_arrays_v1(flags)
+ elif model_version == -1:
+ # When BCQ information not found, print nothing.
+ f_metadata = open(flags.metadata_path, 'w')
+ f_arrays = open(flags.output_arrays_path, 'w')
+ f_metadata.close()
+ f_arrays.close()
+ else:
+ err_msg = "BCQ version of the model(v{}) ".format(model_version)
+ err_msg += "is higher than "
+ err_msg += "the version supported by this program(v{})".format(program_version)
+ raise SystemExit(err_msg)
+
+
+def main():
+ # Parse argument.
+ parser = _get_parser()
+ flags = parser.parse_known_args(args=sys.argv[1:])
+
+ print_bcq_output_arrays(flags[0])
+
+
+if __name__ == "__main__":
+ main()
diff --git a/compiler/bcq-tools/generate_bcq_output_arrays.py b/compiler/bcq-tools/generate_bcq_output_arrays.py
new file mode 100644
index 000000000..0cc131880
--- /dev/null
+++ b/compiler/bcq-tools/generate_bcq_output_arrays.py
@@ -0,0 +1,224 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import tensorflow as tf
+
+import argparse
+import sys
+
+
+# This function is copied from
+# https://github.com/tensorflow/tensorflow/blob/r2.3/tensorflow/examples/label_image/label_image.py#L26
+def load_graph(model_file):
+ graph = tf.Graph()
+ graph_def = tf.compat.v1.GraphDef()
+
+ with open(model_file, "rb") as f:
+ graph_def.ParseFromString(f.read())
+ with graph.as_default():
+ tf.import_graph_def(graph_def, name="")
+
+ return graph
+
+
+def get_bcq_version(input_path):
+ """
+ If BCQ metadata exists, BCQ version is in the second element.
+ Return -1 when the metadata is not found.
+ """
+ graph = load_graph(input_path)
+ graph_def = graph.as_graph_def()
+ for node in graph_def.node:
+ if node.op == "Const" and "one_compiler/bcqinfo_one_metadata" in node.name:
+ metadata_tensor = tf.make_ndarray(node.attr["value"].tensor)
+ return metadata_tensor[1]
+ return -1
+
+
+def get_bcqinfo_output_arrays_v1(input_path, output_arrays):
+ """
+ This function generates a file which includes output arrays of BCQ v1
+ information bundles. Each bundle is consisted with one of candidate
+ operations (BCQ may be applied) and BCQ constant nodes related with
+ the operation.
+ """
+ graph = load_graph(input_path)
+ ops = graph.get_operations()
+
+ # If there is a constant node named PREFIX_1/bcqinfo_alpha,
+ # it is used for applying BCQ to constant node named PREFIX_1.
+ # Collected prefixes will be used for connecting
+ # bcqinfo nodes and user operations of prefix nodes.
+ prefix_set = set()
+ has_dequant_weight = False
+ for op in ops:
+ if op.type == "Const" and "/bcqinfo_" in op.outputs[0].name:
+ # Metadata do not have prefix
+ if "one_compiler/bcqinfo_one_metadata" in op.outputs[0].name:
+ continue
+
+ prefix_index = op.outputs[0].name.index("/bcqinfo_")
+ prefix = op.outputs[0].name[:prefix_index]
+ prefix_set.add(prefix)
+
+ # Usually, output name of op is like "outputname:0"
+ # -2 is for removing ":0"
+ infoname = op.outputs[0].name[prefix_index + 1:-2]
+ if infoname == "bcqinfo_dequant_weight":
+ has_dequant_weight = True
+
+ # Ideal situation is that the user nodes of BCQ applicable constant nodes
+ # are BCQ applicable operations such as MatMul, GatherV2, etc.
+ # However, operations which do not change original values such as
+ # Ideneity or Transpose can exist between them. In view of TensorFlow Lite,
+ # real user nodes of BCQ applicable constant nodes must be found first.
+ # This work is done by BFS search with queue.
+
+ prefix_node_dict = {} # key : prefix / value : list of candidates
+ matmul_node_prefix_dict = {} # key : Name of MatMul node / value : prefix
+
+ queue_prefix = list(prefix_set)
+ queue_nodename = [queue_prefix[idx] + ":0" for idx in range(len(queue_prefix))]
+
+ while len(queue_prefix) > 0:
+ prefix = queue_prefix.pop(0)
+ nodename = queue_nodename.pop(0)
+ if prefix not in prefix_node_dict.keys():
+ prefix_node_dict[prefix] = []
+
+ # Usually, output name of op is like "outputname:0"
+ # -2 is for removing ":0"
+ for op in ops:
+ if op.type == "MatMul" and (op.inputs[0].name == nodename
+ or op.inputs[1].name == nodename):
+ prefix_node_dict[prefix].append(op.outputs[0].name[:-2])
+ matmul_node_prefix_dict[op.outputs[0].name[:-2]] = prefix
+ elif op.type == "Einsum" and (op.inputs[0].name == nodename
+ or op.inputs[1].name == nodename):
+ prefix_node_dict[prefix].append(op.outputs[0].name[:-2])
+ elif op.type == "GatherV2" and op.inputs[0].name == nodename:
+ prefix_node_dict[prefix].append(op.outputs[0].name[:-2])
+ elif len(op.outputs) == 1:
+ for i in range(len(op.inputs)):
+ if op.inputs[i].name == nodename:
+ queue_prefix.append(prefix)
+ queue_nodename.append(op.outputs[0].name)
+ break
+
+ # When TensorFlow model is converted to TensorFlow Lite model,
+ # more than one operation can be fused as one.
+ # For example, MatMul + BiasAdd + ReLU in TensorFlow can be fused as
+ # one FullyConnected in TensorFlow Lite.
+ # It means that even real user nodes of BCQ applicable constant nodes
+ # in TensorFlow are found, they may be real user nodes in TensorFlow Lite.
+ # Therefore additional candidates of real user nodes should be found either.
+ # Finding additional candidates is done by BFS search with queue.
+
+ fuseop_prefix_dict = {} # key : Candidate operation / Value : prefix
+
+ # These ops can be candidate. However other candidates may exists after these ops.
+ mark_type = ["Add", "AddV2", "BiasAdd", "Reshape", "Transpose"]
+
+ # These ops can be candidate. And no more candidates will be found after these ops.
+ mark_and_stop_type = ["Relu", "Relu6", "Tanh"]
+
+ # These ops cannot be candidates but other candidates may exists after these ops.
+ # NOTE : Some of following ops may be removed from the list but not sure for now.
+ pass_type = [
+ "BatchToSpaceND", "Cast", "DepthToSpace", "ExpandDims", "ResizeBilinear",
+ "ResizeNearestNeighbor", "ScatterNd", "SpaceToBatchND", "SpaceToDepth", "Squeeze",
+ "Identity", "Pack", "Unpack", "Stack"
+ ]
+
+ queue_prefix = list(matmul_node_prefix_dict.values())
+ queue_nodename = [matmul + ":0" for matmul in matmul_node_prefix_dict.keys()]
+
+ visited_nodes = set(queue_nodename)
+ while len(queue_prefix) > 0:
+ prefix = queue_prefix.pop(0)
+ nodename = queue_nodename.pop(0)
+
+ # Usually, output name of op is like "outputname:0"
+ # -2 is for removing ":0"
+ for op in ops:
+ for i in range(len(op.inputs)):
+ if nodename == op.inputs[i].name:
+ if op.type in mark_type:
+ if op.outputs[0].name[:-2] not in fuseop_prefix_dict.keys():
+ fuseop_prefix_dict[op.outputs[0].name[:-2]] = set()
+ fuseop_prefix_dict[op.outputs[0].name[:-2]].add(prefix)
+ if op.outputs[0].name not in visited_nodes:
+ queue_prefix.append(prefix)
+ queue_nodename.append(op.outputs[0].name)
+ visited_nodes.add(op.outputs[0].name)
+ elif op.type in mark_and_stop_type:
+ if op.outputs[0].name[:-2] not in fuseop_prefix_dict.keys():
+ fuseop_prefix_dict[op.outputs[0].name[:-2]] = set()
+ fuseop_prefix_dict[op.outputs[0].name[:-2]].add(prefix)
+ elif op.type in pass_type and op.outputs[0].name not in visited_nodes:
+ queue_prefix.append(prefix)
+ queue_nodename.append(op.outputs[0].name)
+ visited_nodes.add(op.outputs[0].name)
+
+ # the name of metadata node
+ ret_output_arrays = ['one_compiler/bcqinfo_one_metadata']
+
+ # given node from user
+ ret_output_arrays.append(output_arrays)
+
+ # all pairs of candidate operations and related BCQ information nodes
+ for prefix in prefix_set:
+ for fusable_op in prefix_node_dict[prefix]:
+ ret_output_arrays.append(prefix + '/bcqinfo_do_w_x')
+ ret_output_arrays.append(prefix + '/bcqinfo_alpha')
+ ret_output_arrays.append(prefix + '/bcqinfo_packed_binary_code')
+ ret_output_arrays.append(prefix + '/bcqinfo_number_of_clusters')
+ ret_output_arrays.append(prefix + '/bcqinfo_size_of_clusters')
+ ret_output_arrays.append(prefix + '/bcqinfo_qbits_of_clusters')
+ ret_output_arrays.append(fusable_op)
+ if has_dequant_weight:
+ ret_output_arrays.append(prefix + '/bcqinfo_dequant_weight')
+ for fuseop in fuseop_prefix_dict.keys():
+ if len(fuseop_prefix_dict[fuseop]) == 1:
+ prefix = fuseop_prefix_dict[fuseop].pop()
+ ret_output_arrays.append(prefix + '/bcqinfo_do_w_x')
+ ret_output_arrays.append(prefix + '/bcqinfo_alpha')
+ ret_output_arrays.append(prefix + '/bcqinfo_packed_binary_code')
+ ret_output_arrays.append(prefix + '/bcqinfo_number_of_clusters')
+ ret_output_arrays.append(prefix + '/bcqinfo_size_of_clusters')
+ ret_output_arrays.append(prefix + '/bcqinfo_qbits_of_clusters')
+ ret_output_arrays.append(fuseop)
+ if has_dequant_weight:
+ ret_output_arrays.append(prefix + '/bcqinfo_dequant_weight')
+
+ return ret_output_arrays
+
+
+def get_bcq_output_arrays(input_path, output_arrays):
+ """Returns BCQ output arrays that the model from input_path has"""
+ program_version = 1
+ model_version = get_bcq_version(input_path)
+
+ if model_version == 1:
+ return get_bcqinfo_output_arrays_v1(input_path, output_arrays)
+ elif model_version == -1:
+ return None
+ else:
+ err_msg = "BCQ version of the model(v{}) ".format(model_version)
+ err_msg += "is higher than "
+ err_msg += "the version supported by this program(v{})".format(program_version)
+ raise SystemExit(err_msg)
diff --git a/compiler/bino/CMakeLists.txt b/compiler/bino/CMakeLists.txt
index 2416c0f22..519eecdc8 100644
--- a/compiler/bino/CMakeLists.txt
+++ b/compiler/bino/CMakeLists.txt
@@ -6,7 +6,7 @@ if(NOT ENABLE_TEST)
endif(NOT ENABLE_TEST)
# Google Test is mandatory for internal testing
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
file(GLOB_RECURSE TESTS "tests/*.cpp")
diff --git a/compiler/caffe2circle/CMakeLists.txt b/compiler/caffe2circle/CMakeLists.txt
new file mode 100644
index 000000000..eaf541705
--- /dev/null
+++ b/compiler/caffe2circle/CMakeLists.txt
@@ -0,0 +1,16 @@
+if(NOT TARGET mir_caffe_importer)
+ return()
+endif()
+
+if(NOT TARGET mir2loco)
+ return()
+endif()
+
+if(NOT TARGET exo)
+ return()
+endif()
+
+message(STATUS "Build caffe2circle: TRUE")
+
+add_executable(caffe2circle src/caffe2circle.cpp)
+target_link_libraries(caffe2circle PRIVATE mir_caffe_importer mir2loco exo)
diff --git a/compiler/caffe2circle/README.md b/compiler/caffe2circle/README.md
new file mode 100644
index 000000000..fe9ea26dd
--- /dev/null
+++ b/compiler/caffe2circle/README.md
@@ -0,0 +1,3 @@
+# caffe2circle
+
+_caffe2circle_ is a Caffe-to-Circle model converter.
diff --git a/compiler/caffe2circle/requires.cmake b/compiler/caffe2circle/requires.cmake
new file mode 100644
index 000000000..b16a51141
--- /dev/null
+++ b/compiler/caffe2circle/requires.cmake
@@ -0,0 +1,3 @@
+require("mir")
+require("mir2loco")
+require("exo")
diff --git a/compiler/caffe2circle/src/caffe2circle.cpp b/compiler/caffe2circle/src/caffe2circle.cpp
new file mode 100644
index 000000000..fb09c0a1c
--- /dev/null
+++ b/compiler/caffe2circle/src/caffe2circle.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <caffe_importer.h>
+#include <mir2loco.h>
+#include <exo/CircleExporter.h>
+
+#include <cstdlib>
+#include <iostream>
+
+int main(int argc, char *argv[])
+{
+ if (argc != 3)
+ {
+ std::cerr << "Usage: caffe2circle <path/to/caffe/model> <path/to/circle/model>\n";
+ return EXIT_FAILURE;
+ }
+
+ const char *caffe_path = argv[1];
+ const char *circle_path = argv[2];
+
+ std::unique_ptr<mir::Graph> mir_graph = mir_caffe::importModelFromBinaryFile(caffe_path);
+ std::unique_ptr<loco::Graph> loco_graph = mir2loco::Transformer().transform(mir_graph.get());
+ exo::CircleExporter(loco_graph.get()).dumpToFile(circle_path);
+ return EXIT_SUCCESS;
+}
diff --git a/compiler/caffegen/CMakeLists.txt b/compiler/caffegen/CMakeLists.txt
index 5ffc43e46..334174dcd 100644
--- a/compiler/caffegen/CMakeLists.txt
+++ b/compiler/caffegen/CMakeLists.txt
@@ -1,4 +1,4 @@
-nncc_find_package(Caffe QUIET)
+nnas_find_package(Caffe QUIET)
if(NOT Caffe_FOUND)
return()
diff --git a/compiler/circle-inspect/CMakeLists.txt b/compiler/circle-inspect/CMakeLists.txt
new file mode 100644
index 000000000..d0775ea2d
--- /dev/null
+++ b/compiler/circle-inspect/CMakeLists.txt
@@ -0,0 +1,14 @@
+if(NOT TARGET mio_circle)
+ return()
+endif(NOT TARGET mio_circle)
+
+set(DRIVER "driver/Driver.cpp")
+
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+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_circle)
+target_link_libraries(circle-inspect safemain)
diff --git a/compiler/circle-inspect/README.md b/compiler/circle-inspect/README.md
new file mode 100644
index 000000000..1f76c8ede
--- /dev/null
+++ b/compiler/circle-inspect/README.md
@@ -0,0 +1,22 @@
+# circle-inspect
+
+_circle-inspect_ allows users to retrieve various information from a Circle model file
+
+## Information to inspect
+
+Operators with `--operators`
+- show operator codes one line at a time in execution order
+
+Example
+```
+$ circle-inspect --operators model.circle
+```
+
+Result
+```
+RESHAPE
+DEPTHWISE_CONV_2D
+ADD
+```
+
+To get the count of specific operator, use other tools like sort, uniq, etc.
diff --git a/compiler/circle-inspect/driver/Driver.cpp b/compiler/circle-inspect/driver/Driver.cpp
new file mode 100644
index 000000000..72cfa28a3
--- /dev/null
+++ b/compiler/circle-inspect/driver/Driver.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dump.h"
+
+#include <arser/arser.h>
+#include <foder/FileLoader.h>
+
+#include <functional>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <vector>
+#include <string>
+
+int entry(int argc, char **argv)
+{
+ arser::Arser arser{
+ "circle-inspect allows users to retrieve various information from a Circle model file"};
+ arser.add_argument("--operators").nargs(0).help("Dump operators in circle file");
+ arser.add_argument("--conv2d_weight")
+ .nargs(0)
+ .help("Dump Conv2D series weight operators in circle file");
+ arser.add_argument("--op_version").nargs(0).help("Dump versions of the operators in circle file");
+ arser.add_argument("circle").type(arser::DataType::STR).help("Circle file to inspect");
+
+ try
+ {
+ arser.parse(argc, argv);
+ }
+ catch (const std::runtime_error &err)
+ {
+ std::cout << err.what() << std::endl;
+ std::cout << arser;
+ return 255;
+ }
+
+ if (!arser["--operators"] && !arser["--conv2d_weight"] && !arser["--op_version"])
+ {
+ std::cout << "At least one option must be specified" << std::endl;
+ std::cout << arser;
+ return 255;
+ }
+
+ std::vector<std::unique_ptr<circleinspect::DumpInterface>> dumps;
+
+ if (arser["--operators"])
+ dumps.push_back(std::make_unique<circleinspect::DumpOperators>());
+ if (arser["--conv2d_weight"])
+ dumps.push_back(std::make_unique<circleinspect::DumpConv2DWeight>());
+ if (arser["--op_version"])
+ dumps.push_back(std::make_unique<circleinspect::DumpOperatorVersion>());
+
+ std::string model_file = arser.get<std::string>("circle");
+
+ // Load Circle model from a circle file
+ foder::FileLoader fileLoader{model_file};
+ std::vector<char> modelData = fileLoader.load();
+ const circle::Model *circleModel = circle::GetModel(modelData.data());
+ if (circleModel == nullptr)
+ {
+ std::cerr << "ERROR: Failed to load circle '" << model_file << "'" << std::endl;
+ return 255;
+ }
+
+ for (auto &dump : dumps)
+ {
+ dump->run(std::cout, circleModel);
+ }
+
+ return 0;
+}
diff --git a/compiler/circle-inspect/requires.cmake b/compiler/circle-inspect/requires.cmake
new file mode 100644
index 000000000..81e0f0dbd
--- /dev/null
+++ b/compiler/circle-inspect/requires.cmake
@@ -0,0 +1,3 @@
+require("arser")
+require("mio-circle")
+require("safemain")
diff --git a/compiler/circle-inspect/src/Dump.cpp b/compiler/circle-inspect/src/Dump.cpp
new file mode 100644
index 000000000..5c71afb3f
--- /dev/null
+++ b/compiler/circle-inspect/src/Dump.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dump.h"
+#include "Reader.h"
+
+#include <ostream>
+
+namespace circleinspect
+{
+
+void DumpOperators::run(std::ostream &os, const circle::Model *model)
+{
+ circleinspect::Reader reader(model);
+
+ const uint32_t subgraph_size = reader.num_subgraph();
+
+ for (uint32_t g = 0; g < subgraph_size; g++)
+ {
+ reader.select_subgraph(g);
+ auto ops = reader.operators();
+
+ // dump operators
+ for (uint32_t i = 0; i < ops->Length(); ++i)
+ {
+ const auto op = ops->Get(i);
+
+ auto op_name = reader.opcode_name(op);
+
+ os << op_name << std::endl;
+ }
+ }
+}
+
+} // namespace circleinspect
+
+namespace
+{
+
+const circle::Operator *operator_match_output(circleinspect::Reader &reader, const int32_t tensor)
+{
+ auto ops = reader.operators();
+
+ for (uint32_t i = 0; i < ops->Length(); ++i)
+ {
+ const auto op = ops->Get(i);
+
+ const std::vector<int32_t> &outputs = circleinspect::as_index_vector(op->outputs());
+
+ for (auto output : outputs)
+ {
+ if (output == tensor)
+ return op;
+ }
+ }
+ return nullptr;
+}
+
+size_t tensor_buffer_size(circleinspect::Reader &reader, const int32_t tensor_id)
+{
+ auto tensors = reader.tensors();
+
+ if (tensor_id < 0 || tensor_id >= tensors->Length())
+ {
+ throw std::runtime_error("Invalid Tensor ID");
+ }
+
+ auto tensor = tensors->Get(tensor_id);
+ auto buffer_id = tensor->buffer();
+
+ size_t size = reader.buffer_info(buffer_id, nullptr);
+
+ return size;
+}
+
+} // namespace
+
+namespace circleinspect
+{
+
+void DumpConv2DWeight::run(std::ostream &os, const circle::Model *model)
+{
+ circleinspect::Reader reader(model);
+
+ const uint32_t subgraph_size = reader.num_subgraph();
+
+ for (uint32_t g = 0; g < subgraph_size; g++)
+ {
+ reader.select_subgraph(g);
+ auto ops = reader.operators();
+
+ // dump Conv2D, DepthwiseConv2D and its weight input operator
+ for (uint32_t i = 0; i < ops->Length(); ++i)
+ {
+ const auto op = ops->Get(i);
+ auto bc = reader.builtin_code(op);
+
+ if (bc == circle::BuiltinOperator_CONV_2D || bc == circle::BuiltinOperator_DEPTHWISE_CONV_2D)
+ {
+ const std::vector<int32_t> &inputs = circleinspect::as_index_vector(op->inputs());
+ if (inputs.size() < 2)
+ {
+ throw std::runtime_error("Operator has invalid input");
+ }
+ auto weight_input = inputs[1]; // Tensor ID of weight input
+
+ const auto op_weight = operator_match_output(reader, weight_input);
+ const auto buffer_size = tensor_buffer_size(reader, weight_input);
+
+ std::string weight_op_name = "?";
+
+ if (op_weight == nullptr && buffer_size > 0)
+ {
+ weight_op_name = "CONST";
+ }
+ else if (op_weight != nullptr)
+ {
+ weight_op_name = reader.opcode_name(op_weight);
+ }
+
+ auto op_name = reader.opcode_name(op);
+ os << op_name << "," << weight_op_name << std::endl;
+ }
+ }
+ }
+}
+
+} // namespace circleinspect
+
+namespace circleinspect
+{
+
+void DumpOperatorVersion::run(std::ostream &os, const circle::Model *model)
+{
+ std::map<std::string, int32_t> op_version_map;
+
+ circleinspect::Reader reader(model);
+
+ // This assert is subject to be changed later
+ assert(reader.num_subgraph() == 1);
+ reader.select_subgraph(0);
+
+ auto ops = reader.operators();
+
+ // Dump operators' version
+ for (uint32_t i = 0; i < ops->Length(); ++i)
+ {
+ const auto op = ops->Get(i);
+
+ auto op_name = reader.opcode_name(op);
+ auto op_version = reader.opcodes().at(op->opcode_index())->version();
+
+ if (op_version_map.find(op_name) == op_version_map.end() ||
+ op_version_map[op_name] < op_version)
+ op_version_map[op_name] = op_version;
+ }
+
+ for (auto op : op_version_map)
+ {
+ os << op.first << "," << op.second << std::endl;
+ }
+}
+
+} // namespace circleinspect
diff --git a/compiler/circle-inspect/src/Dump.h b/compiler/circle-inspect/src/Dump.h
new file mode 100644
index 000000000..996c421f9
--- /dev/null
+++ b/compiler/circle-inspect/src/Dump.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __DUMP_H__
+#define __DUMP_H__
+
+#include <mio/circle/schema_generated.h>
+
+#include <ostream>
+
+namespace circleinspect
+{
+
+class DumpInterface
+{
+public:
+ virtual ~DumpInterface() = default;
+
+public:
+ virtual void run(std::ostream &os, const circle::Model *model) = 0;
+};
+
+class DumpOperators final : public DumpInterface
+{
+public:
+ DumpOperators() = default;
+
+public:
+ void run(std::ostream &os, const circle::Model *model);
+};
+
+class DumpConv2DWeight final : public DumpInterface
+{
+public:
+ DumpConv2DWeight() = default;
+
+public:
+ void run(std::ostream &os, const circle::Model *model);
+};
+
+class DumpOperatorVersion final : public DumpInterface
+{
+public:
+ DumpOperatorVersion() = default;
+
+public:
+ void run(std::ostream &os, const circle::Model *model);
+};
+
+} // namespace circleinspect
+
+#endif // __DUMP_H__
diff --git a/compiler/circle-inspect/src/Reader.cpp b/compiler/circle-inspect/src/Reader.cpp
new file mode 100644
index 000000000..7807db38a
--- /dev/null
+++ b/compiler/circle-inspect/src/Reader.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Reader.h"
+
+#include <sstream>
+#include <string>
+
+namespace circleinspect
+{
+
+bool is_valid(const circle::OperatorCode *opcode)
+{
+ circle::BuiltinOperator code = opcode->builtin_code();
+ return (circle::BuiltinOperator_MIN <= code && code <= circle::BuiltinOperator_MAX);
+}
+
+bool is_custom(const circle::OperatorCode *opcode)
+{
+ circle::BuiltinOperator code = opcode->builtin_code();
+ 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 = opcode->builtin_code();
+ 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)
+{
+ static const char *kEmptyTensorName = "(noname)";
+
+ auto name = tensor->name();
+ if (name)
+ return name->c_str();
+
+ return kEmptyTensorName;
+}
+
+Reader::Reader(const circle::Model *model)
+{
+ _subgraphs = model->subgraphs();
+ _buffers = model->buffers();
+
+ auto opcodes = model->operator_codes();
+ for (const ::circle::OperatorCode *opcode : *opcodes)
+ {
+ _op_codes.push_back(opcode);
+ }
+}
+
+size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data)
+{
+ if (buff_data != nullptr)
+ {
+ *buff_data = nullptr;
+ }
+
+ if (buf_idx == 0)
+ return 0;
+
+ if (auto *buffer = (*_buffers)[buf_idx])
+ {
+ if (auto *array = buffer->data())
+ {
+ if (size_t size = array->size())
+ {
+ if (buff_data != nullptr)
+ {
+ *buff_data = reinterpret_cast<const uint8_t *>(array->data());
+ }
+ return size;
+ }
+ }
+ }
+
+ return 0;
+}
+
+circle::BuiltinOperator Reader::builtin_code(const circle::Operator *op) const
+{
+ uint32_t index = op->opcode_index();
+ assert(index < _op_codes.size());
+ const circle::OperatorCode *opcode = _op_codes.at(index);
+
+ return opcode->builtin_code();
+}
+
+std::string Reader::opcode_name(const circle::Operator *op) const
+{
+ uint32_t index = op->opcode_index();
+ assert(index < _op_codes.size());
+ const circle::OperatorCode *opcode = _op_codes.at(index);
+
+ if (!is_valid(opcode))
+ {
+ std::ostringstream oss;
+ oss << "(invalid: " << index << ")";
+ return oss.str();
+ }
+
+ return circleinspect::opcode_name(opcode);
+}
+
+bool Reader::select_subgraph(uint32_t sgindex)
+{
+ _tensors = nullptr;
+ _operators = nullptr;
+
+ _inputs.clear();
+ _outputs.clear();
+
+ if (_subgraphs->Length() <= sgindex)
+ {
+ assert(false);
+ return false;
+ }
+
+ const circle::SubGraph *subgraph = (*_subgraphs)[sgindex];
+
+ _tensors = subgraph->tensors();
+ _operators = subgraph->operators();
+
+ _inputs = as_index_vector(subgraph->inputs());
+ _outputs = as_index_vector(subgraph->outputs());
+
+ return true;
+}
+
+} // namespace circleinspect
diff --git a/compiler/circle-inspect/src/Reader.h b/compiler/circle-inspect/src/Reader.h
new file mode 100644
index 000000000..b5a99df3f
--- /dev/null
+++ b/compiler/circle-inspect/src/Reader.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __READER_H__
+#define __READER_H__
+
+#include <mio/circle/schema_generated.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace circleinspect
+{
+
+template <typename T> std::vector<T> as_index_vector(const flatbuffers::Vector<T> *flat_array)
+{
+ std::vector<T> ret(flat_array->Length());
+ for (uint32_t i = 0; i < flat_array->Length(); i++)
+ {
+ ret[i] = flat_array->Get(i);
+ }
+ return ret;
+}
+
+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);
+
+/**
+ * @brief Loads Circle file and provides helpers to access attributes
+ */
+class Reader
+{
+private:
+ using CircleSubGraphs_t = flatbuffers::Vector<flatbuffers::Offset<circle::SubGraph>>;
+ using CircleBuffers_t = flatbuffers::Vector<flatbuffers::Offset<circle::Buffer>>;
+ using CircleTensors_t = flatbuffers::Vector<flatbuffers::Offset<circle::Tensor>>;
+ using CircleOperators_t = flatbuffers::Vector<flatbuffers::Offset<circle::Operator>>;
+
+public:
+ Reader(const circle::Model *model);
+
+ Reader() = delete;
+
+public:
+ const std::vector<const circle::OperatorCode *> &opcodes() { return _op_codes; }
+ const CircleBuffers_t *buffers() { return _buffers; }
+ const CircleTensors_t *tensors() { return _tensors; }
+ const CircleOperators_t *operators() { return _operators; }
+ const std::vector<int32_t> &inputs() const { return _inputs; }
+ const std::vector<int32_t> &outputs() const { return _outputs; }
+
+ uint32_t num_subgraph() const { return _subgraphs->Length(); }
+
+ size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data);
+ circle::BuiltinOperator builtin_code(const circle::Operator *op) const;
+ std::string opcode_name(const circle::Operator *op) const;
+
+public:
+ bool select_subgraph(uint32_t subgraph);
+
+private:
+ const CircleSubGraphs_t *_subgraphs{nullptr};
+ const CircleBuffers_t *_buffers{nullptr};
+ const CircleTensors_t *_tensors{nullptr};
+ const CircleOperators_t *_operators{nullptr};
+
+ std::vector<const circle::OperatorCode *> _op_codes;
+ std::vector<int32_t> _inputs;
+ std::vector<int32_t> _outputs;
+};
+
+} // namespace circleinspect
+
+#endif // __READER_H__
diff --git a/compiler/circle-quantizer/CMakeLists.txt b/compiler/circle-quantizer/CMakeLists.txt
new file mode 100644
index 000000000..5075b13d5
--- /dev/null
+++ b/compiler/circle-quantizer/CMakeLists.txt
@@ -0,0 +1,16 @@
+set (SOURCES src/CircleQuantizer.cpp)
+
+add_executable(circle-quantizer "${SOURCES}")
+target_link_libraries(circle-quantizer foder)
+target_link_libraries(circle-quantizer safemain)
+target_link_libraries(circle-quantizer oops)
+target_link_libraries(circle-quantizer loco)
+target_link_libraries(circle-quantizer mio_circle)
+target_link_libraries(circle-quantizer luci_import)
+target_link_libraries(circle-quantizer luci_service)
+target_link_libraries(circle-quantizer luci_pass)
+target_link_libraries(circle-quantizer luci_export)
+target_link_libraries(circle-quantizer arser)
+target_link_libraries(circle-quantizer vconone)
+
+install(TARGETS circle-quantizer DESTINATION bin)
diff --git a/compiler/circle-quantizer/README.md b/compiler/circle-quantizer/README.md
new file mode 100644
index 000000000..2666c8412
--- /dev/null
+++ b/compiler/circle-quantizer/README.md
@@ -0,0 +1,3 @@
+# circle-quantizer
+
+_circle-quantizer_ provides post-training quantization functionalities for Circle models
diff --git a/compiler/circle-quantizer/requires.cmake b/compiler/circle-quantizer/requires.cmake
new file mode 100644
index 000000000..c21e28e8d
--- /dev/null
+++ b/compiler/circle-quantizer/requires.cmake
@@ -0,0 +1,8 @@
+require("foder")
+require("loco")
+require("locop")
+require("safemain")
+require("luci")
+require("oops")
+require("arser")
+require("vconone")
diff --git a/compiler/circle-quantizer/src/CircleQuantizer.cpp b/compiler/circle-quantizer/src/CircleQuantizer.cpp
new file mode 100644
index 000000000..54b38a170
--- /dev/null
+++ b/compiler/circle-quantizer/src/CircleQuantizer.cpp
@@ -0,0 +1,223 @@
+/*
+ * 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 <foder/FileLoader.h>
+
+#include <luci/Importer.h>
+#include <luci/CircleOptimizer.h>
+#include <luci/Service/Validate.h>
+#include <luci/CircleExporter.h>
+#include <luci/CircleFileExpContract.h>
+
+#include <oops/InternalExn.h>
+#include <arser/arser.h>
+#include <vconone/vconone.h>
+
+#include <functional>
+#include <iostream>
+#include <map>
+#include <string>
+
+using OptionHook = std::function<int(const char **)>;
+
+using Algorithms = luci::CircleOptimizer::Options::Algorithm;
+using AlgorithmParameters = luci::CircleOptimizer::Options::AlgorithmParameters;
+
+void print_exclusive_options(void)
+{
+ std::cout << "Use only one of the 3 options below." << std::endl;
+ std::cout << " --quantize_dequantize_weights" << std::endl;
+ std::cout << " --quantize_with_minmax" << std::endl;
+ std::cout << " --requantize" << std::endl;
+}
+
+void print_version(void)
+{
+ std::cout << "circle-quantizer version " << vconone::get_string() << std::endl;
+ std::cout << vconone::get_copyright() << std::endl;
+}
+
+int entry(int argc, char **argv)
+{
+ // Simple argument parser (based on map)
+ std::map<std::string, OptionHook> argparse;
+ luci::CircleOptimizer optimizer;
+
+ auto options = optimizer.options();
+
+ const std::string qdqw = "--quantize_dequantize_weights";
+ const std::string qwmm = "--quantize_with_minmax";
+ const std::string rq = "--requantize";
+
+ arser::Arser arser("circle-quantizer provides circle model quantization");
+
+ arser.add_argument("--version")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("Show version information and exit")
+ .exit_with(print_version);
+
+ arser.add_argument(qdqw)
+ .nargs(3)
+ .type(arser::DataType::STR_VEC)
+ .required(false)
+ .help("Quantize-dequantize weight values required action before quantization. "
+ "Three arguments required: input_dtype(float32) "
+ "output_dtype(uint8) granularity(layer, channel)");
+
+ arser.add_argument(qwmm)
+ .nargs(3)
+ .type(arser::DataType::STR_VEC)
+ .required(false)
+ .help("Quantize with min/max values. "
+ "Three arguments required: input_dtype(float32) "
+ "output_dtype(uint8) granularity(layer, channel)");
+
+ arser.add_argument(rq)
+ .nargs(2)
+ .type(arser::DataType::STR_VEC)
+ .required(false)
+ .help("Requantize a quantized model. "
+ "Two arguments required: input_dtype(int8) "
+ "output_dtype(uint8)");
+
+ arser.add_argument("input").nargs(1).type(arser::DataType::STR).help("Input circle model");
+ arser.add_argument("output").nargs(1).type(arser::DataType::STR).help("Output circle model");
+
+ try
+ {
+ arser.parse(argc, argv);
+ }
+ catch (const std::runtime_error &err)
+ {
+ std::cout << err.what() << std::endl;
+ std::cout << arser;
+ return 255;
+ }
+
+ if (arser[qdqw])
+ {
+ if (arser[qwmm] || arser[rq])
+ {
+ print_exclusive_options();
+ return 255;
+ }
+ auto values = arser.get<std::vector<std::string>>(qdqw);
+ if (values.size() != 3)
+ {
+ std::cerr << arser;
+ return 255;
+ }
+ options->enable(Algorithms::QuantizeDequantizeWeights);
+
+ options->param(AlgorithmParameters::Quantize_input_dtype, values.at(0));
+ options->param(AlgorithmParameters::Quantize_output_dtype, values.at(1));
+ options->param(AlgorithmParameters::Quantize_granularity, values.at(2));
+ }
+
+ if (arser[qwmm])
+ {
+ if (arser[qdqw] || arser[rq])
+ {
+ print_exclusive_options();
+ return 255;
+ }
+ auto values = arser.get<std::vector<std::string>>(qwmm);
+ if (values.size() != 3)
+ {
+ std::cerr << arser;
+ return 255;
+ }
+ options->enable(Algorithms::QuantizeWithMinMax);
+
+ options->param(AlgorithmParameters::Quantize_input_dtype, values.at(0));
+ options->param(AlgorithmParameters::Quantize_output_dtype, values.at(1));
+ options->param(AlgorithmParameters::Quantize_granularity, values.at(2));
+ }
+
+ if (arser[rq])
+ {
+ if (arser[qwmm] || arser[qdqw])
+ {
+ print_exclusive_options();
+ return 255;
+ }
+ auto values = arser.get<std::vector<std::string>>(rq);
+ if (values.size() != 2)
+ {
+ std::cerr << arser;
+ return 255;
+ }
+ options->enable(Algorithms::Requantize);
+
+ options->param(AlgorithmParameters::Quantize_input_dtype, values.at(0));
+ options->param(AlgorithmParameters::Quantize_output_dtype, values.at(1));
+ }
+
+ std::string input_path = arser.get<std::string>("input");
+ std::string output_path = arser.get<std::string>("output");
+
+ // Load model from the file
+ foder::FileLoader file_loader{input_path};
+ std::vector<char> model_data = file_loader.load();
+
+ // Verify flatbuffers
+ flatbuffers::Verifier verifier{reinterpret_cast<uint8_t *>(model_data.data()), model_data.size()};
+ if (!circle::VerifyModelBuffer(verifier))
+ {
+ std::cerr << "ERROR: Invalid input file '" << input_path << "'" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ const circle::Model *circle_model = circle::GetModel(model_data.data());
+ if (circle_model == nullptr)
+ {
+ std::cerr << "ERROR: Failed to load circle '" << input_path << "'" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Import from input Circle file
+ luci::Importer importer;
+ auto module = importer.importModule(circle_model);
+
+ for (size_t idx = 0; idx < module->size(); ++idx)
+ {
+ auto graph = module->graph(idx);
+
+ // quantize the graph
+ optimizer.quantize(graph);
+
+ if (!luci::validate(graph))
+ {
+ std::cerr << "ERROR: Quantized graph is invalid" << std::endl;
+ return 255;
+ }
+ }
+
+ // Export to output Circle file
+ luci::CircleExporter exporter;
+
+ luci::CircleFileExpContract contract(module.get(), output_path);
+
+ if (!exporter.invoke(&contract))
+ {
+ std::cerr << "ERROR: Failed to export '" << output_path << "'" << std::endl;
+ return 255;
+ }
+
+ return 0;
+}
diff --git a/compiler/circle-tensordump/CMakeLists.txt b/compiler/circle-tensordump/CMakeLists.txt
new file mode 100644
index 000000000..e55901fe2
--- /dev/null
+++ b/compiler/circle-tensordump/CMakeLists.txt
@@ -0,0 +1,23 @@
+if(NOT TARGET mio_circle)
+ return()
+endif(NOT TARGET mio_circle)
+
+nnas_find_package(HDF5 QUIET)
+
+if(NOT HDF5_FOUND)
+ message(STATUS "Build circle-tensordump: FAILED (missing HDF5)")
+ return()
+endif(NOT HDF5_FOUND)
+
+set(DRIVER "driver/Driver.cpp")
+
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+add_executable(circle-tensordump ${DRIVER} ${SOURCES})
+target_include_directories(circle-tensordump PRIVATE src)
+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_circle)
+target_link_libraries(circle-tensordump PRIVATE safemain)
diff --git a/compiler/circle-tensordump/README.md b/compiler/circle-tensordump/README.md
new file mode 100644
index 000000000..dcb05d57a
--- /dev/null
+++ b/compiler/circle-tensordump/README.md
@@ -0,0 +1,73 @@
+# circle-tensordump
+
+_circle-tensordump_ allows users to retrieve tensor information from a Circle model file
+
+## options
+
+**--tensors**
+
+dump tensors in circle file
+
+```
+$ ./circle-tensordump --tensors ../luci/tests/Conv2D_000.circle
+
+----------------------------------------------------------------------
+[ifm]
+ └── shape : (1, 3, 3, 2)
+
+----------------------------------------------------------------------
+[ker]
+ ├── shape : (1, 1, 1, 2)
+ └── buffer
+    ├── index : 3
+    ├── size : 8
+    └── data : 0.727939, 0.320132,
+
+----------------------------------------------------------------------
+[bias]
+ ├── shape : (1)
+ └── buffer
+    ├── index : 4
+    ├── size : 4
+    └── data : -0.794465,
+
+----------------------------------------------------------------------
+[ofm]
+ └── shape : (1, 3, 3, 1)
+```
+
+**--tensors_to_hdf5**
+
+dump tensors in circle file to hdf5 file
+
+```
+$ ./circle-tensordump --tensors_to_hdf5 ../luci/tests/Conv2D_000.circle output_path.h5
+$ h5dump output_path.h5
+
+HDF5 "output_path.h5" {
+GROUP "/" {
+ GROUP "bias" {
+ DATASET "weights" {
+ DATATYPE H5T_IEEE_F32LE
+ DATASPACE SIMPLE { ( 1 ) / ( 1 ) }
+ DATA {
+ (0): -0.794465
+ }
+ }
+ }
+ GROUP "ifm" {
+ }
+ GROUP "ker" {
+ DATASET "weights" {
+ DATATYPE H5T_IEEE_F32LE
+ DATASPACE SIMPLE { ( 1, 1, 1, 2 ) / ( 1, 1, 1, 2 ) }
+ DATA {
+ (0,0,0,0): 0.727939, 0.320132
+ }
+ }
+ }
+ GROUP "ofm" {
+ }
+}
+}
+```
diff --git a/compiler/circle-tensordump/driver/Driver.cpp b/compiler/circle-tensordump/driver/Driver.cpp
new file mode 100644
index 000000000..5bab9f59e
--- /dev/null
+++ b/compiler/circle-tensordump/driver/Driver.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 "Dump.h"
+
+#include <arser/arser.h>
+#include <foder/FileLoader.h>
+
+#include <functional>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+int entry(int argc, char **argv)
+{
+ arser::Arser arser{
+ "circle-tensordump allows users to retrieve tensor information from a Circle model file"};
+
+ arser.add_argument("circle").nargs(1).type(arser::DataType::STR).help("Circle file path to dump");
+ arser.add_argument("--tensors").nargs(0).help("Dump to console");
+ arser.add_argument("--tensors_to_hdf5")
+ .nargs(1)
+ .type(arser::DataType::STR)
+ .help("Dump to hdf5 file. Specify hdf5 file path to be dumped");
+
+ try
+ {
+ arser.parse(argc, argv);
+ }
+ catch (const std::runtime_error &err)
+ {
+ std::cout << err.what() << std::endl;
+ std::cout << arser;
+ return 255;
+ }
+
+ if (arser["--tensors_to_hdf5"] == arser["--tensors"])
+ {
+ std::cout << "[Error] You must specify one option for how to print." << std::endl;
+ std::cout << arser;
+ return 255;
+ }
+
+ std::unique_ptr<circletensordump::DumpInterface> dump;
+
+ std::string model_file = arser.get<std::string>("circle");
+ std::string output_path;
+ if (arser["--tensors_to_hdf5"])
+ {
+ dump = std::move(std::make_unique<circletensordump::DumpTensorsToHdf5>());
+ output_path = arser.get<std::string>("--tensors_to_hdf5");
+ }
+ if (arser["--tensors"])
+ {
+ dump = std::move(std::make_unique<circletensordump::DumpTensors>());
+ }
+
+ // Load Circle model from a circle file
+ foder::FileLoader fileLoader{model_file};
+ std::vector<char> modelData = fileLoader.load();
+ const circle::Model *circleModel = circle::GetModel(modelData.data());
+ if (circleModel == nullptr)
+ {
+ std::cerr << "ERROR: Failed to load circle '" << model_file << "'" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ dump->run(std::cout, circleModel, output_path);
+
+ return EXIT_SUCCESS;
+}
diff --git a/compiler/circle-tensordump/requires.cmake b/compiler/circle-tensordump/requires.cmake
new file mode 100644
index 000000000..1c754f518
--- /dev/null
+++ b/compiler/circle-tensordump/requires.cmake
@@ -0,0 +1,4 @@
+require("arser")
+require("foder")
+require("mio-circle")
+require("safemain")
diff --git a/compiler/circle-tensordump/src/Dump.cpp b/compiler/circle-tensordump/src/Dump.cpp
new file mode 100644
index 000000000..dee2f3620
--- /dev/null
+++ b/compiler/circle-tensordump/src/Dump.cpp
@@ -0,0 +1,347 @@
+/*
+ * 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 "Dump.h"
+#include "Reader.h"
+
+#include <H5Cpp.h>
+
+#include <memory>
+#include <ostream>
+#include <string>
+#include <vector>
+
+namespace
+{
+
+template <typename T>
+void print_comma_sepearted(std::ostream &os, const flatbuffers::Vector<T> *vec)
+{
+ if (vec == nullptr)
+ return;
+ for (auto iter = vec->begin(); iter != vec->end(); iter++)
+ {
+ if (iter != vec->begin())
+ os << ", ";
+ os << *iter;
+ }
+}
+
+void print_buffer(std::ostream &os, uint32_t buff_idx, const flatbuffers::Vector<uint8_t> *data_ptr,
+ const circle::TensorType &type)
+{
+ if (data_ptr == nullptr)
+ return;
+
+ os << " └── buffer" << std::endl;
+ os << "    ├── index : " << buff_idx << std::endl;
+ size_t buff_size = data_ptr->size();
+ os << "    ├── size : " << buff_size << std::endl;
+ os << "    └── data : ";
+ switch (type)
+ {
+ case circle::TensorType_UINT8:
+ {
+ const uint8_t *buff_data_ui8 = reinterpret_cast<const uint8_t *>(data_ptr->data());
+ for (uint32_t idx = 0; idx < buff_size / sizeof(uint8_t); idx++)
+ {
+ os << static_cast<const uint32_t>(buff_data_ui8[idx]) << ", ";
+ }
+ break;
+ }
+ case circle::TensorType_INT32:
+ {
+ const int32_t *buff_data_i32 = reinterpret_cast<const int32_t *>(data_ptr->data());
+ for (uint32_t idx = 0; idx < buff_size / sizeof(int32_t); idx++)
+ {
+ os << buff_data_i32[idx] << ", ";
+ }
+ break;
+ }
+ case circle::TensorType_INT64:
+ {
+ const int64_t *buff_data_i64 = reinterpret_cast<const int64_t *>(data_ptr->data());
+ for (uint32_t idx = 0; idx < buff_size / sizeof(int64_t); idx++)
+ {
+ os << buff_data_i64[idx] << ", ";
+ }
+ break;
+ }
+ case circle::TensorType_FLOAT32:
+ {
+ const float *buff_data_f32 = reinterpret_cast<const float *>(data_ptr->data());
+ for (uint32_t idx = 0; idx < buff_size / sizeof(float); idx++)
+ {
+ os << buff_data_f32[idx] << ", ";
+ }
+ break;
+ }
+ default:
+ throw std::runtime_error("NYI tensor type : " + std::to_string(type));
+ }
+ os << std::endl;
+}
+
+} // namespace
+
+namespace circletensordump
+{
+
+void DumpTensors::run(std::ostream &os, const circle::Model *model, const std::string &)
+{
+ circletensordump::Reader reader(model);
+ uint32_t num_subgraph = reader.num_subgraph();
+ auto buffers = reader.buffers();
+
+ for (uint32_t subgraph_idx = 0; subgraph_idx < num_subgraph; subgraph_idx++)
+ {
+ reader.select_subgraph(subgraph_idx);
+
+ auto tensors = reader.tensors();
+ for (const auto &tensor : *tensors)
+ {
+ os << std::string(70, '-') << std::endl;
+ os << "[" << tensor->name()->str() << "]" << std::endl;
+ auto buff_idx = tensor->buffer();
+ auto buff_data_ptr = reader.buffers()->Get(buff_idx)->data();
+ auto quant_param = tensor->quantization();
+ std::string print_format = (!buff_data_ptr && !quant_param) ? "└──" : "├──";
+
+ // shape
+ auto shape = tensor->shape();
+ os << " " + print_format + " shape : (";
+ ::print_comma_sepearted(os, shape);
+ os << ")" << std::endl;
+
+ // quantization paramters
+ if (quant_param)
+ {
+ std::string print_format1 = buff_data_ptr ? "├──" : "└──";
+ std::string print_format2 = buff_data_ptr ? "│" : " ";
+ os << " " + print_format1 + " quantization" << std::endl;
+ auto min = quant_param->min();
+ auto max = quant_param->max();
+ auto scale = quant_param->scale();
+ auto zero_point = quant_param->zero_point();
+ auto quantized_dimension = quant_param->quantized_dimension();
+
+ os << " " + print_format2 + "   ├── min : ";
+ ::print_comma_sepearted(os, min);
+ os << std::endl;
+ os << " " + print_format2 + "   ├── max : ";
+ ::print_comma_sepearted(os, max);
+ os << std::endl;
+ os << " " + print_format2 + "   ├── scale : ";
+ ::print_comma_sepearted(os, scale);
+ os << std::endl;
+ os << " " + print_format2 + "   ├── zero_point : ";
+ ::print_comma_sepearted(os, zero_point);
+ os << std::endl;
+ os << " " + print_format2 + "   └── quantized_dimension : " << quantized_dimension;
+ os << std::endl;
+ }
+
+ // buffer
+ print_buffer(os, buff_idx, buff_data_ptr, tensor->type());
+ os << std::endl;
+ }
+ }
+}
+
+} // namespace circletensordump
+
+namespace
+{
+
+// HDF5 forbids the inclusion of '/' in the name.
+std::string mangle(const std::string &name)
+{
+ std::string ret{name};
+ std::replace(ret.begin(), ret.end(), '/', '_');
+ return ret;
+}
+
+H5::PredType hdf5_dtype_cast(const circle::TensorType &circle_type)
+{
+ switch (circle_type)
+ {
+ case circle::TensorType_UINT8:
+ {
+ return H5::PredType::NATIVE_UINT8;
+ }
+ case circle::TensorType_INT16:
+ {
+ return H5::PredType::NATIVE_INT16;
+ }
+ case circle::TensorType_INT32:
+ {
+ return H5::PredType::NATIVE_INT32;
+ }
+ case circle::TensorType_INT64:
+ {
+ return H5::PredType::NATIVE_INT64;
+ }
+ case circle::TensorType_FLOAT32:
+ {
+ return H5::PredType::NATIVE_FLOAT;
+ }
+ default:
+ throw std::runtime_error("NYI tensor type : " + std::to_string(circle_type));
+ }
+}
+
+/**
+ * In order to create a dataspace, its rank and dimensions are required as hsize_t type.
+ * This function converts flatbuffers::Vector<T> to std::vector<hsize_t>.
+ *
+ * If "dims" parameter is passed, the parameter will be converted. However, if
+ * not passed(nullptr), data is considered as a rank 1 vector.
+ */
+template <typename T>
+std::vector<hsize_t> hdf5_dims_cast(const flatbuffers::Vector<T> *data,
+ const flatbuffers::Vector<int32_t> *dims = nullptr)
+{
+ std::vector<hsize_t> ret;
+ if (data != nullptr)
+ {
+ if (dims == nullptr)
+ {
+ ret.resize(1);
+ ret.at(0) = data->size();
+ }
+ else
+ {
+ const uint32_t rank = dims->size();
+ ret.resize(rank);
+ for (uint32_t d = 0; d < rank; d++)
+ {
+ ret.at(d) = dims->Get(d);
+ }
+ }
+ }
+ return ret;
+}
+
+/**
+ * This function writes vector data to given hdf5 file like below.
+ *
+ * GROUP "group_name"
+ * ㄴDATATYPE "type"
+ * ㄴDATASET "dataset_name"
+ * ㄴDATASPACE "dims"
+ * ㄴDATA "data"
+ */
+template <typename T>
+void write_vector_data_to_hdf5(H5::H5File &file, std::string &group_name, std::string dataset_name,
+ const H5::PredType &type, const flatbuffers::Vector<T> *data,
+ std::vector<hsize_t> dims)
+{
+ if (data == nullptr)
+ return;
+ auto dataspace = std::make_unique<H5::DataSpace>(dims.size(), dims.data());
+ auto dataset = std::make_unique<H5::DataSet>(
+ file.createDataSet(group_name + "/" + dataset_name, type, *dataspace));
+ dataset->write(data->data(), type);
+}
+
+/// @brief This function writes scalar data to given hdf5 file
+template <typename T>
+void write_scalar_data_to_hdf5(H5::H5File &file, std::string &group_name, std::string dataset_name,
+ const H5::PredType &type, T data)
+{
+ auto dataspace = std::make_unique<H5::DataSpace>(H5S_SCALAR);
+ auto dataset = std::make_unique<H5::DataSet>(
+ file.createDataSet(group_name + "/" + dataset_name, type, *dataspace));
+ dataset->write(&data, type);
+}
+
+} // namespace
+
+namespace circletensordump
+{
+
+/**
+ * HDF5 layout is like below
+ *
+ * GROUP "/"
+ * ㄴGROUP "tensor name"
+ * ㄴDATASET "weights" : Shape (x, y, ...), type(uint8, int16)
+ * ㄴDATASET "min" : Shape (n)
+ * ㄴDATASET "max" : Shape (n)
+ * ㄴDATASET "scale" : Shape (m)
+ * ㄴDATASET "zero_point" : Shape (m)
+ *
+ * NOTE All Dataset is optional. It means that if tensor doesn't have the data, it won't be created
+ * as a Dataset
+ *
+ */
+void DumpTensorsToHdf5::run(std::ostream &os, const circle::Model *model,
+ const std::string &output_path)
+{
+ // loads a circle model
+ circletensordump::Reader reader(model);
+ uint32_t num_subgraph = reader.num_subgraph();
+
+ // create a hdf5 file
+ H5::H5File file{output_path, H5F_ACC_TRUNC};
+
+ for (uint32_t subgraph_idx = 0; subgraph_idx < num_subgraph; subgraph_idx++)
+ {
+ reader.select_subgraph(subgraph_idx);
+
+ auto tensors = reader.tensors();
+ for (const auto &tensor : *tensors)
+ {
+ // create a group for each tensor whose name is its tensor name
+ std::string group_name = ::mangle(tensor->name()->c_str());
+ std::unique_ptr<H5::Group> tensor_group =
+ std::make_unique<H5::Group>(file.createGroup(group_name));
+
+ // write a buffer data
+ uint32_t buff_idx = tensor->buffer();
+ auto buff_data_ptr = reader.buffers()->Get(buff_idx)->data();
+ if (buff_data_ptr)
+ {
+ ::write_vector_data_to_hdf5(file, group_name, "weights", ::hdf5_dtype_cast(tensor->type()),
+ buff_data_ptr,
+ ::hdf5_dims_cast(buff_data_ptr, tensor->shape()));
+ }
+
+ // write quantization parameters
+ auto quant_param = tensor->quantization();
+ if (quant_param)
+ {
+ auto min = quant_param->min();
+ ::write_vector_data_to_hdf5(file, group_name, "min", H5::PredType::NATIVE_FLOAT, min,
+ ::hdf5_dims_cast(min));
+ auto max = quant_param->max();
+ ::write_vector_data_to_hdf5(file, group_name, "max", H5::PredType::NATIVE_FLOAT, max,
+ ::hdf5_dims_cast(max));
+ auto scale = quant_param->scale();
+ ::write_vector_data_to_hdf5(file, group_name, "scale", H5::PredType::NATIVE_FLOAT, scale,
+ ::hdf5_dims_cast(scale));
+ auto zero_point = quant_param->zero_point();
+ ::write_vector_data_to_hdf5(file, group_name, "zero_point", H5::PredType::NATIVE_INT64,
+ zero_point, ::hdf5_dims_cast(zero_point));
+ auto quantized_dimension = quant_param->quantized_dimension();
+ ::write_scalar_data_to_hdf5(file, group_name, "quantized_dimension",
+ H5::PredType::NATIVE_INT32, quantized_dimension);
+ }
+ }
+ }
+}
+
+} // namespace circletensordump
diff --git a/compiler/circle-tensordump/src/Dump.h b/compiler/circle-tensordump/src/Dump.h
new file mode 100644
index 000000000..5dfa59d44
--- /dev/null
+++ b/compiler/circle-tensordump/src/Dump.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_TENSORDUMP_DUMP_H__
+#define __CIRCLE_TENSORDUMP_DUMP_H__
+
+#include <mio/circle/schema_generated.h>
+
+#include <ostream>
+
+namespace circletensordump
+{
+
+class DumpInterface
+{
+public:
+ virtual ~DumpInterface() = default;
+
+public:
+ virtual void run(std::ostream &os, const circle::Model *model,
+ const std::string &output_path = {}) = 0;
+};
+
+class DumpTensors final : public DumpInterface
+{
+public:
+ DumpTensors() = default;
+
+public:
+ void run(std::ostream &os, const circle::Model *model, const std::string &) override;
+};
+
+class DumpTensorsToHdf5 final : public DumpInterface
+{
+public:
+ DumpTensorsToHdf5() = default;
+
+public:
+ void run(std::ostream &os, const circle::Model *model, const std::string &output_path) override;
+};
+
+} // namespace circletensordump
+
+#endif // __CIRCLE_TENSORDUMP_DUMP_H__
diff --git a/compiler/circle-tensordump/src/Reader.cpp b/compiler/circle-tensordump/src/Reader.cpp
new file mode 100644
index 000000000..429736bfe
--- /dev/null
+++ b/compiler/circle-tensordump/src/Reader.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Reader.h"
+
+#include <sstream>
+#include <string>
+
+namespace circletensordump
+{
+
+bool is_valid(const circle::OperatorCode *opcode)
+{
+ circle::BuiltinOperator code = opcode->builtin_code();
+ return (circle::BuiltinOperator_MIN <= code && code <= circle::BuiltinOperator_MAX);
+}
+
+bool is_custom(const circle::OperatorCode *opcode)
+{
+ circle::BuiltinOperator code = opcode->builtin_code();
+ 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 = opcode->builtin_code();
+ 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)
+{
+ static const char *kEmptyTensorName = "(noname)";
+
+ auto name = tensor->name();
+ if (name)
+ return name->c_str();
+
+ return kEmptyTensorName;
+}
+
+Reader::Reader(const circle::Model *model)
+{
+ _subgraphs = model->subgraphs();
+ _buffers = model->buffers();
+
+ auto opcodes = model->operator_codes();
+ for (const ::circle::OperatorCode *opcode : *opcodes)
+ {
+ _op_codes.push_back(opcode);
+ }
+}
+
+size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data)
+{
+ if (buff_data != nullptr)
+ {
+ *buff_data = nullptr;
+ }
+
+ if (buf_idx == 0)
+ return 0;
+
+ if (auto *buffer = (*_buffers)[buf_idx])
+ {
+ if (auto *array = buffer->data())
+ {
+ if (size_t size = array->size())
+ {
+ if (buff_data != nullptr)
+ {
+ *buff_data = reinterpret_cast<const uint8_t *>(array->data());
+ }
+ return size;
+ }
+ }
+ }
+
+ return 0;
+}
+
+circle::BuiltinOperator Reader::builtin_code(const circle::Operator *op) const
+{
+ uint32_t index = op->opcode_index();
+ assert(index < _op_codes.size());
+ const circle::OperatorCode *opcode = _op_codes.at(index);
+
+ return opcode->builtin_code();
+}
+
+std::string Reader::opcode_name(const circle::Operator *op) const
+{
+ uint32_t index = op->opcode_index();
+ assert(index < _op_codes.size());
+ const circle::OperatorCode *opcode = _op_codes.at(index);
+
+ if (!is_valid(opcode))
+ {
+ std::ostringstream oss;
+ oss << "(invalid: " << index << ")";
+ return oss.str();
+ }
+
+ return circletensordump::opcode_name(opcode);
+}
+
+bool Reader::select_subgraph(uint32_t sgindex)
+{
+ _tensors = nullptr;
+ _operators = nullptr;
+
+ _inputs.clear();
+ _outputs.clear();
+
+ if (_subgraphs->Length() <= sgindex)
+ {
+ assert(false);
+ return false;
+ }
+
+ const circle::SubGraph *subgraph = (*_subgraphs)[sgindex];
+
+ _tensors = subgraph->tensors();
+ _operators = subgraph->operators();
+
+ _inputs = as_index_vector(subgraph->inputs());
+ _outputs = as_index_vector(subgraph->outputs());
+
+ return true;
+}
+
+} // namespace circletensordump
diff --git a/compiler/circle-tensordump/src/Reader.h b/compiler/circle-tensordump/src/Reader.h
new file mode 100644
index 000000000..bbb039552
--- /dev/null
+++ b/compiler/circle-tensordump/src/Reader.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_TENSORDUMP_READER_H__
+#define __CIRCLE_TENSORDUMP_READER_H__
+
+#include <mio/circle/schema_generated.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace circletensordump
+{
+
+template <typename T> std::vector<T> as_index_vector(const flatbuffers::Vector<T> *flat_array)
+{
+ std::vector<T> ret(flat_array->Length());
+ for (uint32_t i = 0; i < flat_array->Length(); i++)
+ {
+ ret[i] = flat_array->Get(i);
+ }
+ return ret;
+}
+
+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);
+
+/**
+ * @brief Loads Circle file and provides helpers to access attributes
+ */
+class Reader
+{
+private:
+ using CircleSubGraphs_t = flatbuffers::Vector<flatbuffers::Offset<circle::SubGraph>>;
+ using CircleBuffers_t = flatbuffers::Vector<flatbuffers::Offset<circle::Buffer>>;
+ using CircleTensors_t = flatbuffers::Vector<flatbuffers::Offset<circle::Tensor>>;
+ using CircleOperators_t = flatbuffers::Vector<flatbuffers::Offset<circle::Operator>>;
+
+public:
+ Reader(const circle::Model *model);
+
+ Reader() = delete;
+
+public:
+ const std::vector<const circle::OperatorCode *> &opcodes() { return _op_codes; }
+ const CircleBuffers_t *buffers() { return _buffers; }
+ const CircleTensors_t *tensors() { return _tensors; }
+ const CircleOperators_t *operators() { return _operators; }
+ const std::vector<int32_t> &inputs() const { return _inputs; }
+ const std::vector<int32_t> &outputs() const { return _outputs; }
+
+ uint32_t num_subgraph() const { return _subgraphs->Length(); }
+
+ size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data);
+ circle::BuiltinOperator builtin_code(const circle::Operator *op) const;
+ std::string opcode_name(const circle::Operator *op) const;
+
+public:
+ bool select_subgraph(uint32_t subgraph);
+
+private:
+ const CircleSubGraphs_t *_subgraphs{nullptr};
+ const CircleBuffers_t *_buffers{nullptr};
+ const CircleTensors_t *_tensors{nullptr};
+ const CircleOperators_t *_operators{nullptr};
+
+ std::vector<const circle::OperatorCode *> _op_codes;
+ std::vector<int32_t> _inputs;
+ std::vector<int32_t> _outputs;
+};
+
+} // namespace circletensordump
+
+#endif // __CIRCLE_TENSORDUMP_READER_H__
diff --git a/compiler/circle-verify/CMakeLists.txt b/compiler/circle-verify/CMakeLists.txt
new file mode 100644
index 000000000..f22174865
--- /dev/null
+++ b/compiler/circle-verify/CMakeLists.txt
@@ -0,0 +1,13 @@
+if(NOT TARGET mio_circle)
+ return()
+endif(NOT TARGET mio_circle)
+
+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_circle)
+target_link_libraries(circle-verify safemain)
+target_link_libraries(circle-verify cwrap)
+target_link_libraries(circle-verify foder)
diff --git a/compiler/circle-verify/README.md b/compiler/circle-verify/README.md
new file mode 100644
index 000000000..1eda8a99e
--- /dev/null
+++ b/compiler/circle-verify/README.md
@@ -0,0 +1,23 @@
+# circle-verify
+
+_circle-verify_ allows users to verify Circle models.
+
+## Usage
+
+Provide _circle_ file as a parameter to verify validity.
+
+```
+$ circle-verify circlefile.circle
+```
+
+Result for valid file
+```
+[ RUN ] Check circlefile.circle
+[ PASS ] Check circlefile.circle
+```
+
+Result for invalid file
+```
+[ RUN ] Check circlefile.circle
+[ FAIL ] Check circlefile.circle
+```
diff --git a/compiler/circle-verify/requires.cmake b/compiler/circle-verify/requires.cmake
new file mode 100644
index 000000000..e1b7fb212
--- /dev/null
+++ b/compiler/circle-verify/requires.cmake
@@ -0,0 +1,5 @@
+require("arser")
+require("mio-circle")
+require("safemain")
+require("cwrap")
+require("foder")
diff --git a/compiler/circle-verify/src/Driver.cpp b/compiler/circle-verify/src/Driver.cpp
new file mode 100644
index 000000000..7a44c65b9
--- /dev/null
+++ b/compiler/circle-verify/src/Driver.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VerifyFlatBuffers.h"
+
+#include <arser/arser.h>
+
+#include <iostream>
+#include <memory>
+#include <string>
+
+int entry(int argc, char **argv)
+{
+ arser::Arser arser;
+ arser.add_argument("circle").type(arser::DataType::STR).help("Circle file path to verify");
+
+ try
+ {
+ arser.parse(argc, argv);
+ }
+ catch (const std::runtime_error &err)
+ {
+ std::cout << err.what() << std::endl;
+ std::cout << arser;
+ return 255;
+ }
+
+ auto verifier = std::make_unique<VerifyFlatbuffers>();
+
+ std::string model_file = arser.get<std::string>("circle");
+
+ std::cout << "[ RUN ] Check " << model_file << std::endl;
+
+ auto result = verifier->run(model_file);
+
+ if (result == 0)
+ {
+ std::cout << "[ PASS ] Check " << model_file << std::endl;
+ }
+ else
+ {
+ std::cout << "[ FAIL ] Check " << model_file << std::endl;
+ }
+
+ return result;
+}
diff --git a/compiler/circle-verify/src/VerifyFlatBuffers.cpp b/compiler/circle-verify/src/VerifyFlatBuffers.cpp
new file mode 100644
index 000000000..e8557d2ef
--- /dev/null
+++ b/compiler/circle-verify/src/VerifyFlatBuffers.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VerifyFlatBuffers.h"
+
+#include <foder/FileLoader.h>
+#include <mio/circle/schema_generated.h>
+
+int VerifyFlatbuffers::run(const std::string &model_file)
+{
+ foder::FileLoader fileLoader{model_file};
+ std::vector<char> modeldata = fileLoader.load();
+
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(modeldata.data());
+ flatbuffers::Verifier verifier{data, modeldata.size()};
+
+ if (!circle::VerifyModelBuffer(verifier))
+ {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/compiler/circle-verify/src/VerifyFlatBuffers.h b/compiler/circle-verify/src/VerifyFlatBuffers.h
new file mode 100644
index 000000000..c301b5b10
--- /dev/null
+++ b/compiler/circle-verify/src/VerifyFlatBuffers.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VERIFY_FLATBUFFERS_H__
+#define __VERIFY_FLATBUFFERS_H__
+
+#include <ostream>
+#include <string>
+
+class VerifyFlatbuffers
+{
+public:
+ VerifyFlatbuffers() = default;
+
+public:
+ int run(const std::string &model_file);
+};
+
+#endif // __VERIFY_FLATBUFFERS_H__
diff --git a/compiler/circle2circle-dredd-recipe-test/CMakeLists.txt b/compiler/circle2circle-dredd-recipe-test/CMakeLists.txt
new file mode 100644
index 000000000..4bcaae347
--- /dev/null
+++ b/compiler/circle2circle-dredd-recipe-test/CMakeLists.txt
@@ -0,0 +1,112 @@
+nnas_include(TargetRequire)
+
+unset(REQUIRED_TARGETS)
+list(APPEND REQUIRED_TARGETS circle-inspect)
+list(APPEND REQUIRED_TARGETS circle-verify)
+list(APPEND REQUIRED_TARGETS circle2circle)
+list(APPEND REQUIRED_TARGETS dredd_rule_lib)
+TargetRequire_Return(${REQUIRED_TARGETS})
+
+unset(TEST_DEPS)
+unset(TEST_NAMES)
+
+set(options "")
+set(oneValueArgs "")
+set(multiValueArgs PASS)
+
+get_target_property(ARTIFACTS_BIN_PATH testDataGenerator BINARY_DIR)
+
+macro(Add RECIPE)
+ cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+ unset(OPT_OPTIONS)
+ foreach(src ${ARG_PASS})
+ # option = "--${src}"
+ list(APPEND OPT_OPTIONS "--${src}")
+ endforeach(src ${ARG_PASS})
+
+ set(CIRCLE_FILE "${RECIPE}.circle")
+ set(CIRCLE_PATH "${ARTIFACTS_BIN_PATH}/${CIRCLE_FILE}")
+
+ set(OPT_CIRCLE_FILE "${RECIPE}.opt.circle")
+ set(OPT_CIRCLE_OUTPUT_PATH "${CMAKE_CURRENT_BINARY_DIR}/${OPT_CIRCLE_FILE}")
+
+ # Generate optimized .circle
+ add_custom_command(OUTPUT ${OPT_CIRCLE_OUTPUT_PATH}
+ COMMAND $<TARGET_FILE:circle2circle> ${OPT_OPTIONS} ${CIRCLE_PATH} ${OPT_CIRCLE_OUTPUT_PATH}
+ DEPENDS $<TARGET_FILE:circle2circle> ${CIRCLE_PATH}
+ COMMENT "Generate ${OPT_CIRCLE_FILE}"
+ )
+
+ list(APPEND TEST_DEPS ${OPT_CIRCLE_OUTPUT_PATH})
+ list(APPEND TEST_NAMES ${RECIPE})
+endmacro(Add)
+
+# Read "test.lst"
+include("test.lst")
+
+##
+## Copy testall
+##
+set(TEST_RUNNER "${CMAKE_CURRENT_BINARY_DIR}/testall.sh")
+set(TEST_RUNNER_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/testall.sh")
+
+add_custom_command(
+ OUTPUT ${TEST_RUNNER}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TEST_RUNNER_SOURCE}" "${TEST_RUNNER}"
+ DEPENDS ${TEST_RUNNER_SOURCE}
+ COMMENT "Generate test runner"
+)
+
+list(APPEND TEST_DEPS "${TEST_RUNNER}")
+
+###
+### Generate test.config
+###
+set(TEST_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/test.config")
+
+add_custom_command(
+ OUTPUT ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'CIRCLE_INSPECT_PATH=\"$<TARGET_FILE:circle-inspect>\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'CIRCLE_VERIFY_PATH=\"$<TARGET_FILE:circle-verify>\"' >> ${TEST_CONFIG}
+ DEPENDS
+ circle-inspect
+ circle-verify
+ COMMENT "Generate test configuration"
+)
+
+list(APPEND TEST_DEPS "${TEST_CONFIG}")
+
+#
+# copy rule-lib.sh (a library of shell script functions)
+#
+
+# getting path for rule-lib.sh in dredd-rule-lib
+get_target_property(DREDD_RULE_LIB_DIR dredd_rule_lib BINARY_DIR)
+
+set(RULE_LIB_SOURCE_PATH "${DREDD_RULE_LIB_DIR}/rule-lib.sh")
+set(RULE_LIB_BINARY_PATH "${CMAKE_CURRENT_BINARY_DIR}/rule-lib.sh")
+
+add_custom_command(
+ OUTPUT ${RULE_LIB_BINARY_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${RULE_LIB_SOURCE_PATH}" "${RULE_LIB_BINARY_PATH}"
+ DEPENDS ${RULE_LIB_SOURCE_PATH}
+ COMMENT "Generate rule lib"
+)
+
+list(APPEND TEST_DEPS "${RULE_LIB_BINARY_PATH}")
+
+# Generate dependencies
+add_custom_target(circle2circle_dredd_recipe_test ALL DEPENDS ${TEST_DEPS})
+add_dependencies(circle2circle_dredd_recipe_test common_artifacts_deps)
+
+get_target_property(ARTIFACTS_BIN_PATH testDataGenerator BINARY_DIR)
+
+# Run tests
+add_test(
+ NAME circle2circle_dredd_recipe_test
+ COMMAND "${TEST_RUNNER}"
+ "${TEST_CONFIG}"
+ "${ARTIFACTS_BIN_PATH}"
+ ${TEST_NAMES}
+)
diff --git a/compiler/circle2circle-dredd-recipe-test/README.md b/compiler/circle2circle-dredd-recipe-test/README.md
new file mode 100644
index 000000000..85140a8d1
--- /dev/null
+++ b/compiler/circle2circle-dredd-recipe-test/README.md
@@ -0,0 +1,21 @@
+# circle2circle-dredd-recipe-test
+
+It tests the non-functional conditions of the optimized circle binary resulting from circle2circle.
+
+This test basically refers to the _TensorFlowLiteRecipes_ resource. So you should add what you want to test to both of the resource and `test.lst`.
+
+## Example
+
+```
+# TensorFlowLiteRecipes
+res/TensorFlowLiteRecipes/BatchMatMulV2_000
+├── test.recipe # What you want to test
+└── test.rule # Non-functional conditions to be satisfied
+
+# test.lst
+...
+Add(BatchMatMulV2_000 PASS resolve_customop_batchmatmul)
+...
+```
+
+For more information on the rules, see _dredd-rule-lib_ module.
diff --git a/compiler/circle2circle-dredd-recipe-test/requires.cmake b/compiler/circle2circle-dredd-recipe-test/requires.cmake
new file mode 100644
index 000000000..70e7c5295
--- /dev/null
+++ b/compiler/circle2circle-dredd-recipe-test/requires.cmake
@@ -0,0 +1,5 @@
+require("circle2circle")
+require("circle-inspect")
+require("circle-verify")
+require("common-artifacts")
+require("dredd-rule-lib")
diff --git a/compiler/circle2circle-dredd-recipe-test/test.lst b/compiler/circle2circle-dredd-recipe-test/test.lst
new file mode 100644
index 000000000..3a95e2be2
--- /dev/null
+++ b/compiler/circle2circle-dredd-recipe-test/test.lst
@@ -0,0 +1,28 @@
+## EXAMPLE
+#
+# Add(RECIPE_REPO PASS pass1 pass2 ...)
+#
+## SUPPORTED PASS
+#
+# fuse_instnorm
+# resolve_customop_batchmatmul
+# resolve_customop_matmul
+
+## TFLITE RECIPE
+
+Add(Net_Preactivation_BN_000 PASS fuse_preactivation_batchnorm)
+Add(Net_TConv_Add_000 PASS fuse_add_with_tconv)
+Add(Net_TConv_Add_001 PASS fuse_add_with_tconv)
+Add(Net_TConv_Add_002 PASS fuse_add_with_tconv)
+Add(Net_TConv_BN_000 PASS fuse_batchnorm_with_tconv)
+Add(Net_TConv_BN_001 PASS fuse_batchnorm_with_tconv)
+Add(Net_InstanceNorm_001 PASS fuse_instnorm)
+Add(Net_InstanceNorm_002 PASS fuse_instnorm)
+Add(Net_InstanceNorm_003 PASS fuse_instnorm)
+Add(BatchMatMulV2_000 PASS resolve_customop_batchmatmul)
+Add(MatMul_000 PASS resolve_customop_matmul)
+Add(DepthwiseConv2D_003 PASS)
+
+## CIRCLE RECIPE
+
+Add(CircleBatchMatMul_000)
diff --git a/compiler/circle2circle-dredd-recipe-test/testall.sh b/compiler/circle2circle-dredd-recipe-test/testall.sh
new file mode 100755
index 000000000..2899587ba
--- /dev/null
+++ b/compiler/circle2circle-dredd-recipe-test/testall.sh
@@ -0,0 +1,99 @@
+#!/bin/bash
+
+# Need at least 2 arguments
+if [[ $# -lt 2 ]]; then
+ echo "USAGE: $0 ..."
+ echo
+ echo "ARGUMENTS:"
+ echo " [test.config path]"
+ echo " [WORKDIR]"
+ echo " [Prefix1]"
+ echo " [Prefix2]"
+ echo " ..."
+ exit 255
+fi
+
+WORKDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+CONFIG_PATH="$1"; shift
+RESOURCE_DIR="$1"; shift
+
+source "${CONFIG_PATH}"
+
+echo "-- Found circle-inspect: ${CIRCLE_INSPECT_PATH}"
+echo "-- Found circle-verify: ${CIRCLE_VERIFY_PATH}"
+echo "-- Found circle2circle: ${CIRCLE2CIRCLE_PATH}"
+echo "-- Found common-artifacts: ${RESOURCE_DIR}"
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+pushd ${WORKDIR}
+while [[ $# -ne 0 ]]; do
+ PREFIX="$1"; shift
+
+ TESTED+=("${PREFIX}")
+
+ PASSED_TAG="${PREFIX}.passed"
+
+ rm -f "${PASSED_TAG}"
+
+ cat > "${PREFIX}.log" <(
+ exec 2>&1
+
+ echo "-- Found circle: ${PREFIX}.opt.circle"
+
+ # Exit immediately if any command fails
+ set -e
+ # Show commands
+ set -x
+
+ #
+ # Check if rule is satisfied
+ #
+
+ # Note: turn off 'command printing'. Otherwise printing will be so messy
+ set +x
+
+ # (COMPILED_FILE, INSPECT_PROG_PATH, VERIFY_PROG_PATH, ERROR_LOG) must be set for rule-lib.sh
+ COMPILED_FILE="${PREFIX}.opt.circle"
+ INSPECT_PROG_PATH=${CIRCLE_INSPECT_PATH}
+ VERIFY_PROG_PATH=${CIRCLE_VERIFY_PATH}
+ ERROR_LOG="${PREFIX}.error"
+
+ rm -f "${ERROR_LOG}"
+
+ # in case error while running rule-lib.sh, prints error msg
+ trap 'echo "** ERROR **" ; cat "${ERROR_LOG}"' ERR
+
+ source rule-lib.sh
+ source "${RESOURCE_DIR}/${PREFIX}.rule"
+
+ # unset
+ trap - ERR
+ set -x
+
+ # At this point, the exit code of all commands is 0
+ # If not 0, execution of this script ends because of "set -e"
+ touch "${PASSED_TAG}"
+ )
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ PASSED+=("$PREFIX")
+ else
+ FAILED+=("$PREFIX")
+ 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/circle2circle/CMakeLists.txt b/compiler/circle2circle/CMakeLists.txt
new file mode 100644
index 000000000..f60c896d8
--- /dev/null
+++ b/compiler/circle2circle/CMakeLists.txt
@@ -0,0 +1,48 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_executable(circle2circle "${SOURCES}")
+target_include_directories(circle2circle PRIVATE include)
+target_include_directories(circle2circle PRIVATE src)
+target_link_libraries(circle2circle foder)
+target_link_libraries(circle2circle nncc_common)
+target_link_libraries(circle2circle safemain)
+target_link_libraries(circle2circle oops)
+target_link_libraries(circle2circle hermes)
+target_link_libraries(circle2circle hermes_std)
+target_link_libraries(circle2circle loco)
+target_link_libraries(circle2circle mio_circle)
+target_link_libraries(circle2circle luci_env)
+target_link_libraries(circle2circle luci_import)
+target_link_libraries(circle2circle luci_service)
+target_link_libraries(circle2circle luci_pass)
+target_link_libraries(circle2circle luci_export)
+target_link_libraries(circle2circle arser)
+target_link_libraries(circle2circle vconone)
+
+install(TARGETS circle2circle DESTINATION bin)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(circle2circle_test ${TESTS} ${SOURCES})
+target_include_directories(circle2circle_test PRIVATE include)
+target_include_directories(circle2circle_test PRIVATE src)
+target_link_libraries(circle2circle_test foder)
+target_link_libraries(circle2circle_test nncc_common)
+target_link_libraries(circle2circle_test oops)
+target_link_libraries(circle2circle_test hermes)
+target_link_libraries(circle2circle_test hermes_std)
+target_link_libraries(circle2circle_test loco)
+target_link_libraries(circle2circle_test mio_circle)
+target_link_libraries(circle2circle_test luci_env)
+target_link_libraries(circle2circle_test luci_import)
+target_link_libraries(circle2circle_test luci_service)
+target_link_libraries(circle2circle_test luci_pass)
+target_link_libraries(circle2circle_test luci_export)
+target_link_libraries(circle2circle_test arser)
+target_link_libraries(circle2circle_test vconone)
diff --git a/compiler/circle2circle/README.md b/compiler/circle2circle/README.md
new file mode 100644
index 000000000..3e94d2540
--- /dev/null
+++ b/compiler/circle2circle/README.md
@@ -0,0 +1,3 @@
+# circle2circle
+
+_circle2circle_ provides Circle optimizations as executable tool
diff --git a/compiler/circle2circle/include/Model.h b/compiler/circle2circle/include/Model.h
new file mode 100644
index 000000000..35b7e3239
--- /dev/null
+++ b/compiler/circle2circle/include/Model.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE2CIRCLE_MODEL_H__
+#define __CIRCLE2CIRCLE_MODEL_H__
+
+#include <mio/circle/schema_generated.h>
+
+#include <memory>
+
+namespace luci
+{
+
+struct Model
+{
+ virtual ~Model() = default;
+
+ virtual const ::circle::Model *model(void) = 0;
+};
+
+/**
+ * @brief Load Circle model (as a raw Model) from a given path
+ *
+ * @note May return a nullptr
+ */
+std::unique_ptr<Model> load_model(const std::string &path);
+
+} // namespace luci
+
+#endif // __CIRCLE2CIRCLE_MODEL_H__
diff --git a/compiler/circle2circle/requires.cmake b/compiler/circle2circle/requires.cmake
new file mode 100644
index 000000000..36a9efd16
--- /dev/null
+++ b/compiler/circle2circle/requires.cmake
@@ -0,0 +1,12 @@
+require("foder")
+require("loco")
+require("locop")
+require("logo-core")
+require("safemain")
+require("mio-circle")
+require("oops")
+require("hermes")
+require("hermes-std")
+require("luci")
+require("arser")
+require("vconone")
diff --git a/compiler/circle2circle/src/Circle2Circle.cpp b/compiler/circle2circle/src/Circle2Circle.cpp
new file mode 100644
index 000000000..20e3ea9b6
--- /dev/null
+++ b/compiler/circle2circle/src/Circle2Circle.cpp
@@ -0,0 +1,316 @@
+/*
+ * 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 <foder/FileLoader.h>
+
+#include <luci/Importer.h>
+#include <luci/CircleOptimizer.h>
+#include <luci/Service/Validate.h>
+#include <luci/CircleExporter.h>
+#include <luci/CircleFileExpContract.h>
+#include <luci/UserSettings.h>
+
+#include <oops/InternalExn.h>
+#include <arser/arser.h>
+#include <vconone/vconone.h>
+
+#include <functional>
+#include <iostream>
+#include <string>
+
+using Algorithms = luci::CircleOptimizer::Options::Algorithm;
+using AlgorithmParameters = luci::CircleOptimizer::Options::AlgorithmParameters;
+
+void print_version(void)
+{
+ std::cout << "circle2circle version " << vconone::get_string() << std::endl;
+ std::cout << vconone::get_copyright() << std::endl;
+}
+
+int entry(int argc, char **argv)
+{
+ // Simple argument parser (based on map)
+ luci::CircleOptimizer optimizer;
+
+ auto options = optimizer.options();
+ auto settings = luci::UserSettings::settings();
+
+ arser::Arser arser("circle2circle provides circle model optimization and transformations");
+
+ arser.add_argument("--version")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("Show version information and exit")
+ .exit_with(print_version);
+
+ arser.add_argument("--all").nargs(0).required(false).default_value(false).help(
+ "Enable all optimize options");
+
+ arser.add_argument("--fold_dequantize")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("This will fold dequantize op");
+
+ arser.add_argument("--fuse_activation_function")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("This will fuse Activation function to a preceding operator");
+
+ arser.add_argument("--fuse_add_with_tconv")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("This will fuse Add operator to Transposed Convolution operator");
+
+ arser.add_argument("--fuse_batchnorm_with_tconv")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("This will fuse BatchNorm operators to Transposed Convolution operator");
+
+ arser.add_argument("--fuse_bcq")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("This will fuse operators and apply Binary Coded Quantization");
+
+ arser.add_argument("--fuse_instnorm")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("This will fuse operators to InstanceNorm operator");
+
+ arser.add_argument("--make_batchnorm_gamma_positive")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("This will make negative gamma of BatchNorm into a small positive value (1e-10). Note "
+ "that this pass can change the execution result of the model. So, use it only when the "
+ "impact is known to be acceptable.");
+
+ arser.add_argument("--fuse_preactivation_batchnorm")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("This will fuse BatchNorm operators of pre-activations to Convolution operator");
+
+ arser.add_argument("--resolve_customop_add")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("This will convert Custom(Add) to Add operator");
+
+ arser.add_argument("--resolve_customop_batchmatmul")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("This will convert Custom(BatchMatmul) to BatchMatmul operator");
+
+ arser.add_argument("--resolve_customop_matmul")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("This will convert Custom(Matmul) to Matmul operator");
+
+ arser.add_argument("--mute_warnings")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("This will turn off warning messages");
+
+ arser.add_argument("--disable_validation")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("This will turn off operator validations. May help input model investigation.");
+
+ arser.add_argument("input").nargs(1).type(arser::DataType::STR).help("Input circle model");
+ arser.add_argument("output").nargs(1).type(arser::DataType::STR).help("Output circle model");
+
+ // sparsification argument
+ arser.add_argument("--sparsify_tensor")
+ .nargs(1)
+ .type(arser::DataType::STR)
+ .required(false)
+ .help("Tensor name that you want to sparsify");
+
+ arser.add_argument("--sparsify_traversal_order")
+ .nargs(1)
+ .type(arser::DataType::STR)
+ .required(false)
+ .default_value("0,1,2,3")
+ .help("Traversal order of dimensions. Default value: 0,1,2,3");
+
+ arser.add_argument("--sparsify_format")
+ .nargs(1)
+ .type(arser::DataType::STR)
+ .required(false)
+ .default_value("d,s")
+ .help("Format of each dimension. 'd' stands for dense, 's' stands for sparse(CSR). Default "
+ "value: d,s");
+
+ arser.add_argument("--sparsify_block_size")
+ .nargs(1)
+ .type(arser::DataType::STR)
+ .required(false)
+ .help("Size of each block dimension");
+
+ arser.add_argument("--sparsify_block_map")
+ .nargs(1)
+ .type(arser::DataType::STR)
+ .required(false)
+ .default_value("0,1")
+ .help("Map from block dimension to the original tensor dimension. Default value: 0,1");
+
+ try
+ {
+ arser.parse(argc, argv);
+ }
+ catch (const std::runtime_error &err)
+ {
+ std::cout << err.what() << std::endl;
+ std::cout << arser;
+ return 255;
+ }
+
+ if (arser.get<bool>("--all"))
+ {
+ options->enable(Algorithms::FuseBCQ);
+ options->enable(Algorithms::FuseInstanceNorm);
+ options->enable(Algorithms::ResolveCustomOpAdd);
+ options->enable(Algorithms::ResolveCustomOpBatchMatMul);
+ options->enable(Algorithms::ResolveCustomOpMatMul);
+ }
+ if (arser.get<bool>("--fold_dequantize"))
+ options->enable(Algorithms::FoldDequantize);
+ if (arser.get<bool>("--fuse_activation_function"))
+ options->enable(Algorithms::FuseActivationFunction);
+ if (arser.get<bool>("--fuse_add_with_tconv"))
+ options->enable(Algorithms::FuseAddWithTConv);
+ if (arser.get<bool>("--fuse_batchnorm_with_tconv"))
+ options->enable(Algorithms::FuseBatchNormWithTConv);
+ if (arser.get<bool>("--fuse_bcq"))
+ options->enable(Algorithms::FuseBCQ);
+ if (arser.get<bool>("--fuse_instnorm"))
+ options->enable(Algorithms::FuseInstanceNorm);
+ if (arser.get<bool>("--make_batchnorm_gamma_positive"))
+ options->enable(Algorithms::MakeBatchNormGammaPositive);
+ if (arser.get<bool>("--fuse_preactivation_batchnorm"))
+ options->enable(Algorithms::FusePreActivationBatchNorm);
+ if (arser.get<bool>("--resolve_customop_add"))
+ options->enable(Algorithms::ResolveCustomOpAdd);
+ if (arser.get<bool>("--resolve_customop_batchmatmul"))
+ options->enable(Algorithms::ResolveCustomOpBatchMatMul);
+ if (arser.get<bool>("--resolve_customop_matmul"))
+ options->enable(Algorithms::ResolveCustomOpMatMul);
+
+ if (arser.get<bool>("--mute_warnings"))
+ settings->set(luci::UserSettings::Key::MuteWarnings, true);
+ if (arser.get<bool>("--disable_validation"))
+ settings->set(luci::UserSettings::Key::DisableValidation, true);
+
+ std::string input_path = arser.get<std::string>("input");
+ std::string output_path = arser.get<std::string>("output");
+
+ if (arser["--sparsify_tensor"])
+ {
+ options->enable(Algorithms::SparsifyTensorPass);
+ options->param(AlgorithmParameters::Sparsify_tensor_name,
+ arser.get<std::string>("--sparsify_tensor"));
+ options->param(AlgorithmParameters::Sparsify_traversal_order,
+ arser.get<std::string>("--sparsify_traversal_order"));
+ options->param(AlgorithmParameters::Sparsify_format,
+ arser.get<std::string>("--sparsify_format"));
+ if (arser["--sparsify_block_size"])
+ options->param(AlgorithmParameters::Sparsify_block_size,
+ arser.get<std::string>("--sparsify_block_size"));
+ else
+ {
+ std::cerr << "ERROR: Block size not provided" << std::endl;
+ return 255;
+ }
+ options->param(AlgorithmParameters::Sparsify_block_map,
+ arser.get<std::string>("--sparsify_block_map"));
+ }
+
+ // Load model from the file
+ foder::FileLoader file_loader{input_path};
+ std::vector<char> model_data;
+
+ try
+ {
+ model_data = file_loader.load();
+ }
+ catch (const std::runtime_error &err)
+ {
+ std::cerr << err.what() << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ flatbuffers::Verifier verifier{reinterpret_cast<uint8_t *>(model_data.data()), model_data.size()};
+ if (!circle::VerifyModelBuffer(verifier))
+ {
+ std::cerr << "ERROR: Invalid input file '" << input_path << "'" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ const circle::Model *circle_model = circle::GetModel(model_data.data());
+ if (circle_model == nullptr)
+ {
+ std::cerr << "ERROR: Failed to load circle '" << input_path << "'" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Import from input Circle file
+ luci::Importer importer;
+ auto module = importer.importModule(circle_model);
+
+ for (size_t idx = 0; idx < module->size(); ++idx)
+ {
+ auto graph = module->graph(idx);
+
+ // call luci optimizations
+ optimizer.optimize(graph);
+ optimizer.sparsify(graph);
+
+ if (!luci::validate(graph))
+ {
+ if (settings->get(luci::UserSettings::Key::DisableValidation))
+ std::cerr << "WARNING: Optimized graph is invalid" << std::endl;
+ else
+ {
+ std::cerr << "ERROR: Optimized graph is invalid" << std::endl;
+ return 255;
+ }
+ }
+ }
+
+ // Export to output Circle file
+ luci::CircleExporter exporter;
+
+ luci::CircleFileExpContract contract(module.get(), output_path);
+
+ if (!exporter.invoke(&contract))
+ {
+ std::cerr << "ERROR: Failed to export '" << output_path << "'" << std::endl;
+ return 255;
+ }
+
+ return 0;
+}
diff --git a/compiler/circle2circle/src/Circle2Circle.test.cpp b/compiler/circle2circle/src/Circle2Circle.test.cpp
new file mode 100644
index 000000000..d8a3ed030
--- /dev/null
+++ b/compiler/circle2circle/src/Circle2Circle.test.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+TEST(Circle2CircleTest, NoArg_NEG)
+{
+ Argv<1> argv;
+ argv.add("circle2circle");
+
+ ::testing::internal::CaptureStdout();
+ int result = entry(1, argv.argv());
+ ASSERT_EQ(255, result);
+}
diff --git a/compiler/circle2circle/src/TestHelper.h b/compiler/circle2circle/src/TestHelper.h
new file mode 100644
index 000000000..f4dbe23a9
--- /dev/null
+++ b/compiler/circle2circle/src/TestHelper.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE2CIRCLE_TEST_HELPER_H__
+#define __CIRCLE2CIRCLE_TEST_HELPER_H__
+
+#include <cassert>
+#include <cstdio>
+#include <string.h>
+
+int entry(int argc, char **argv);
+
+template <size_t N> class Argv
+{
+public:
+ typedef char *pchar_t;
+
+public:
+ ~Argv()
+ {
+ for (size_t n = 0; n < _ptr; ++n)
+ delete _argv[n];
+ }
+
+ void add(const char *in)
+ {
+ assert(_ptr < N);
+ _argv[_ptr] = new char[strlen(in) + 1];
+ strcpy(_argv[_ptr], in);
+ _ptr++;
+ }
+
+ pchar_t *argv(void) { return _argv; }
+
+private:
+ pchar_t _argv[N] = {
+ nullptr,
+ };
+ size_t _ptr = 0;
+};
+
+#endif // __CIRCLE2CIRCLE_TEST_HELPER_H__
diff --git a/compiler/circlechef/CMakeLists.txt b/compiler/circlechef/CMakeLists.txt
new file mode 100644
index 000000000..3e2ddcbb3
--- /dev/null
+++ b/compiler/circlechef/CMakeLists.txt
@@ -0,0 +1,23 @@
+nnas_find_package(Protobuf QUIET)
+
+if(NOT Protobuf_FOUND)
+ return()
+endif(NOT Protobuf_FOUND)
+
+if(NOT TARGET mio_circle)
+ return()
+endif(NOT TARGET mio_circle)
+
+# Recipe Parser
+add_subdirectory(proto)
+# Log
+add_subdirectory(log)
+# Core Library
+add_subdirectory(core)
+# Circle Library
+add_subdirectory(circle)
+# Tools
+add_subdirectory(tools)
+if(ENABLE_TEST)
+ add_subdirectory(tests)
+endif(ENABLE_TEST)
diff --git a/compiler/circlechef/README.md b/compiler/circlechef/README.md
new file mode 100644
index 000000000..1871a0660
--- /dev/null
+++ b/compiler/circlechef/README.md
@@ -0,0 +1,8 @@
+# circlechef
+
+## What is circlechef?
+
+Do you need a circle model for testing? Ask it to _circlechef_.
+Given a recipe, _circlechef_ will cook a circle model for you.
+
+**NOTE** _circlechef_ covers only what _tflchef_ does not cover. This is to support ops that exist only in circle shema, and other things can be made using _tflchef_ and _tflite2circle_.
diff --git a/compiler/circlechef/circle/CMakeLists.txt b/compiler/circlechef/circle/CMakeLists.txt
new file mode 100644
index 000000000..2ca016b84
--- /dev/null
+++ b/compiler/circlechef/circle/CMakeLists.txt
@@ -0,0 +1,10 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+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_circle)
+target_link_libraries(circlechef_circle stdex)
+target_link_libraries(circlechef_circle cwrap)
+target_link_libraries(circlechef_circle souschef)
diff --git a/compiler/circlechef/circle/include/circlechef/RecipeChef.h b/compiler/circlechef/circle/include/circlechef/RecipeChef.h
new file mode 100644
index 000000000..d3cafa282
--- /dev/null
+++ b/compiler/circlechef/circle/include/circlechef/RecipeChef.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __RECIPE_CHEF_H__
+#define __RECIPE_CHEF_H__
+
+#include <mio/circle/schema_generated.h>
+#include <circlechef.pb.h>
+
+#include <memory>
+#include <string>
+
+namespace circlechef
+{
+
+/**
+ * @brief Create ModelRecipe from circle::Model
+ */
+std::unique_ptr<ModelRecipe> generate_recipe(const circle::Model *model);
+
+/**
+ * @brief Write ModelRecipe to file with given name
+ */
+bool write_recipe(const std::string &filename, std::unique_ptr<ModelRecipe> &recipe);
+
+} // namespace circlechef
+
+#endif // __RECIPE_CHEF_H__
diff --git a/compiler/circlechef/circle/src/CircleImport.cpp b/compiler/circlechef/circle/src/CircleImport.cpp
new file mode 100644
index 000000000..e970fbce3
--- /dev/null
+++ b/compiler/circlechef/circle/src/CircleImport.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CircleImport.h"
+
+#include "Convert.h"
+
+#include <sstream>
+
+namespace circlechef
+{
+
+const char *kEmptyTensorName = "(noname)";
+
+const char *tensor_type(const circle::Tensor *tensor)
+{
+ return circle::EnumNameTensorType(tensor->type());
+}
+
+const char *tensor_name(const circle::Tensor *tensor)
+{
+ auto name = tensor->name();
+ if (name)
+ return name->c_str();
+ return kEmptyTensorName;
+}
+
+bool is_valid(const circle::OperatorCode *opcode)
+{
+ circle::BuiltinOperator code = opcode->builtin_code();
+ return (circle::BuiltinOperator_MIN <= code && code <= circle::BuiltinOperator_MAX);
+}
+
+bool is_custom(const circle::OperatorCode *opcode)
+{
+ circle::BuiltinOperator code = opcode->builtin_code();
+ return (code == circle::BuiltinOperator_CUSTOM);
+}
+
+CircleImport::CircleImport(const circle::Model *model)
+{
+ _subgraphs = model->subgraphs();
+ _buffers = model->buffers();
+
+ auto opcodes = model->operator_codes();
+ for (const ::circle::OperatorCode *opcode : *opcodes)
+ {
+ _op_codes.push_back(opcode);
+ }
+}
+
+bool CircleImport::select_sub_graph(uint32_t sgindex)
+{
+ _tensors = nullptr;
+ _operators = nullptr;
+ _inputs.clear();
+ _outputs.clear();
+
+ if (_subgraphs->Length() <= sgindex)
+ {
+ assert(false);
+ return false;
+ }
+
+ const circle::SubGraph *subgraph = (*_subgraphs)[sgindex];
+
+ _tensors = subgraph->tensors();
+ _operators = subgraph->operators();
+
+ _inputs = as_index_vector(subgraph->inputs());
+ _outputs = as_index_vector(subgraph->outputs());
+
+ return true;
+}
+
+circle::BuiltinOperator CircleImport::builtin_code(const circle::Operator *op) const
+{
+ uint32_t index = op->opcode_index();
+ assert(index < _op_codes.size());
+ const circle::OperatorCode *opcode = _op_codes.at(index);
+
+ return opcode->builtin_code();
+}
+
+std::string CircleImport::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 (!is_valid(opcode))
+ {
+ std::ostringstream oss;
+ oss << "(invalid: " << index << ")";
+ return oss.str();
+ }
+
+ if (is_custom(opcode))
+ {
+ if (!opcode->custom_code())
+ return "(invalid custom)";
+
+ return opcode->custom_code()->c_str();
+ }
+
+ circle::BuiltinOperator code = opcode->builtin_code();
+ return EnumNameBuiltinOperator(code);
+}
+
+size_t CircleImport::buffer_info(const circle::Tensor *tensor, const uint8_t **buff_data)
+{
+ *buff_data = nullptr;
+
+ if (tensor->buffer() == 0)
+ return 0;
+
+ if (auto *buffer = (*_buffers)[tensor->buffer()])
+ {
+ if (auto *array = buffer->data())
+ {
+ if (size_t size = array->size())
+ {
+ *buff_data = reinterpret_cast<const uint8_t *>(array->data());
+ return size;
+ }
+ }
+ }
+
+ return 0;
+}
+
+} // namespace circlechef
diff --git a/compiler/circlechef/circle/src/CircleImport.h b/compiler/circlechef/circle/src/CircleImport.h
new file mode 100644
index 000000000..23ca29beb
--- /dev/null
+++ b/compiler/circlechef/circle/src/CircleImport.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_IMPORT_H__
+#define __CIRCLE_IMPORT_H__
+
+#include <mio/circle/schema_generated.h>
+
+#include <souschef/TensorFiller.h>
+
+#include <circlechef.pb.h>
+
+#include <map>
+#include <vector>
+
+namespace circlechef
+{
+
+using CircleSubGraphs_t = flatbuffers::Vector<flatbuffers::Offset<circle::SubGraph>>;
+using CircleTensors_t = flatbuffers::Vector<flatbuffers::Offset<circle::Tensor>>;
+using CircleBuffers_t = flatbuffers::Vector<flatbuffers::Offset<circle::Buffer>>;
+using CircleOperators_t = flatbuffers::Vector<flatbuffers::Offset<circle::Operator>>;
+
+const char *tensor_type(const circle::Tensor *tensor);
+const char *tensor_name(const circle::Tensor *tensor);
+bool is_valid(const circle::OperatorCode *opcode);
+bool is_custom(const circle::OperatorCode *opcode);
+
+/**
+ * @brief Loads TF lite file and provides helpers to access attributes
+ */
+class CircleImport : public souschef::TensorFiller
+{
+public:
+ CircleImport(const circle::Model *model);
+
+ CircleImport() = delete;
+
+public:
+ bool select_sub_graph(uint32_t subgraph);
+
+public:
+ const CircleBuffers_t *buffers() { return _buffers; }
+ const CircleTensors_t *tensors() { return _tensors; }
+ const CircleOperators_t *operators() { return _operators; }
+ const std::vector<int32_t> &inputs() const { return _inputs; }
+ const std::vector<int32_t> &outputs() const { return _outputs; }
+
+ uint32_t num_subgraph() const { return _subgraphs->Length(); }
+
+ circle::BuiltinOperator builtin_code(const circle::Operator *op) const;
+ std::string opcode_name(const circle::Operator *op) const;
+ size_t buffer_info(const circle::Tensor *tensor, const uint8_t **buff_data);
+
+private:
+ const CircleSubGraphs_t *_subgraphs{nullptr};
+ const CircleBuffers_t *_buffers{nullptr};
+ const CircleTensors_t *_tensors{nullptr};
+ const CircleOperators_t *_operators{nullptr};
+
+ std::vector<const circle::OperatorCode *> _op_codes{};
+ std::vector<int32_t> _inputs{};
+ std::vector<int32_t> _outputs{};
+};
+
+} // namespace circlechef
+
+#endif // __CIRCLE_IMPORT_H__
diff --git a/compiler/circlechef/circle/src/CircleOpChef.h b/compiler/circlechef/circle/src/CircleOpChef.h
new file mode 100644
index 000000000..a3bcd97d4
--- /dev/null
+++ b/compiler/circlechef/circle/src/CircleOpChef.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_OP_CHEF_H__
+#define __CIRCLE_OP_CHEF_H__
+
+#include <mio/circle/schema_generated.h>
+
+#include <circlechef.pb.h>
+
+#include "CircleImport.h"
+
+namespace circlechef
+{
+
+/**
+ * @brief Interface for each operators to build circlechef
+ */
+class CircleOpChef
+{
+public:
+ virtual void filler(const circle::Operator *op, CircleImport *import,
+ circlechef::ModelRecipe *model_recipe) const = 0;
+ virtual ::circlechef::Operation *build(const circle::Operator *op, CircleImport *import,
+ circlechef::ModelRecipe *model_recipe) const = 0;
+ virtual ~CircleOpChef() {}
+};
+
+} // namespace circlechef
+
+#endif // __CIRCLE_OP_CHEF_H__
diff --git a/compiler/circlechef/circle/src/CircleOpChefs.h b/compiler/circlechef/circle/src/CircleOpChefs.h
new file mode 100644
index 000000000..6a0ce5dc3
--- /dev/null
+++ b/compiler/circlechef/circle/src/CircleOpChefs.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_OP_CHEFS_H__
+#define __CIRCLE_OP_CHEFS_H__
+
+// In alphabet order
+#include "Op/BatchMatMul.h"
+#include "Op/BCQFullyConnected.h"
+#include "Op/BCQGather.h"
+#include "Op/InstanceNorm.h"
+
+#endif // __CIRCLE_OP_CHEFS_H__
diff --git a/compiler/circlechef/circle/src/CircleOpRegistry.h b/compiler/circlechef/circle/src/CircleOpRegistry.h
new file mode 100644
index 000000000..2bf1e19ed
--- /dev/null
+++ b/compiler/circlechef/circle/src/CircleOpRegistry.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_OP_REGISTRY_H__
+#define __CIRCLE_OP_REGISTRY_H__
+
+#include "CircleOpChef.h"
+#include "CircleOpChefs.h"
+
+#include <memory>
+
+namespace circlechef
+{
+
+/**
+ * @brief circlechef operator registry
+ */
+class CircleOpRegistry
+{
+public:
+ /**
+ * @brief Returns registered CircleOpChef pointer for BuiltinOperator or
+ * nullptr if not registered
+ */
+ const CircleOpChef *lookup(circle::BuiltinOperator op) const
+ {
+ if (_circleop_map.find(op) == _circleop_map.end())
+ return nullptr;
+
+ return _circleop_map.at(op).get();
+ }
+
+ static CircleOpRegistry &get()
+ {
+ static CircleOpRegistry me;
+ return me;
+ }
+
+private:
+ CircleOpRegistry()
+ {
+#define REG_TFL_OP(OPCODE, CLASS) \
+ _circleop_map[circle::BuiltinOperator_##OPCODE] = std::make_unique<CLASS>()
+
+ REG_TFL_OP(BATCH_MATMUL, CircleOpBatchMatMul);
+ REG_TFL_OP(BCQ_FULLY_CONNECTED, CircleOpBCQFullyConnected);
+ REG_TFL_OP(BCQ_GATHER, CircleOpBCQGather);
+ REG_TFL_OP(INSTANCE_NORM, CircleOpInstanceNorm);
+#undef REG_TFL_OP
+ }
+
+private:
+ std::map<circle::BuiltinOperator, std::unique_ptr<CircleOpChef>> _circleop_map;
+};
+
+} // namespace circlechef
+
+#endif // __CIRCLE_OP_REGISTRY_H__
diff --git a/compiler/circlechef/circle/src/Convert.cpp b/compiler/circlechef/circle/src/Convert.cpp
new file mode 100644
index 000000000..77614d9b5
--- /dev/null
+++ b/compiler/circlechef/circle/src/Convert.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Convert.h"
+
+namespace circlechef
+{
+
+circlechef::TensorType as_circlechef_type(const circle::TensorType type)
+{
+ switch (type)
+ {
+ case circle::TensorType_FLOAT32:
+ return circlechef::FLOAT32;
+ case circle::TensorType_INT32:
+ return circlechef::INT32;
+ case circle::TensorType_INT64:
+ return circlechef::INT64;
+ case circle::TensorType_UINT8:
+ return circlechef::UINT8;
+ case circle::TensorType_BOOL:
+ return circlechef::BOOL;
+ // TODO handle other types
+ // TensorType_FLOAT16
+ // TensorType_STRING
+ // TensorType_INT16
+ // TensorType_COMPLEX64
+ default:
+ throw std::runtime_error{"unsupported tensor type"};
+ }
+}
+
+circlechef::Activation as_circlechef_activation(const circle::ActivationFunctionType type)
+{
+ switch (type)
+ {
+ case circle::ActivationFunctionType_NONE:
+ return circlechef::NONE;
+ case circle::ActivationFunctionType_RELU:
+ return circlechef::RELU;
+ case circle::ActivationFunctionType_RELU6:
+ return circlechef::RELU6;
+ // TODO handle other types
+ // ActivationFunctionType_RELU_N1_TO_1
+ // ActivationFunctionType_TANH
+ // ActivationFunctionType_SIGN_BIT
+ default:
+ throw std::runtime_error{"unsupported activation type"};
+ }
+}
+
+circlechef::Padding as_circlechef_padding(const circle::Padding padding)
+{
+ switch (padding)
+ {
+ case circle::Padding_SAME:
+ return circlechef::SAME;
+ case circle::Padding_VALID:
+ return circlechef::VALID;
+ default:
+ throw std::runtime_error{"unsupported padding"};
+ }
+}
+
+} // namespace circlechef
diff --git a/compiler/circlechef/circle/src/Convert.h b/compiler/circlechef/circle/src/Convert.h
new file mode 100644
index 000000000..7842c4b01
--- /dev/null
+++ b/compiler/circlechef/circle/src/Convert.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONVERT_H__
+#define __CONVERT_H__
+
+#include <mio/circle/schema_generated.h>
+
+#include <circlechef.pb.h>
+
+namespace circlechef
+{
+
+circlechef::TensorType as_circlechef_type(const circle::TensorType type);
+circlechef::Activation as_circlechef_activation(const circle::ActivationFunctionType type);
+circlechef::Padding as_circlechef_padding(const circle::Padding padding);
+
+/**
+ * @brief extract buffer data to std::vector<DT>
+ */
+template <typename DT> std::vector<DT> extract_buffer(const circle::Buffer *buffer)
+{
+ auto buffer_length = buffer->data()->size();
+ auto num_elements = buffer_length / sizeof(DT);
+ std::vector<DT> result(num_elements);
+ std::memcpy(result.data(), buffer->data()->data(), buffer_length);
+ return result;
+}
+
+template <typename T> std::vector<T> as_index_vector(const flatbuffers::Vector<T> *flat_array)
+{
+ if (flat_array == nullptr)
+ throw std::runtime_error("flat_array is nullptr");
+
+ std::vector<T> ret(flat_array->Length());
+ for (uint32_t i = 0; i < flat_array->Length(); i++)
+ {
+ ret[i] = flat_array->Get(i);
+ }
+ return ret;
+}
+
+} // namespace circlechef
+
+#endif // __CONVERT_H__
diff --git a/compiler/circlechef/circle/src/Op/BCQFullyConnected.cpp b/compiler/circlechef/circle/src/Op/BCQFullyConnected.cpp
new file mode 100644
index 000000000..0e85f3969
--- /dev/null
+++ b/compiler/circlechef/circle/src/Op/BCQFullyConnected.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 "BCQFullyConnected.h"
+
+#include "Convert.h"
+
+namespace circlechef
+{
+
+void CircleOpBCQFullyConnected::filler(const circle::Operator *op, CircleImport *import,
+ circlechef::ModelRecipe *model_recipe) const
+{
+ const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
+
+ import->set_tensor_filler(inputs[1]);
+ import->set_tensor_filler(inputs[3]);
+
+ const circle::Tensor *tensor2 = import->tensors()->Get(inputs[2]);
+ assert(tensor2->type() == circle::TensorType::TensorType_INT32);
+ const circle::Buffer *buffer2 = import->buffers()->Get(tensor2->buffer());
+ auto vec2 = extract_buffer<int32_t>(buffer2);
+ import->set_tensor_filler(inputs[2], vec2);
+
+ const circle::Tensor *tensor4 = import->tensors()->Get(inputs[4]);
+ assert(tensor4->type() == circle::TensorType::TensorType_INT32);
+ const circle::Buffer *buffer4 = import->buffers()->Get(tensor4->buffer());
+ auto vec4 = extract_buffer<int32_t>(buffer4);
+ import->set_tensor_filler(inputs[4], vec4);
+}
+
+circlechef::Operation *CircleOpBCQFullyConnected::build(const circle::Operator *op,
+ CircleImport *import,
+ circlechef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_BCQFullyConnectedOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("BCQFullyConnected");
+
+ auto op_options = operation->mutable_bcq_fully_connected_options();
+
+ op_options->set_weights_hidden_size(op_params->weights_hidden_size());
+ op_options->set_activation(as_circlechef_activation(op_params->fused_activation_function()));
+
+ return operation;
+}
+
+} // namespace circlechef
diff --git a/compiler/circlechef/circle/src/Op/BCQFullyConnected.h b/compiler/circlechef/circle/src/Op/BCQFullyConnected.h
new file mode 100644
index 000000000..c0ea581d9
--- /dev/null
+++ b/compiler/circlechef/circle/src/Op/BCQFullyConnected.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_OP_BCQFULLYCONNECTED_H__
+#define __CIRCLE_OP_BCQFULLYCONNECTED_H__
+
+#include "CircleOpChef.h"
+
+namespace circlechef
+{
+
+/**
+ * @brief circlechef operator builder for BCQFullyConnected
+ */
+class CircleOpBCQFullyConnected : public CircleOpChef
+{
+public:
+ void filler(const circle::Operator *op, CircleImport *import,
+ circlechef::ModelRecipe *model_recipe) const override;
+ circlechef::Operation *build(const circle::Operator *op, CircleImport *import,
+ circlechef::ModelRecipe *model_recipe) const override;
+};
+
+} // namespace circlechef
+
+#endif // __CIRCLE_OP_BCQFULLYCONNECTED_H__
diff --git a/compiler/circlechef/circle/src/Op/BCQGather.cpp b/compiler/circlechef/circle/src/Op/BCQGather.cpp
new file mode 100644
index 000000000..cde345a34
--- /dev/null
+++ b/compiler/circlechef/circle/src/Op/BCQGather.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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 "BCQGather.h"
+
+#include "Convert.h"
+
+namespace circlechef
+{
+
+void CircleOpBCQGather::filler(const circle::Operator *op, CircleImport *import,
+ circlechef::ModelRecipe *model_recipe) const
+{
+ const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
+
+ import->set_tensor_filler(inputs[0]);
+
+ const circle::Tensor *tensor1 = import->tensors()->Get(inputs[1]);
+ assert(tensor1->type() == circle::TensorType::TensorType_INT32);
+ const circle::Buffer *buffer1 = import->buffers()->Get(tensor1->buffer());
+ auto vec1 = extract_buffer<int32_t>(buffer1);
+ import->set_tensor_filler(inputs[1], vec1);
+
+ const circle::Tensor *tensor2 = import->tensors()->Get(inputs[2]);
+ assert(tensor2->type() == circle::TensorType::TensorType_INT32);
+ const circle::Buffer *buffer2 = import->buffers()->Get(tensor2->buffer());
+ auto vec2 = extract_buffer<int32_t>(buffer2);
+ import->set_tensor_filler(inputs[2], vec2);
+
+ const circle::Tensor *tensor3 = import->tensors()->Get(inputs[3]);
+ assert(tensor3->type() == circle::TensorType::TensorType_INT32);
+ const circle::Buffer *buffer3 = import->buffers()->Get(tensor3->buffer());
+ auto vec3 = extract_buffer<int32_t>(buffer3);
+ import->set_tensor_filler(inputs[3], vec3);
+}
+
+circlechef::Operation *CircleOpBCQGather::build(const circle::Operator *op, CircleImport *import,
+ circlechef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_BCQGatherOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("BCQGather");
+
+ auto op_options = operation->mutable_bcq_gather_options();
+
+ op_options->set_input_hidden_size(op_params->input_hidden_size());
+ op_options->set_axis(op_params->axis());
+
+ return operation;
+}
+
+} // namespace circlechef
diff --git a/compiler/circlechef/circle/src/Op/BCQGather.h b/compiler/circlechef/circle/src/Op/BCQGather.h
new file mode 100644
index 000000000..0ff040551
--- /dev/null
+++ b/compiler/circlechef/circle/src/Op/BCQGather.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_OP_BCQGATHER_H__
+#define __CIRCLE_OP_BCQGATHER_H__
+
+#include "CircleOpChef.h"
+
+namespace circlechef
+{
+
+/**
+ * @brief circlechef operator builder for BCQGather
+ */
+class CircleOpBCQGather : public CircleOpChef
+{
+public:
+ void filler(const circle::Operator *op, CircleImport *import,
+ circlechef::ModelRecipe *model_recipe) const override;
+ circlechef::Operation *build(const circle::Operator *op, CircleImport *import,
+ circlechef::ModelRecipe *model_recipe) const override;
+};
+
+} // namespace circlechef
+
+#endif // __CIRCLE_OP_BCQGATHER_H__
diff --git a/compiler/circlechef/circle/src/Op/BatchMatMul.cpp b/compiler/circlechef/circle/src/Op/BatchMatMul.cpp
new file mode 100644
index 000000000..bcf218865
--- /dev/null
+++ b/compiler/circlechef/circle/src/Op/BatchMatMul.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BatchMatMul.h"
+
+#include "Convert.h"
+
+namespace circlechef
+{
+
+void CircleOpBatchMatMul::filler(const circle::Operator *op, CircleImport *import,
+ circlechef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+circlechef::Operation *CircleOpBatchMatMul::build(const circle::Operator *op, CircleImport *import,
+ circlechef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_BatchMatMulOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("BatchMatMul");
+
+ auto op_options = operation->mutable_batch_matmul_options();
+
+ op_options->set_adjoint_lhs(op_params->adjoint_lhs());
+ op_options->set_adjoint_rhs(op_params->adjoint_rhs());
+
+ return operation;
+}
+
+} // namespace circlechef
diff --git a/compiler/circlechef/circle/src/Op/BatchMatMul.h b/compiler/circlechef/circle/src/Op/BatchMatMul.h
new file mode 100644
index 000000000..3d4036877
--- /dev/null
+++ b/compiler/circlechef/circle/src/Op/BatchMatMul.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_OP_BATCHMATMUL_H__
+#define __CIRCLE_OP_BATCHMATMUL_H__
+
+#include "CircleOpChef.h"
+
+namespace circlechef
+{
+
+/**
+ * @brief circlechef operator builder for batchmatmul
+ */
+class CircleOpBatchMatMul : public CircleOpChef
+{
+public:
+ void filler(const circle::Operator *op, CircleImport *import,
+ circlechef::ModelRecipe *model_recipe) const override;
+ circlechef::Operation *build(const circle::Operator *op, CircleImport *import,
+ circlechef::ModelRecipe *model_recipe) const override;
+};
+
+} // namespace circlechef
+
+#endif // __CIRCLE_OP_BATCHMATMUL_H__
diff --git a/compiler/circlechef/circle/src/Op/InstanceNorm.cpp b/compiler/circlechef/circle/src/Op/InstanceNorm.cpp
new file mode 100644
index 000000000..a1395a578
--- /dev/null
+++ b/compiler/circlechef/circle/src/Op/InstanceNorm.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InstanceNorm.h"
+
+#include "Convert.h"
+
+namespace circlechef
+{
+
+void CircleOpInstanceNorm::filler(const circle::Operator *op, CircleImport *import,
+ circlechef::ModelRecipe *model_recipe) const
+{
+ // index 1 and 2 maybe constant
+ const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
+ assert(inputs.size() == 3);
+
+ import->set_tensor_filler(inputs[1]); // set gaussian filler
+ import->set_tensor_filler(inputs[2]);
+}
+
+circlechef::Operation *CircleOpInstanceNorm::build(const circle::Operator *op, CircleImport *import,
+ circlechef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("InstanceNorm");
+
+ auto op_options = operation->mutable_instance_norm_options();
+
+ auto op_params = op->builtin_options_as_InstanceNormOptions();
+ assert(op_params != nullptr);
+
+ op_options->set_epsilon(op_params->epsilon());
+ op_options->set_activation(as_circlechef_activation(op_params->fused_activation_function()));
+
+ return operation;
+}
+
+} // namespace circlechef
diff --git a/compiler/circlechef/circle/src/Op/InstanceNorm.h b/compiler/circlechef/circle/src/Op/InstanceNorm.h
new file mode 100644
index 000000000..9cb48e184
--- /dev/null
+++ b/compiler/circlechef/circle/src/Op/InstanceNorm.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_OP_INSTANCE_NORM_H__
+#define __CIRCLE_OP_INSTANCE_NORM_H__
+
+#include "CircleOpChef.h"
+
+namespace circlechef
+{
+
+/**
+ * @brief circlechef operator builder for INSTANCE_NORM
+ */
+class CircleOpInstanceNorm : public CircleOpChef
+{
+public:
+ void filler(const circle::Operator *op, CircleImport *import,
+ circlechef::ModelRecipe *model_recipe) const override;
+ circlechef::Operation *build(const circle::Operator *op, CircleImport *import,
+ circlechef::ModelRecipe *model_recipe) const override;
+};
+
+} // namespace circlechef
+
+#endif // __CIRCLE_OP_INSTANCE_NORM_H__
diff --git a/compiler/circlechef/circle/src/RecipeChef.cpp b/compiler/circlechef/circle/src/RecipeChef.cpp
new file mode 100644
index 000000000..cd520cbc3
--- /dev/null
+++ b/compiler/circlechef/circle/src/RecipeChef.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <circlechef/RecipeChef.h>
+
+#include "Convert.h"
+#include "CircleImport.h"
+#include "CircleOpChef.h"
+#include "CircleOpChefs.h"
+#include "CircleOpRegistry.h"
+
+#include <fstream>
+#include <sstream>
+
+namespace circlechef
+{
+
+void set_inputs(CircleImport *import, circlechef::Operation *operation, const circle::Operator *op)
+{
+ auto tensors = import->tensors();
+ const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
+
+ for (auto input : inputs)
+ {
+ if (input == -1)
+ {
+ operation->add_input("");
+ }
+ else
+ {
+ auto tensor = tensors->Get(input);
+ std::string name = tensor_name(tensor);
+ operation->add_input(name);
+ }
+ }
+}
+
+void set_outputs(CircleImport *import, circlechef::Operation *operation, const circle::Operator *op)
+{
+ auto tensors = import->tensors();
+ const std::vector<int32_t> &outputs = as_index_vector(op->outputs());
+
+ for (auto output : outputs)
+ {
+ auto tensor = tensors->Get(output);
+ std::string name = tensor_name(tensor);
+ operation->add_output(name);
+ }
+}
+
+/**
+ * @brief This will build ModelRecipe from circle::Model
+ * First to check operand filler options by scanning all operators,
+ * then translate all operands and operators.
+ * Last will set network inputs and outputs.
+ */
+std::unique_ptr<ModelRecipe> generate_recipe(const circle::Model *model)
+{
+ std::unique_ptr<ModelRecipe> model_recipe{new ModelRecipe()};
+
+ CircleImport circle_import(model);
+
+ assert(circle_import.num_subgraph() == 1);
+ circle_import.select_sub_graph(0);
+
+ auto tensors = circle_import.tensors();
+ auto buffers = circle_import.buffers();
+ auto operators = circle_import.operators();
+
+ // operand fillers for adding all operators
+ for (uint32_t i = 0; i < operators->Length(); ++i)
+ {
+ const auto *op = operators->Get(i);
+ circle::BuiltinOperator builtincode = circle_import.builtin_code(op);
+
+ if (const auto *graph_builder = CircleOpRegistry::get().lookup(builtincode))
+ {
+ graph_builder->filler(op, &circle_import, model_recipe.get());
+ }
+ else
+ {
+ std::string opcodename = circle_import.opcode_name(op);
+ throw std::runtime_error{"Not supported: " + opcodename};
+ }
+ }
+
+ // add all operands(tensors)
+ for (uint32_t i = 0; i < tensors->Length(); ++i)
+ {
+ auto tensor = tensors->Get(i);
+
+ // check buffer
+ if (tensor->buffer() >= buffers->size())
+ throw std::runtime_error{"file load failed"};
+
+ ::circlechef::Operand *operand = model_recipe->add_operand();
+
+ operand->set_name(tensor_name(tensor));
+ operand->set_type(as_circlechef_type(tensor->type()));
+
+ std::vector<int32_t> dims = as_index_vector(tensor->shape());
+ ::circlechef::TensorShape *shape = operand->mutable_shape();
+ for (auto dim : dims)
+ {
+ shape->add_dim(dim);
+ }
+
+ // filler for weights, bias and so on
+ std::vector<int32_t> expvalues;
+ std::vector<float> expfvalues;
+ if (circle_import.get_tensor_filler(i))
+ {
+ circlechef::TensorFiller *filler = operand->mutable_filler();
+ // Note: it is OK to use random weights for functionality validation
+ filler->set_tag("gaussian");
+ filler->add_arg("0.0"); // average
+ filler->add_arg("0.1"); // standard deviation
+ }
+ else if (circle_import.get_tensor_filler(i, expvalues))
+ {
+ circlechef::TensorFiller *filler = operand->mutable_filler();
+ filler->set_tag("explicit");
+ for (auto value : expvalues)
+ {
+ std::ostringstream ss;
+ ss << value;
+ filler->add_arg(ss.str());
+ }
+ }
+ else if (circle_import.get_tensor_filler(i, expfvalues))
+ {
+ circlechef::TensorFiller *filler = operand->mutable_filler();
+ filler->set_tag("explicit");
+ for (auto value : expfvalues)
+ {
+ std::ostringstream ss;
+ ss << value;
+ filler->add_arg(ss.str());
+ }
+ }
+
+ auto quant = tensor->quantization();
+ if (quant != nullptr)
+ {
+ // Note: Calling 'operand->mutable_quant()' will create empty 'quant' node
+ // in the recipe file. We want this only when valid parameter exist.
+ if (quant->min() != nullptr && quant->min()->size() > 0)
+ {
+ circlechef::TensorQuantization *chef_quant = operand->mutable_quant();
+ for (uint32_t idx = 0; idx < quant->min()->size(); ++idx)
+ chef_quant->add_min(quant->min()->Get(idx));
+ }
+ if (quant->max() != nullptr && quant->max()->size() > 0)
+ {
+ circlechef::TensorQuantization *chef_quant = operand->mutable_quant();
+ for (uint32_t idx = 0; idx < quant->max()->size(); idx++)
+ chef_quant->add_max(quant->max()->Get(idx));
+ }
+ if (quant->scale() != nullptr && quant->scale()->size() > 0)
+ {
+ circlechef::TensorQuantization *chef_quant = operand->mutable_quant();
+ for (uint32_t idx = 0; idx < quant->scale()->size(); ++idx)
+ chef_quant->add_scale(quant->scale()->Get(idx));
+ }
+ if (quant->zero_point() != nullptr && quant->zero_point()->size() > 0)
+ {
+ circlechef::TensorQuantization *chef_quant = operand->mutable_quant();
+ for (uint32_t idx = 0; idx < quant->zero_point()->size(); ++idx)
+ chef_quant->add_zero_point(quant->zero_point()->Get(idx));
+ }
+ circlechef::TensorQuantization *chef_quant = operand->mutable_quant();
+ chef_quant->set_quantized_dimension(quant->quantized_dimension());
+ }
+
+ auto shape_signature = tensor->shape_signature();
+ if (shape_signature != nullptr)
+ {
+ circlechef::ShapeSignature *chef_shape_signature = operand->mutable_shape_signature();
+ for (uint32_t i = 0; i < shape_signature->size(); ++i)
+ {
+ chef_shape_signature->add_dim(shape_signature->Get(i));
+ }
+ }
+ }
+
+ // add all operators
+ for (uint32_t i = 0; i < operators->Length(); ++i)
+ {
+ const auto *op = operators->Get(i);
+ circle::BuiltinOperator builtincode = circle_import.builtin_code(op);
+
+ if (const auto *graph_builder = CircleOpRegistry::get().lookup(builtincode))
+ {
+ auto operation = graph_builder->build(op, &circle_import, model_recipe.get());
+
+ // common for all operators: inputs, outputs
+ set_inputs(&circle_import, operation, op);
+ set_outputs(&circle_import, operation, op);
+ }
+ else
+ {
+ std::string opcodename = circle_import.opcode_name(op);
+ throw std::runtime_error{"Not supported: " + opcodename};
+ }
+ }
+
+ // network inputs/outputs
+ const std::vector<int32_t> &inputs = circle_import.inputs();
+ const std::vector<int32_t> &outputs = circle_import.outputs();
+
+ for (const auto input : inputs)
+ {
+ auto tensor = tensors->Get(input);
+ std::string name = tensor_name(tensor);
+
+ model_recipe->add_input(name);
+ }
+ for (const auto output : outputs)
+ {
+ auto tensor = tensors->Get(output);
+ std::string name = tensor_name(tensor);
+
+ model_recipe->add_output(name);
+ }
+
+ return std::move(model_recipe);
+}
+
+bool write_recipe(const std::string &filename, std::unique_ptr<ModelRecipe> &recipe)
+{
+ std::fstream fo(filename, std::ios::binary | std::ios::out);
+
+ if (!fo.is_open())
+ {
+ throw std::runtime_error{"file store failed"};
+ }
+
+ // Note: SerializeToString() or SerializeToOstream() writes in binary mode
+ // DebugString() and Utf8DebugString() will print as a human readable text
+ fo << recipe->Utf8DebugString();
+
+ fo.close();
+
+ return true;
+}
+
+} // namespace circlechef
diff --git a/compiler/circlechef/core/CMakeLists.txt b/compiler/circlechef/core/CMakeLists.txt
new file mode 100644
index 000000000..54b3ea53d
--- /dev/null
+++ b/compiler/circlechef/core/CMakeLists.txt
@@ -0,0 +1,9 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+add_library(circlechef_core STATIC ${SOURCES})
+target_include_directories(circlechef_core PUBLIC include)
+target_include_directories(circlechef_core PRIVATE src)
+target_link_libraries(circlechef_core circlechef_proto)
+target_link_libraries(circlechef_core circlechef_log)
+target_link_libraries(circlechef_core mio_circle)
+target_link_libraries(circlechef_core souschef)
diff --git a/compiler/circlechef/core/include/circlechef/ModelChef.h b/compiler/circlechef/core/include/circlechef/ModelChef.h
new file mode 100644
index 000000000..64326179c
--- /dev/null
+++ b/compiler/circlechef/core/include/circlechef/ModelChef.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MODEL_CHEF_H__
+#define __MODEL_CHEF_H__
+
+#include <circlechef.pb.h>
+
+#include <memory>
+
+namespace circlechef
+{
+
+class GeneratedModel final
+{
+public:
+ struct Impl
+ {
+ virtual ~Impl() = default;
+
+ virtual const char *base(void) const = 0;
+ virtual size_t size(void) const = 0;
+ };
+
+public:
+ GeneratedModel(std::unique_ptr<Impl> &&impl) : _impl{std::move(impl)}
+ {
+ // DO NOTHING
+ }
+
+public:
+ const char *base(void) const { return _impl->base(); }
+ size_t size(void) const { return _impl->size(); }
+
+private:
+ std::unique_ptr<Impl> _impl;
+};
+
+GeneratedModel cook(const ModelRecipe &model_recipe);
+
+} // namespace circlechef
+
+#endif // __MODEL_CHEF_H__
diff --git a/compiler/circlechef/core/src/Arguments.h b/compiler/circlechef/core/src/Arguments.h
new file mode 100644
index 000000000..9fe7bbb77
--- /dev/null
+++ b/compiler/circlechef/core/src/Arguments.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ARGUMENTS_H__
+#define __ARGUMENTS_H__
+
+#include <cstdint>
+#include <string>
+
+/**
+ * @brief Read-only string sequence view
+ */
+struct Arguments
+{
+ virtual ~Arguments() = default;
+
+ virtual uint32_t count(void) const = 0;
+ virtual const std::string &value(uint32_t n) const = 0;
+};
+
+#endif // __ARGUMENTS_H__
diff --git a/compiler/circlechef/core/src/Convert.cpp b/compiler/circlechef/core/src/Convert.cpp
new file mode 100644
index 000000000..2db0a6212
--- /dev/null
+++ b/compiler/circlechef/core/src/Convert.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "Convert.h"
+
+#include <stdexcept>
+
+circle::Padding as_circle_padding(const circlechef::Padding &value)
+{
+ switch (value)
+ {
+ case circlechef::SAME:
+ return circle::Padding_SAME;
+ case circlechef::VALID:
+ return circle::Padding_VALID;
+ default:
+ break;
+ }
+
+ throw std::runtime_error{"Unknown padding value"};
+}
+
+circle::ActivationFunctionType as_circle_activation(const circlechef::Activation &value)
+{
+ switch (value)
+ {
+ case circlechef::NONE:
+ return circle::ActivationFunctionType_NONE;
+ case circlechef::RELU:
+ return circle::ActivationFunctionType_RELU;
+ case circlechef::RELU6:
+ return circle::ActivationFunctionType_RELU6;
+ default:
+ break;
+ }
+
+ throw std::runtime_error{"Unknown activation"};
+}
+
+circle::TensorType as_circle_tensortype(const circlechef::TensorType &value)
+{
+ switch (value)
+ {
+ case circlechef::FLOAT32:
+ return circle::TensorType_FLOAT32;
+ case circlechef::INT32:
+ return circle::TensorType_INT32;
+ case circlechef::UINT8:
+ return circle::TensorType_UINT8;
+ case circlechef::INT64:
+ return circle::TensorType_INT64;
+ case circlechef::BOOL:
+ return circle::TensorType_BOOL;
+ default:
+ break;
+ }
+
+ throw std::runtime_error{"Unknown tensor type"};
+}
diff --git a/compiler/circlechef/core/src/Convert.h b/compiler/circlechef/core/src/Convert.h
new file mode 100644
index 000000000..766098da2
--- /dev/null
+++ b/compiler/circlechef/core/src/Convert.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file Convert.h
+ * @brief This header declares various as_circle_TYPE functions
+ */
+#ifndef __CONVERT_H__
+#define __CONVERT_H__
+
+#include <circlechef.pb.h>
+#include <mio/circle/schema_generated.h>
+
+circle::Padding as_circle_padding(const circlechef::Padding &value);
+circle::ActivationFunctionType as_circle_activation(const circlechef::Activation &value);
+circle::TensorType as_circle_tensortype(const circlechef::TensorType &value);
+
+#endif // __CONVERT_H__
diff --git a/compiler/circlechef/core/src/ModelChef.cpp b/compiler/circlechef/core/src/ModelChef.cpp
new file mode 100644
index 000000000..4f25d62c0
--- /dev/null
+++ b/compiler/circlechef/core/src/ModelChef.cpp
@@ -0,0 +1,598 @@
+/*
+ * 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 "circlechef/ModelChef.h"
+#include <souschef/RangedArguments.h>
+#include <souschef/Registry.h>
+
+#include "Convert.h"
+
+#include <souschef/DataChefs.h>
+
+#include "OpChef.h"
+#include "OpChefs.h"
+
+#include <souschef/Dataset.h>
+#include <souschef/Dims.h>
+
+#include "Log.h"
+
+#include <iterator>
+#include <map>
+#include <string>
+#include <vector>
+
+#include <cassert>
+#include <fstream>
+#include <iostream>
+#include <numeric>
+#include <sstream>
+#include <stdexcept>
+
+using namespace souschef;
+
+namespace
+{
+
+class GeneratedModelImpl final : public circlechef::GeneratedModel::Impl
+{
+public:
+ GeneratedModelImpl(std::unique_ptr<flatbuffers::FlatBufferBuilder> &&builder)
+ : _builder{std::move(builder)}
+ {
+ // DO NOTHING
+ }
+
+public:
+ const char *base(void) const override
+ {
+ // Return the base address of generated flatbuffer model
+ return reinterpret_cast<const char *>(_builder->GetBufferPointer());
+ }
+
+public:
+ size_t size(void) const override
+ {
+ // Return the size of generated flatbuffer model
+ return _builder->GetSize();
+ }
+
+private:
+ std::unique_ptr<flatbuffers::FlatBufferBuilder> _builder;
+};
+
+} // namespace
+
+namespace
+{
+
+struct DataChefRegistry final : public Registry<DataChefFactory>
+{
+};
+
+DataChefRegistry &data_chef_registry(const circlechef::TensorType &type)
+{
+ static DataChefRegistry s32;
+ static DataChefRegistry s64;
+ static DataChefRegistry fp32;
+ static DataChefRegistry u8;
+ static DataChefRegistry boolean;
+
+ switch (type)
+ {
+ case circlechef::INT32:
+ return s32;
+ case circlechef::INT64:
+ return s64;
+ case circlechef::FLOAT32:
+ return fp32;
+ case circlechef::UINT8:
+ return u8;
+ case circlechef::BOOL:
+ return boolean;
+ default:
+ break;
+ }
+
+ throw std::runtime_error{"Unknown tensor type"};
+}
+
+struct OpChefRegistry final : public Registry<OpChefFactory>
+{
+};
+
+OpChefRegistry &op_chef_registry(void)
+{
+ static OpChefRegistry registry;
+ return registry;
+}
+
+/// @brief This will prepare a map of unique builtin codes in the model recipe
+std::map<circle::BuiltinOperator, int32_t>
+gather_builtincode_map(const ::circlechef::ModelRecipe &model_recipe)
+{
+ // Key and value of the map are BuiltinOperator and operator version
+ std::map<circle::BuiltinOperator, int32_t> builtin_map;
+
+ for (const auto &operation : model_recipe.operation())
+ {
+ auto op_chef = op_chef_registry().lookup(operation.type()).create(&operation);
+ if (op_chef->code() == circle::BuiltinOperator_CUSTOM)
+ continue;
+
+ // Various operation version is unified as the highest version among them
+ if (builtin_map.find(op_chef->code()) == builtin_map.end() ||
+ builtin_map[op_chef->code()] < operation.version())
+ builtin_map[op_chef->code()] = operation.version();
+ }
+
+ // Add ops used in Graphs(subgraphs)
+ for (int g = 0; g < model_recipe.graph_size(); ++g)
+ {
+ const auto &graph = model_recipe.graph(g);
+ for (const auto &operation : graph.operation())
+ {
+ auto op_chef = op_chef_registry().lookup(operation.type()).create(&operation);
+ if (op_chef->code() == circle::BuiltinOperator_CUSTOM)
+ continue;
+
+ // Various operation version is unified as the highest version among them
+ if (builtin_map.find(op_chef->code()) == builtin_map.end() ||
+ builtin_map[op_chef->code()] < operation.version())
+ builtin_map[op_chef->code()] = operation.version();
+ }
+ }
+
+ return builtin_map;
+}
+
+/// @brief This will prepare a set of unique custom codes in the mode recipe
+std::set<std::string> gather_customcode_set(const ::circlechef::ModelRecipe &model_recipe)
+{
+ std::set<std::string> customcode_set;
+ for (const auto &operation : model_recipe.operation())
+ {
+ auto op_chef = op_chef_registry().lookup(operation.type()).create(&operation);
+ if (op_chef->code() == circle::BuiltinOperator_CUSTOM)
+ customcode_set.insert(operation.type());
+ }
+
+ // Add ops used in Graphs(subgraphs)
+ for (int g = 0; g < model_recipe.graph_size(); ++g)
+ {
+ const auto &graph = model_recipe.graph(g);
+ for (const auto &operation : graph.operation())
+ {
+ auto op_chef = op_chef_registry().lookup(operation.type()).create(&operation);
+ if (op_chef->code() == circle::BuiltinOperator_CUSTOM)
+ customcode_set.insert(operation.type());
+ }
+ }
+
+ return customcode_set;
+}
+
+} // namespace
+
+namespace
+{
+
+struct CookParams
+{
+ std::vector<flatbuffers::Offset<::circle::Buffer>> &buffer_vec;
+ std::vector<flatbuffers::Offset<::circle::OperatorCode>> &code_vec;
+ std::vector<flatbuffers::Offset<::circle::SubGraph>> &subgraph_vec;
+ std::unique_ptr<flatbuffers::FlatBufferBuilder> &flatbuffer_builder;
+ std::map<circle::BuiltinOperator, int32_t> &builtin_code_map;
+ std::string noname;
+};
+
+template <typename T> void cook_graph(const T &graph, CookParams &cp)
+{
+ LOGGER(l);
+
+ std::vector<flatbuffers::Offset<::circle::Buffer>> &buffer_vec = cp.buffer_vec;
+ std::vector<flatbuffers::Offset<::circle::OperatorCode>> &code_vec = cp.code_vec;
+ std::vector<flatbuffers::Offset<::circle::SubGraph>> &subgraph_vec = cp.subgraph_vec;
+ std::unique_ptr<flatbuffers::FlatBufferBuilder> &flatbuffer_builder = cp.flatbuffer_builder;
+ std::map<circle::BuiltinOperator, int32_t> &builtin_code_map = cp.builtin_code_map;
+
+ // Operand-related
+ std::vector<flatbuffers::Offset<::circle::Tensor>> tensor_vec;
+
+ // Operation-related
+ std::vector<flatbuffers::Offset<::circle::Operator>> operator_vec;
+
+ // default name for graph
+ std::string graph_name = cp.noname;
+ if (graph.has_name())
+ graph_name = graph.name();
+
+ // Tensor Name -> Tensor ID mapping (per Graph)
+ std::map<std::string, int32_t> symbol_table;
+
+ auto lookup = [&symbol_table, &graph_name](const std::string &name) {
+ if (symbol_table.find(name) != symbol_table.end())
+ return symbol_table.at(name);
+ else if (name == "")
+ return -1; // -1 in circle means that optional input tensor is empty.
+ else
+ {
+ std::string msg = "circlechef : input not found in " + graph_name + " graph";
+ throw std::runtime_error(msg.c_str());
+ }
+ };
+
+ int32_t buffer_start = buffer_vec.size();
+ int32_t buffer_index = 0;
+
+ // Create buffer(s) 1~n(I) for input(s)
+ const auto size_input = graph.input_size();
+ for (int ci = 0; ci < size_input; ++ci)
+ {
+ circle::BufferBuilder buffer_builder{*flatbuffer_builder};
+ buffer_vec.emplace_back(buffer_builder.Finish());
+ }
+ // Create buffer(s) n(I)+1~n(I)+n(O) for output(s)
+ const auto size_output = graph.output_size();
+ for (int co = 0; co < size_output; ++co)
+ {
+ circle::BufferBuilder buffer_builder{*flatbuffer_builder};
+ buffer_vec.emplace_back(buffer_builder.Finish());
+ }
+
+ auto input_names = as_dataset(graph.input()).vectorize();
+ auto output_names = as_dataset(graph.output()).vectorize();
+
+ for (const auto &operand : graph.operand())
+ {
+ assert(operand.has_name());
+
+ assert(operand.has_type());
+
+ flatbuffers::Offset<flatbuffers::Vector<int32_t>> shape;
+ std::vector<int32_t> dims;
+ if (operand.has_shape())
+ {
+ dims = as_dims(operand.shape());
+ shape = flatbuffer_builder->CreateVector(dims);
+ }
+
+ auto name = flatbuffer_builder->CreateString(operand.name());
+
+ buffer_index = 0;
+
+ // Create Buffer if filler is specified
+ if (operand.has_filler())
+ {
+ const auto &filler = operand.filler();
+
+ assert(filler.has_tag());
+
+ auto args = ranged_arguments(filler.arg().begin(), filler.arg().end());
+ auto chef = data_chef_registry(operand.type()).lookup(filler.tag()).create(args);
+
+ assert(chef != nullptr);
+
+ // Create Data
+ int32_t count = (element_count(dims) > 0) ? element_count(dims) : filler.arg_size();
+ auto data_vec = chef->generate(count);
+ auto data = flatbuffer_builder->CreateVector(data_vec);
+
+ // Create Buffer
+ circle::BufferBuilder buffer_builder{*flatbuffer_builder};
+ buffer_builder.add_data(data);
+ auto buffer = buffer_builder.Finish();
+
+ // Update Buffer Index & Vector
+ buffer_index = buffer_vec.size();
+ buffer_vec.emplace_back(buffer);
+ }
+ else
+ {
+ // if this is input or output, assign to that buffer_index
+ int idx = 0;
+ for (auto it = input_names.begin(); it != input_names.end(); ++it, ++idx)
+ {
+ if (*it == operand.name())
+ {
+ buffer_index = buffer_start + idx;
+ break;
+ }
+ }
+ if (buffer_index == 0)
+ {
+ idx = 0;
+ for (auto it = output_names.begin(); it != output_names.end(); ++it, ++idx)
+ {
+ if (*it == operand.name())
+ {
+ buffer_index = buffer_start + size_input + idx;
+ break;
+ }
+ }
+ }
+ if (buffer_index == 0)
+ {
+ // we couldn't find the buffer; create an empty buffer for this tensor
+ buffer_index = buffer_vec.size();
+
+ circle::BufferBuilder buffer_builder{*flatbuffer_builder};
+ buffer_vec.emplace_back(buffer_builder.Finish());
+ }
+ }
+ assert(buffer_index != 0);
+
+ flatbuffers::Offset<circle::QuantizationParameters> quant_index;
+
+ // Create QuantizationParameters if quant is specified
+ if (operand.has_quant())
+ {
+ const auto &quant = operand.quant();
+
+ // Create each parameters
+ // NOTE if some parameters are not given, those will be set to default value
+ std::vector<float> quant_max_vec(quant.max_size());
+ std::vector<float> quant_min_vec(quant.min_size());
+ std::vector<float> quant_scale_vec(quant.scale_size());
+ std::vector<int64_t> quant_zero_point_vec(quant.zero_point_size());
+
+ for (uint32_t i = 0; i < quant.max_size(); ++i)
+ quant_max_vec.at(i) = quant.max(i);
+ for (uint32_t i = 0; i < quant.min_size(); ++i)
+ quant_min_vec.at(i) = quant.min(i);
+ for (uint32_t i = 0; i < quant.scale_size(); ++i)
+ quant_scale_vec.at(i) = quant.scale(i);
+ for (uint32_t i = 0; i < quant.zero_point_size(); ++i)
+ quant_zero_point_vec.at(i) = quant.zero_point(i);
+
+ auto quant_max = flatbuffer_builder->CreateVector(quant_max_vec);
+ auto quant_min = flatbuffer_builder->CreateVector(quant_min_vec);
+ auto quant_scale = flatbuffer_builder->CreateVector(quant_scale_vec);
+ auto quant_zero_point = flatbuffer_builder->CreateVector(quant_zero_point_vec);
+
+ // Create QuantizationParameters
+ circle::QuantizationParametersBuilder quant_builder{*flatbuffer_builder};
+ quant_builder.add_max(quant_max);
+ quant_builder.add_min(quant_min);
+ quant_builder.add_scale(quant_scale);
+ quant_builder.add_zero_point(quant_zero_point);
+ quant_builder.add_quantized_dimension(quant.quantized_dimension());
+
+ // Update QuantizationParameters Index
+ quant_index = quant_builder.Finish();
+ }
+
+ flatbuffers::Offset<flatbuffers::Vector<int32_t>> shape_signature;
+ if (operand.has_shape_signature())
+ {
+ auto signature = as_dims(operand.shape_signature());
+ shape_signature = flatbuffer_builder->CreateVector(signature);
+ }
+
+ // Create Tensor
+ circle::TensorBuilder tensor_builder{*flatbuffer_builder};
+
+ tensor_builder.add_shape(shape);
+ tensor_builder.add_type(as_circle_tensortype(operand.type()));
+ tensor_builder.add_buffer(buffer_index);
+ tensor_builder.add_name(name);
+ if (operand.has_quant())
+ tensor_builder.add_quantization(quant_index);
+ if (operand.has_shape_signature())
+ tensor_builder.add_shape_signature(shape_signature);
+
+ // Append!
+ tensor_vec.emplace_back(tensor_builder.Finish());
+
+ // Update Tensor Name -> Tensor Index Map
+ int32_t tensor_index = symbol_table.size();
+ const auto &tensor_name = operand.name();
+
+ INFO(l) << "Symbol [" << tensor_name << "] = Tensor " << tensor_index << std::endl;
+
+ symbol_table[tensor_name] = tensor_index;
+ }
+
+ // Create Operator
+ for (const auto &operation : graph.operation())
+ {
+ assert(operation.has_type());
+
+ auto op_chef = op_chef_registry().lookup(operation.type()).create(&operation);
+
+ // Create 'inputs'
+ std::vector<int32_t> input_vec = as_dataset(operation.input()).map(lookup).vectorize();
+ auto inputs = flatbuffer_builder->CreateVector(input_vec);
+
+ // Create 'outputs'
+ std::vector<int32_t> output_vec = as_dataset(operation.output()).map(lookup).vectorize();
+ auto outputs = flatbuffer_builder->CreateVector(output_vec);
+
+ // Create Option
+ auto options = op_chef->value(*flatbuffer_builder);
+
+ // Create Custom option
+ auto circle_custom_options = op_chef->custom_value(*flatbuffer_builder);
+
+ // Create Operator
+ circle::OperatorBuilder op_builder{*flatbuffer_builder};
+
+ // Get operator code index from builtin_code_map with assumption, order of
+ // builtin_code_map is same as that of code_vec
+ auto op_it = builtin_code_map.find(op_chef->code());
+ assert(op_it != builtin_code_map.end());
+ uint32_t opcode_index = std::distance(builtin_code_map.begin(), op_it);
+
+ op_builder.add_opcode_index(opcode_index);
+ op_builder.add_inputs(inputs);
+ op_builder.add_outputs(outputs);
+ op_builder.add_builtin_options_type(op_chef->type());
+ op_builder.add_builtin_options(options);
+ op_builder.add_custom_options(circle_custom_options);
+ op_builder.add_custom_options_format(circle::CustomOptionsFormat_FLEXBUFFERS);
+ // Append Operator
+ operator_vec.emplace_back(op_builder.Finish());
+ }
+
+ // Create network input/output vector
+ std::vector<int32_t> input_vec = as_dataset(graph.input()).map(lookup).vectorize();
+ std::vector<int32_t> output_vec = as_dataset(graph.output()).map(lookup).vectorize();
+
+ // Create "SubGraph" arguments
+ auto tensors = flatbuffer_builder->CreateVector(tensor_vec);
+ auto inputs = flatbuffer_builder->CreateVector(input_vec);
+ auto outputs = flatbuffer_builder->CreateVector(output_vec);
+ auto operators = flatbuffer_builder->CreateVector(operator_vec);
+ auto name = flatbuffer_builder->CreateString(graph_name);
+
+ circle::SubGraphBuilder subgraph_builder{*flatbuffer_builder};
+
+ subgraph_builder.add_tensors(tensors);
+ subgraph_builder.add_inputs(inputs);
+ subgraph_builder.add_outputs(outputs);
+ subgraph_builder.add_operators(operators);
+ subgraph_builder.add_name(name);
+
+ subgraph_vec.emplace_back(subgraph_builder.Finish());
+}
+
+} // namespace
+
+namespace circlechef
+{
+
+/**
+ * @brief Generate a (in-memory) TensorFlow Lite model from a given model recipe
+ */
+GeneratedModel cook(const ::circlechef::ModelRecipe &model_recipe)
+{
+// Initialize Op Chef Registry
+#define OP_CHEF(NAME, FACTORY_CLASS) \
+ op_chef_registry().add(#NAME, std::unique_ptr<FACTORY_CLASS>(new FACTORY_CLASS()));
+#include "OpChef.def"
+#undef OP_CHEF
+
+// Initialize Data Chef Registry
+#define DATA_CHEF(TYPE, NAME, FACTORY_CLASS) \
+ data_chef_registry(::circlechef::TYPE) \
+ .add(#NAME, std::unique_ptr<FACTORY_CLASS>(new FACTORY_CLASS()));
+#include <souschef/DataChef.def>
+#undef DATA_CHEF
+
+ //
+ // Create FlatBufferBuilder
+ //
+ auto flatbuffer_builder =
+ std::unique_ptr<flatbuffers::FlatBufferBuilder>(new flatbuffers::FlatBufferBuilder(1024));
+
+ // Operand-related
+ std::vector<flatbuffers::Offset<::circle::Buffer>> buffer_vec;
+
+ // Operation-related
+ std::vector<flatbuffers::Offset<::circle::OperatorCode>> code_vec;
+
+ // Graphs-related
+ std::vector<flatbuffers::Offset<::circle::SubGraph>> subgraph_vec;
+
+ // Create OperatorCode with Builtin Operator
+ std::map<circle::BuiltinOperator, int32_t> builtin_code_map =
+ gather_builtincode_map(model_recipe);
+ for (auto const &opcode : builtin_code_map)
+ {
+ circle::OperatorCodeBuilder code_builder{*flatbuffer_builder};
+ code_builder.add_builtin_code(opcode.first);
+ code_builder.add_version(opcode.second);
+ auto code = code_builder.Finish();
+ // Update OperatorCode vector
+ code_vec.emplace_back(code);
+ }
+
+ // Create OperatorCode with Custom Operator
+ std::set<std::string> custom_code_set = gather_customcode_set(model_recipe);
+ if (custom_code_set.size() &&
+ builtin_code_map.find(circle::BuiltinOperator_CUSTOM) == builtin_code_map.end())
+ builtin_code_map[circle::BuiltinOperator_CUSTOM] = 1;
+
+ for (auto opcode : custom_code_set)
+ {
+ auto custom_code = flatbuffer_builder->CreateString(opcode);
+ circle::OperatorCodeBuilder code_builder{*flatbuffer_builder};
+ code_builder.add_builtin_code(circle::BuiltinOperator_CUSTOM);
+ code_builder.add_custom_code(custom_code);
+ auto code = code_builder.Finish();
+ // Update OperatorCode vector
+ code_vec.emplace_back(code);
+ }
+
+ // Create an Empty Buffer
+ //
+ // Buffer 0 SHOULD be an empty buffer in TensorFlow Lite model file
+ // (Please refer to the comment for Tensor.buffer field in schema)
+ {
+ circle::BufferBuilder buffer_builder{*flatbuffer_builder};
+ buffer_vec.emplace_back(buffer_builder.Finish());
+ }
+
+ //
+ // Create Main graph
+ //
+ CookParams cp{buffer_vec, code_vec, subgraph_vec, flatbuffer_builder, builtin_code_map, "main"};
+
+ cook_graph<::circlechef::ModelRecipe>(model_recipe, cp);
+
+ //
+ // Create subgraphs if exist
+ //
+ for (int g = 0; g < model_recipe.graph_size(); ++g)
+ {
+ const auto &graph = model_recipe.graph(g);
+
+ std::ostringstream stringStream;
+ stringStream << "sub_" << (g + 1);
+
+ CookParams cp{buffer_vec, code_vec, subgraph_vec,
+ flatbuffer_builder, builtin_code_map, stringStream.str()};
+
+ cook_graph<::circlechef::Graph>(graph, cp);
+ }
+
+ // Create "Model" arguments
+ auto buffers = flatbuffer_builder->CreateVector(buffer_vec);
+ auto operator_codes = flatbuffer_builder->CreateVector(code_vec);
+ auto subgraphs = flatbuffer_builder->CreateVector(subgraph_vec);
+ auto description = flatbuffer_builder->CreateString("Generated by circlechef");
+
+ // Create "Model"
+ circle::ModelBuilder model_builder{*flatbuffer_builder};
+
+ model_builder.add_version(3);
+ model_builder.add_operator_codes(operator_codes);
+ model_builder.add_subgraphs(subgraphs);
+ model_builder.add_description(description);
+ model_builder.add_buffers(buffers);
+
+ auto model = model_builder.Finish();
+
+ // Finalize
+ ::circle::FinishModelBuffer(*flatbuffer_builder, model);
+
+ // Return "GenerateModel"
+ return GeneratedModel{
+ std::unique_ptr<GeneratedModelImpl>(new GeneratedModelImpl(std::move(flatbuffer_builder)))};
+}
+
+} // namespace circlechef
diff --git a/compiler/circlechef/core/src/Op/BCQFullyConnected.cpp b/compiler/circlechef/core/src/Op/BCQFullyConnected.cpp
new file mode 100644
index 000000000..4c82c52cc
--- /dev/null
+++ b/compiler/circlechef/core/src/Op/BCQFullyConnected.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BCQFullyConnected.h"
+
+#include "Convert.h"
+
+flatbuffers::Offset<void> BCQFullyConnectedChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_bcq_fully_connected_options());
+
+ circle::BCQFullyConnectedOptionsBuilder bcq_fully_connected_options_builder{fbb};
+ bcq_fully_connected_options_builder.add_weights_hidden_size(
+ operation.bcq_fully_connected_options().weights_hidden_size());
+ bcq_fully_connected_options_builder.add_fused_activation_function(
+ as_circle_activation(operation.bcq_fully_connected_options().activation()));
+
+ return bcq_fully_connected_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef>
+BCQFullyConnectedChefFactory::create(const circlechef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new BCQFullyConnectedChef{operation}};
+}
diff --git a/compiler/circlechef/core/src/Op/BCQFullyConnected.h b/compiler/circlechef/core/src/Op/BCQFullyConnected.h
new file mode 100644
index 000000000..41e6b53d5
--- /dev/null
+++ b/compiler/circlechef/core/src/Op/BCQFullyConnected.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_BCQFULLYCONNECTED_H__
+#define __OP_BCQFULLYCONNECTED_H__
+
+#include "OpChef.h"
+
+class BCQFullyConnectedChef final : public OpChef
+{
+public:
+ explicit BCQFullyConnectedChef(const circlechef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ circle::BuiltinOperator code(void) const override
+ {
+ return circle::BuiltinOperator_BCQ_FULLY_CONNECTED;
+ }
+
+ circle::BuiltinOptions type(void) const override
+ {
+ return circle::BuiltinOptions_BCQFullyConnectedOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const circlechef::Operation *_operation;
+};
+
+struct BCQFullyConnectedChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const circlechef::Operation *operation) const override;
+};
+
+#endif // __OP_BCQFULLYCONNECTED_H__
diff --git a/compiler/circlechef/core/src/Op/BCQGather.cpp b/compiler/circlechef/core/src/Op/BCQGather.cpp
new file mode 100644
index 000000000..08f6f611f
--- /dev/null
+++ b/compiler/circlechef/core/src/Op/BCQGather.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BCQGather.h"
+
+flatbuffers::Offset<void> BCQGatherChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_bcq_gather_options());
+
+ circle::BCQGatherOptionsBuilder bcq_gather_options_builder{fbb};
+ bcq_gather_options_builder.add_input_hidden_size(
+ operation.bcq_gather_options().input_hidden_size());
+ bcq_gather_options_builder.add_axis(operation.bcq_gather_options().axis());
+
+ return bcq_gather_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> BCQGatherChefFactory::create(const circlechef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new BCQGatherChef{operation}};
+}
diff --git a/compiler/circlechef/core/src/Op/BCQGather.h b/compiler/circlechef/core/src/Op/BCQGather.h
new file mode 100644
index 000000000..24a797a41
--- /dev/null
+++ b/compiler/circlechef/core/src/Op/BCQGather.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_BCQGATHER_H__
+#define __OP_BCQGATHER_H__
+
+#include "OpChef.h"
+
+class BCQGatherChef final : public OpChef
+{
+public:
+ explicit BCQGatherChef(const circlechef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ circle::BuiltinOperator code(void) const override { return circle::BuiltinOperator_BCQ_GATHER; }
+
+ circle::BuiltinOptions type(void) const override
+ {
+ return circle::BuiltinOptions_BCQGatherOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const circlechef::Operation *_operation;
+};
+
+struct BCQGatherChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const circlechef::Operation *operation) const override;
+};
+
+#endif // __OP_BCQGATHER_H__
diff --git a/compiler/circlechef/core/src/Op/BatchMatMul.cpp b/compiler/circlechef/core/src/Op/BatchMatMul.cpp
new file mode 100644
index 000000000..d98c0801a
--- /dev/null
+++ b/compiler/circlechef/core/src/Op/BatchMatMul.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BatchMatMul.h"
+
+flatbuffers::Offset<void> BatchMatMulChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_batch_matmul_options());
+
+ circle::BatchMatMulOptionsBuilder batch_matmul_options_options_builder{fbb};
+ batch_matmul_options_options_builder.add_adjoint_lhs(
+ operation.batch_matmul_options().adjoint_lhs());
+ batch_matmul_options_options_builder.add_adjoint_rhs(
+ operation.batch_matmul_options().adjoint_rhs());
+
+ return batch_matmul_options_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> BatchMatMulChefFactory::create(const circlechef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new BatchMatMulChef{operation}};
+}
diff --git a/compiler/circlechef/core/src/Op/BatchMatMul.h b/compiler/circlechef/core/src/Op/BatchMatMul.h
new file mode 100644
index 000000000..fbb411eff
--- /dev/null
+++ b/compiler/circlechef/core/src/Op/BatchMatMul.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_BATCH_MATMUL_H__
+#define __OP_BATCH_MATMUL_H__
+
+#include "OpChef.h"
+
+class BatchMatMulChef final : public OpChef
+{
+public:
+ explicit BatchMatMulChef(const circlechef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ circle::BuiltinOperator code(void) const override { return circle::BuiltinOperator_BATCH_MATMUL; }
+
+ circle::BuiltinOptions type(void) const override
+ {
+ return circle::BuiltinOptions_BatchMatMulOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const circlechef::Operation *_operation;
+};
+
+struct BatchMatMulChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const circlechef::Operation *operation) const override;
+};
+
+#endif // __OP_BATCH_MATMUL_H__
diff --git a/compiler/circlechef/core/src/Op/InstanceNorm.cpp b/compiler/circlechef/core/src/Op/InstanceNorm.cpp
new file mode 100644
index 000000000..115eceffc
--- /dev/null
+++ b/compiler/circlechef/core/src/Op/InstanceNorm.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InstanceNorm.h"
+
+#include "Convert.h"
+
+flatbuffers::Offset<void> InstanceNormChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_instance_norm_options());
+ auto circle_activation = as_circle_activation(operation.instance_norm_options().activation());
+
+ circle::InstanceNormOptionsBuilder options_builder{fbb};
+ options_builder.add_epsilon(operation.instance_norm_options().epsilon());
+ options_builder.add_fused_activation_function(circle_activation);
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef>
+InstanceNormChefFactory::create(const circlechef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new InstanceNormChef{operation}};
+}
diff --git a/compiler/circlechef/core/src/Op/InstanceNorm.h b/compiler/circlechef/core/src/Op/InstanceNorm.h
new file mode 100644
index 000000000..f36b5d7b9
--- /dev/null
+++ b/compiler/circlechef/core/src/Op/InstanceNorm.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_INSTANCE_NORM_H__
+#define __OP_INSTANCE_NORM_H__
+
+#include "OpChef.h"
+
+class InstanceNormChef final : public OpChef
+{
+public:
+ explicit InstanceNormChef(const circlechef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ circle::BuiltinOperator code(void) const override
+ {
+ return circle::BuiltinOperator_INSTANCE_NORM;
+ }
+
+ circle::BuiltinOptions type(void) const override
+ {
+ return circle::BuiltinOptions_InstanceNormOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const circlechef::Operation *_operation;
+};
+
+struct InstanceNormChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const circlechef::Operation *operation) const override;
+};
+
+#endif // __OP_INSTANCE_NORM_H__
diff --git a/compiler/circlechef/core/src/OpChef.def b/compiler/circlechef/core/src/OpChef.def
new file mode 100644
index 000000000..3128d3ba2
--- /dev/null
+++ b/compiler/circlechef/core/src/OpChef.def
@@ -0,0 +1,10 @@
+#ifndef OP_CHEF
+#error "Define OP first"
+#endif // OP_CHEF
+
+// Please keep the list in alphabetical order
+// OP_CHEF(NAME, FACTORY_CLASS)
+OP_CHEF(BatchMatMul, BatchMatMulChefFactory)
+OP_CHEF(BCQFullyConnected, BCQFullyConnectedChefFactory)
+OP_CHEF(BCQGather, BCQGatherChefFactory)
+OP_CHEF(InstanceNorm, InstanceNormChefFactory)
diff --git a/compiler/circlechef/core/src/OpChef.h b/compiler/circlechef/core/src/OpChef.h
new file mode 100644
index 000000000..3479e51ef
--- /dev/null
+++ b/compiler/circlechef/core/src/OpChef.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_CHEF_H__
+#define __OP_CHEF_H__
+
+#include <circlechef.pb.h>
+#include <mio/circle/schema_generated.h>
+
+#include <memory>
+
+struct OpChef
+{
+ virtual ~OpChef() = default;
+
+ virtual circle::BuiltinOperator code(void) const = 0;
+ virtual circle::BuiltinOptions type(void) const = 0;
+ virtual flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const = 0;
+
+ // TODO Find a way to place this method in a better place
+ virtual flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
+ custom_value(flatbuffers::FlatBufferBuilder &fbb) const
+ {
+ return flatbuffers::Offset<flatbuffers::Vector<uint8_t>>();
+ }
+};
+
+struct OpChefFactory
+{
+ virtual ~OpChefFactory() = default;
+
+ virtual std::unique_ptr<OpChef> create(const circlechef::Operation *operation) const = 0;
+};
+
+#endif // __OP_CHEF_H__
diff --git a/compiler/circlechef/core/src/OpChefs.h b/compiler/circlechef/core/src/OpChefs.h
new file mode 100644
index 000000000..e13c5e0c6
--- /dev/null
+++ b/compiler/circlechef/core/src/OpChefs.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_CHEFS_H__
+#define __OP_CHEFS_H__
+
+#include "Op/BatchMatMul.h"
+#include "Op/BCQFullyConnected.h"
+#include "Op/BCQGather.h"
+#include "Op/InstanceNorm.h"
+
+#endif // __OP_CHEFS_H__
diff --git a/compiler/circlechef/log/CMakeLists.txt b/compiler/circlechef/log/CMakeLists.txt
new file mode 100644
index 000000000..6527ca7d8
--- /dev/null
+++ b/compiler/circlechef/log/CMakeLists.txt
@@ -0,0 +1,7 @@
+# TODO Find how to test logging framework
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+add_library(circlechef_log STATIC ${SOURCES})
+target_include_directories(circlechef_log PUBLIC include)
+target_link_libraries(circlechef_log PUBLIC hermes)
+target_link_libraries(circlechef_log PRIVATE hermes_std)
diff --git a/compiler/circlechef/log/include/Log.h b/compiler/circlechef/log/include/Log.h
new file mode 100644
index 000000000..ef00b26cc
--- /dev/null
+++ b/compiler/circlechef/log/include/Log.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLECHEF_LOG_H__
+#define __CIRCLECHEF_LOG_H__
+
+#include <hermes.h>
+
+namespace circlechef
+{
+
+/**
+ * @brief Logger Implementation
+ */
+class Logger final : public hermes::Source
+{
+public:
+ Logger(hermes::Context *ctx);
+ ~Logger();
+};
+
+/**
+ * @brief Logger Configuration
+ *
+ * Users are able to turn logging on/off via CIRCLECHEF_LOG environment variable.
+ */
+class LoggerConfig final : public hermes::Config
+{
+public:
+ LoggerConfig();
+
+public:
+ void configure(const hermes::Source *, hermes::Source::Setting &) const final;
+ void configure(const Logger *, hermes::Source::Setting &) const;
+
+private:
+ bool _enabled;
+};
+
+} // namespace circlechef
+
+#include "LoggingContext.h"
+
+/**
+ * HOW TO USE:
+ *
+ * LOGGER(l);
+ *
+ * INFO(l) << "Hello, World" << std::endl;
+ *
+ */
+#define LOGGER(name) ::circlechef::Logger name{::circlechef::LoggingContext::get()};
+
+// TODO Support FATAL, ERROR, WARN, and VERBOSE
+#define INFO(name) HERMES_INFO(name)
+
+// WARNING!
+//
+// THE CURRENT IMPLEMENTATION IS NOT THREAD SAFE.
+//
+
+#endif // __CIRCLECHEF_LOG_H__
diff --git a/compiler/circlechef/log/include/LoggingContext.h b/compiler/circlechef/log/include/LoggingContext.h
new file mode 100644
index 000000000..1282cfd45
--- /dev/null
+++ b/compiler/circlechef/log/include/LoggingContext.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLECHEF_LOGGING_CONTEXT_H__
+#define __CIRCLECHEF_LOGGING_CONTEXT_H__
+
+#include <hermes.h>
+
+namespace circlechef
+{
+
+/**
+ * @brief Global logging context
+ */
+struct LoggingContext
+{
+ static hermes::Context *get(void);
+};
+
+} // namespace circlechef
+
+#endif // __CIRCLECHEF_LOGGING_CONTEXT_H__
diff --git a/compiler/circlechef/log/src/Log.cpp b/compiler/circlechef/log/src/Log.cpp
new file mode 100644
index 000000000..11a60fb8d
--- /dev/null
+++ b/compiler/circlechef/log/src/Log.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 "Log.h"
+
+#include <cassert>
+#include <cstdlib>
+#include <iostream>
+
+// TODO Extract these lexical conversion routines as a library
+namespace
+{
+
+/**
+ * @brief Convert C-string as a value of type T
+ *
+ * safecast(s, v) returns v if s is nullptr.
+ */
+template <typename T> T safecast(const char *, const T &);
+
+template <> bool safecast<bool>(const char *s, const bool &value)
+{
+ return (s == nullptr) ? value : (std::stoi(s) != 0);
+}
+
+} // namespace
+
+//
+// Logger
+//
+namespace circlechef
+{
+
+Logger::Logger(hermes::Context *ctx) { activate(ctx->sources(), ctx->bus()); }
+Logger::~Logger() { deactivate(); }
+
+} // namespace circlechef
+
+//
+// LoggerConfig
+//
+namespace circlechef
+{
+
+LoggerConfig::LoggerConfig()
+{
+ // Turn on logging if CIRCLECHEF_LOG is set as non-zero value
+ _enabled = safecast<bool>(std::getenv("CIRCLECHEF_LOG"), false);
+}
+
+void LoggerConfig::configure(const hermes::Source *source, hermes::Source::Setting &setting) const
+{
+ // Let's ignore hermes::Sources if that is not a moco logger
+ if (auto logger = dynamic_cast<const Logger *>(source))
+ {
+ configure(logger, setting);
+ }
+}
+
+void LoggerConfig::configure(const Logger *, hermes::Source::Setting &setting) const
+{
+ if (_enabled)
+ {
+ // Enable all catagories
+ setting.accept_all();
+ }
+ else
+ {
+ // Disable all catagories
+ setting.reject_all();
+ }
+}
+
+} // namespace circlechef
diff --git a/compiler/circlechef/log/src/LoggingContext.cpp b/compiler/circlechef/log/src/LoggingContext.cpp
new file mode 100644
index 000000000..b64bd3f3d
--- /dev/null
+++ b/compiler/circlechef/log/src/LoggingContext.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LoggingContext.h"
+#include "Log.h"
+
+#include <hermes/ConsoleReporter.h>
+
+#include <memory>
+
+namespace circlechef
+{
+
+hermes::Context *LoggingContext::get(void)
+{
+ static hermes::Context *ctx = nullptr;
+
+ if (ctx == nullptr)
+ {
+ ctx = new hermes::Context;
+ ctx->sinks()->append(std::make_unique<hermes::ConsoleReporter>());
+ ctx->config(std::make_unique<LoggerConfig>());
+ }
+
+ return ctx;
+}
+
+} // namespace circlechef
diff --git a/compiler/circlechef/proto/CMakeLists.txt b/compiler/circlechef/proto/CMakeLists.txt
new file mode 100644
index 000000000..3cc26de84
--- /dev/null
+++ b/compiler/circlechef/proto/CMakeLists.txt
@@ -0,0 +1,5 @@
+Protobuf_Generate(CIRCLECHEF_PROTO "${CMAKE_CURRENT_BINARY_DIR}/generated" "${CMAKE_CURRENT_SOURCE_DIR}" "circlechef.proto")
+
+add_library(circlechef_proto STATIC ${CIRCLECHEF_PROTO_SOURCES})
+target_include_directories(circlechef_proto PUBLIC ${CIRCLECHEF_PROTO_INCLUDE_DIRS})
+target_link_libraries(circlechef_proto libprotobuf)
diff --git a/compiler/circlechef/proto/circlechef.proto b/compiler/circlechef/proto/circlechef.proto
new file mode 100644
index 000000000..83d2dfe9c
--- /dev/null
+++ b/compiler/circlechef/proto/circlechef.proto
@@ -0,0 +1,116 @@
+syntax = "proto2";
+
+package circlechef;
+
+//
+// Initial version
+// - Our initial version
+//
+// Version 1
+// - Backward compatible with Initial version
+// - Added Graph to represent sub graphs
+// - Added name, version(default as 1), graph in ModelRecipe
+//
+
+// This enum value corresponds to TensorType in TensorFlow Lite schema
+enum TensorType {
+ FLOAT32 = 0;
+ INT32 = 2;
+ UINT8 = 3;
+ INT64 = 4;
+ BOOL = 6;
+}
+
+message TensorShape {
+ repeated uint32 dim = 3;
+}
+
+message ShapeSignature {
+ repeated int32 dim = 1;
+}
+
+message TensorFiller {
+ optional string tag = 1;
+ repeated string arg = 2;
+}
+
+message TensorQuantization {
+ repeated float min = 1;
+ repeated float max = 2;
+ repeated float scale = 3;
+ repeated int64 zero_point = 4;
+ optional int32 quantized_dimension = 5 [default = 0];
+}
+
+message Operand {
+ optional string name = 1;
+ optional TensorType type = 2;
+ optional TensorShape shape = 3;
+ optional TensorFiller filler = 4;
+ optional TensorQuantization quant = 5;
+ optional ShapeSignature shape_signature = 8;
+}
+
+// This enum value corresponds to Padding in TensorFlow Lite schema
+enum Padding {
+ SAME = 0;
+ VALID = 1;
+}
+
+// This enum value corresponds to ActivationFunctionType in TensorFlow Lite schema
+enum Activation {
+ NONE = 0;
+ RELU = 1;
+ RELU6 = 3;
+}
+
+message BatchMatMulOptions {
+ optional bool adjoint_lhs = 1 [default = false];
+ optional bool adjoint_rhs = 2 [default = false];
+}
+
+message InstanceNormOptions {
+ optional float epsilon = 1 [default = 1e-05];
+ optional Activation activation = 2 [default = NONE];
+}
+
+message BCQFullyConnectedOptions {
+ optional int32 weights_hidden_size = 1 [default = 0];
+ optional Activation activation = 2 [default = NONE];
+}
+
+message BCQGatherOptions {
+ optional int32 input_hidden_size = 1 [default = 0];
+ optional int32 axis = 2 [default = 0];
+}
+
+message Operation {
+ optional string type = 1;
+ repeated string input = 2;
+ repeated string output = 3;
+ optional int32 version = 4 [default = 1];
+
+ optional BatchMatMulOptions batch_matmul_options = 100;
+ optional InstanceNormOptions instance_norm_options = 101;
+ optional BCQFullyConnectedOptions bcq_fully_connected_options = 102;
+ optional BCQGatherOptions bcq_gather_options = 103;
+}
+
+// For additional subgraphs
+message Graph {
+ repeated Operand operand = 1;
+ repeated Operation operation = 2;
+ repeated string input = 3;
+ repeated string output = 4;
+ optional string name = 5;
+}
+
+message ModelRecipe {
+ repeated Operand operand = 1;
+ repeated Operation operation = 2;
+ repeated string input = 3;
+ repeated string output = 4;
+ optional string name = 5;
+ optional uint32 version = 6 [default = 1];
+ repeated Graph graph = 7;
+}
diff --git a/compiler/circlechef/requires.cmake b/compiler/circlechef/requires.cmake
new file mode 100644
index 000000000..2106146d7
--- /dev/null
+++ b/compiler/circlechef/requires.cmake
@@ -0,0 +1,9 @@
+require("arser")
+require("nnkit")
+require("cwrap")
+require("mio-circle")
+require("safemain")
+require("hermes")
+require("hermes-std")
+require("foder")
+require("souschef")
diff --git a/compiler/circlechef/tests/CMakeLists.txt b/compiler/circlechef/tests/CMakeLists.txt
new file mode 100644
index 000000000..4dc58addf
--- /dev/null
+++ b/compiler/circlechef/tests/CMakeLists.txt
@@ -0,0 +1,70 @@
+nncc_find_resource(CircleRecipes)
+set(CIRCLERECIPES_DIR "${CircleRecipes_DIR}")
+
+file(GLOB RECIPES RELATIVE ${CIRCLERECIPES_DIR} "${CIRCLERECIPES_DIR}/*/test.recipe")
+
+foreach(RECIPE IN ITEMS ${RECIPES})
+ get_filename_component(RECIPE_PREFIX ${RECIPE} DIRECTORY)
+
+ set(RECIPE_SOURCE_FILE "${RECIPE_PREFIX}.recipe")
+ set(RECIPE_OUTPUT_FILE "${RECIPE_PREFIX}.circle")
+
+ # Copy .recipe
+ add_custom_command(OUTPUT ${RECIPE_SOURCE_FILE}
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
+ "${CIRCLERECIPES_DIR}/${RECIPE}" ${RECIPE_SOURCE_FILE}
+ DEPENDS "${CIRCLERECIPES_DIR}/${RECIPE}"
+ COMMENT "Generating ${RECIPE_SOURCE_FILE}")
+
+ # Generate .circle
+ add_custom_command(OUTPUT ${RECIPE_OUTPUT_FILE}
+ COMMAND circlechef-file ${RECIPE_SOURCE_FILE} ${RECIPE_OUTPUT_FILE}
+ DEPENDS circlechef-file ${RECIPE_SOURCE_FILE}
+ COMMENT "Generating ${RECIPE_OUTPUT_FILE}")
+
+ list(APPEND TESTS ${RECIPE_PREFIX})
+ list(APPEND TESTFILES ${RECIPE_OUTPUT_FILE})
+endforeach(RECIPE)
+
+#Test circlechef-reverse
+file(GLOB GEN_CIRCLEFILES RELATIVE ${CIRCLERECIPES_DIR} "${CIRCLERECIPES_DIR}/*/test.reverse")
+# Note: While in development, circlechef-reverse may not handle the operator.
+# To separate this linkage scan empty test.reverse for test targets for circlechef-reverse.
+
+foreach(CIRCLEFILE IN ITEMS ${GEN_CIRCLEFILES})
+ get_filename_component(CIRCLE_PREFIX ${CIRCLEFILE} DIRECTORY)
+
+ # file from above circlechef-file block
+ # use circle file as input of circlechef-reverse generated from circlechef-file
+ set(RECIPE_OUTPUT_FILE "${CIRCLE_PREFIX}.circle")
+ set(RECIPE_GEN_OUTPUT_FILE "${CIRCLE_PREFIX}.gen.recipe")
+ set(RECIPE_GEN_OUTPUT_FILE2 "${CIRCLE_PREFIX}.gen.circle")
+
+ # Generate .gen.recipe from generated .circle
+ add_custom_command(OUTPUT ${RECIPE_GEN_OUTPUT_FILE}
+ COMMAND circlechef-reverse ${RECIPE_OUTPUT_FILE} ${RECIPE_GEN_OUTPUT_FILE}
+ DEPENDS circlechef-reverse ${RECIPE_OUTPUT_FILE}
+ COMMENT "Generating ${RECIPE_GEN_OUTPUT_FILE}")
+
+ # now we are going to generate .gen.circle from .gen.recipe
+ # to check generated .gen.recipe file is correct by using it.
+ # as weight values may be different, binary comparision is not acceptable.
+ add_custom_command(OUTPUT ${RECIPE_GEN_OUTPUT_FILE2}
+ COMMAND circlechef-file ${RECIPE_GEN_OUTPUT_FILE} ${RECIPE_GEN_OUTPUT_FILE2}
+ DEPENDS circlechef-file ${RECIPE_GEN_OUTPUT_FILE}
+ COMMENT "Generating ${RECIPE_GEN_OUTPUT_FILE2}")
+
+ list(APPEND TESTS ${CIRCLE_PREFIX}.gen)
+ list(APPEND TESTFILES ${RECIPE_GEN_OUTPUT_FILE2})
+endforeach(CIRCLEFILE)
+
+# Add a dummy target to create a target-level dependency.
+# TODO Find a way to create a dependency between circlechef_test and generated testfiles.
+add_custom_target(circlechef_testfiles ALL DEPENDS ${TESTFILES})
+
+# Using circle_verify for temporary as it only calls flatbuffer validate
+# TODO do testing with running the model with runtime/interpreter
+add_test(NAME circlechef_test
+ COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/runvalidate.sh"
+ $<TARGET_FILE:circle-verify>
+ ${TESTS})
diff --git a/compiler/circlechef/tests/runvalidate.sh b/compiler/circlechef/tests/runvalidate.sh
new file mode 100755
index 000000000..46ad125ae
--- /dev/null
+++ b/compiler/circlechef/tests/runvalidate.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+
+if [[ $# -le 2 ]]; then
+ echo "USAGE: $0 [circle-verify path] [prefix 0] "
+ exit 255
+fi
+
+CIRCLE_VERIFY_PATH="$1"; shift
+
+echo "-- Found circle-verify: ${CIRCLE_VERIFY_PATH}"
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+pushd "${WORKDIR}"
+while [[ $# -ne 0 ]]; do
+ PREFIX="$1"; shift
+
+ TESTED+=("${PREFIX}")
+
+ PASSED_TAG="${PREFIX}.passed"
+
+ rm -f "${PASSED_TAG}"
+
+ cat > "${PREFIX}.log" <(
+ exec 2>&1
+
+ echo "'${CIRCLE_VERIFY_PATH}' '${PREFIX}.circle'"
+ "${CIRCLE_VERIFY_PATH}" "${PREFIX}.circle"
+
+ if [[ $? -eq 0 ]]; then
+ touch "${PASSED_TAG}"
+ fi
+ )
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ PASSED+=("$PREFIX")
+ else
+ FAILED+=("$PREFIX")
+ fi
+done
+popd
+
+echo "SUMMARY: ${#PASSED[@]} PASS AND ${#FAILED[@]} FAIL AMONG ${#TESTED[@]} TESTS"
+
+if [[ ${#TESTED[@]} -ne ${#PASSED[@]} ]]; then
+ echo "FAILED"
+ for TEST in "${FAILED[@]}"
+ do
+ echo "- ${TEST}"
+ done
+ exit 255
+fi
+
+exit 0
diff --git a/compiler/circlechef/tools/CMakeLists.txt b/compiler/circlechef/tools/CMakeLists.txt
new file mode 100644
index 000000000..c958614b2
--- /dev/null
+++ b/compiler/circlechef/tools/CMakeLists.txt
@@ -0,0 +1,6 @@
+# Console-based tool (circlechef)
+add_subdirectory(console)
+# File-based tool (circlechef-file)
+add_subdirectory(file)
+# Reverse tool to generate recipe from circle (circlechef-reverse)
+add_subdirectory(reverse)
diff --git a/compiler/circlechef/tools/console/CMakeLists.txt b/compiler/circlechef/tools/console/CMakeLists.txt
new file mode 100644
index 000000000..10168fca3
--- /dev/null
+++ b/compiler/circlechef/tools/console/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_executable(circlechef Driver.cpp)
+target_link_libraries(circlechef circlechef_core)
+target_link_libraries(circlechef safemain)
diff --git a/compiler/circlechef/tools/console/Driver.cpp b/compiler/circlechef/tools/console/Driver.cpp
new file mode 100644
index 000000000..0909f5927
--- /dev/null
+++ b/compiler/circlechef/tools/console/Driver.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "circlechef/ModelChef.h"
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/text_format.h>
+
+#include <iostream>
+
+int entry(int argc, char **argv)
+{
+ int32_t model_version = 1;
+
+ ::circlechef::ModelRecipe model_recipe;
+
+ // Read a model recipe from standard input
+ {
+ google::protobuf::io::IstreamInputStream iis{&std::cin};
+ if (!google::protobuf::TextFormat::Parse(&iis, &model_recipe))
+ {
+ std::cerr << "ERROR: Failed to parse recipe" << std::endl;
+ return 255;
+ }
+
+ if (model_recipe.has_version())
+ {
+ model_version = model_recipe.version();
+ }
+ }
+
+ if (model_version > 1)
+ {
+ std::cerr << "ERROR: Unsupported recipe version: " << model_version << std::endl;
+ return 255;
+ }
+
+ auto generated_model = circlechef::cook(model_recipe);
+
+ // Write a generated model into standard output
+ std::cout.write(generated_model.base(), generated_model.size());
+
+ return 0;
+}
diff --git a/compiler/circlechef/tools/file/CMakeLists.txt b/compiler/circlechef/tools/file/CMakeLists.txt
new file mode 100644
index 000000000..2524a657c
--- /dev/null
+++ b/compiler/circlechef/tools/file/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_executable(circlechef-file Driver.cpp)
+target_link_libraries(circlechef-file arser)
+target_link_libraries(circlechef-file circlechef_core)
+target_link_libraries(circlechef-file safemain)
diff --git a/compiler/circlechef/tools/file/Driver.cpp b/compiler/circlechef/tools/file/Driver.cpp
new file mode 100644
index 000000000..bcc0c7ae9
--- /dev/null
+++ b/compiler/circlechef/tools/file/Driver.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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 "circlechef/ModelChef.h"
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/text_format.h>
+
+#include <arser/arser.h>
+
+#include <fstream>
+#include <iostream>
+
+int entry(int argc, char **argv)
+{
+ arser::Arser arser;
+ arser.add_argument("recipe")
+ .type(arser::DataType::STR)
+ .help("Source recipe file path to convert");
+ arser.add_argument("circle").type(arser::DataType::STR).help("Target circle file path");
+
+ try
+ {
+ arser.parse(argc, argv);
+ }
+ catch (const std::runtime_error &err)
+ {
+ std::cout << err.what() << std::endl;
+ std::cout << arser;
+ return 255;
+ }
+
+ int32_t model_version = 1;
+
+ ::circlechef::ModelRecipe model_recipe;
+
+ std::string recipe_path = arser.get<std::string>("recipe");
+ // Load model recipe from a file
+ {
+ std::ifstream is{recipe_path};
+ google::protobuf::io::IstreamInputStream iis{&is};
+ if (!google::protobuf::TextFormat::Parse(&iis, &model_recipe))
+ {
+ std::cerr << "ERROR: Failed to parse recipe '" << recipe_path << "'" << std::endl;
+ return 255;
+ }
+
+ if (model_recipe.has_version())
+ {
+ model_version = model_recipe.version();
+ }
+ }
+
+ if (model_version > 1)
+ {
+ std::cerr << "ERROR: Unsupported recipe version: " << model_version << ", '" << recipe_path
+ << "'" << std::endl;
+ return 255;
+ }
+
+ auto generated_model = circlechef::cook(model_recipe);
+
+ std::string circle_path = arser.get<std::string>("circle");
+ // Dump generated model into a file
+ {
+ std::ofstream os{circle_path, std::ios::binary};
+ os.write(generated_model.base(), generated_model.size());
+ }
+
+ return 0;
+}
diff --git a/compiler/circlechef/tools/reverse/CMakeLists.txt b/compiler/circlechef/tools/reverse/CMakeLists.txt
new file mode 100644
index 000000000..a1606c94e
--- /dev/null
+++ b/compiler/circlechef/tools/reverse/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_executable(circlechef-reverse Driver.cpp)
+target_link_libraries(circlechef-reverse arser)
+target_link_libraries(circlechef-reverse circlechef_circle)
+target_link_libraries(circlechef-reverse safemain)
+target_link_libraries(circlechef-reverse foder)
diff --git a/compiler/circlechef/tools/reverse/Driver.cpp b/compiler/circlechef/tools/reverse/Driver.cpp
new file mode 100644
index 000000000..8a2b85fc7
--- /dev/null
+++ b/compiler/circlechef/tools/reverse/Driver.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 <circlechef/RecipeChef.h>
+
+#include <arser/arser.h>
+#include <foder/FileLoader.h>
+
+#include <memory>
+#include <iostream>
+
+int entry(int argc, char **argv)
+{
+ arser::Arser arser;
+ arser.add_argument("circle")
+ .type(arser::DataType::STR)
+ .help("Source circle file path to convert");
+ arser.add_argument("recipe").type(arser::DataType::STR).help("Target recipe file path");
+
+ try
+ {
+ arser.parse(argc, argv);
+ }
+ catch (const std::runtime_error &err)
+ {
+ std::cout << err.what() << std::endl;
+ std::cout << arser;
+ return 255;
+ }
+
+ std::string circle_path = arser.get<std::string>("circle");
+ // Load TF lite model from a circle file
+ const foder::FileLoader fileLoader{circle_path};
+ std::vector<char> modelData = fileLoader.load();
+ const circle::Model *circlemodel = circle::GetModel(modelData.data());
+ if (circlemodel == nullptr)
+ {
+ std::cerr << "ERROR: Failed to load circle '" << circle_path << "'" << std::endl;
+ return 255;
+ }
+
+ // Generate ModelRecipe recipe
+ std::unique_ptr<circlechef::ModelRecipe> recipe = circlechef::generate_recipe(circlemodel);
+ if (recipe.get() == nullptr)
+ {
+ std::cerr << "ERROR: Failed to generate recipe" << std::endl;
+ return 255;
+ }
+
+ std::string recipe_path = arser.get<std::string>("recipe");
+ // Save to a file
+ bool result = circlechef::write_recipe(recipe_path, recipe);
+ if (!result)
+ {
+ std::cerr << "ERROR: Failed to write to recipe '" << recipe_path << "'" << std::endl;
+ return 255;
+ }
+ return 0;
+}
diff --git a/compiler/circledump/CMakeLists.txt b/compiler/circledump/CMakeLists.txt
new file mode 100644
index 000000000..fb72b1d66
--- /dev/null
+++ b/compiler/circledump/CMakeLists.txt
@@ -0,0 +1,14 @@
+if(NOT TARGET mio_circle)
+ return()
+endif(NOT TARGET mio_circle)
+
+set(DRIVER "driver/Driver.cpp")
+
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+add_executable(circledump ${DRIVER} ${SOURCES})
+target_include_directories(circledump PRIVATE include)
+target_link_libraries(circledump arser)
+target_link_libraries(circledump mio_circle)
+target_link_libraries(circledump safemain)
+target_link_libraries(circledump flatbuffers)
diff --git a/compiler/circledump/README.md b/compiler/circledump/README.md
new file mode 100644
index 000000000..686e918ac
--- /dev/null
+++ b/compiler/circledump/README.md
@@ -0,0 +1,71 @@
+# circledump
+
+### What is this?
+
+circledump is a tool that dumps binary circle file into human readable text to console.
+
+circledump is implemented with C++ not python. We can do the same thing much easier
+with python but this tool doesn't need to install TensorFlow python package.
+
+Schema for FlatBuffer used is from TensorFlow v1.13.1 release.
+
+### Design philosophy
+
+Make the code simple.
+
+### To do
+
+- Print weight values other than uint8_t
+- Add more operators
+
+### How to use
+
+Command argument format:
+```
+circledump circle_file
+```
+
+Example output of dump `readme.circle` file
+```
+Dump: readme.circle
+
+Data Format:
+CHANNEL_LAST (NHWC for 2d, NDHWC for 3d data)
+
+Operator Codes: [order] OpCodeName (OpCode Enum)
+[0] CONV_2D (code: 3)
+
+Buffers: B(index) (length) values, if any
+B(0) (0)
+B(1) (8) 0x94 0x5b 0x95 0xbf 0x42 0xa4 0x52 0xbf ...
+B(2) (4) 0xcd 0xcc 0x8c 0x3f
+
+Operands: T(tensor index) TYPE (shape) B(buffer index) OperandName
+T(0) FLOAT32 (1, 3, 3, 2) B(0) ifm
+T(1) FLOAT32 (1, 1, 1, 2) B(1) ker
+T(2) FLOAT32 (1) B(2) bias
+T(3) FLOAT32 (1, 3, 3, 1) B(0) ofm
+
+Operators: O(operator index) OpCodeName
+ Option(values) ... <-- depending on OpCode
+ I T(tensor index) OperandName <-- as input
+ O T(tensor index) OperandName <-- as output
+O(0) CONV_2D
+ Padding(1) Stride.W(1) Stride.H(1) Activation(0)
+ I T(0) ifm
+ I T(1) ker
+ I T(2) bias
+ O T(3) ofm
+
+Inputs/Outputs: I(input)/O(output) T(tensor index) OperandName
+I T(0) ifm
+I T(1) ker
+O T(3) ofm
+```
+
+### Dependency
+
+- mio-circle
+- safemain
+- stdex
+- FlatBuffers
diff --git a/compiler/circledump/driver/Driver.cpp b/compiler/circledump/driver/Driver.cpp
new file mode 100644
index 000000000..657f24fe0
--- /dev/null
+++ b/compiler/circledump/driver/Driver.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <arser/arser.h>
+#include <circleread/Model.h>
+#include <circledump/Dump.h>
+
+#include <iostream>
+
+int entry(int argc, char **argv)
+{
+ arser::Arser arser;
+ arser.add_argument("circle").type(arser::DataType::STR).help("Circle file path to dump");
+
+ try
+ {
+ arser.parse(argc, argv);
+ }
+ catch (const std::runtime_error &err)
+ {
+ std::cout << err.what() << '\n';
+ std::cout << arser;
+ return 255;
+ }
+
+ std::string circle_path = arser.get<std::string>("circle");
+ // Load Circle model from a circle file
+ std::unique_ptr<circleread::Model> model = circleread::load_circle(circle_path);
+ if (model == nullptr)
+ {
+ std::cerr << "ERROR: Failed to load circle '" << circle_path << "'" << std::endl;
+ return 255;
+ }
+
+ const circle::Model *circlemodel = model->model();
+ if (circlemodel == nullptr)
+ {
+ std::cerr << "ERROR: Failed to load circle '" << circle_path << "'" << std::endl;
+ return 255;
+ }
+
+ std::cout << "Dump: " << circle_path << std::endl << std::endl;
+
+ std::cout << circlemodel << std::endl;
+
+ return 0;
+}
diff --git a/compiler/circledump/include/circledump/Dump.h b/compiler/circledump/include/circledump/Dump.h
new file mode 100644
index 000000000..a129458f4
--- /dev/null
+++ b/compiler/circledump/include/circledump/Dump.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLEDUMP_DUMP_H__
+#define __CIRCLEDUMP_DUMP_H__
+
+#include <mio/circle/schema_generated.h>
+
+#include <ostream>
+
+namespace circledump
+{
+
+void dump_model(std::ostream &os, const circle::Model *model);
+}
+
+std::ostream &operator<<(std::ostream &os, const circle::Model *model);
+
+#endif // __CIRCLEDUMP_DUMP_H__
diff --git a/compiler/circledump/include/circleread/Model.h b/compiler/circledump/include/circleread/Model.h
new file mode 100644
index 000000000..234db8b4c
--- /dev/null
+++ b/compiler/circledump/include/circleread/Model.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLEREAD_MODEL_H__
+#define __CIRCLEREAD_MODEL_H__
+
+#include <mio/circle/schema_generated.h>
+
+#include <memory>
+
+namespace circleread
+{
+
+struct Model
+{
+ virtual ~Model() = default;
+
+ virtual const ::circle::Model *model(void) const = 0;
+};
+
+/**
+ * @brief Load Circle model (as a raw Model) from a given path
+ *
+ * @note May return a nullptr
+ */
+std::unique_ptr<Model> load_circle(const std::string &path);
+
+} // namespace circleread
+
+#endif // __CIRCLEREAD_MODEL_H__
diff --git a/compiler/circledump/requires.cmake b/compiler/circledump/requires.cmake
new file mode 100644
index 000000000..81e0f0dbd
--- /dev/null
+++ b/compiler/circledump/requires.cmake
@@ -0,0 +1,3 @@
+require("arser")
+require("mio-circle")
+require("safemain")
diff --git a/compiler/circledump/src/Dump.cpp b/compiler/circledump/src/Dump.cpp
new file mode 100644
index 000000000..f8e2d61f3
--- /dev/null
+++ b/compiler/circledump/src/Dump.cpp
@@ -0,0 +1,418 @@
+/*
+ * 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 <circledump/Dump.h>
+
+#include "Read.h"
+#include "OpPrinter.h"
+
+#include <ostream>
+
+#include <algorithm> // min
+#include <iomanip> // setfill
+
+namespace circledump
+{
+
+void dump_buffer(std::ostream &os, const uint8_t *buffer, size_t size, size_t amount)
+{
+ std::ios_base::fmtflags saveflags(os.flags());
+
+ bool second = false;
+ bool ellipsis = amount > 0 && size > 4;
+ size_t count = ellipsis ? std::min(size, amount) : size;
+
+ for (size_t i = 0; i < count; i++)
+ {
+ if (second)
+ {
+ os << " ";
+ }
+
+ os << std::showbase << std::setfill('0') << std::setw(2);
+ os << std::hex << (uint32_t)buffer[i];
+
+ second = true;
+ }
+ if (ellipsis)
+ {
+ os << " ...";
+ }
+
+ os.flags(saveflags);
+}
+
+void dump_vector(std::ostream &os, const std::vector<int32_t> &vs)
+{
+ uint32_t seq = 0;
+ for (auto &v : vs)
+ {
+ if (seq)
+ os << ", ";
+ os << v;
+ seq++;
+ }
+}
+
+std::ostream &operator<<(std::ostream &os, const std::vector<int32_t> &vect)
+{
+ circledump::dump_vector(os, vect);
+ return os;
+}
+
+template <typename T>
+void dump_fbvect(std::ostream &os, const flatbuffers::Vector<T> *fbvect, uint32_t size)
+{
+ for (uint32_t q = 0; q < size; q++)
+ {
+ if (q)
+ os << ", ";
+ os << fbvect->Get(q);
+ }
+}
+
+template <>
+void dump_fbvect(std::ostream &os, const flatbuffers::Vector<uint8_t> *fbvect, uint32_t size)
+{
+ assert(fbvect);
+ for (uint32_t q = 0; q < size; q++)
+ {
+ if (q)
+ os << ", ";
+ os << static_cast<uint32_t>(fbvect->Get(q));
+ }
+}
+
+template <typename T>
+std::ostream &operator<<(std::ostream &os, const flatbuffers::Vector<T> *fbvect)
+{
+ if (fbvect == nullptr)
+ return os;
+
+ bool ellipsis = (fbvect->size() > 4);
+ auto limit_size = ellipsis ? 4 : fbvect->size();
+
+ if (ellipsis)
+ {
+ os << "(" << fbvect->size() << ") ";
+ }
+
+ dump_fbvect(os, fbvect, limit_size);
+
+ if (ellipsis)
+ {
+ os << " ... ";
+ }
+
+ return os;
+}
+
+void dump_sub_graph(std::ostream &os, circleread::Reader &reader)
+{
+ auto tensors = reader.tensors();
+ auto operators = reader.operators();
+ auto data_format = reader.data_format();
+
+ // dump data_format
+ os << "Data Format:" << std::endl;
+ if (data_format == circle::DataFormat::DataFormat_CHANNELS_LAST)
+ {
+ os << "CHANNEL_LAST (NHWC for 2d, NDHWC for 3d data)" << std::endl;
+ }
+ else if (data_format == circle::DataFormat::DataFormat_CHANNELS_FIRST)
+ {
+ os << "CHANNEL_FIRST (NCHW for 2d, NCDHW for 3d data)" << std::endl;
+ }
+ os << std::endl;
+
+ // dump operands(tensors)
+ os << "Operands: T(subgraph index : tensor index) TYPE (shape) (shape_signature) "
+ << "B(buffer index) OperandName" << std::endl;
+ for (uint32_t i = 0; i < tensors->Length(); ++i)
+ {
+ // TODO refactor to some better structure
+ auto tensor = tensors->Get(i);
+ std::vector<int32_t> dims = {-1};
+
+ if (tensor->shape())
+ dims = circleread::as_index_vector(tensor->shape());
+
+ os << "T(" << reader.subgraph_index() << ":" << i << ") " << circleread::tensor_type(tensor)
+ << " ";
+ os << "(" << dims << ") ";
+ if (tensor->shape_signature())
+ {
+ std::vector<int32_t> dims_sig = circleread::as_index_vector(tensor->shape_signature());
+ os << "(" << dims_sig << ") ";
+ }
+ os << "B(" << tensor->buffer() << ") ";
+ os << circleread::tensor_name(tensor) << std::endl;
+
+ if (auto q_params = tensor->quantization())
+ {
+ if ((q_params->min() && q_params->max()) || (q_params->scale() && q_params->zero_point()))
+ {
+ std::string strquantiz = " Quantization: ";
+ std::string strqindent(strquantiz.size(), ' ');
+ os << strquantiz;
+
+ if (q_params->min())
+ {
+ os << "min(" << q_params->min() << ") ";
+ if (q_params->min()->size() > 1)
+ os << std::endl << strqindent;
+ }
+ if (q_params->max())
+ {
+ os << "max(" << q_params->max() << ") ";
+ if (q_params->max()->size() > 1)
+ os << std::endl << strqindent;
+ }
+ if (q_params->scale())
+ {
+ os << "scale(" << q_params->scale() << ") ";
+ if (q_params->scale()->size() > 1)
+ os << std::endl << strqindent;
+ }
+ if (q_params->zero_point())
+ {
+ os << "zeropt(" << q_params->zero_point() << ") ";
+ if (q_params->zero_point()->size() > 1)
+ os << std::endl << strqindent;
+ }
+ os << "quantized_dimension(" << q_params->quantized_dimension() << ")";
+
+ os << std::endl;
+ }
+ }
+
+ if (const auto &s_params = tensor->sparsity())
+ {
+ std::string strsparsity = " Sparsity: ";
+ std::string strsindent(strsparsity.size(), ' ');
+ os << strsparsity;
+
+ if (s_params->traversal_order())
+ {
+ os << "traversal_order(" << s_params->traversal_order() << ") ";
+ os << std::endl << strsindent;
+ }
+ if (s_params->block_map())
+ {
+ os << "block_map(" << s_params->block_map() << ") ";
+ os << std::endl << strsindent;
+ }
+ if (const auto &dim_metadata = s_params->dim_metadata())
+ {
+ uint32_t idx = 0;
+ for (const auto &dm : *dim_metadata)
+ {
+ std::string strdm = "dim_metadata[" + std::to_string(idx++) + "]: ";
+ std::string strdm_indent = strsindent + std::string(strdm.size(), ' ');
+ os << strdm;
+
+ os << "format(" << circle::EnumNameDimensionType(dm->format()) << ") ";
+ os << std::endl << strdm_indent;
+
+ os << "dense_size(" << dm->dense_size() << ") ";
+ os << std::endl << strdm_indent;
+
+ os << "array_segments_type("
+ << circle::EnumNameSparseIndexVector(dm->array_segments_type()) << ") ";
+ os << std::endl << strdm_indent;
+
+ os << "array_segments(";
+ switch (dm->array_segments_type())
+ {
+ case circle::SparseIndexVector_NONE:
+ // DO NOTHING
+ break;
+ case circle::SparseIndexVector_Int32Vector:
+ os << dm->array_segments_as_Int32Vector()->values();
+ break;
+ case circle::SparseIndexVector_Uint16Vector:
+ os << dm->array_segments_as_Uint16Vector()->values();
+ break;
+ case circle::SparseIndexVector_Uint8Vector:
+ os << dm->array_segments_as_Uint8Vector()->values();
+ break;
+ default:
+ throw std::runtime_error("Invalid SparseIndexVector type of array_segments");
+ }
+ os << ")" << std::endl << strdm_indent;
+
+ os << "array_indices_type(" << circle::EnumNameSparseIndexVector(dm->array_indices_type())
+ << ") ";
+ os << std::endl << strdm_indent;
+
+ os << "array_indices(";
+ switch (dm->array_indices_type())
+ {
+ case circle::SparseIndexVector_NONE:
+ // DO NOTHING
+ break;
+ case circle::SparseIndexVector_Int32Vector:
+ os << dm->array_indices_as_Int32Vector()->values();
+ break;
+ case circle::SparseIndexVector_Uint16Vector:
+ os << dm->array_indices_as_Uint16Vector()->values();
+ break;
+ case circle::SparseIndexVector_Uint8Vector:
+ os << dm->array_indices_as_Uint8Vector()->values();
+ break;
+ default:
+ throw std::runtime_error("Invalid SparseIndexVector type of array_indices");
+ }
+ os << ")" << std::endl << strsindent;
+ }
+ }
+ }
+ os << std::endl;
+ }
+
+ // dump operators
+ os << "Operators: O(subgraph index : operator index) OpCodeName " << std::endl;
+ os << " Option(values) ... <-- depending on OpCode" << std::endl;
+ os << " I T(tensor index) OperandName <-- as input" << std::endl;
+ os << " O T(tensor index) OperandName <-- as output" << std::endl;
+ for (uint32_t i = 0; i < operators->Length(); ++i)
+ {
+ const auto op = operators->Get(i);
+ circle::BuiltinOperator builtincode = reader.builtin_code(op);
+
+ const std::vector<int32_t> &inputs = circleread::as_index_vector(op->inputs());
+ const std::vector<int32_t> &outputs = circleread::as_index_vector(op->outputs());
+ auto op_name = reader.opcode_name(op);
+
+ os << "O(" << reader.subgraph_index() << ":" << i << ") " << op_name << " ";
+ os << std::endl;
+
+ if (auto op_prn = OpPrinterRegistry::get().lookup(builtincode))
+ {
+ op_prn->options(op, os);
+ }
+
+ for (auto input : inputs)
+ {
+ os << " I T(" << reader.subgraph_index() << ":" << input << ") ";
+ if (input >= 0)
+ {
+ auto tensor = tensors->Get(input);
+ os << circleread::tensor_name(tensor);
+ }
+ os << std::endl;
+ }
+ for (auto output : outputs)
+ {
+ os << " O T(" << reader.subgraph_index() << ":" << output << ") ";
+ if (output >= 0)
+ {
+ auto tensor = tensors->Get(output);
+ os << circleread::tensor_name(tensor);
+ }
+ os << std::endl;
+ }
+ }
+ os << std::endl;
+
+ // dump network inputs/outputs
+ os << "Inputs/Outputs: I(input)/O(output) T(tensor index) OperandName" << std::endl;
+
+ for (const auto input : reader.inputs())
+ {
+ auto tensor = tensors->Get(input);
+ std::string name = circleread::tensor_name(tensor);
+ os << "I T(" << reader.subgraph_index() << ":" << input << ") " << name << std::endl;
+ }
+
+ for (const auto output : reader.outputs())
+ {
+ auto tensor = tensors->Get(output);
+ std::string name = circleread::tensor_name(tensor);
+ os << "O T(" << reader.subgraph_index() << ":" << output << ") " << name << std::endl;
+ }
+
+ os << std::endl;
+}
+
+void dump_model(std::ostream &os, const circle::Model *model)
+{
+ circleread::Reader reader(model);
+
+ uint32_t num_subgraph = reader.num_subgraph();
+
+ // dump model version
+ os << "===================================================================" << std::endl;
+ os << "Model version: " << reader.version() << std::endl;
+ os << " # sub graphs: " << num_subgraph << std::endl;
+ os << std::endl;
+
+ auto opcodes = reader.opcodes();
+ auto buffers = reader.buffers();
+
+ // dump operator_codes
+ os << "Operator Codes: [order] OpCodeName (OpCode Enum)" << std::endl;
+ int32_t opcode_index = 0;
+ for (auto opcode : opcodes)
+ {
+ circle::BuiltinOperator op_code = opcode->builtin_code();
+ auto op_name = circleread::opcode_name(opcode);
+ auto op_version = opcode->version();
+
+ os << "[" << opcode_index << "] " << op_name << " (code: " << op_code
+ << ", version: " << op_version << ")" << std::endl;
+
+ opcode_index++;
+ }
+ os << std::endl;
+
+ // dump buffer
+ os << "Buffers: B(index) (length) values, if any" << std::endl;
+ for (uint32_t i = 0; i < buffers->Length(); ++i)
+ {
+ const uint8_t *buff_data;
+ size_t size = reader.buffer_info(i, &buff_data);
+
+ os << "B(" << i << ") (" << size << ") ";
+ if (buff_data != nullptr)
+ {
+ dump_buffer(os, buff_data, size, 16);
+ }
+ os << std::endl;
+ }
+ os << std::endl;
+
+ for (uint32_t sg = 0; sg < num_subgraph; ++sg)
+ {
+ reader.select_subgraph(sg);
+
+ os << "-------------------------------------------------------------------" << std::endl;
+ os << "Sub-Graph: #" << sg << " " << reader.subgraph_name() << std::endl;
+ os << std::endl;
+
+ dump_sub_graph(os, reader);
+ }
+
+ os << "===================================================================" << std::endl;
+}
+
+} // namespace circledump
+
+std::ostream &operator<<(std::ostream &os, const circle::Model *model)
+{
+ circledump::dump_model(os, model);
+ return os;
+}
diff --git a/compiler/circledump/src/Load.cpp b/compiler/circledump/src/Load.cpp
new file mode 100644
index 000000000..ec91ed189
--- /dev/null
+++ b/compiler/circledump/src/Load.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <circleread/Model.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+namespace
+{
+
+class MemoryMappedModel final : public circleread::Model
+{
+public:
+ /**
+ * @require fd and data SHOULD be valid
+ */
+ explicit MemoryMappedModel(int fd, void *data, size_t size) : _fd{fd}, _data{data}, _size{size}
+ {
+ // DO NOTHING
+ }
+
+public:
+ ~MemoryMappedModel()
+ {
+ munmap(_data, _size);
+ close(_fd);
+ }
+
+public:
+ MemoryMappedModel(const MemoryMappedModel &) = delete;
+ MemoryMappedModel(MemoryMappedModel &&) = delete;
+
+public:
+ const ::circle::Model *model(void) const override { return ::circle::GetModel(_data); }
+
+private:
+ int _fd = -1;
+ void *_data = nullptr;
+ size_t _size = 0;
+};
+
+class FileDescriptor final
+{
+public:
+ FileDescriptor(int value) : _value{value}
+ {
+ // DO NOTHING
+ }
+
+public:
+ // NOTE Copy is not allowed
+ FileDescriptor(const FileDescriptor &) = delete;
+
+public:
+ // NOTE Move is allowed
+ FileDescriptor(FileDescriptor &&fd) { _value = fd.release(); }
+
+public:
+ ~FileDescriptor()
+ {
+ if (_value != -1)
+ {
+ // Close on descturction
+ close(_value);
+ }
+ }
+
+public:
+ int value(void) const { return _value; }
+
+public:
+ int release(void)
+ {
+ auto res = _value;
+ _value = -1;
+ return res;
+ }
+
+private:
+ int _value = -1;
+};
+
+} // namespace
+
+namespace circleread
+{
+
+std::unique_ptr<Model> load_circle(const std::string &path)
+{
+ FileDescriptor fd = open(path.c_str(), O_RDONLY);
+
+ if (fd.value() == -1)
+ {
+ // Return nullptr on open failure
+ return nullptr;
+ }
+
+ struct stat st;
+ if (fstat(fd.value(), &st) == -1)
+ {
+ // Return nullptr on fstat failure
+ return nullptr;
+ }
+
+ auto size = st.st_size;
+ auto data = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd.value(), 0);
+
+ if (data == MAP_FAILED)
+ {
+ // Return nullptr on mmap failure
+ return nullptr;
+ }
+
+ return std::unique_ptr<circleread::Model>{new MemoryMappedModel(fd.release(), data, size)};
+}
+
+} // namespace circleread
diff --git a/compiler/circledump/src/OpPrinter.cpp b/compiler/circledump/src/OpPrinter.cpp
new file mode 100644
index 000000000..ef22baaee
--- /dev/null
+++ b/compiler/circledump/src/OpPrinter.cpp
@@ -0,0 +1,795 @@
+/*
+ * 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 "OpPrinter.h"
+#include "Read.h"
+
+#include <memory>
+
+#include <flatbuffers/flexbuffers.h>
+
+using std::make_unique;
+
+namespace circledump
+{
+
+// TODO move to some header
+std::ostream &operator<<(std::ostream &os, const std::vector<int32_t> &vect);
+
+// TODO Re-arrange in alphabetical order
+
+class AddPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_AddOptions())
+ {
+ os << " ";
+ os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function())
+ << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class ArgMaxPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_ArgMaxOptions())
+ {
+ os << " ";
+ os << "OutputType(" << EnumNameTensorType(params->output_type()) << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class ArgMinPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_ArgMinOptions())
+ {
+ os << " ";
+ os << "OutputType(" << EnumNameTensorType(params->output_type()) << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class BatchMatMulPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_BatchMatMulOptions())
+ {
+ os << " ";
+ os << std::boolalpha;
+ os << "adjoint_lhs(" << params->adjoint_lhs() << ") ";
+ os << "adjoint_rhs(" << params->adjoint_rhs() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class CastPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto cast_params = op->builtin_options_as_CastOptions())
+ {
+ os << " ";
+ os << "in_data_type(" << circle::EnumNameTensorType(cast_params->in_data_type()) << ") ";
+ os << "out_data_type(" << circle::EnumNameTensorType(cast_params->out_data_type()) << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class Conv2DPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto conv_params = op->builtin_options_as_Conv2DOptions())
+ {
+ os << " ";
+ os << "Padding(" << conv_params->padding() << ") ";
+ os << "Stride.W(" << conv_params->stride_w() << ") ";
+ os << "Stride.H(" << conv_params->stride_h() << ") ";
+ os << "Dilation.W(" << conv_params->dilation_w_factor() << ") ";
+ os << "Dilation.H(" << conv_params->dilation_h_factor() << ") ";
+ os << "Activation("
+ << EnumNameActivationFunctionType(conv_params->fused_activation_function()) << ")";
+ os << std::endl;
+ }
+ }
+};
+
+class DepthToSpacePrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *std_params = op->builtin_options_as_DepthToSpaceOptions())
+ {
+ os << " ";
+ os << "BlockSize(" << std_params->block_size() << ")";
+ os << std::endl;
+ }
+ }
+};
+
+class DivPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_DivOptions())
+ {
+ os << " ";
+ os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function())
+ << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class Pool2DPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto pool_params = op->builtin_options_as_Pool2DOptions())
+ {
+ os << " ";
+ os << "Padding(" << pool_params->padding() << ") ";
+ os << "Stride.W(" << pool_params->stride_w() << ") ";
+ os << "Stride.H(" << pool_params->stride_h() << ") ";
+ os << "Filter.W(" << pool_params->filter_width() << ") ";
+ os << "Filter.H(" << pool_params->filter_height() << ") ";
+ os << "Activation("
+ << EnumNameActivationFunctionType(pool_params->fused_activation_function()) << ")";
+ os << std::endl;
+ }
+ }
+};
+
+class ConcatenationPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *concatenation_params = op->builtin_options_as_ConcatenationOptions())
+ {
+ os << " ";
+ os << "Activation("
+ << EnumNameActivationFunctionType(concatenation_params->fused_activation_function())
+ << ") ";
+ os << "Axis(" << concatenation_params->axis() << ")";
+ os << std::endl;
+ }
+ }
+};
+
+class ReducerPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto reducer_params = op->builtin_options_as_ReducerOptions())
+ {
+ os << " ";
+ os << "keep_dims(" << reducer_params->keep_dims() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class ReshapePrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *reshape_params = op->builtin_options_as_ReshapeOptions())
+ {
+ auto new_shape = circleread::as_index_vector(reshape_params->new_shape());
+ os << " ";
+ os << "NewShape(" << new_shape << ")";
+ os << std::endl;
+ }
+ }
+};
+
+class ResizeBilinearPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *resize_params = op->builtin_options_as_ResizeBilinearOptions())
+ {
+ os << " ";
+ os << std::boolalpha;
+ os << "align_corners(" << resize_params->align_corners() << ")";
+ os << "half_pixel_centers(" << resize_params->half_pixel_centers() << ")";
+ os << std::endl;
+ }
+ }
+};
+
+class ResizeNearestNeighborPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *resize_params = op->builtin_options_as_ResizeNearestNeighborOptions())
+ {
+ os << " ";
+ os << std::boolalpha;
+ os << "align_corners(" << resize_params->align_corners() << ")";
+ os << std::endl;
+ }
+ }
+};
+
+class ReverseSequencePrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_ReverseSequenceOptions())
+ {
+ os << " ";
+ os << "seq_dim(" << params->seq_dim() << ") ";
+ os << "batch_dim(" << params->batch_dim() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class DepthwiseConv2DPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto conv_params = op->builtin_options_as_DepthwiseConv2DOptions())
+ {
+ os << " ";
+ os << "Padding(" << conv_params->padding() << ") ";
+ os << "Stride.W(" << conv_params->stride_w() << ") ";
+ os << "Stride.H(" << conv_params->stride_h() << ") ";
+ os << "DepthMultiplier(" << conv_params->depth_multiplier() << ") ";
+ os << "Dilation.W(" << conv_params->dilation_w_factor() << ") ";
+ os << "Dilation.H(" << conv_params->dilation_h_factor() << ")";
+ os << "Activation("
+ << EnumNameActivationFunctionType(conv_params->fused_activation_function()) << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class FullyConnectedPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_FullyConnectedOptions())
+ {
+ os << " ";
+ os << "WeightFormat(" << EnumNameFullyConnectedOptionsWeightsFormat(params->weights_format())
+ << ") ";
+ os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function())
+ << ") ";
+
+ os << std::endl;
+ }
+ }
+};
+
+class GatherPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_GatherOptions())
+ {
+ os << " ";
+ os << "Axis(" << params->axis() << ") ";
+
+ os << std::endl;
+ }
+ }
+};
+
+class IfPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_IfOptions())
+ {
+ os << " ";
+ os << "then_subgraph_index(" << params->then_subgraph_index() << ") ";
+ os << "else_subgraph_index(" << params->else_subgraph_index() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class L2NormPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_L2NormOptions())
+ {
+ os << " ";
+ os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function())
+ << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class LeakyReluPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_LeakyReluOptions())
+ {
+ os << " ";
+ os << "alpha(" << params->alpha() << ") ";
+ }
+ }
+};
+
+class LocalResponseNormalizationPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_LocalResponseNormalizationOptions())
+ {
+ os << " ";
+ os << "radius(" << params->radius() << ") ";
+ os << "bias(" << params->bias() << ") ";
+ os << "alpha(" << params->alpha() << ") ";
+ os << "beta(" << params->beta() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class MirrorPadPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_MirrorPadOptions())
+ {
+ os << " ";
+ os << "mode(" << EnumNameMirrorPadMode(params->mode()) << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class MulPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_MulOptions())
+ {
+ os << " ";
+ os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function())
+ << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class OneHotPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_OneHotOptions())
+ {
+ os << " ";
+ os << "Axis(" << params->axis() << ") ";
+
+ os << std::endl;
+ }
+ }
+};
+
+class PackPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_PackOptions())
+ {
+ os << " ";
+ os << "ValuesCount(" << params->values_count() << ") ";
+ os << "Axis(" << params->axis() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class ShapePrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_ShapeOptions())
+ {
+ os << " ";
+ os << "out_type(" << EnumNameTensorType(params->out_type()) << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class SoftmaxPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *softmax_params = op->builtin_options_as_SoftmaxOptions())
+ {
+ os << " ";
+ os << "Beta(" << softmax_params->beta() << ")";
+ os << std::endl;
+ }
+ }
+};
+
+class SpaceToDepthPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *std_params = op->builtin_options_as_SpaceToDepthOptions())
+ {
+ os << " ";
+ os << "BlockSize(" << std_params->block_size() << ")";
+ os << std::endl;
+ }
+ }
+};
+
+class SparseToDensePrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *std_params = op->builtin_options_as_SparseToDenseOptions())
+ {
+ os << " ";
+ os << "ValidateIndices(" << std_params->validate_indices() << ")";
+ os << std::endl;
+ }
+ }
+};
+
+class SplitPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_SplitOptions())
+ {
+ os << " ";
+ os << "num_splits(" << params->num_splits() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class SplitVPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_SplitVOptions())
+ {
+ os << " ";
+ os << "num_splits(" << params->num_splits() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class SqueezePrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_SqueezeOptions())
+ {
+ os << " ";
+ os << "SqueezeDims(";
+ for (int i = 0; i < params->squeeze_dims()->size(); ++i)
+ {
+ if (i != 0)
+ os << ", ";
+ os << params->squeeze_dims()->Get(i);
+ }
+ os << ")";
+ os << std::endl;
+ }
+ }
+};
+
+class StridedSlicePrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *strided_slice_params = op->builtin_options_as_StridedSliceOptions())
+ {
+ os << " ";
+ os << "begin_mask(" << strided_slice_params->begin_mask() << ") ";
+ os << "end_mask(" << strided_slice_params->end_mask() << ") ";
+ os << "ellipsis_mask(" << strided_slice_params->ellipsis_mask() << ") ";
+ os << "new_axis_mask(" << strided_slice_params->new_axis_mask() << ") ";
+ os << "shrink_axis_mask(" << strided_slice_params->shrink_axis_mask() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class SubPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_SubOptions())
+ {
+ os << " ";
+ os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function())
+ << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+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())
+ {
+ os << " ";
+ os << "Padding(" << conv_params->padding() << ") ";
+ os << "Stride.W(" << conv_params->stride_w() << ") ";
+ os << "Stride.H(" << conv_params->stride_h() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class UnidirectionalSequenceLSTMPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_UnidirectionalSequenceLSTMOptions())
+ {
+ os << " ";
+ os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function())
+ << ") ";
+ os << "cell_clip(" << params->cell_clip() << ") ";
+ os << "proj_clip(" << params->proj_clip() << ") ";
+ os << "time_major(" << params->time_major() << ") ";
+ os << "asymmetric_quantize_inputs(" << params->asymmetric_quantize_inputs() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class UniquePrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_UniqueOptions())
+ {
+ os << " ";
+ os << "idx_out_type(" << EnumNameTensorType(params->idx_out_type()) << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class WhilePrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_WhileOptions())
+ {
+ os << " ";
+ os << "cond_subgraph_index(" << params->cond_subgraph_index() << ") ";
+ os << "body_subgraph_index(" << params->body_subgraph_index() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class CustomOpPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (op->custom_options_format() != circle::CustomOptionsFormat::CustomOptionsFormat_FLEXBUFFERS)
+ {
+ os << " ";
+ os << "Unknown custom option format";
+ return;
+ }
+
+ const flatbuffers::Vector<uint8_t> *option_buf = op->custom_options();
+
+ if (option_buf == nullptr || option_buf->size() == 0)
+ {
+ os << "No attrs found." << std::endl;
+ return;
+ }
+
+ // printing attrs
+ // attrs of custom ops are encoded in flexbuffer format
+ auto attr_map = flexbuffers::GetRoot(option_buf->data(), option_buf->size()).AsMap();
+
+ os << " ";
+ auto keys = attr_map.Keys();
+ for (int i = 0; i < keys.size(); i++)
+ {
+ auto key = keys[i].ToString();
+ os << key << "(" << attr_map[key].ToString() << ") ";
+ }
+
+ // Note: attr in "Shape" type does not seem to be converted by circle_convert.
+ // When the converted circle file (with custom op) is opened with hexa editory,
+ // attrs names can be found but attr name in "Shape" type is not found.
+
+ os << std::endl;
+ }
+};
+
+class BCQFullyConnectedPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_BCQFullyConnectedOptions())
+ {
+ os << " ";
+ os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function())
+ << ") ";
+ os << "weights_hidden_size(" << params->weights_hidden_size() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class BCQGatherPrinter : public OpPrinter
+{
+public:
+ void options(const circle::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_BCQGatherOptions())
+ {
+ os << " ";
+ os << "axis(" << params->axis() << ") ";
+ os << "weights_hidden_size(" << params->input_hidden_size() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+OpPrinterRegistry::OpPrinterRegistry()
+{
+ _op_map[circle::BuiltinOperator_ADD] = make_unique<AddPrinter>();
+ // There is no Option for ADD_N
+ _op_map[circle::BuiltinOperator_ARG_MAX] = make_unique<ArgMaxPrinter>();
+ _op_map[circle::BuiltinOperator_ARG_MIN] = make_unique<ArgMinPrinter>();
+ _op_map[circle::BuiltinOperator_AVERAGE_POOL_2D] = make_unique<Pool2DPrinter>();
+ _op_map[circle::BuiltinOperator_BATCH_MATMUL] = make_unique<BatchMatMulPrinter>();
+ _op_map[circle::BuiltinOperator_CAST] = make_unique<CastPrinter>();
+ // There is no Option for CEIL
+ _op_map[circle::BuiltinOperator_CONCATENATION] = make_unique<ConcatenationPrinter>();
+ _op_map[circle::BuiltinOperator_CONV_2D] = make_unique<Conv2DPrinter>();
+ _op_map[circle::BuiltinOperator_DEPTH_TO_SPACE] = make_unique<DepthToSpacePrinter>();
+ _op_map[circle::BuiltinOperator_DEPTHWISE_CONV_2D] = make_unique<DepthwiseConv2DPrinter>();
+ // There is no Option for DEQUANTIZE
+ _op_map[circle::BuiltinOperator_DIV] = make_unique<DivPrinter>();
+ // There is no Option for FLOOR
+ // There is no Option for FLOOR_MOD
+ _op_map[circle::BuiltinOperator_FULLY_CONNECTED] = make_unique<FullyConnectedPrinter>();
+ _op_map[circle::BuiltinOperator_GATHER] = make_unique<GatherPrinter>();
+ _op_map[circle::BuiltinOperator_IF] = make_unique<IfPrinter>();
+ _op_map[circle::BuiltinOperator_L2_NORMALIZATION] = make_unique<L2NormPrinter>();
+ _op_map[circle::BuiltinOperator_L2_POOL_2D] = make_unique<Pool2DPrinter>();
+ _op_map[circle::BuiltinOperator_LEAKY_RELU] = make_unique<LeakyReluPrinter>();
+ _op_map[circle::BuiltinOperator_LOCAL_RESPONSE_NORMALIZATION] =
+ make_unique<LocalResponseNormalizationPrinter>();
+ // There is no Option for LOG
+ // There is no Option for LOGISTIC
+ // There is no Option for LOG_SOFTMAX
+ _op_map[circle::BuiltinOperator_MAX_POOL_2D] = make_unique<Pool2DPrinter>();
+ _op_map[circle::BuiltinOperator_MIRROR_PAD] = make_unique<MirrorPadPrinter>();
+ _op_map[circle::BuiltinOperator_MUL] = make_unique<MulPrinter>();
+ // There is no Option for NON_MAX_SUPPRESSION_V4
+ // There is no Option for NON_MAX_SUPPRESSION_V5
+ _op_map[circle::BuiltinOperator_ONE_HOT] = make_unique<OneHotPrinter>();
+ _op_map[circle::BuiltinOperator_PACK] = make_unique<PackPrinter>();
+ // There is no Option for PAD
+ // There is no Option for PADV2
+ // There is no Option for PRELU
+ // There is no Option for RELU
+ // There is no Option for RELU6
+ // There is no Option for RELU_N1_TO_1
+ _op_map[circle::BuiltinOperator_REDUCE_ANY] = make_unique<ReducerPrinter>();
+ _op_map[circle::BuiltinOperator_REDUCE_MAX] = make_unique<ReducerPrinter>();
+ _op_map[circle::BuiltinOperator_REDUCE_MIN] = make_unique<ReducerPrinter>();
+ _op_map[circle::BuiltinOperator_REDUCE_PROD] = make_unique<ReducerPrinter>();
+ _op_map[circle::BuiltinOperator_RESHAPE] = make_unique<ReshapePrinter>();
+ _op_map[circle::BuiltinOperator_RESIZE_BILINEAR] = make_unique<ResizeBilinearPrinter>();
+ _op_map[circle::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR] =
+ make_unique<ResizeNearestNeighborPrinter>();
+ _op_map[circle::BuiltinOperator_REVERSE_SEQUENCE] = make_unique<ReverseSequencePrinter>();
+ // There is no Option for ROUND
+ // There is no Option for SELECT
+ // There is no Option for SELECT_V2
+ _op_map[circle::BuiltinOperator_SHAPE] = make_unique<ShapePrinter>();
+ // There is no Option for SIN
+ // There is no Option for SLICE
+ _op_map[circle::BuiltinOperator_SOFTMAX] = make_unique<SoftmaxPrinter>();
+ _op_map[circle::BuiltinOperator_SPACE_TO_DEPTH] = make_unique<SpaceToDepthPrinter>();
+ // There is no Option for SPACE_TO_BATCH_ND
+ _op_map[circle::BuiltinOperator_SPARSE_TO_DENSE] = make_unique<SparseToDensePrinter>();
+ _op_map[circle::BuiltinOperator_SPLIT] = make_unique<SplitPrinter>();
+ _op_map[circle::BuiltinOperator_SPLIT_V] = make_unique<SplitVPrinter>();
+ _op_map[circle::BuiltinOperator_SQUEEZE] = make_unique<SqueezePrinter>();
+ _op_map[circle::BuiltinOperator_STRIDED_SLICE] = make_unique<StridedSlicePrinter>();
+ _op_map[circle::BuiltinOperator_SUB] = make_unique<SubPrinter>();
+ _op_map[circle::BuiltinOperator_SUM] = make_unique<ReducerPrinter>();
+ _op_map[circle::BuiltinOperator_TRANSPOSE_CONV] = make_unique<TransposeConvPrinter>();
+ // There is no Option for TOPK_V2
+ _op_map[circle::BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM] =
+ make_unique<UnidirectionalSequenceLSTMPrinter>();
+ _op_map[circle::BuiltinOperator_UNIQUE] = make_unique<UniquePrinter>();
+ _op_map[circle::BuiltinOperator_WHILE] = make_unique<WhilePrinter>();
+ _op_map[circle::BuiltinOperator_CUSTOM] = make_unique<CustomOpPrinter>();
+
+ // Circle only
+ _op_map[circle::BuiltinOperator_BCQ_FULLY_CONNECTED] = make_unique<BCQFullyConnectedPrinter>();
+ _op_map[circle::BuiltinOperator_BCQ_GATHER] = make_unique<BCQGatherPrinter>();
+}
+
+} // namespace circledump
diff --git a/compiler/circledump/src/OpPrinter.h b/compiler/circledump/src/OpPrinter.h
new file mode 100644
index 000000000..6b978a4c7
--- /dev/null
+++ b/compiler/circledump/src/OpPrinter.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLEDUMP_OPPRINTER_H__
+#define __CIRCLEDUMP_OPPRINTER_H__
+
+#include <mio/circle/schema_generated.h>
+
+#include <ostream>
+#include <map>
+
+namespace circledump
+{
+
+class OpPrinter
+{
+public:
+ virtual void options(const circle::Operator *, std::ostream &) const {};
+};
+
+class OpPrinterRegistry
+{
+public:
+ OpPrinterRegistry();
+
+public:
+ const OpPrinter *lookup(circle::BuiltinOperator op) const
+ {
+ if (_op_map.find(op) == _op_map.end())
+ return nullptr;
+
+ return _op_map.at(op).get();
+ }
+
+public:
+ static OpPrinterRegistry &get()
+ {
+ static OpPrinterRegistry me;
+ return me;
+ }
+
+private:
+ std::map<circle::BuiltinOperator, std::unique_ptr<OpPrinter>> _op_map;
+};
+
+} // namespace circledump
+
+#endif // __CIRCLEDUMP_OPPRINTER_H__
diff --git a/compiler/circledump/src/Read.cpp b/compiler/circledump/src/Read.cpp
new file mode 100644
index 000000000..053225536
--- /dev/null
+++ b/compiler/circledump/src/Read.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Read.h"
+
+#include <sstream>
+#include <string>
+
+namespace circleread
+{
+
+bool is_valid(const circle::OperatorCode *opcode)
+{
+ circle::BuiltinOperator code = opcode->builtin_code();
+ return (circle::BuiltinOperator_MIN <= code && code <= circle::BuiltinOperator_MAX);
+}
+
+bool is_custom(const circle::OperatorCode *opcode)
+{
+ circle::BuiltinOperator code = opcode->builtin_code();
+ 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 = opcode->builtin_code();
+ 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)
+{
+ static const char *kEmptyTensorName = "(noname)";
+
+ auto name = tensor->name();
+ if (name)
+ return name->c_str();
+
+ return kEmptyTensorName;
+}
+
+Reader::Reader(const circle::Model *model)
+{
+ _version = model->version();
+ _subgraphs = model->subgraphs();
+ _buffers = model->buffers();
+
+ auto opcodes = model->operator_codes();
+ for (const ::circle::OperatorCode *opcode : *opcodes)
+ {
+ _op_codes.push_back(opcode);
+ }
+}
+
+size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data)
+{
+ *buff_data = nullptr;
+
+ if (buf_idx == 0)
+ return 0;
+
+ if (auto *buffer = (*_buffers)[buf_idx])
+ {
+ if (auto *array = buffer->data())
+ {
+ if (size_t size = array->size())
+ {
+ *buff_data = reinterpret_cast<const uint8_t *>(array->data());
+ return size;
+ }
+ }
+ }
+
+ return 0;
+}
+
+circle::BuiltinOperator Reader::builtin_code(const circle::Operator *op) const
+{
+ uint32_t index = op->opcode_index();
+ assert(index < _op_codes.size());
+ const circle::OperatorCode *opcode = _op_codes.at(index);
+
+ return opcode->builtin_code();
+}
+
+std::string Reader::opcode_name(const circle::Operator *op) const
+{
+ uint32_t index = op->opcode_index();
+ assert(index < _op_codes.size());
+ const circle::OperatorCode *opcode = _op_codes.at(index);
+
+ if (!is_valid(opcode))
+ {
+ std::ostringstream oss;
+ oss << "(invalid: " << index << ")";
+ return oss.str();
+ }
+
+ return circleread::opcode_name(opcode);
+}
+
+bool Reader::select_subgraph(uint32_t sgindex)
+{
+ _subgraph_index = sgindex;
+ _tensors = nullptr;
+ _operators = nullptr;
+
+ _inputs.clear();
+ _outputs.clear();
+
+ if (_subgraphs->Length() <= sgindex)
+ {
+ assert(false);
+ return false;
+ }
+
+ const circle::SubGraph *subgraph = (*_subgraphs)[sgindex];
+
+ auto name = subgraph->name();
+ _subgraph_name = name ? name->c_str() : "(noname)";
+
+ _tensors = subgraph->tensors();
+ _operators = subgraph->operators();
+ _data_format = subgraph->data_format();
+
+ _inputs = as_index_vector(subgraph->inputs());
+ _outputs = as_index_vector(subgraph->outputs());
+
+ return true;
+}
+
+} // namespace circleread
diff --git a/compiler/circledump/src/Read.h b/compiler/circledump/src/Read.h
new file mode 100644
index 000000000..dd1ef20b6
--- /dev/null
+++ b/compiler/circledump/src/Read.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLEREAD_READ_H__
+#define __CIRCLEREAD_READ_H__
+
+#include <mio/circle/schema_generated.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace circleread
+{
+
+template <typename T> std::vector<T> as_index_vector(const flatbuffers::Vector<T> *flat_array)
+{
+ std::vector<T> ret(flat_array->Length());
+ for (uint32_t i = 0; i < flat_array->Length(); i++)
+ {
+ ret[i] = flat_array->Get(i);
+ }
+ return ret;
+}
+
+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);
+
+/**
+ * @brief Loads Circle file and provides helpers to access attributes
+ */
+class Reader
+{
+private:
+ using CircleSubGraphs_t = flatbuffers::Vector<flatbuffers::Offset<circle::SubGraph>>;
+ using CircleBuffers_t = flatbuffers::Vector<flatbuffers::Offset<circle::Buffer>>;
+ using CircleTensors_t = flatbuffers::Vector<flatbuffers::Offset<circle::Tensor>>;
+ using CircleOperators_t = flatbuffers::Vector<flatbuffers::Offset<circle::Operator>>;
+
+public:
+ Reader(const circle::Model *model);
+
+ Reader() = delete;
+
+public:
+ uint32_t version() const { return _version; }
+
+ const std::vector<const circle::OperatorCode *> &opcodes() { return _op_codes; }
+ const CircleBuffers_t *buffers() { return _buffers; }
+ const CircleTensors_t *tensors() { return _tensors; }
+ const CircleOperators_t *operators() { return _operators; }
+ const std::vector<int32_t> &inputs() const { return _inputs; }
+ const std::vector<int32_t> &outputs() const { return _outputs; }
+ const circle::DataFormat &data_format() const { return _data_format; }
+
+ uint32_t num_subgraph() const { return _subgraphs->Length(); }
+
+ size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data);
+ circle::BuiltinOperator builtin_code(const circle::Operator *op) const;
+ std::string opcode_name(const circle::Operator *op) const;
+
+public:
+ bool select_subgraph(uint32_t subgraph);
+ const std::string &subgraph_name(void) const { return _subgraph_name; }
+ uint32_t subgraph_index(void) const { return _subgraph_index; }
+
+private:
+ uint32_t _version;
+
+ const CircleSubGraphs_t *_subgraphs{nullptr};
+ const CircleBuffers_t *_buffers{nullptr};
+ const CircleTensors_t *_tensors{nullptr};
+ const CircleOperators_t *_operators{nullptr};
+
+ uint32_t _subgraph_index;
+ std::string _subgraph_name;
+ std::vector<const circle::OperatorCode *> _op_codes;
+ std::vector<int32_t> _inputs;
+ std::vector<int32_t> _outputs;
+ circle::DataFormat _data_format;
+};
+
+} // namespace circleread
+
+#endif // __CIRCLEREAD_READ_H__
diff --git a/compiler/cli/CMakeLists.txt b/compiler/cli/CMakeLists.txt
index 15055be4a..22948fff9 100644
--- a/compiler/cli/CMakeLists.txt
+++ b/compiler/cli/CMakeLists.txt
@@ -4,7 +4,7 @@ list(APPEND TESTS "src/App.test.cpp")
add_library(cli ${SOURCES})
target_include_directories(cli PUBLIC include)
-nncc_find_package(GTest QUIET)
+nnas_find_package(GTest QUIET)
if(NOT GTest_FOUND)
return()
diff --git a/compiler/coco/core/CMakeLists.txt b/compiler/coco/core/CMakeLists.txt
index d09609659..8c6844733 100644
--- a/compiler/coco/core/CMakeLists.txt
+++ b/compiler/coco/core/CMakeLists.txt
@@ -6,6 +6,7 @@ add_library(coco_core SHARED ${SOURCES})
target_include_directories(coco_core PUBLIC include)
# NOTE Some coco_core PUBLIC headers include angkor headers
target_link_libraries(coco_core PUBLIC angkor)
+target_link_libraries(coco_core PRIVATE pepper_assert)
target_link_libraries(coco_core PRIVATE stdex)
# Let's apply nncc common compile options
# NOTE This will enable strict compilation (warnings as error).
@@ -17,7 +18,7 @@ if(NOT ENABLE_TEST)
endif(NOT ENABLE_TEST)
# Google Test is required for internal testing
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
GTest_AddTest(coco_core_test ${TESTS})
target_link_libraries(coco_core_test coco_core)
diff --git a/compiler/coco/core/src/IR/Conv2D.cpp b/compiler/coco/core/src/IR/Conv2D.cpp
index 58ffff5b7..19395a158 100644
--- a/compiler/coco/core/src/IR/Conv2D.cpp
+++ b/compiler/coco/core/src/IR/Conv2D.cpp
@@ -16,7 +16,7 @@
#include "coco/IR/Ops.h"
-#include <cassert>
+#include <pepper/assert.h>
namespace coco
{
@@ -33,7 +33,7 @@ uint32_t Conv2D::arity(void) const
return 1;
}
-Op *Conv2D::arg(uint32_t n) const
+Op *Conv2D::arg(DBGARG(uint32_t, n)) const
{
assert(n < arity());
return arg();
diff --git a/compiler/coco/core/src/IR/Op.cpp b/compiler/coco/core/src/IR/Op.cpp
index bcc65cf34..d3808a9d6 100644
--- a/compiler/coco/core/src/IR/Op.cpp
+++ b/compiler/coco/core/src/IR/Op.cpp
@@ -18,7 +18,7 @@
#include "coco/IR/Step.h"
#include "coco/IR/Part.h"
-#include <cassert>
+#include <pepper/assert.h>
namespace coco
{
@@ -73,7 +73,7 @@ uint32_t UnaryOp::arity(void) const
return 1;
}
-Op *UnaryOp::arg(uint32_t n) const
+Op *UnaryOp::arg(DBGARG(uint32_t, n)) const
{
assert(n < 1);
return arg();
diff --git a/compiler/coco/generic/CMakeLists.txt b/compiler/coco/generic/CMakeLists.txt
index e02bd47c4..02fbf67f5 100644
--- a/compiler/coco/generic/CMakeLists.txt
+++ b/compiler/coco/generic/CMakeLists.txt
@@ -13,7 +13,7 @@ if(NOT ENABLE_TEST)
endif(NOT ENABLE_TEST)
# Google Test is required for internal testing
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
GTest_AddTest(coco_generic_test ${TESTS})
target_link_libraries(coco_generic_test coco_generic)
diff --git a/compiler/common-artifacts/CMakeLists.txt b/compiler/common-artifacts/CMakeLists.txt
new file mode 100644
index 000000000..ec9e3cf85
--- /dev/null
+++ b/compiler/common-artifacts/CMakeLists.txt
@@ -0,0 +1,278 @@
+#[[ Generate common python virtual enviornment ]]
+find_package(PythonInterp 3 QUIET)
+find_package(PythonLibs 3 QUIET)
+
+if(NOT ${PYTHONINTERP_FOUND})
+ message(STATUS "Build common-artifacts: FALSE (Python3 is missing)")
+ return()
+endif()
+
+if(${PYTHON_VERSION_MINOR} LESS 3)
+ message(STATUS "Build common-artifacts: FALSE (You need to install Python version higher than 3.3)")
+ return()
+endif()
+
+# Create python virtual environment with tensorflow 1.13.2
+set(VIRTUALENV_OVERLAY_TF_1_13_2 "${NNCC_OVERLAY_DIR}/venv_1_13_2")
+
+# Create python virtual environment with tensorflow 2.3.0
+set(VIRTUALENV_OVERLAY_TF_2_3_0 "${NNCC_OVERLAY_DIR}/venv_2_3_0")
+
+add_custom_command(
+ OUTPUT ${VIRTUALENV_OVERLAY_TF_1_13_2}
+ COMMAND ${PYTHON_EXECUTABLE} -m venv ${VIRTUALENV_OVERLAY_TF_1_13_2}
+)
+
+add_custom_command(
+ OUTPUT ${VIRTUALENV_OVERLAY_TF_2_3_0}
+ COMMAND ${PYTHON_EXECUTABLE} -m venv ${VIRTUALENV_OVERLAY_TF_2_3_0}
+)
+
+# Create requirements.txt and install required pip packages
+set(REQUIREMENTS_FILE "requirements.txt")
+set(REQUIREMENTS_OVERLAY_PATH_TF_1_13_2 "${VIRTUALENV_OVERLAY_TF_1_13_2}/${REQUIREMENTS_FILE}")
+set(REQUIREMENTS_OVERLAY_PATH_TF_2_3_0 "${VIRTUALENV_OVERLAY_TF_2_3_0}/${REQUIREMENTS_FILE}")
+
+# TODO remove version number of '--upgrade pip==20.2.1 setuptools==49.3.0'
+# NOTE adding version is for temporary hotfix of setuptools 50.x.y version
+add_custom_command(
+ OUTPUT ${REQUIREMENTS_OVERLAY_PATH_TF_1_13_2}
+ COMMAND ${CMAKE_COMMAND} -E echo "tensorflow==1.13.2" > ${REQUIREMENTS_OVERLAY_PATH_TF_1_13_2}
+ COMMAND ${VIRTUALENV_OVERLAY_TF_1_13_2}/bin/python -m pip --default-timeout=1000 install --upgrade pip==20.2.1 setuptools==49.3.0
+ COMMAND ${VIRTUALENV_OVERLAY_TF_1_13_2}/bin/python -m pip --default-timeout=1000 install -r ${REQUIREMENTS_OVERLAY_PATH_TF_1_13_2} --upgrade
+ DEPENDS ${VIRTUALENV_OVERLAY_TF_1_13_2}
+)
+
+add_custom_command(
+ OUTPUT ${REQUIREMENTS_OVERLAY_PATH_TF_2_3_0}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${REQUIREMENTS_OVERLAY_PATH_TF_2_3_0}
+ COMMAND ${CMAKE_COMMAND} -E echo "tensorflow-cpu==2.3.0" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_3_0}
+ COMMAND ${CMAKE_COMMAND} -E echo "flatbuffers==1.12" >> ${REQUIREMENTS_OVERLAY_PATH_TF_2_3_0}
+ COMMAND ${VIRTUALENV_OVERLAY_TF_2_3_0}/bin/python -m pip --default-timeout=1000 install --upgrade pip==20.2.1 setuptools==49.3.0
+ COMMAND ${VIRTUALENV_OVERLAY_TF_2_3_0}/bin/python -m pip --default-timeout=1000 install -r ${REQUIREMENTS_OVERLAY_PATH_TF_2_3_0} --upgrade
+ DEPENDS ${VIRTUALENV_OVERLAY_TF_2_3_0}
+)
+
+add_custom_target(common_artifacts_python_deps ALL
+ DEPENDS ${VIRTUALENV_OVERLAY_TF_1_13_2} ${VIRTUALENV_OVERLAY_TF_2_3_0} ${REQUIREMENTS_OVERLAY_PATH_TF_1_13_2} ${REQUIREMENTS_OVERLAY_PATH_TF_2_3_0}
+)
+
+#[[ Generate common resources ]]
+# TODO add pbtxt
+nnas_find_package(HDF5 QUIET)
+
+if(NOT HDF5_FOUND)
+ message(STATUS "Build common-artifacts: FAILED (missing HDF5)")
+ return()
+endif(NOT HDF5_FOUND)
+
+set(SOURCES src/TestDataGenerator.cpp)
+
+add_executable(testDataGenerator ${SOURCES})
+target_include_directories(testDataGenerator PRIVATE ${HDF5_INCLUDE_DIRS})
+target_link_libraries(testDataGenerator PRIVATE ${HDF5_CXX_LIBRARIES})
+target_link_libraries(testDataGenerator PRIVATE arser)
+target_link_libraries(testDataGenerator PRIVATE foder)
+target_link_libraries(testDataGenerator PRIVATE luci_import)
+target_link_libraries(testDataGenerator PRIVATE luci_interpreter)
+target_link_libraries(testDataGenerator PRIVATE mio_circle)
+target_link_libraries(testDataGenerator PRIVATE safemain)
+
+unset(TEST_DEPS)
+
+# Include recipe repo
+nncc_find_resource(TensorFlowLiteRecipes)
+nncc_find_resource(CircleRecipes)
+set(TFLITE_RECIPE_REPO "${TensorFlowLiteRecipes_DIR}")
+set(CIRCLE_RECIPE_REPO "${CircleRecipes_DIR}")
+set(TEST_RECIPE_FILENAME "test.recipe")
+set(TEST_RULE_FILENAME "test.rule")
+
+set(MODEL2NNPKG "${NNAS_PROJECT_SOURCE_DIR}/tools/nnpackage_tool/model2nnpkg/model2nnpkg.sh")
+# Get test case list
+unset(RECIPES)
+file(GLOB TFLITE_SUBDIR RELATIVE ${TFLITE_RECIPE_REPO} ${TFLITE_RECIPE_REPO}/*)
+foreach(DIR IN ITEMS ${TFLITE_SUBDIR})
+ if(IS_DIRECTORY ${TFLITE_RECIPE_REPO}/${DIR})
+ list(APPEND RECIPES ${DIR})
+ endif()
+endforeach()
+file(GLOB CIRCLE_SUBDIR RELATIVE ${CIRCLE_RECIPE_REPO} ${CIRCLE_RECIPE_REPO}/*)
+foreach(DIR IN ITEMS ${CIRCLE_SUBDIR})
+ if(IS_DIRECTORY ${CIRCLE_RECIPE_REPO}/${DIR})
+ list(APPEND RECIPES ${DIR})
+ endif()
+endforeach()
+
+macro(circlize NAME)
+ set(NO_CIRCLIZE_${NAME} TRUE)
+ set(NO_OPTIMIZE_${NAME} TRUE)
+ set(NO_TCGEN_${NAME} TRUE)
+endmacro()
+macro(optimize NAME)
+ set(NO_OPTIMIZE_${NAME} TRUE)
+endmacro()
+macro(tcgenerate NAME)
+ set(NO_TCGEN_${NAME} TRUE)
+endmacro()
+
+include("exclude.lst")
+
+foreach(RECIPE IN ITEMS ${RECIPES})
+ unset(OPT_FORMAT)
+ unset(MODEL_FORMAT)
+
+ set(RECIPE_FILE "${RECIPE}.recipe")
+ set(RULE_FILE "${RECIPE}.rule")
+ set(TFLITE_RECIPE_SOURCE_PATH "${TFLITE_RECIPE_REPO}/${RECIPE}/${TEST_RECIPE_FILENAME}")
+ set(CIRCLE_RECIPE_SOURCE_PATH "${CIRCLE_RECIPE_REPO}/${RECIPE}/${TEST_RECIPE_FILENAME}")
+
+ if(NOT EXISTS "${TFLITE_RECIPE_SOURCE_PATH}")
+ if(NOT EXISTS "${CIRCLE_RECIPE_SOURCE_PATH}")
+ message(FATAL_ERROR "Missing recipe of '${RECIPE}' test")
+ else()
+ # circle recipe
+ set(MODEL_FORMAT "circle")
+ set(RECIPE_SOURCE_PATH ${CIRCLE_RECIPE_SOURCE_PATH})
+ endif()
+ else()
+ # tflite recipe
+ set(MODEL_FORMAT "tflite")
+ set(RECIPE_SOURCE_PATH ${TFLITE_RECIPE_SOURCE_PATH})
+ endif()
+
+ set(TFLITE_RULE_SOURCE_PATH "${TFLITE_RECIPE_REPO}/${RECIPE}/${TEST_RULE_FILENAME}")
+ set(CIRCLE_RULE_SOURCE_PATH "${CIRCLE_RECIPE_REPO}/${RECIPE}/${TEST_RULE_FILENAME}")
+
+ unset(RULE_SOURCE_PATH)
+ if(EXISTS "${TFLITE_RULE_SOURCE_PATH}")
+ set(RULE_SOURCE_PATH ${TFLITE_RULE_SOURCE_PATH})
+ endif()
+ if(EXISTS "${CIRCLE_RULE_SOURCE_PATH}")
+ set(RULE_SOURCE_PATH ${CIRCLE_RULE_SOURCE_PATH})
+ endif()
+
+ set(RECIPE_BINARY_PATH "${CMAKE_CURRENT_BINARY_DIR}/${RECIPE_FILE}")
+ set(RULE_BINARY_PATH "${CMAKE_CURRENT_BINARY_DIR}/${RULE_FILE}")
+
+ set(TFLITE_FILE "${RECIPE}.tflite")
+ set(TFLITE_OUTPUT_PATH "${CMAKE_CURRENT_BINARY_DIR}/${TFLITE_FILE}")
+ set(CIRCLE_FILE "${RECIPE}.circle")
+ set(CIRCLE_OUTPUT_PATH "${CMAKE_CURRENT_BINARY_DIR}/${CIRCLE_FILE}")
+
+ # Copy .recipe
+ add_custom_command(OUTPUT ${RECIPE_BINARY_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${RECIPE_SOURCE_PATH}" "${RECIPE_BINARY_PATH}"
+ DEPENDS ${RECIPE_SOURCE_PATH}
+ COMMENT "Generate ${RECIPE_FILE}"
+ )
+ list(APPEND TEST_DEPS ${RECIPE_BINARY_PATH})
+
+ if(DEFINED RULE_SOURCE_PATH)
+ # Copy .rule
+ add_custom_command(OUTPUT ${RULE_BINARY_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${RULE_SOURCE_PATH}" "${RULE_BINARY_PATH}"
+ DEPENDS ${RULE_SOURCE_PATH}
+ COMMENT "Generate ${RULE_FILE}"
+ )
+ list(APPEND TEST_DEPS ${RULE_BINARY_PATH})
+ endif()
+
+ if(${MODEL_FORMAT} STREQUAL "tflite")
+ # Generate .tflite
+ add_custom_command(OUTPUT ${TFLITE_OUTPUT_PATH}
+ COMMAND $<TARGET_FILE:tflchef-file> ${RECIPE_BINARY_PATH} ${TFLITE_OUTPUT_PATH}
+ DEPENDS $<TARGET_FILE:tflchef-file> ${RECIPE_BINARY_PATH}
+ COMMENT "Generate ${TFLITE_FILE}"
+ )
+ list(APPEND TEST_DEPS ${TFLITE_OUTPUT_PATH})
+
+ if(NOT DEFINED NO_CIRCLIZE_${RECIPE})
+ # Generate .circle
+ add_custom_command(OUTPUT ${CIRCLE_OUTPUT_PATH}
+ COMMAND $<TARGET_FILE:tflite2circle> ${TFLITE_OUTPUT_PATH} ${CIRCLE_OUTPUT_PATH}
+ DEPENDS $<TARGET_FILE:tflite2circle> ${TFLITE_OUTPUT_PATH}
+ COMMENT "Generate ${CIRCLE_FILE}"
+ )
+ set(MODEL_FORMAT "circle")
+ list(APPEND TEST_DEPS ${CIRCLE_OUTPUT_PATH})
+ endif()
+ else()
+ # Generate .circle
+ add_custom_command(OUTPUT ${CIRCLE_OUTPUT_PATH}
+ COMMAND $<TARGET_FILE:circlechef-file> ${RECIPE_BINARY_PATH} ${CIRCLE_OUTPUT_PATH}
+ DEPENDS $<TARGET_FILE:circlechef-file> ${RECIPE_BINARY_PATH}
+ COMMENT "Generate ${CIRCLE_FILE}"
+ )
+ list(APPEND TEST_DEPS ${CIRCLE_OUTPUT_PATH})
+ endif()
+
+ set(OPT_CIRCLE_FILE "${RECIPE}.opt.circle")
+ set(OPT_CIRCLE_OUTPUT_PATH "${CMAKE_CURRENT_BINARY_DIR}/${OPT_CIRCLE_FILE}")
+
+ if(NOT DEFINED NO_OPTIMIZE_${RECIPE})
+ # Generate optimized .circle
+ add_custom_command(OUTPUT ${OPT_CIRCLE_OUTPUT_PATH}
+ COMMAND $<TARGET_FILE:circle2circle> --all ${CIRCLE_OUTPUT_PATH} ${OPT_CIRCLE_OUTPUT_PATH}
+ DEPENDS $<TARGET_FILE:circle2circle> ${CIRCLE_OUTPUT_PATH}
+ COMMENT "Generate ${OPT_CIRCLE_FILE}"
+ )
+ set(OPT_FORMAT ".opt")
+ list(APPEND TEST_DEPS ${OPT_CIRCLE_OUTPUT_PATH})
+ endif()
+
+ set(MODEL_FILE "${RECIPE}${OPT_FORMAT}.${MODEL_FORMAT}")
+ set(MODEL_PATH "${CMAKE_CURRENT_BINARY_DIR}/${MODEL_FILE}")
+ set(NNPKG_FILE "${RECIPE}${OPT_FORMAT}")
+ set(NNPKG_PATH "${CMAKE_CURRENT_BINARY_DIR}/${NNPKG_FILE}")
+
+ add_custom_command(OUTPUT ${NNPKG_PATH}
+ COMMAND ${MODEL2NNPKG} ${MODEL_PATH}
+ DEPENDS ${MODEL2NNPKG} ${MODEL_PATH}
+ COMMENT "Generate ${RECIPE} nnpackage"
+ )
+ list(APPEND TEST_DEPS ${NNPKG_PATH})
+
+ set(INPUT_HDF5_FILE "${RECIPE}${OPT_FORMAT}.input.h5")
+ set(INPUT_BIN_PATH "${CMAKE_CURRENT_BINARY_DIR}/${INPUT_HDF5_FILE}")
+
+ set(EXPECTED_HDF5_FILE "${RECIPE}${OPT_FORMAT}.expected.h5")
+ set(EXPECTED_BIN_PATH "${CMAKE_CURRENT_BINARY_DIR}/${EXPECTED_HDF5_FILE}")
+
+ if(NOT DEFINED NO_TCGEN_${RECIPE})
+ # Generate input.h5, expected.h5
+ add_custom_command(OUTPUT ${INPUT_BIN_PATH} ${EXPECTED_BIN_PATH}
+ COMMAND $<TARGET_FILE:testDataGenerator> ${MODEL_FILE}
+ DEPENDS $<TARGET_FILE:testDataGenerator> ${MODEL_FILE}
+ COMMENT "Generate ${INPUT_BIN_PATH} and ${EXPECTED_BIN_PATH}"
+ )
+
+ # Generate test directory
+ set(TC_DIRECTORY "${NNPKG_PATH}/metadata/tc")
+ add_custom_command(OUTPUT ${TC_DIRECTORY}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${TC_DIRECTORY}
+ DEPENDS ${NNPKG_PATH}
+ COMMENT "Generate ${RECIPE} nnpackage test directory"
+ )
+
+ # Move input hdf5 file to test directory
+ set(INPUT_NNPKG_PATH "${TC_DIRECTORY}/input.h5")
+ add_custom_command(OUTPUT ${INPUT_NNPKG_PATH}
+ COMMAND ${CMAKE_COMMAND} -E rename ${INPUT_BIN_PATH} ${INPUT_NNPKG_PATH}
+ DEPENDS ${INPUT_BIN_PATH} ${TC_DIRECTORY}
+ COMMENT "Move ${INPUT_HDF5_FILE} to nnpackage"
+ )
+
+ # Move expected hdf5 file to test directory
+ set(EXPECTED_NNPKG_PATH "${TC_DIRECTORY}/expected.h5")
+ add_custom_command(OUTPUT ${EXPECTED_NNPKG_PATH}
+ COMMAND ${CMAKE_COMMAND} -E rename ${EXPECTED_BIN_PATH} ${EXPECTED_NNPKG_PATH}
+ DEPENDS ${EXPECTED_BIN_PATH} ${TC_DIRECTORY}
+ COMMENT "Move ${EXPECTED_HDF5_FILE} to nnpackage"
+ )
+ list(APPEND TEST_DEPS ${TC_DIRECTORY} ${INPUT_BIN_PATH} ${EXPECTED_BIN_PATH}
+ ${INPUT_NNPKG_PATH} ${EXPECTED_NNPKG_PATH})
+ endif()
+endforeach()
+
+add_custom_target(common_artifacts_deps ALL DEPENDS ${TEST_DEPS})
diff --git a/compiler/common-artifacts/README.md b/compiler/common-artifacts/README.md
new file mode 100644
index 000000000..cb6ba402e
--- /dev/null
+++ b/compiler/common-artifacts/README.md
@@ -0,0 +1,37 @@
+# common-artifacts
+
+`common-artifacts` is a module that produces intermediate artifacts that are commonly generated for compiler testing.
+
+There are four modules used here.
+- tflchef : recipe -> tflite
+- tflite2circle : tflite -> circle (circlize)
+- circle2circle : circle -> circle (optimize)
+- TestDataGenerator : generate input.h5 and expected.h5 (tcgenerate)
+
+## List of intermediate artifacts
+- recipe
+- tflite
+- circle
+- circle (applied all optimizations in circle2circle)
+- input data for nnpackage (.h5)
+- expected output data for nnpackage (.h5)
+
+## How to exclude from resource generation
+Sometimes a specific module that generates a resource does not support the generation of the resource, so exclusion is sometimes required.
+
+There is a `exclude.lst` that performs the function. If you enter the name of steps(circlize, optimize, tcgenerate) and operator you want to exclude there, you can omit the module's step.
+
+e.g.
+```
+$ cat exclude.lst
+# circlize : Exclude from tflite-to-circle conversion(tflite2circle)
+
+# optimize : Exclude from circle optimization(circle2circle)
+optimize(ReLU6_000)
+optimize(Where_000)
+optimize(Where_001)
+
+# tcgenerate : Exclude from test data generation(TestDataGenerator)
+tcgenerate(Abs_000)
+tcgenerate(AddN_000)
+```
diff --git a/compiler/common-artifacts/exclude.lst b/compiler/common-artifacts/exclude.lst
new file mode 100644
index 000000000..b2abfd583
--- /dev/null
+++ b/compiler/common-artifacts/exclude.lst
@@ -0,0 +1,186 @@
+#[[ circlize : Exclude from tflite-to-circle conversion(tflite2circle) ]]
+## TensorFlowLiteRecipes
+
+## CircleRecipes
+
+#[[ optimize : Exclude from circle optimization(circle2circle) ]]
+## TensorFlowLiteRecipes
+optimize(UnidirectionalSequenceLSTM_001) # This recipe contains is_variable Tensor
+
+## CircleRecipes
+
+#[[ tcgenerate : Exclude from test data generation(TestDataGenerator) ]]
+## TensorFlowLiteRecipes
+tcgenerate(Abs_000)
+tcgenerate(AddN_000)
+tcgenerate(Add_001) # runtime doesn't support
+tcgenerate(Add_U8_000)
+tcgenerate(All_000)
+tcgenerate(ArgMax_U8_000)
+tcgenerate(ArgMax_U8_001)
+tcgenerate(ArgMax_U8_002)
+tcgenerate(ArgMax_U8_003)
+tcgenerate(ArgMin_000)
+tcgenerate(ArgMin_001)
+tcgenerate(ArgMin_002)
+tcgenerate(ArgMin_003)
+tcgenerate(ArgMin_U8_000)
+tcgenerate(ArgMin_U8_001)
+tcgenerate(ArgMin_U8_002)
+tcgenerate(ArgMin_U8_003)
+tcgenerate(BatchMatMul_000)
+tcgenerate(BatchMatMulV2_000)
+tcgenerate(BatchMatMulV2_001)
+tcgenerate(BatchToSpaceND_000)
+tcgenerate(Cast_000)
+tcgenerate(Cast_001)
+tcgenerate(Ceil_000)
+tcgenerate(Concatenation_U8_000)
+tcgenerate(Conv2D_003) # runtime doesn't support dilation
+tcgenerate(Conv2D_U8_000)
+tcgenerate(Conv2D_U8_001)
+tcgenerate(Cos_000)
+tcgenerate(DepthwiseConv2D_001) # runtime doesn't support dilation
+tcgenerate(DepthwiseConv2D_003) # runtime doesn't support dilation
+tcgenerate(DepthwiseConv2D_U8_000)
+tcgenerate(DepthwiseConv2D_U8_001) # luci-interpreter doesn't support channel-wise quantization yet
+tcgenerate(Dequantize_000) # runtime and luci-interpreter doesn't support Dequantize op yet
+tcgenerate(Div_000)
+tcgenerate(Equal_000)
+tcgenerate(Exp_000)
+tcgenerate(ExpandDims_000)
+tcgenerate(ExpandDims_001)
+tcgenerate(ExpandDims_002)
+tcgenerate(ExpandDims_003)
+tcgenerate(Fill_000)
+tcgenerate(Fill_001)
+tcgenerate(Floor_000)
+tcgenerate(FloorDiv_000)
+tcgenerate(FloorDiv_001)
+tcgenerate(FloorMod_000)
+tcgenerate(FloorMod_001)
+tcgenerate(FullyConnected_002)
+tcgenerate(FullyConnected_U8_000)
+tcgenerate(Gather_000)
+tcgenerate(GatherNd_000)
+tcgenerate(GatherNd_001)
+tcgenerate(Greater_000)
+tcgenerate(GreaterEqual_000)
+tcgenerate(If_000)
+tcgenerate(If_001)
+tcgenerate(L2Pool2D_U8_000)
+tcgenerate(Less_000)
+tcgenerate(LessEqual_000)
+tcgenerate(Log_000)
+tcgenerate(LogicalAnd_000)
+tcgenerate(LogicalNot_000)
+tcgenerate(LogicalOr_000)
+tcgenerate(LogSoftmax_000)
+tcgenerate(MatMul_000)
+tcgenerate(MatrixBandPart_000)
+tcgenerate(MatrixDiag_000)
+tcgenerate(MatrixSetDiag_000)
+tcgenerate(Maximum_000)
+tcgenerate(MaxPool2D_U8_000)
+tcgenerate(MaxPoolWithArgMax_000)
+tcgenerate(MaxPoolWithArgMax_001)
+tcgenerate(MaxPoolWithArgMax_002)
+tcgenerate(Mean_U8_000)
+tcgenerate(Minimum_000)
+tcgenerate(NonMaxSuppressionV4_000)
+tcgenerate(NonMaxSuppressionV4_001)
+tcgenerate(NonMaxSuppressionV5_000)
+tcgenerate(NonMaxSuppressionV5_001)
+tcgenerate(MirrorPad_000)
+tcgenerate(Mul_U8_000)
+tcgenerate(Neg_000)
+tcgenerate(Net_Dangle_001)
+tcgenerate(Net_InstanceNorm_001)
+tcgenerate(Net_InstanceNorm_002)
+tcgenerate(Net_InstanceNorm_003)
+tcgenerate(Net_ZeroDim_001) # luci-interpreter doesn't support zero dim
+tcgenerate(NotEqual_000)
+tcgenerate(OneHot_000)
+tcgenerate(OneHot_001)
+tcgenerate(OneHot_002)
+tcgenerate(OneHot_003)
+tcgenerate(Pack_000)
+tcgenerate(Pack_U8_000)
+tcgenerate(Pad_U8_000)
+tcgenerate(PadV2_000)
+tcgenerate(Pow_000)
+tcgenerate(Range_000)
+tcgenerate(Rank_000)
+tcgenerate(ReduceAny_000)
+tcgenerate(ReduceAny_001)
+tcgenerate(ReduceAny_002)
+tcgenerate(ReduceAny_003)
+tcgenerate(ReduceMax_000)
+tcgenerate(ReduceMin_000)
+tcgenerate(ReduceProd_000)
+tcgenerate(ReduceProd_001)
+tcgenerate(ReduceProd_002)
+tcgenerate(ReduceProd_003)
+tcgenerate(ReLU_000)
+tcgenerate(ReLU6_000)
+tcgenerate(ReLUN1To1_000)
+tcgenerate(Reshape_003) # luci-interpreter doesn't support reshape without built-in option
+tcgenerate(Reshape_U8_000)
+tcgenerate(ResizeBilinear_000)
+tcgenerate(ResizeBilinear_U8_000) # luci-interpreter
+tcgenerate(ResizeNearestNeighbor_000)
+tcgenerate(ReverseSequence_000)
+tcgenerate(ReverseV2_000)
+tcgenerate(Round_000)
+tcgenerate(ScatterNd_000)
+tcgenerate(SegmentSum_000)
+tcgenerate(Select_000)
+tcgenerate(Select_001)
+tcgenerate(Select_002)
+tcgenerate(SelectV2_000)
+tcgenerate(SelectV2_001)
+tcgenerate(SelectV2_002)
+tcgenerate(Shape_000)
+tcgenerate(Sin_000)
+tcgenerate(Softmax_U8_000)
+tcgenerate(SpaceToBatchND_000)
+tcgenerate(SpaceToBatchND_001)
+tcgenerate(SpaceToBatchND_002)
+tcgenerate(SpaceToBatchND_003)
+tcgenerate(SparseToDense_000)
+tcgenerate(SplitV_000)
+tcgenerate(Square_000)
+tcgenerate(SquaredDifference_000)
+tcgenerate(Sub_000)
+tcgenerate(Sub_001)
+tcgenerate(Sub_U8_000)
+tcgenerate(Sum_000)
+tcgenerate(Sum_001)
+tcgenerate(Tile_000)
+tcgenerate(Tile_U8_000)
+tcgenerate(TopKV2_000)
+tcgenerate(TopKV2_001)
+tcgenerate(UnidirectionalSequenceLSTM_000) # runtime and luci-interpreter doesn't support UnidirectionalSequenceLSTM op yet
+tcgenerate(UnidirectionalSequenceLSTM_001) # runtime and luci-interpreter doesn't support UnidirectionalSequenceLSTM op yet
+tcgenerate(Unique_000)
+tcgenerate(Unique_001)
+tcgenerate(Unique_002)
+tcgenerate(Unique_003)
+tcgenerate(Unique_U8_000)
+tcgenerate(Unique_U8_001)
+tcgenerate(Where_000)
+tcgenerate(Where_001)
+tcgenerate(While_000)
+tcgenerate(While_001)
+tcgenerate(While_002)
+tcgenerate(While_003)
+tcgenerate(YUV_TO_RGB_000)
+tcgenerate(YUV_TO_RGB_U8_000)
+tcgenerate(ZerosLike_000)
+
+## CircleRecipes
+tcgenerate(BCQFullyConnected_000)
+tcgenerate(BCQFullyConnected_001)
+tcgenerate(BCQGather_000)
+tcgenerate(CircleBatchMatMul_000)
+tcgenerate(InstanceNorm_000)
diff --git a/compiler/common-artifacts/requires.cmake b/compiler/common-artifacts/requires.cmake
new file mode 100644
index 000000000..d7bed21fe
--- /dev/null
+++ b/compiler/common-artifacts/requires.cmake
@@ -0,0 +1,9 @@
+require("arser")
+require("circle2circle")
+require("circlechef")
+require("foder")
+require("luci")
+require("luci-interpreter")
+require("mio-circle")
+require("safemain")
+require("tflchef")
diff --git a/compiler/common-artifacts/src/TestDataGenerator.cpp b/compiler/common-artifacts/src/TestDataGenerator.cpp
new file mode 100644
index 000000000..f8f014442
--- /dev/null
+++ b/compiler/common-artifacts/src/TestDataGenerator.cpp
@@ -0,0 +1,258 @@
+/*
+ * 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 <arser/arser.h>
+#include <foder/FileLoader.h>
+#include <luci/Importer.h>
+#include <luci_interpreter/Interpreter.h>
+#include <mio/circle/schema_generated.h>
+
+#include <H5Cpp.h>
+
+#include <algorithm>
+#include <iostream>
+#include <memory>
+#include <random>
+#include <string>
+
+namespace
+{
+
+uint32_t element_num(std::vector<hsize_t> &vec)
+{
+ return static_cast<uint32_t>(
+ std::accumulate(std::begin(vec), std::end(vec), 1, std::multiplies<uint32_t>()));
+}
+
+H5::PredType hdf5_dtype_cast(const loco::DataType loco_dtype)
+{
+ switch (loco_dtype)
+ {
+ case loco::DataType::U8:
+ return H5::PredType::NATIVE_UINT8;
+ case loco::DataType::S32:
+ return H5::PredType::NATIVE_INT32;
+ case loco::DataType::S64:
+ return H5::PredType::NATIVE_INT64;
+ case loco::DataType::FLOAT32:
+ return H5::PredType::NATIVE_FLOAT;
+ case loco::DataType::BOOL:
+ return H5::PredType::NATIVE_HBOOL;
+ default:
+ throw std::runtime_error("NYI data type.");
+ }
+}
+
+template <typename T> void geneate_random_data(std::mt19937 &gen, void *data, uint32_t size)
+{
+ std::normal_distribution<float> distrib(0, 2); // mean(0), stddev(2)
+ for (uint32_t i = 0; i < size; i++)
+ {
+ static_cast<T *>(data)[i] = static_cast<T>(distrib(gen));
+ }
+}
+
+void fill_random_data(void *data, uint32_t size, loco::DataType dtype, uint32_t seed)
+{
+ std::mt19937 gen(seed); // standard mersenne_twister_engine seeded with rd()
+
+ switch (dtype)
+ {
+ case loco::DataType::U8:
+ geneate_random_data<uint8_t>(gen, data, size);
+ break;
+ case loco::DataType::S32:
+ geneate_random_data<int32_t>(gen, data, size);
+ break;
+ case loco::DataType::S64:
+ geneate_random_data<int64_t>(gen, data, size);
+ break;
+ case loco::DataType::FLOAT32:
+ geneate_random_data<float>(gen, data, size);
+ break;
+ default:
+ break;
+ }
+}
+
+} // namespace
+
+int entry(int argc, char **argv)
+{
+ arser::Arser arser;
+ arser.add_argument("circle").type(arser::DataType::STR).help("Circle file you want to test");
+ arser.add_argument("--fixed_seed")
+ .required(false)
+ .nargs(0)
+ .help("Put a fixed seed into the random number generator");
+
+ try
+ {
+ arser.parse(argc, argv);
+ }
+ catch (const std::runtime_error &err)
+ {
+ std::cout << err.what() << std::endl;
+ std::cout << arser;
+ return 255;
+ }
+
+ std::string circle_file = arser.get<std::string>("circle");
+ size_t last_dot_index = circle_file.find_last_of(".");
+ std::string prefix = circle_file.substr(0, last_dot_index);
+
+ // load circle file
+ foder::FileLoader file_loader{circle_file};
+ std::vector<char> model_data = file_loader.load();
+ const circle::Model *circle_model = circle::GetModel(model_data.data());
+ if (circle_model == nullptr)
+ {
+ std::cerr << "ERROR: Failed to load circle '" << circle_file << "'" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // load luci module
+ std::unique_ptr<luci::Module> module = luci::Importer().importModule(circle_model);
+ luci_interpreter::Interpreter interpreter(module.get());
+
+ /**
+ * HDF5 layout is like below
+ *
+ * GROUP "/"
+ * ㄴGROUP "name"
+ * ㄴATTRIBUTE "0"
+ * ㄴDATA (0): "input_01:0"
+ * ㄴATTRIBUTE "1"
+ * ㄴDATA (0): "input_02:0"
+ * ㄴGROUP "value"
+ * ㄴDATASET "0"
+ * ㄴDATA ...
+ * ㄴDATASET "1"
+ * ㄴDATA ...
+ */
+ // create random data and dump into hdf5 file
+ H5::H5File input_file{prefix + ".input.h5", H5F_ACC_TRUNC};
+ std::unique_ptr<H5::Group> input_name_group =
+ std::make_unique<H5::Group>(input_file.createGroup("name"));
+ std::unique_ptr<H5::Group> input_value_group =
+ std::make_unique<H5::Group>(input_file.createGroup("value"));
+
+ H5::H5File output_file{prefix + ".expected.h5", H5F_ACC_TRUNC};
+ std::unique_ptr<H5::Group> output_name_group =
+ std::make_unique<H5::Group>(output_file.createGroup("name"));
+ std::unique_ptr<H5::Group> output_value_group =
+ std::make_unique<H5::Group>(output_file.createGroup("value"));
+
+ std::random_device rd; // used to obtain a seed for the random number engine
+ uint32_t input_index = 0;
+ for (uint32_t g = 0; g < circle_model->subgraphs()->size(); g++)
+ {
+ const auto input_nodes = loco::input_nodes(module->graph(g));
+ for (const auto &node : input_nodes)
+ {
+ const auto *input_node = dynamic_cast<const luci::CircleInput *>(node);
+ std::string name = input_node->name();
+ if (name.find(":") == std::string::npos)
+ name += ":0";
+
+ // create attribute
+ H5::DataSpace name_dataspace(H5S_SCALAR);
+ H5::StrType name_datatype(H5::PredType::C_S1, name.size());
+
+ auto name_attr = input_name_group->createAttribute(std::to_string(input_index), name_datatype,
+ name_dataspace);
+
+ name_attr.write(name_datatype, name);
+
+ // create value
+ std::vector<hsize_t> dims(input_node->rank());
+ for (uint32_t d = 0; d < input_node->rank(); d++)
+ {
+ dims.at(d) = input_node->dim(d).value();
+ assert(dims.at(d) >= 0);
+ }
+ auto dataspace = std::make_unique<H5::DataSpace>(dims.size(), dims.data());
+ auto dtype = hdf5_dtype_cast(input_node->dtype());
+ auto dataset = std::make_unique<H5::DataSet>(
+ input_file.createDataSet("value/" + std::to_string(input_index), dtype, *dataspace));
+
+ auto data_size = ::element_num(dims);
+ auto dtype_size = loco::size(input_node->dtype());
+ auto byte_size = dtype_size * data_size;
+ std::vector<int8_t> data(byte_size);
+
+ // generate random data
+ if (arser["--fixed_seed"])
+ fill_random_data(data.data(), data_size, input_node->dtype(), 0);
+ else
+ fill_random_data(data.data(), data_size, input_node->dtype(), rd());
+
+ dataset->write(data.data(), dtype);
+
+ interpreter.writeInputTensor(input_node, data.data(), byte_size);
+
+ input_index++;
+ }
+ }
+
+ interpreter.interpret();
+
+ // dump output data into hdf5 file
+ uint32_t output_index = 0;
+ for (uint32_t g = 0; g < circle_model->subgraphs()->size(); g++)
+ {
+ const auto output_nodes = loco::output_nodes(module->graph(g));
+ for (const auto &node : output_nodes)
+ {
+ const auto *output_node = dynamic_cast<const luci::CircleOutput *>(node);
+ std::string name = output_node->name();
+ if (name.find(":") == std::string::npos)
+ name += ":0";
+
+ // create attribute
+ H5::DataSpace name_dataspace(H5S_SCALAR);
+ H5::StrType name_datatype(H5::PredType::C_S1, name.size());
+
+ auto name_attr = output_name_group->createAttribute(std::to_string(output_index),
+ name_datatype, name_dataspace);
+
+ name_attr.write(name_datatype, name);
+
+ // create value
+ std::vector<hsize_t> dims(output_node->rank());
+ for (uint32_t d = 0; d < output_node->rank(); d++)
+ {
+ dims.at(d) = output_node->dim(d).value();
+ assert(dims.at(d) >= 0);
+ }
+ auto dataspace = std::make_unique<H5::DataSpace>(dims.size(), dims.data());
+ auto dtype = hdf5_dtype_cast(output_node->dtype());
+ auto dataset = std::make_unique<H5::DataSet>(
+ output_file.createDataSet("value/" + std::to_string(output_index), dtype, *dataspace));
+
+ uint32_t tensor_bytesize = loco::size(output_node->dtype());
+ tensor_bytesize *= ::element_num(dims);
+ std::vector<int8_t> output_data(tensor_bytesize);
+ interpreter.readOutputTensor(output_node, output_data.data(), output_data.size());
+
+ dataset->write(output_data.data(), dtype);
+
+ output_index++;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/compiler/cwrap/CMakeLists.txt b/compiler/cwrap/CMakeLists.txt
index 858b9e3af..e1ae4d0b5 100644
--- a/compiler/cwrap/CMakeLists.txt
+++ b/compiler/cwrap/CMakeLists.txt
@@ -16,7 +16,7 @@ if(NOT ENABLE_TEST)
endif(NOT ENABLE_TEST)
# Google Test is mandatory for testing
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
GTest_AddTest(cwrap_test ${TESTS})
target_link_libraries(cwrap_test cwrap)
diff --git a/compiler/cwrap/include/cwrap/Fildes.h b/compiler/cwrap/include/cwrap/Fildes.h
index f1061cc57..22a95ef7c 100644
--- a/compiler/cwrap/include/cwrap/Fildes.h
+++ b/compiler/cwrap/include/cwrap/Fildes.h
@@ -47,7 +47,7 @@ public:
int release(void);
private:
- int _value;
+ int _value{-1};
};
bool valid(const Fildes &);
diff --git a/compiler/cwrap/src/Fildes.cpp b/compiler/cwrap/src/Fildes.cpp
index 1988f513d..5ccb83f05 100644
--- a/compiler/cwrap/src/Fildes.cpp
+++ b/compiler/cwrap/src/Fildes.cpp
@@ -22,8 +22,13 @@
namespace
{
-bool error_value(int fd) { return fd == -1; }
-bool valid_value(int fd) { return fd >= 0; }
+/**
+ * @note making inline to this function will prevent unused function error
+ * as error_value() is used only inside assert()
+ */
+inline bool error_value(int fd) { return fd == -1; }
+
+inline bool valid_value(int fd) { return fd >= 0; }
} // namespace
diff --git a/compiler/cwrap/src/Fildes.test.cpp b/compiler/cwrap/src/Fildes.test.cpp
index 08e1e2a5e..f9fa20f9e 100644
--- a/compiler/cwrap/src/Fildes.test.cpp
+++ b/compiler/cwrap/src/Fildes.test.cpp
@@ -32,7 +32,9 @@ namespace
int make_temp(char *name_template)
{
+ mode_t mask = umask(S_IRWXU);
int fd = mkstemp(name_template);
+ umask(mask);
if (fd == -1)
{
@@ -55,9 +57,13 @@ TEST(FildesTest, value_constructor)
{
DECLARE_TEMPLATE(name_template);
- cwrap::Fildes fildes{make_temp(name_template)};
+ {
+ cwrap::Fildes fildes{make_temp(name_template)};
+
+ ASSERT_TRUE(cwrap::valid(fildes));
+ }
- ASSERT_TRUE(cwrap::valid(fildes));
+ unlink(name_template);
}
TEST(FildesTest, move_constructor)
@@ -68,19 +74,24 @@ TEST(FildesTest, move_constructor)
int src_fd = make_temp(src_template);
int dst_fd = make_temp(dst_template);
- cwrap::Fildes src{src_fd};
- cwrap::Fildes dst{dst_fd};
+ {
+ cwrap::Fildes src{src_fd};
+ cwrap::Fildes dst{dst_fd};
- dst = std::move(src);
+ dst = std::move(src);
- ASSERT_FALSE(cwrap::valid(src));
- ASSERT_TRUE(cwrap::valid(dst));
+ ASSERT_FALSE(cwrap::valid(src));
+ ASSERT_TRUE(cwrap::valid(dst));
- ASSERT_EQ(dst.get(), src_fd);
+ ASSERT_EQ(dst.get(), src_fd);
- // "src_fd" SHOULD be valid, and "dst_fd" SHOULD be closed
- ASSERT_NE(fcntl(src_fd, F_GETFD), -1);
- ASSERT_EQ(fcntl(dst_fd, F_GETFD), -1);
+ // "src_fd" SHOULD be valid, and "dst_fd" SHOULD be closed
+ ASSERT_NE(fcntl(src_fd, F_GETFD), -1);
+ ASSERT_EQ(fcntl(dst_fd, F_GETFD), -1);
+ }
+
+ unlink(src_template);
+ unlink(dst_template);
}
TEST(FildesTest, destructor)
@@ -94,4 +105,6 @@ TEST(FildesTest, destructor)
cwrap::Fildes fildes{fd};
}
ASSERT_EQ(fcntl(fd, F_GETFD), -1);
+
+ unlink(name_template);
}
diff --git a/compiler/dredd-rule-lib/CMakeLists.txt b/compiler/dredd-rule-lib/CMakeLists.txt
new file mode 100644
index 000000000..b39d86272
--- /dev/null
+++ b/compiler/dredd-rule-lib/CMakeLists.txt
@@ -0,0 +1,21 @@
+#
+# copy rule-lib.sh (a library of shell script functions)
+#
+set(SOURCE_RULE_LIB "${CMAKE_CURRENT_SOURCE_DIR}/rule-lib.sh")
+set(TARGET_RULE_LIB "${CMAKE_CURRENT_BINARY_DIR}/rule-lib.sh")
+
+add_custom_command(
+ OUTPUT ${TARGET_RULE_LIB}
+ COMMAND ${CMAKE_COMMAND} -E copy "${SOURCE_RULE_LIB}" "${TARGET_RULE_LIB}"
+ DEPENDS ${SOURCE_RULE_LIB}
+ COMMENT "Generate rule lib"
+)
+
+# Generate dependencies
+add_custom_target(dredd_rule_lib ALL DEPENDS ${TARGET_RULE_LIB})
+
+# How to get the path of rule-lib.sh in other CMakeLists.txt
+#
+# get_target_property(DREDD_RULE_LIB_DIR
+# dredd_rule_lib BINARY_DIR)
+# set(RULE_LIB_PATH "${DREDD_RULE_LIB_DIR}/rule-lib.sh")
diff --git a/compiler/dredd-rule-lib/README.md b/compiler/dredd-rule-lib/README.md
new file mode 100644
index 000000000..348b0aefb
--- /dev/null
+++ b/compiler/dredd-rule-lib/README.md
@@ -0,0 +1,112 @@
+# dredd-rule-lib
+
+*dredd-rule-lib* is a library that defines functions to run *dredd* tests, which checks non-functional aspect of compiled files.
+
+## Terms
+
+Assume that we want to check the size of generated tflite file to be less than 1024 Bytes.
+In such case, we'd like to use the following terms:
+
+- "metric" : *file size*
+- "rule" : *file size < 1024*
+- "metric function": `file_size` that returns size of a compiled tflite file
+
+Models (input of test) exist in *model repo*, where
+
+- "model repo" : directory where models exist. For *tf2tflite-dredd-pbtxt-test*, model repo is
+ `res/TensorFlowTests`.
+
+## Metrics supported
+
+The following metric functions are provided:
+- `all_op_count` : the count of operations inside a compiled tflite file
+- `file_size` : the size of compiled tflite file
+- In addition, `op_count`, `conv2d_weight_not_constant`, etc.
+- Please , refer to [`rule-lib.sh`](rule-lib.sh) for metric functions
+
+## Related projects - *dredd* tests
+
+Four *dredd* test projects use *dredd-rule-lib*:
+
+- *tf2tflite-dredd-pbtxt-test*
+ - Models in `pbtxt`, text file, are compiled into `tflite` file.
+ - Then `rule` file that each model has is checked against the `tflite` file.
+- *tf2tflite-dredd-pb-test*
+ - Models in `pb`, binary file, are compiled into `tflite` file.
+ - Then `rule` file that each model has is checked against the `tflite` file.
+- *tf2circle-dredd-pbtxt-test*
+ - Models in `pbtxt`, text file, are compiled into `circle` file.
+ - Then `rule` file that each model has is checked against the `circle` file.
+- *tf2circle-dredd-pb-test*
+ - Models in `pb`, binary file, are compiled into `circle` file.
+ - Then `rule` file that each model has is checked against the `circle` file.
+
+## Rule file
+
+To be a target of *dredd*-tests, a `.rule` file **must** exist in a model directory.
+Please refer to `res/TensorFlowTests/NET_0025/tflite_1.0_rel_requirement.rule` for an example.
+
+### Naming convention of rule file
+
+Note that the file name `tflite_1.0_rel_requirement.rule` is our convention containing the
+information below:
+- Generated file type (`tflite`)
+- SDK version (`1.0_rel`)
+- Purpose (`requirement`)
+
+## How do all these work?
+
+For *tf2tflite-dredd-pbtxt-test*, (*tf2circle-dredd-pbtxt-test* works similarly)
+
+```
+model repo tf2tflite-dredd-pbtxt-test
+-----------------------------------------------------------------------------------------------
+ NET_0025
+ ├── test.pbtxt ----------------------> converted to NET_0025.pb, and then NET_0025.tflite
+ | /|\
+ ├── test.info ---------------------------+
+ | (input/output info of model)
+ |
+ └── tflite_1.0_rel_requirement.rule --> running rule file against tflite --> pass or fail
+ /|\
+ dredd-rule-lib | (using)
+ ---------------------- |
+ rule-lib.sh |
+ - defining rule function --+
+```
+
+For *tf2tflite-dredd-pb-test*, (*tf2circle-dredd-pb-test* works similarly)
+
+```
+model repo tf2tflite-dredd-pb-test
+-----------------------------------------------------------------------------------------------
+ Inception_v3
+ ├── model.pb ------------------------> converted to Inception_v3.tflite
+ | /|\
+ ├── model.info --------------------------+
+ | (input/output info of model)
+ |
+ └── tflite_1.0_rel_requirement.rule --> running rule file against tflite --> pass or fail
+ /|\
+ dredd-rule-lib | (using)
+ ---------------------- |
+ rule-lib.sh |
+ - defining rule function --+
+```
+
+## Model repo and How to add a model as a target of a *dredd*-test.
+
+For *tf2tflite-dredd-pbtxt-test* and *tf2circle-dredd-pbtxt-test*,
+model repo is `res/TensorFlowTests`.
+
+To add a model into these tests, the model directory name should be added into one of the following files:
+- `test.lst` : This file resides in git
+- `test.local.lst` : This file is ignored by git. Use this for personal purpose.
+
+For *tf2tflite-dredd-pb-test* and *tf2circle-dredd-pb-test*,
+model repo is `tf2tflite-dredd-pb-test/contrib` and .`tf2circle-dredd-pb-test/contrib` respectively.
+
+Use these tests for binary models in large size.
+
+To add a model into these tests, the model directory name should be added into the following file:
+- `contrib.lst` : This file is ignored by git.
diff --git a/compiler/dredd-rule-lib/rule-lib.sh b/compiler/dredd-rule-lib/rule-lib.sh
new file mode 100755
index 000000000..9254cc9a7
--- /dev/null
+++ b/compiler/dredd-rule-lib/rule-lib.sh
@@ -0,0 +1,220 @@
+#!/bin/bash
+
+# the following env vars should be defined to call dredd function (except RULE):
+# COMPILED_FILE
+# INSPECT_PROG_PATH
+# VERIFY_PROG_PATH
+# ERROR_LOG
+
+# exit if unknown var is used
+set -u
+
+# ---------------
+# HELPER FUNCTION
+
+init_error_log()
+{
+ # create ${ERROR_LOG} that redirect stderr for pipe
+ exec 2>"${ERROR_LOG}"
+}
+
+argc_check()
+{
+ ACTUAL_ARGC=$1
+ EXPECTED_ARGC=$2
+
+ if [ "$#" -ne 2 ];then
+ echo "argc_check : param count must be 2" > ${ERROR_LOG}
+ echo "error" # return value of sub-shell
+ exit 1
+ fi
+
+ if [ ${ACTUAL_ARGC} -ne ${EXPECTED_ARGC} ];then
+ echo "arg count mismatch: actual = ${ACTUAL_ARGC} vs expected = ${EXPECTED_ARGC}" > ${ERROR_LOG}
+ echo "error" # return value of sub-shell
+ exit 1
+ fi
+}
+
+file_path_check()
+{
+ argc_check $# 1
+
+ if [ ! -f $1 ]; then
+ echo "$1 does not exist" > ${ERROR_LOG}
+ echo "error" # return value of sub-shell
+ exit 1
+ fi
+}
+
+check_success_exit_code()
+{
+ ACTUAL_EXIT_CODE=$1
+ EXPECTED_SUCCESS_CODE=$2
+
+ if [ ${ACTUAL_EXIT_CODE} -ne ${EXPECTED_SUCCESS_CODE} ];then
+ echo "error"
+ exit 1
+ fi
+}
+
+check_error_exit_code()
+{
+ ACTUAL_EXIT_CODE=$1
+ EXPECTED_ERROR_CODE=$2
+
+ if [ ${ACTUAL_EXIT_CODE} -eq ${EXPECTED_ERROR_CODE} ];then
+ echo "error"
+ exit 1
+ fi
+}
+
+# END of HELPER FUNCTION
+# ----------------------
+
+#
+# Define rule
+#
+# - Params: rule name (metric), actual value, condition, expected value
+# - condition is '=', '!=', '<', '>', '<=', '>='. Refer to "man expr"
+# - Return
+# - 0 : success
+# - 1 : fail (condition check fail)
+#
+
+RULE()
+{
+ argc_check $# 4
+
+ RULE_NAME=$1
+ ACTUAL=$2
+ COND=$3
+ EXPECTED=$4
+
+ # not to exit when expr result with 0
+ set +e
+
+ expr ${ACTUAL} ${COND} ${EXPECTED} > /dev/null
+ RESULT=$?
+
+ # roll-back
+ set -e
+
+ # Note: return value of 'expr'
+ # - 0 : result is true
+ # - 1 : result is false
+ # - 2 : error
+
+ if [ ${RESULT} -eq 0 ];then
+ echo -e "** [${RULE_NAME}] \t success \t ([actual: ${ACTUAL}] ${COND} [expected: ${EXPECTED}])"
+ elif [ ${RESULT} -eq 1 ];then
+ echo -e "** [${RULE_NAME}] \t ** fail \t ([actual: ${ACTUAL}] ${COND} [expected: ${EXPECTED}])"
+ else
+ echo -e "\t** Error in [expr ${ACTUAL} ${COND} ${EXPECTED}]"
+ fi
+
+ return ${RESULT}
+}
+
+#
+# Define each function to get quality value
+#
+
+# Note: These function is called by a sub-shell.
+# So return value should be passed through "echo return_value"
+# tip: for debugging, surround the code with "set -x" and "set +x"
+
+file_size()
+{
+ file_path_check ${COMPILED_FILE}
+
+ set -o pipefail
+
+ ACTUAL=`init_error_log ; cat ${COMPILED_FILE} | wc -c`
+
+ check_success_exit_code $? 0
+
+ echo ${ACTUAL}
+}
+
+all_op_count()
+{
+ file_path_check ${COMPILED_FILE}
+ file_path_check ${INSPECT_PROG_PATH}
+
+ set -o pipefail
+
+ ACTUAL=`init_error_log ; ${INSPECT_PROG_PATH} --operators ${COMPILED_FILE} | wc -l`
+
+ check_success_exit_code $? 0
+
+ echo ${ACTUAL}
+}
+
+op_count()
+{
+ argc_check $# 1
+ file_path_check ${COMPILED_FILE}
+ file_path_check ${INSPECT_PROG_PATH}
+
+ set -o pipefail
+
+ RESULT=`init_error_log ; ${INSPECT_PROG_PATH} --operators ${COMPILED_FILE}`
+ check_success_exit_code $? 0
+
+ # note : grep's exit code is 2 in case of error.
+ ACTUAL=`init_error_log ; echo "${RESULT}" | grep -wc "$1"`
+ check_error_exit_code $? 2
+
+ echo ${ACTUAL}
+}
+
+conv2d_weight_not_constant()
+{
+ file_path_check ${COMPILED_FILE}
+ file_path_check ${INSPECT_PROG_PATH}
+
+ set -o pipefail
+
+ ACTUAL=`init_error_log ; \
+ ${INSPECT_PROG_PATH} --conv2d_weight ${COMPILED_FILE} | \
+ awk -F, '{ if ($2 != "CONST") print $0}' | wc -l`
+
+ check_success_exit_code $? 0
+
+ echo ${ACTUAL}
+}
+
+verify_file_format()
+{
+ file_path_check ${COMPILED_FILE}
+ file_path_check ${VERIFY_PROG_PATH}
+
+ set -o pipefail
+
+ ACTUAL=`init_error_log ; ${VERIFY_PROG_PATH} ${COMPILED_FILE} | grep -c "PASS"`
+
+ # note grep can exit with 1 ("PASS" not found) and this is treated as an error
+ check_success_exit_code $? 0
+
+ echo ${ACTUAL}
+}
+
+op_version()
+{
+ argc_check $# 1
+ file_path_check ${COMPILED_FILE}
+ file_path_check ${INSPECT_PROG_PATH}
+
+ set -o pipefail
+
+ ACTUAL=`init_error_log ; \
+ ${INSPECT_PROG_PATH} --op_version ${COMPILED_FILE} | \
+ awk -F, -v opname="$1" '{ if ($1 == opname) print $2}'`
+
+ check_success_exit_code $? 0
+
+ echo ${ACTUAL}
+}
+
+# TODO define more qullity test function
diff --git a/compiler/enco-intf/README.md b/compiler/enco-intf/README.md
new file mode 100644
index 000000000..5f265bce0
--- /dev/null
+++ b/compiler/enco-intf/README.md
@@ -0,0 +1 @@
+# enco-intf
diff --git a/compiler/enco/core/CMakeLists.txt b/compiler/enco/core/CMakeLists.txt
index 30632a52d..f437e687a 100644
--- a/compiler/enco/core/CMakeLists.txt
+++ b/compiler/enco/core/CMakeLists.txt
@@ -21,7 +21,7 @@ target_link_libraries(enco_core PRIVATE stdex)
# Let's use nncc project-wide build options
target_link_libraries(enco_core PRIVATE nncc_common)
-nncc_find_package(GTest QUIET)
+nnas_find_package(GTest QUIET)
if(NOT GTest_FOUND)
return()
diff --git a/compiler/enco/core/src/Backend.cpp b/compiler/enco/core/src/Backend.cpp
index 3e0fe2029..d4bec7447 100644
--- a/compiler/enco/core/src/Backend.cpp
+++ b/compiler/enco/core/src/Backend.cpp
@@ -58,7 +58,7 @@ namespace
// has_inout_bag(m) returns true if there is a pair of coco::Input and coco::Output that share
// the same bag as their backing storage
-bool has_inout_bag(const coco::Module *m)
+inline bool has_inout_bag(const coco::Module *m)
{
for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n)
{
diff --git a/compiler/enco/core/src/Support/Debugging.cpp b/compiler/enco/core/src/Support/Debugging.cpp
index ceb62fb59..bd65a27d8 100644
--- a/compiler/enco/core/src/Support/Debugging.cpp
+++ b/compiler/enco/core/src/Support/Debugging.cpp
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#include "Debugging.h"
#include <pp/LinearDocument.h>
diff --git a/compiler/enco/core/src/Transforms/Split.cpp b/compiler/enco/core/src/Transforms/Split.cpp
index 1292bb01e..b57b8f882 100644
--- a/compiler/enco/core/src/Transforms/Split.cpp
+++ b/compiler/enco/core/src/Transforms/Split.cpp
@@ -268,7 +268,6 @@ public:
{
auto obj = _ker;
auto shape = obj->shape();
- auto len = nncc::core::ADT::kernel::num_elements(shape);
auto ovl = data->f32()->read(obj);
assert(ovl != nullptr);
@@ -301,7 +300,7 @@ public:
}
}
- assert(values.size() == len);
+ assert(values.size() == nncc::core::ADT::kernel::num_elements(shape));
binder->setOperand(ker, values.begin(), values.end());
}
diff --git a/compiler/enco/frontend/caffe/CMakeLists.txt b/compiler/enco/frontend/caffe/CMakeLists.txt
index 530504205..ce43a41d3 100644
--- a/compiler/enco/frontend/caffe/CMakeLists.txt
+++ b/compiler/enco/frontend/caffe/CMakeLists.txt
@@ -1,4 +1,4 @@
-nncc_find_package(CaffeProto QUIET)
+nnas_find_package(CaffeProto QUIET)
if(NOT CaffeProto_FOUND)
return()
@@ -18,13 +18,13 @@ target_link_libraries(enco_caffe_frontend morph)
target_link_libraries(enco_caffe_frontend caffeproto)
target_link_libraries(enco_caffe_frontend stdex)
-nncc_find_package(GTest QUIET)
+nnas_find_package(GTest QUIET)
if(NOT GTest_FOUND)
return()
endif(NOT GTest_FOUND)
-nncc_find_package(Caffe QUIET)
+nnas_find_package(Caffe QUIET)
if(NOT Caffe_FOUND)
return()
diff --git a/compiler/enco/frontend/tflite/CMakeLists.txt b/compiler/enco/frontend/tflite/CMakeLists.txt
index a643dc1a2..77159879e 100644
--- a/compiler/enco/frontend/tflite/CMakeLists.txt
+++ b/compiler/enco/frontend/tflite/CMakeLists.txt
@@ -1,4 +1,4 @@
-nncc_find_package(FlatBuffers QUIET)
+nnas_find_package(FlatBuffers QUIET)
if(NOT FlatBuffers_FOUND)
return()
@@ -23,7 +23,7 @@ target_link_libraries(enco_tflite_frontend stdex)
target_link_libraries(enco_tflite_frontend morph)
target_link_libraries(enco_tflite_frontend cwrap)
-nncc_find_package(GTest QUIET)
+nnas_find_package(GTest QUIET)
if(NOT GTest_FOUND)
return()
diff --git a/compiler/encodump/CMakeLists.txt b/compiler/encodump/CMakeLists.txt
index 3a250f915..58fe17a51 100644
--- a/compiler/encodump/CMakeLists.txt
+++ b/compiler/encodump/CMakeLists.txt
@@ -1,8 +1,17 @@
+if(NOT TARGET enco_intf_frontend)
+ return()
+endif(NOT TARGET enco_intf_frontend)
+
+if(NOT TARGET enco_core)
+ return()
+endif(NOT TARGET enco_core)
+
file(GLOB_RECURSE SOURCES "src/*.cpp")
add_executable(encodump ${SOURCES})
target_include_directories(encodump PRIVATE src)
target_link_libraries(encodump enco_intf_frontend)
target_link_libraries(encodump enco_core)
+target_link_libraries(encodump safemain)
target_link_libraries(encodump stdex)
target_link_libraries(encodump dl)
diff --git a/compiler/encodump/requires.cmake b/compiler/encodump/requires.cmake
new file mode 100644
index 000000000..3d1ca094e
--- /dev/null
+++ b/compiler/encodump/requires.cmake
@@ -0,0 +1 @@
+require("safemain")
diff --git a/compiler/encodump/src/Driver.cpp b/compiler/encodump/src/Driver.cpp
index 416145e71..f27cbe904 100644
--- a/compiler/encodump/src/Driver.cpp
+++ b/compiler/encodump/src/Driver.cpp
@@ -153,7 +153,7 @@ private:
* --frontend-arg build/compiler/enco/test/caffe/Convolution_003.prototxt \
* --frontend-arg build/compiler/enco/test/caffe/Convolution_003.caffemodel
*/
-static int entry(int argc, char **argv)
+int entry(int argc, char **argv)
{
// Usage:
// [Command] --frontend [Frontend .so path] --frontend-arg ...
@@ -205,26 +205,3 @@ static int entry(int argc, char **argv)
return 0;
}
-
-#ifdef NDEBUG
-int main(int argc, char **argv)
-{
- try
- {
- return entry(argc, argv);
- }
- catch (const std::exception &e)
- {
- std::cerr << "ERROR: " << e.what() << std::endl;
- }
-
- return 255;
-}
-#else // NDEBUG
-int main(int argc, char **argv)
-{
- // NOTE main does not catch internal exceptions for debug build to make it easy to
- // check the stacktrace with a debugger
- return entry(argc, argv);
-}
-#endif // !NDEBUG
diff --git a/compiler/encodump/src/Dump.h b/compiler/encodump/src/Dump.h
index e7ea7d1f0..6ea69b978 100644
--- a/compiler/encodump/src/Dump.h
+++ b/compiler/encodump/src/Dump.h
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#ifndef __DUMP_H__
#define __DUMP_H__
diff --git a/compiler/exo-tflite/CMakeLists.txt b/compiler/exo-tflite/CMakeLists.txt
deleted file mode 100644
index 961a39d1a..000000000
--- a/compiler/exo-tflite/CMakeLists.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-nncc_find_package(FlatBuffers QUIET)
-
-if(NOT FlatBuffers_FOUND)
- message(STATUS "Build exo-tflite: FALSE (missing FlatBuffers)")
- return()
-endif(NOT FlatBuffers_FOUND)
-
-nncc_find_package(TensorFlowSource EXACT 1.14 QUIET)
-
-if(NOT TensorFlowSource_FOUND)
- message(STATUS "Build exo-tflite: FALSE (missing TensorFlowSource)")
- return()
-endif(NOT TensorFlowSource_FOUND)
-
-message(STATUS "Build exo-tflite: TRUE")
-
-set(TFLITE_SCHEMA_DIR "${TensorFlowSource_DIR}/tensorflow/lite/schema")
-
-FlatBuffers_Target(exo_tflite_fbs
- OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen"
- SCHEMA_DIR "${TFLITE_SCHEMA_DIR}"
- SCHEMA_FILES schema.fbs
-)
-
-file(GLOB_RECURSE SOURCES "src/*.cpp")
-file(GLOB_RECURSE TESTS "src/*.test.cpp")
-list(REMOVE_ITEM SOURCES ${TESTS})
-
-add_library(exo_tflite SHARED ${SOURCES})
-target_include_directories(exo_tflite PUBLIC include)
-target_include_directories(exo_tflite PRIVATE src)
-target_link_libraries(exo_tflite PUBLIC exo_tflite_fbs)
-target_link_libraries(exo_tflite PUBLIC loco)
-target_link_libraries(exo_tflite PRIVATE stdex)
-target_link_libraries(exo_tflite PRIVATE pepper_str)
-target_link_libraries(exo_tflite PRIVATE pepper_strcast)
-target_link_libraries(exo_tflite PRIVATE locoex_customop)
-target_link_libraries(exo_tflite PRIVATE locop)
-target_link_libraries(exo_tflite PRIVATE hermes_std)
-target_link_libraries(exo_tflite PRIVATE logo)
-
-# Let's apply nncc common compile options
-#
-# NOTE This will enable strict compilation (warnings as error).
-# Please refer to the top-level CMakeLists.txt for details
-target_link_libraries(exo_tflite PRIVATE nncc_common)
-
-if (NOT ENABLE_TEST)
- return()
-endif (NOT ENABLE_TEST)
-
-# Google Test is mandatory for internal testing
-nncc_find_package(GTest REQUIRED)
-
-GTest_AddTest(exo_tflite_test ${TESTS})
-target_include_directories(exo_tflite_test PRIVATE src)
-target_link_libraries(exo_tflite_test stdex)
-target_link_libraries(exo_tflite_test pepper_str)
-target_link_libraries(exo_tflite_test exo_tflite)
-target_link_libraries(exo_tflite_test hermes_std)
-target_link_libraries(exo_tflite_test logo)
diff --git a/compiler/exo-tflite/README.md b/compiler/exo-tflite/README.md
deleted file mode 100644
index e52bea9a6..000000000
--- a/compiler/exo-tflite/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# exo-tflite
-
-_exo-tflite_ includes _loco_-to-_T/F Lite_ exporter (as a library).
diff --git a/compiler/exo-tflite/requires.cmake b/compiler/exo-tflite/requires.cmake
deleted file mode 100644
index 0b6a8858c..000000000
--- a/compiler/exo-tflite/requires.cmake
+++ /dev/null
@@ -1,5 +0,0 @@
-require("stdex")
-require("loco")
-require("locoex-customop")
-require("logo")
-require("pepper-str")
diff --git a/compiler/exo-tflite/src/Check.h b/compiler/exo-tflite/src/Check.h
deleted file mode 100644
index 41d49a143..000000000
--- a/compiler/exo-tflite/src/Check.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __CHECK_H__
-#define __CHECK_H__
-
-#include <pepper/str.h>
-
-#include <stdexcept>
-#include <cassert>
-#include <iostream>
-
-// TODO Add macro for Release version
-
-#define EXO_THROW(msg) throw std::runtime_error(pepper::str(msg, "at ", __FILE__, ":", __LINE__))
-
-#define EXO_ASSERT(condition, msg) \
- { \
- if (!(condition)) \
- { \
- std::cerr << "[assert failed] " << (msg) << ". " << std::endl; \
- assert((condition)); \
- } \
- }
-
-#endif // __CHECK_H__
diff --git a/compiler/exo-tflite/src/Conversion/AvgPool2DConverter.cpp b/compiler/exo-tflite/src/Conversion/AvgPool2DConverter.cpp
deleted file mode 100644
index 0bb8a54aa..000000000
--- a/compiler/exo-tflite/src/Conversion/AvgPool2DConverter.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "AvgPool2DConverter.h"
-
-#include "Dialect/IR/TFLNodes.h"
-
-#include "GraphBlock.h"
-#include "Check.h"
-
-#include <loco.h>
-
-#include <stdex/Memory.h>
-
-namespace exo
-{
-/**
- * @brief Converts loco::AvgPool2D to locoex::TFLAveragePool2D
- *
- * How it works: (note: ten->fea means input: tensor, output: feature)
- *
- * Before:
- * Foo ---- FeatureEncode ---- AvgPool2D ---- FeatureDecode ---- Bar
- * ten->ten ten->fea fea->fea fea->ten ten->ten
- *
- * After: AvgPool2D
- * /
- * Foo -- FeatureEncode - FeatureDecode - TFLAvgPool2D - FeatureEncode - FeatureDecode -- Bar
- * ten->ten ten->fea fea->ten ten->ten ten->fea fea->ten ten->ten
- *
- * @note This method replaces AvgPool2D with "FeatureDecode -- TFLAvgPool2D -- FeatureEncode".
- * Redundant nodes will be removed during transforms.
- */
-bool AvgPool2DConverter::convert(loco::AvgPool2D *origin)
-{
- auto *graph = origin->graph();
-
- auto dec = make_feature_decode<DefaultLayout::NHWC>(origin->ifm());
- auto tfl_average = graph->nodes()->create<locoex::TFLAveragePool2D>();
- {
- tfl_average->value(dec);
-
- // set attributes
- tfl_average->stride()->w(origin->stride()->horizontal());
- tfl_average->stride()->h(origin->stride()->vertical());
-
- tfl_average->filter()->w(origin->window()->horizontal());
- tfl_average->filter()->h(origin->window()->vertical());
-
- auto pad = origin->pad();
- if (pad->bottom() == 0 && pad->top() == 0 && pad->left() == 0 && pad->right() == 0)
- tfl_average->padding(locoex::Padding::VALID);
- else
- tfl_average->padding(locoex::Padding::SAME);
-
- tfl_average->fusedActivationFunction(locoex::FusedActFunc::NONE);
- }
- auto enc = make_feature_encode<DefaultLayout::NHWC>(tfl_average);
-
- // replace canonical node
- loco::replace(origin).with(enc);
- origin->ifm(nullptr);
-
- return true;
-}
-
-} // namespace exo
diff --git a/compiler/exo-tflite/src/Conversion/CanonicalNodeConverter.cpp b/compiler/exo-tflite/src/Conversion/CanonicalNodeConverter.cpp
deleted file mode 100644
index b72fa60bd..000000000
--- a/compiler/exo-tflite/src/Conversion/CanonicalNodeConverter.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "CanonicalNodeConverter.h"
-
-#include <loco.h>
-#include <loco/IR/CanonicalDialect.h>
-
-namespace exo
-{
-
-template <typename CanonicalType>
-bool CanonicalNodeConverter<CanonicalType>::run(loco::Graph *graph)
-{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == loco::CanonicalDialect::get())
- {
- auto the_node = dynamic_cast<CanonicalType *>(node);
- if (the_node != nullptr)
- {
- if (convert(the_node))
- changed = true;
- }
- }
- }
-
- return changed;
-}
-
-// template instantiation
-template bool CanonicalNodeConverter<loco::AvgPool2D>::run(loco::Graph *graph);
-// TODO loco::ConstGen
-// TODO loco::Conv2D
-// TODO loco::DepthwiseConv2D
-// TODO loco::DepthwiseFilterEncode
-template bool CanonicalNodeConverter<loco::EltwiseAdd>::run(loco::Graph *graph);
-// TODO loco::EltwiseDiv
-template bool CanonicalNodeConverter<loco::EltwiseMul>::run(loco::Graph *graph);
-// TODO loco::EltwiseSqrt
-// TODO loco::EltwiseSub
-template bool CanonicalNodeConverter<loco::FeatureBiasAdd>::run(loco::Graph *graph);
-// TODO loco::FixedReshape
-template bool CanonicalNodeConverter<loco::MaxPool2D>::run(loco::Graph *graph);
-template bool CanonicalNodeConverter<loco::ReLU>::run(loco::Graph *graph);
-// TODO loco::ReLU6
-// TODO loco::Tanh
-// TODO loco::TensorConcat
-// TODO loco::TensorBiasAdd
-// NOTE TensorBroadcastConverter is skipped here as this directly inherits
-// loco::Pass instead of CanonicalNodeConverter
-// TODO loco::TensorSoftmax
-
-} // namespace exo
diff --git a/compiler/exo-tflite/src/Conversion/CanonicalNodeConverter.h b/compiler/exo-tflite/src/Conversion/CanonicalNodeConverter.h
deleted file mode 100644
index b4c6c0bd4..000000000
--- a/compiler/exo-tflite/src/Conversion/CanonicalNodeConverter.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __CONVERSION_CANONICAL_NODE_CONVERTER_H__
-#define __CONVERSION_CANONICAL_NODE_CONVERTER_H__
-
-#include "Convert.h"
-
-#include <loco.h>
-#include <logo/Pass.h>
-
-namespace exo
-{
-
-/**
- * @brief Class to convert a canonical node to TFL node
- */
-template <typename CanonicalType> class CanonicalNodeConverter : public logo::Pass
-{
-public:
- virtual const char *name(void) const { return nullptr; }
-
-public:
- bool run(loco::Graph *graph);
-
-protected:
- virtual bool convert(CanonicalType *node) = 0;
-};
-
-} // namespace exo
-
-#endif //__CONVERSION_CANONICAL_NODE_CONVERTER_H__
diff --git a/compiler/exo-tflite/src/Conversion/EltwiseAddConverter.cpp b/compiler/exo-tflite/src/Conversion/EltwiseAddConverter.cpp
deleted file mode 100644
index d3d078e24..000000000
--- a/compiler/exo-tflite/src/Conversion/EltwiseAddConverter.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "EltwiseAddConverter.h"
-
-#include "GraphBlock.h"
-#include "Check.h"
-
-#include "Dialect/IR/TFLNodes.h"
-
-#include <loco/Service/ShapeInference.h>
-
-namespace exo
-{
-
-bool EltwiseAddConverter::convert(loco::EltwiseAdd *origin)
-{
- if (!loco::shape_known(origin))
- {
- return false;
- }
-
- if (loco::shape_get(origin).domain() == loco::Domain::Tensor)
- {
- auto tfl_add = origin->graph()->nodes()->create<locoex::TFLAdd>();
- tfl_add->x(origin->lhs());
- tfl_add->y(origin->rhs());
-
- loco::replace(origin).with(tfl_add);
- origin->lhs(nullptr);
- origin->rhs(nullptr);
-
- return true;
- }
- else if (loco::shape_get(origin).domain() == loco::Domain::Feature)
- {
- /*
- if EltwiseAdd's domain is Feature, EltwiseAdd is replaced with
- FeatureDecoder-TFLAdd-FeatureEncoder.
-
- Before :
- A (output: feature) -- loco::EltwiseAdd --- B (input:feature)
-
- After :
- A -- loco::FeatureDecode -- locoex::TFLAdd -- loco::FeatureEncode --- B
-
- loco::EltwiseAdd (dead node)
- */
- auto graph = origin->graph();
- auto dec_l = make_feature_decode<DefaultLayout::NHWC>(origin->lhs());
- auto dec_r = make_feature_decode<DefaultLayout::NHWC>(origin->rhs());
- auto tfl_add = graph->nodes()->create<locoex::TFLAdd>();
- {
- tfl_add->x(dec_l);
- tfl_add->y(dec_r);
- }
- auto enc = make_feature_encode<DefaultLayout::NHWC>(tfl_add);
-
- loco::replace(origin).with(enc);
- origin->lhs(nullptr);
- origin->rhs(nullptr);
-
- return true;
- }
- else
- EXO_THROW("Not yet supported loco::Domain");
-}
-
-} // namespace exo
diff --git a/compiler/exo-tflite/src/Conversion/EltwiseMulConverter.cpp b/compiler/exo-tflite/src/Conversion/EltwiseMulConverter.cpp
deleted file mode 100644
index 5b48838c1..000000000
--- a/compiler/exo-tflite/src/Conversion/EltwiseMulConverter.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "EltwiseMulConverter.h"
-
-#include "GraphBlock.h"
-#include "Check.h"
-
-#include "Dialect/IR/TFLNodes.h"
-
-#include <loco/Service/ShapeInference.h>
-
-namespace exo
-{
-
-bool EltwiseMulConverter::convert(loco::EltwiseMul *origin)
-{
- if (!loco::shape_known(origin))
- {
- return false;
- }
-
- if (loco::shape_get(origin).domain() == loco::Domain::Tensor)
- {
- auto tfl_mul = origin->graph()->nodes()->create<locoex::TFLMul>();
- tfl_mul->x(origin->lhs());
- tfl_mul->y(origin->rhs());
-
- loco::replace(origin).with(tfl_mul);
- origin->lhs(nullptr);
- origin->rhs(nullptr);
-
- return true;
- }
- else if (loco::shape_get(origin).domain() == loco::Domain::Feature)
- {
- /*
- if EltwiseMul's domain is Feature, EltwiseMul is replaced with
- FeatureDecoder-TFLMul-FeatureEncoder.
-
- Before :
- A (output: feature) -- loco::EltwiseMul --- B (input:feature)
-
- After :
- A -- loco::FeatureDecode -- locoex::TFLMul -- loco::FeatureEncode --- B
-
- loco::EltwiseMul (dead node)
- */
- auto graph = origin->graph();
- auto dec_l = make_feature_decode<DefaultLayout::NHWC>(origin->lhs());
- auto dec_r = make_feature_decode<DefaultLayout::NHWC>(origin->rhs());
- auto tfl_mul = graph->nodes()->create<locoex::TFLMul>();
- {
- tfl_mul->x(dec_l);
- tfl_mul->y(dec_r);
- }
- auto enc = make_feature_encode<DefaultLayout::NHWC>(tfl_mul);
-
- loco::replace(origin).with(enc);
- origin->lhs(nullptr);
- origin->rhs(nullptr);
-
- return true;
- }
- else
- EXO_THROW("Not yet supported loco::Domain");
-}
-
-} // namespace exo
diff --git a/compiler/exo-tflite/src/Conversion/FeatureBiasAddConverter.cpp b/compiler/exo-tflite/src/Conversion/FeatureBiasAddConverter.cpp
deleted file mode 100644
index 1163f0e3c..000000000
--- a/compiler/exo-tflite/src/Conversion/FeatureBiasAddConverter.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "FeatureBiasAddConverter.h"
-
-#include "Dialect/IR/TFLNodes.h"
-
-#include "GraphBlock.h"
-
-#include <loco.h>
-#include <loco/Service/ShapeInference.h>
-
-#include <cassert>
-
-namespace exo
-{
-
-/**
- * @brief Converts loco::FeatureBiasAdd to locoex::TFLAdd
- *
- * Before:
- * Foo ---+
- * |
- * loco::FeatureBiasAdd - FeatureDecode - ...
- * |
- * Bar - BiasEncode --+
- *
- * After:
- *
- * Foo - loco::FeatureDecode --+ loco::FeatureBiasAdd
- * |(x)
- * TFLAdd -- loco::FeatureEncode - FeatureDecode - ...
- * |(y)
- * Bar - BiasEncode - loco::BiasDecode --+
- */
-bool FeatureBiasAddConverter::convert(loco::FeatureBiasAdd *origin)
-{
- auto *graph = origin->graph();
-
- auto tfl_add = graph->nodes()->create<locoex::TFLAdd>();
-
- // handling input x
- assert(loco::shape_get(origin->value()).domain() == loco::Domain::Feature);
-
- auto fea_dec = make_feature_decode<DefaultLayout::NHWC>(origin->value());
- tfl_add->x(fea_dec);
-
- // handling input y
- auto bias_dec = graph->nodes()->create<loco::BiasDecode>();
- assert(bias_dec != nullptr);
-
- bias_dec->input(origin->bias());
-
- tfl_add->y(bias_dec);
-
- // handling output
- auto fea_enc = make_feature_encode<DefaultLayout::NHWC>(tfl_add);
-
- loco::replace(origin).with(fea_enc);
- origin->value(nullptr);
-
- return true;
-}
-
-} // namespace exo
diff --git a/compiler/exo-tflite/src/Conversion/MaxPool2DConverter.cpp b/compiler/exo-tflite/src/Conversion/MaxPool2DConverter.cpp
deleted file mode 100644
index 7b7fdfc5d..000000000
--- a/compiler/exo-tflite/src/Conversion/MaxPool2DConverter.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "MaxPool2DConverter.h"
-
-#include "Dialect/IR/TFLNodes.h"
-#include "GraphBlock.h"
-
-#include <loco.h>
-
-#include <stdex/Memory.h>
-
-namespace exo
-{
-
-/**
- * @brief Converts loco::MaxPool2D to locoex::TFLMaxPool2D
- *
- * @note This works similar to AvgPool2DConverter. Please refer to the comment in
- * AvgPool2DConverter.
- */
-bool MaxPool2DConverter::convert(loco::MaxPool2D *origin)
-{
- auto *graph = origin->graph();
-
- auto dec = make_feature_decode<DefaultLayout::NHWC>(origin->ifm());
- auto tfl_max = graph->nodes()->create<locoex::TFLMaxPool2D>();
- {
- tfl_max->value(dec);
-
- // set attributes
- tfl_max->stride()->w(origin->stride()->horizontal());
- tfl_max->stride()->h(origin->stride()->vertical());
-
- tfl_max->filter()->w(origin->window()->horizontal());
- tfl_max->filter()->h(origin->window()->vertical());
-
- auto pad = origin->pad();
- if (pad->bottom() == 0 && pad->top() == 0 && pad->left() == 0 && pad->right() == 0)
- tfl_max->padding(locoex::Padding::VALID);
- else
- tfl_max->padding(locoex::Padding::SAME);
-
- tfl_max->fusedActivationFunction(locoex::FusedActFunc::NONE);
- }
-
- auto enc = make_feature_encode<DefaultLayout::NHWC>(tfl_max);
-
- loco::replace(origin).with(enc);
- origin->ifm(nullptr);
-
- return true;
-}
-
-} // namespace exo
diff --git a/compiler/exo-tflite/src/Conversion/ReluConverter.cpp b/compiler/exo-tflite/src/Conversion/ReluConverter.cpp
deleted file mode 100644
index a0ac1f768..000000000
--- a/compiler/exo-tflite/src/Conversion/ReluConverter.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ReluConverter.h"
-
-#include "GraphBlock.h"
-#include "Check.h"
-
-#include "Dialect/IR/TFLNodes.h"
-
-#include <loco/Service/ShapeInference.h>
-
-namespace exo
-{
-
-bool ReluConverter::convert(loco::ReLU *origin)
-{
- if (!loco::shape_known(origin))
- {
- return false;
- }
-
- if (loco::shape_get(origin).domain() == loco::Domain::Tensor)
- {
- auto tfl_relu = origin->graph()->nodes()->create<locoex::TFLRelu>();
- tfl_relu->features(origin->input());
-
- loco::replace(origin).with(tfl_relu);
- origin->input(nullptr);
-
- return true;
- }
- else if (loco::shape_get(origin).domain() == loco::Domain::Feature)
- {
- /*
- if ReLU's domain is Feature, ReLU is replaced with FeatureDecoder-TFLRelu-FeatureEncoder.
-
- Before :
- A (output: feature) -- loco::ReLU --- B (input:feature)
-
- After :
- A -- loco::FeatureDecode -- locoex::TFLRelu -- loco::FeatureEncode --- B
-
- loco::ReLU (dead node)
- */
- auto graph = origin->graph();
- auto dec = make_feature_decode<DefaultLayout::NHWC>(origin->input());
- auto tfl_relu = graph->nodes()->create<locoex::TFLRelu>();
- {
- tfl_relu->features(dec);
- }
- auto enc = make_feature_encode<DefaultLayout::NHWC>(tfl_relu);
-
- loco::replace(origin).with(enc);
- origin->input(nullptr);
-
- return true;
- }
- else
- EXO_THROW("Not yet supported loco::Domain");
-}
-
-} // namespace exo
diff --git a/compiler/exo-tflite/src/Conversion/ReluConverter.test.cpp b/compiler/exo-tflite/src/Conversion/ReluConverter.test.cpp
deleted file mode 100644
index ef4810b30..000000000
--- a/compiler/exo-tflite/src/Conversion/ReluConverter.test.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ReluConverter.h"
-
-#include "GraphBlock.h"
-#include "Dialect/IR/TFLNodes.h"
-
-#include "TestHelper.h"
-
-#include <gtest/gtest.h>
-
-TEST(ReluConverterTest, relu_tensor_inout)
-{
- auto g = loco::make_graph();
-
- auto pull = g->nodes()->create<loco::Pull>();
- {
- pull->dtype(loco::DataType::FLOAT32);
- pull->shape({2, 2});
- }
- auto relu = g->nodes()->create<loco::ReLU>();
- {
- relu->input(pull);
- }
- auto push = g->nodes()->create<loco::Push>();
- {
- push->from(relu);
- }
-
- auto input = g->inputs()->create();
- {
- input->name("input");
- loco::link(input, pull);
- }
- auto output = g->outputs()->create();
- {
- output->name("output");
- loco::link(output, push);
- }
-
- // let's convert
- exo::test::TypeShapeReadyPhase test_phase;
- {
- test_phase.add_pass<exo::ReluConverter>();
- test_phase.run(g.get());
- }
-
- // Check
- EXO_TEST_ASSERT_NODE_COUNT({push}, 3);
-
- auto plan = loco::postorder_traversal(loco::output_nodes(g.get()));
- {
- int n = 0;
- ASSERT_TRUE(dynamic_cast<loco::Pull *>(plan.at(n++)));
- ASSERT_TRUE(dynamic_cast<locoex::TFLRelu *>(plan.at(n++)));
- ASSERT_TRUE(dynamic_cast<loco::Push *>(plan.at(n++)));
- }
-}
-
-TEST(ReluConverterTest, relu_feature_inout)
-{
- // g = Pull - FeatureEncode - Relu - FeatureDecode - Push
- auto g = loco::make_graph();
-
- auto pull = g->nodes()->create<loco::Pull>();
- {
- pull->dtype(loco::DataType::FLOAT32);
- pull->shape({1, 2, 3, 4});
- }
-
- auto enc = exo::make_feature_encode<exo::DefaultLayout::NHWC>(pull);
-
- auto relu = g->nodes()->create<loco::ReLU>();
- {
- relu->input(enc);
- }
-
- auto dec = exo::make_feature_decode<exo::DefaultLayout::NHWC>(relu);
-
- auto push = g->nodes()->create<loco::Push>();
- {
- push->from(dec);
- }
-
- auto input = g->inputs()->create();
- {
- input->name("input");
- loco::link(input, pull);
- }
- auto output = g->outputs()->create();
- {
- output->name("output");
- loco::link(output, push);
- }
-
- exo::test::TypeShapeReadyPhase test_phase;
- {
- test_phase.add_pass<exo::ReluConverter>();
- test_phase.run(g.get());
- }
-
- // now, g = Pull - FeatureEncode - FeatureDecode - TFLRelu - FeatureEncode - FeatureDecode - Push
-
- // Check
- EXO_TEST_ASSERT_NODE_COUNT({push}, 7);
-
- // Check [FeatureEncode - FeatureDecode - TFLRelu - FeatureEncode - FeatureDecode] chunk
- loco::Node *node = exo::test::find_first_node_bytype<loco::FeatureEncode>(g.get());
- ASSERT_TRUE(node != nullptr);
- node = exo::test::get_only_succ<loco::FeatureDecode>(node);
- ASSERT_TRUE(node != nullptr);
- node = exo::test::get_only_succ<locoex::TFLRelu>(node);
- ASSERT_TRUE(node != nullptr);
- node = exo::test::get_only_succ<loco::FeatureEncode>(node);
- ASSERT_TRUE(node != nullptr);
- node = exo::test::get_only_succ<loco::FeatureDecode>(node);
- ASSERT_TRUE(node != nullptr);
-}
diff --git a/compiler/exo-tflite/src/Conversion/ShapeInferencePass.cpp b/compiler/exo-tflite/src/Conversion/ShapeInferencePass.cpp
deleted file mode 100644
index 8b9416ec9..000000000
--- a/compiler/exo-tflite/src/Conversion/ShapeInferencePass.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ShapeInferencePass.h"
-
-#include "Dialect/IR/TFLDialect.h"
-#include "Dialect/Service/TFLShapeInferenceRule.h"
-
-#include <loco.h>
-#include <loco/IR/CanonicalDialect.h>
-#include <loco/Service/CanonicalShapeInferenceRule.h>
-#include <loco/Service/ShapeInference.h>
-#include <loco/Service/MultiDialectShapeInferenceRule.h>
-
-#include <locoex/COpDialect.h>
-#include <locoex/Service/COpShapeInferenceRule.h>
-
-namespace exo
-{
-
-bool ShapeInferencePass::run(loco::Graph *g)
-{
- loco::CanonicalShapeInferenceRule canonical_rule;
- locoex::TFLShapeInferenceRule tfl_rule;
- locoex::COpShapeInferenceRule cop_rule;
-
- loco::MultiDialectShapeInferenceRule rules;
-
- rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
- .bind(locoex::TFLDialect::get(), &tfl_rule)
- .bind(locoex::COpDialect::get(), &cop_rule);
-
- return loco::apply(&rules).to(g);
-}
-
-} // namespace exo
diff --git a/compiler/exo-tflite/src/Conversion/ShapeInferencePass.h b/compiler/exo-tflite/src/Conversion/ShapeInferencePass.h
deleted file mode 100644
index 4db9a4307..000000000
--- a/compiler/exo-tflite/src/Conversion/ShapeInferencePass.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __CONVERSION_SHAPE_INFERENCE_PASS_H__
-#define __CONVERSION_SHAPE_INFERENCE_PASS_H__
-
-#include <loco.h>
-#include <logo/Pass.h>
-
-namespace exo
-{
-
-/**
- * @brief Pass to infer shape of nodes
- */
-class ShapeInferencePass : public logo::Pass
-{
-public:
- virtual const char *name(void) const { return "exo::ShapeInferencePass"; }
-
-public:
- bool run(loco::Graph *graph);
-};
-
-} // namespace exo
-
-#endif //__CONVERSION_SHAPE_INFERENCE_PASS_H__
diff --git a/compiler/exo-tflite/src/Conversion/TensorBroadcastConverter.cpp b/compiler/exo-tflite/src/Conversion/TensorBroadcastConverter.cpp
deleted file mode 100644
index 84f74a2dd..000000000
--- a/compiler/exo-tflite/src/Conversion/TensorBroadcastConverter.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TensorBroadcastConverter.h"
-
-#include "Dialect/IR/TFLDialect.h"
-#include "Dialect/IR/TFLNodeVisitor.h"
-
-#include <loco.h>
-#include <loco/IR/CanonicalDialect.h>
-#include <loco/IR/CanonicalNode.h>
-
-#include <set>
-
-namespace
-{
-
-template <class T> loco::TensorBroadcast *input_as_tbc(T *node)
-{
- loco::TensorBroadcast *tbc = dynamic_cast<loco::TensorBroadcast *>(node->x());
- if (tbc == nullptr)
- tbc = dynamic_cast<loco::TensorBroadcast *>(node->y());
-
- return tbc;
-}
-
-struct Collector final : public locoex::TFLNodeMutableVisitor<void>
-{
- using NodePair = std::pair<loco::TensorBroadcast *, loco::Node *>;
-
- void visit(locoex::TFLAdd *node) final
- {
- if (auto tbc = input_as_tbc<locoex::TFLAdd>(node))
- {
- NodePair pair(tbc, node);
- candidates.insert(pair);
- }
- }
-
- // TODO ADD TFLDiv
-
- void visit(locoex::TFLMul *node) final
- {
- if (auto tbc = input_as_tbc<locoex::TFLMul>(node))
- {
- NodePair pair(tbc, node);
- candidates.insert(pair);
- }
- }
-
- // TODO ADD TFLSub
-
- void visit(locoex::TFLNode *) final { return; }
-
- std::set<NodePair> candidates;
-};
-
-bool mapping_condition(Collector::NodePair &)
-{
- // TODO fill condition
-
- return true;
-}
-
-template <class T> void jump_connection(loco::TensorBroadcast *tbc, T *tflnode)
-{
- if (tflnode->x() == tbc)
- tflnode->x(tbc->input());
- else if (tflnode->y() == tbc)
- tflnode->y(tbc->input());
- else
- assert(false);
-
- tbc->input(nullptr);
-}
-
-} // namespace
-
-namespace exo
-{
-
-/**
- * @brief Disconnects loco::TensorBroadcast from the graph if following node
- * is one of binary node: TFLAdd, TFLSub, TFLMul, TFLDiv
- * and meets condition (TBA)
- * @note
- * Before:
- * x --- TensorBroadcast --- TFLXXX --- output
- * y ----------------------/
- *
- * After:
- * --- TensorBroadcast ---
- * x --- TFLXXX --- output
- * y --/
- */
-bool TensorBroadcastConverter::run(loco::Graph *graph)
-{
- Collector collector;
-
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == locoex::TFLDialect::get())
- {
- auto tfl_node = dynamic_cast<locoex::TFLNode *>(node);
- tfl_node->accept(&collector);
- }
- }
-
- bool changed = false;
-
- for (auto pair : collector.candidates)
- {
- if (mapping_condition(pair))
- {
- loco::TensorBroadcast *tensorbroadcast = pair.first;
- if (auto tfladd = dynamic_cast<locoex::TFLAdd *>(pair.second))
- {
- jump_connection<locoex::TFLAdd>(tensorbroadcast, tfladd);
- changed = true;
- }
- // TODO ADD TFLDiv
- else if (auto tflmul = dynamic_cast<locoex::TFLMul *>(pair.second))
- {
- jump_connection<locoex::TFLMul>(tensorbroadcast, tflmul);
- changed = true;
- }
- // TODO ADD TFLSub
- else
- {
- assert(false);
- }
- }
- }
-
- return changed;
-}
-
-} // namespace exo
diff --git a/compiler/exo-tflite/src/Conversion/TypeInferencePass.cpp b/compiler/exo-tflite/src/Conversion/TypeInferencePass.cpp
deleted file mode 100644
index 0996828fa..000000000
--- a/compiler/exo-tflite/src/Conversion/TypeInferencePass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TypeInferencePass.h"
-
-#include "Dialect/IR/TFLDialect.h"
-#include "Dialect/Service/TFLTypeInferenceRule.h"
-
-#include <loco.h>
-#include <loco/IR/CanonicalDialect.h>
-#include <loco/Service/TypeInference.h>
-
-#include <locoex/COpDialect.h>
-#include <locoex/Service/COpTypeInference.h>
-
-namespace exo
-{
-
-bool TypeInferencePass::run(loco::Graph *g)
-{
- loco::CanonicalTypeInferenceRule canonical_rule;
- locoex::TFLTypeInferenceRule tfl_rule;
- locoex::COpTypeInferenceRule cop_rule;
-
- loco::MultiDialectTypeInferenceRule rules;
-
- rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
- .bind(locoex::TFLDialect::get(), &tfl_rule)
- .bind(locoex::COpDialect::get(), &cop_rule);
-
- return loco::apply(&rules).to(g);
-}
-
-} // namespace exo
diff --git a/compiler/exo-tflite/src/Conversion/TypeInferencePass.h b/compiler/exo-tflite/src/Conversion/TypeInferencePass.h
deleted file mode 100644
index 3e0b710a9..000000000
--- a/compiler/exo-tflite/src/Conversion/TypeInferencePass.h
+++ /dev/null
@@ -1,41 +0,0 @@
-
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __CONVERSION_TYPE_INFERENCE_PASS_H__
-#define __CONVERSION_TYPE_INFERENCE_PASS_H__
-
-#include <loco.h>
-#include <logo/Pass.h>
-
-namespace exo
-{
-
-/**
- * @brief Pass to infer type of nodes
- */
-class TypeInferencePass : public logo::Pass
-{
-public:
- virtual const char *name(void) const { return "exo::TypeInferencePass"; }
-
-public:
- bool run(loco::Graph *graph);
-};
-
-} // namespace exo
-
-#endif //__CONVERSION_TYPE_INFERENCE_PASS_H__
diff --git a/compiler/exo-tflite/src/Conversions.h b/compiler/exo-tflite/src/Conversions.h
deleted file mode 100644
index 8f2e428b2..000000000
--- a/compiler/exo-tflite/src/Conversions.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __CONVERSIONS_H__
-#define __CONVERSIONS_H__
-
-#include "Conversion/AvgPool2DConverter.h"
-// TODO loco::ConstGen
-// TODO loco::Conv2D
-// TODO loco::DepthwiseConv2D
-// TODO loco::DepthwiseFilterEncode
-#include "Conversion/EltwiseAddConverter.h"
-// TODO loco::EltwiseDiv
-#include "Conversion/EltwiseMulConverter.h"
-// TODO loco::EltwiseSqrt
-// TODO loco::EltwiseSub
-// TODO loco::FeatureBiasAdd
-// TODO loco::FixedReshape
-#include "Conversion/MaxPool2DConverter.h"
-#include "Conversion/ReluConverter.h"
-// TODO loco::ReLU6
-// TODO loco::Tanh
-// TODO loco::TensorConcat
-// TODO loco::TensorBiasAdd
-#include "Conversion/TensorBroadcastConverter.h"
-// TODO loco::TensorSoftmax
-
-#endif // __CONVERSIONS_H__
diff --git a/compiler/exo-tflite/src/Convert.cpp b/compiler/exo-tflite/src/Convert.cpp
deleted file mode 100644
index 56f3c63bb..000000000
--- a/compiler/exo-tflite/src/Convert.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Convert.h"
-
-#include "Conversions.h"
-
-#include "Conversion/TypeInferencePass.h"
-#include "Conversion/ShapeInferencePass.h"
-#include "ProgressReporter.h"
-#include "Knob.h"
-
-#include <loco.h>
-#include <loco/Service/ShapeInference.h>
-#include <loco/Service/CanonicalShapeInferenceRule.h>
-#include <loco/Service/TypeInference.h>
-
-#include <logo/SimplifyDomainConversionPass.h>
-#include <logo/RemoveDeadNodePass.h>
-#include <logo/RemoveForwardNodePass.h>
-
-#include <logo/Phase.h>
-#include <stdex/Memory.h>
-
-namespace exo
-{
-
-void convert_to_TFLNodes(loco::Graph *graph)
-{
- // run Shape and Type inference must be run before conversion
- loco::CanonicalShapeInferenceRule shape_rule;
- loco::apply(&shape_rule).to(graph);
-
- loco::CanonicalTypeInferenceRule type_rule;
- loco::apply(&type_rule).to(graph);
-
- logo::Phase phase;
- {
- // prepare type and shape before conversion
- phase.emplace_back(stdex::make_unique<TypeInferencePass>());
- phase.emplace_back(stdex::make_unique<ShapeInferencePass>());
-
- // Add converters for canonical nodes. Note: Not all loco canonical nodes are listed.
-
- if (get<Knob::ConvertAvgPool2D>())
- phase.emplace_back(stdex::make_unique<AvgPool2DConverter>());
-
- // TODO loco::ConstGen
- // TODO loco::Conv2D
- // TODO loco::DepthwiseConv2D
- // TODO loco::DepthwiseFilterEncode
- if (get<Knob::ConvertEltwiseAdd>())
- phase.emplace_back(stdex::make_unique<EltwiseAddConverter>());
-
- // TODO loco::EltwiseDiv
- if (get<Knob::ConvertEltwiseMul>())
- phase.emplace_back(stdex::make_unique<EltwiseMulConverter>());
-
- // TODO loco::EltwiseSqrt
- // TODO loco::EltwiseSub
- // TODO loco::FeatureBiasAdd
- // TODO loco::FixedReshape
-
- if (get<Knob::ConvertMaxPool2D>())
- phase.emplace_back(stdex::make_unique<MaxPool2DConverter>());
-
- if (get<Knob::ConvertRelu>())
- phase.emplace_back(stdex::make_unique<ReluConverter>());
-
- // TODO loco::ReLU6
- // TODO loco::Tanh
- // TODO loco::TensorConcat
- // TODO loco::TensorBiasAdd
- if (get<Knob::ConvertTensorBroadcast>())
- phase.emplace_back(stdex::make_unique<TensorBroadcastConverter>());
-
- // TODO loco::TensorSoftmax
-
- // Add optimization below
- phase.emplace_back(stdex::make_unique<logo::SimplifyDomainConversionPass>());
- phase.emplace_back(stdex::make_unique<logo::RemoveForwardNodePass>());
- phase.emplace_back(stdex::make_unique<logo::RemoveDeadNodePass>());
- }
-
- logo::PhaseRunner<logo::PhaseStrategy::Saturate> phase_runner{graph};
-
- ProgressReporter prog(graph, logo::PhaseStrategy::Saturate);
- phase_runner.attach(&prog);
- phase_runner.run(phase);
-
- // TODO Assert if all canonical nodes are converted to TFL node
-}
-
-} // namespace exo
diff --git a/compiler/exo-tflite/src/Dialect/IR/TFLDialect.test.cpp b/compiler/exo-tflite/src/Dialect/IR/TFLDialect.test.cpp
deleted file mode 100644
index 136721e2d..000000000
--- a/compiler/exo-tflite/src/Dialect/IR/TFLDialect.test.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TFLDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFLDialectTest, get)
-{
- using locoex::TFLDialect;
-
- auto d = TFLDialect::get();
-
- // get() SHOULD return a valid(non-null) pointer
- ASSERT_NE(d, nullptr);
- // The return value SHOULD be stable across multiple invocations
- ASSERT_EQ(d, TFLDialect::get());
-}
diff --git a/compiler/exo-tflite/src/Dialect/IR/TFLNodeImpl.h b/compiler/exo-tflite/src/Dialect/IR/TFLNodeImpl.h
deleted file mode 100644
index 849a34266..000000000
--- a/compiler/exo-tflite/src/Dialect/IR/TFLNodeImpl.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __LOCOEX_IR_TFLNODEIMPL_H__
-#define __LOCOEX_IR_TFLNODEIMPL_H__
-
-#include "TFLNode.h"
-#include "TFLNodes.h"
-#include "TFLNodeVisitor.h"
-
-#include <cassert>
-
-namespace locoex
-{
-
-template <typename T> T TFLNode::accept(TFLNodeVisitorBase<T> *v) const
-{
- switch (this->opcode())
- {
-#define TFL_NODE(OPCODE, CLASS) \
- \
- case TFLOpcode::OPCODE: \
- return v->visit(dynamic_cast<const CLASS *>(this));
-
-#include "TFLNodes.lst"
-#undef TFL_NODE
-
- default:
- break;
- }
-
- assert(false);
-}
-
-template <typename T> T TFLNode::accept(TFLNodeMutableVisitorBase<T> *v)
-{
- switch (this->opcode())
- {
-#define TFL_NODE(OPCODE, CLASS) \
- \
- case TFLOpcode::OPCODE: \
- return v->visit(dynamic_cast<CLASS *>(this));
-
-#include "TFLNodes.lst"
-#undef TFL_NODE
-
- default:
- break;
- }
-
- assert(false);
-}
-
-} // namespace locoex
-
-#endif // __LOCOEX_IR_TFLNODEIMPL_H__
diff --git a/compiler/exo-tflite/src/Dialect/IR/TFLNodeVisitor.h b/compiler/exo-tflite/src/Dialect/IR/TFLNodeVisitor.h
deleted file mode 100644
index 038e6004e..000000000
--- a/compiler/exo-tflite/src/Dialect/IR/TFLNodeVisitor.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __LOCOEX_IR_TFLNODE_VISITOR_H__
-#define __LOCOEX_IR_TFLNODE_VISITOR_H__
-
-#include "TFLNode.h"
-#include "TFLNodes.h"
-
-#include <cassert>
-
-namespace locoex
-{
-
-/**
- * DO NOT use this class. Use TFLNodeVisitor instead.
- */
-template <typename T> struct TFLNodeVisitorBase
-{
- virtual ~TFLNodeVisitorBase() = default;
-
-#define TFL_NODE(OPCODE, TFL_CLASS) virtual T visit(const TFL_CLASS *) = 0;
-
-#include "TFLNodes.lst"
-#undef TFL_NODE
-};
-
-template <typename T> struct TFLNodeVisitor : public TFLNodeVisitorBase<T>
-{
- virtual ~TFLNodeVisitor() = default;
-
-#define TFL_NODE(OPCODE, TFL_CLASS) \
- \
- virtual T visit(const TFL_CLASS *node) { return visit(static_cast<const TFLNode *>(node)); }
-
-#include "TFLNodes.lst"
-#undef TFL_NODE
-
- /// @brief Default fallback
- virtual T visit(const TFLNode *) { assert(false); }
-};
-
-/**
- * DO NOT use this class. Use TFLNodeMutableVisitor instead.
- */
-template <typename T> struct TFLNodeMutableVisitorBase
-{
- virtual ~TFLNodeMutableVisitorBase() = default;
-
-#define TFL_NODE(OPCODE, TFL_CLASS) virtual T visit(TFL_CLASS *) = 0;
-
-#include "TFLNodes.lst"
-#undef TFL_NODE
-};
-
-template <typename T> struct TFLNodeMutableVisitor : public TFLNodeMutableVisitorBase<T>
-{
- virtual ~TFLNodeMutableVisitor() = default;
-
-#define TFL_NODE(OPCODE, TFL_CLASS) \
- \
- virtual T visit(TFL_CLASS *node) { return visit(static_cast<TFLNode *>(node)); }
-
-#include "TFLNodes.lst"
-#undef TFL_NODE
-
- /// @brief Default fallback
- virtual T visit(TFLNode *) { assert(false); }
-};
-
-} // namespace locoex
-
-#endif // __LOCOEX_IR_TFLNODE_VISITOR_H__
diff --git a/compiler/exo-tflite/src/Dialect/IR/TFLNodes.cpp b/compiler/exo-tflite/src/Dialect/IR/TFLNodes.cpp
deleted file mode 100644
index 7287fc505..000000000
--- a/compiler/exo-tflite/src/Dialect/IR/TFLNodes.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TFLNodes.h"
-
-#include <loco.h>
-
-#include <cassert>
-
-namespace locoex
-{
-
-template <loco::DataType DT> uint32_t TFLConst::size(void) const
-{
- assert(dtype() == DT);
- assert(_data.size() % sizeof(typename loco::DataTypeImpl<DT>::Type) == 0);
- return _data.size() / sizeof(typename loco::DataTypeImpl<DT>::Type);
-}
-
-template <loco::DataType DT> void TFLConst::size(uint32_t l)
-{
- assert(dtype() == DT);
- _data.resize(l * sizeof(typename loco::DataTypeImpl<DT>::Type));
-}
-
-template <loco::DataType DT>
-const typename loco::DataTypeImpl<DT>::Type &TFLConst::at(uint32_t n) const
-{
- assert(dtype() == DT);
- assert(n < size<DT>());
- return *(reinterpret_cast<const typename loco::DataTypeImpl<DT>::Type *>(_data.data()) + n);
-}
-
-template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &TFLConst::at(uint32_t n)
-{
- assert(dtype() == DT);
- assert(n < size<DT>());
- return *(reinterpret_cast<typename loco::DataTypeImpl<DT>::Type *>(_data.data()) + n);
-}
-
-#define INSTANTIATE(DT) \
- template uint32_t TFLConst::size<DT>(void) const; \
- template void TFLConst::size<DT>(uint32_t); \
- template const typename loco::DataTypeImpl<DT>::Type &TFLConst::at<DT>(uint32_t) const; \
- template typename loco::DataTypeImpl<DT>::Type &TFLConst::at<DT>(uint32_t);
-
-INSTANTIATE(loco::DataType::S32);
-INSTANTIATE(loco::DataType::FLOAT32);
-
-#undef INSTANTIATE
-
-} // namespace locoex
diff --git a/compiler/exo-tflite/src/Dialect/IR/TFLNodes.h b/compiler/exo-tflite/src/Dialect/IR/TFLNodes.h
deleted file mode 100644
index f12b62d89..000000000
--- a/compiler/exo-tflite/src/Dialect/IR/TFLNodes.h
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __LOCOEX_IR_TFLNODES_H__
-#define __LOCOEX_IR_TFLNODES_H__
-
-#include "TFLNodeDecl.h"
-#include "TFLOpcode.h"
-
-#include <loco/IR/Node.h>
-#include <loco/IR/NodeMixins.h>
-#include <loco/IR/DataTypeTraits.h>
-
-#include <array>
-
-namespace locoex
-{
-
-/**
- * @brief Nodes with the fixed number of inputs
- */
-template <unsigned N, typename Base> class FixedArityNode : public Base
-{
-public:
- FixedArityNode()
- {
- for (uint32_t n = 0; n < N; ++n)
- {
- _args[n] = std::unique_ptr<loco::Use>(new loco::Use{this});
- }
- }
-
- virtual ~FixedArityNode() = default;
-
-public:
- unsigned arity(void) const final { return N; }
-
- loco::Node *arg(uint32_t n) const final { return _args.at(n)->node(); }
-
- void drop(void) final
- {
- for (uint32_t n = 0; n < N; ++n)
- {
- _args.at(n)->node(nullptr);
- }
- }
-
-protected:
- // This API allows inherited classes to access "_args" field.
- loco::Use *at(unsigned n) const { return _args.at(n).get(); }
-
-private:
- std::array<std::unique_ptr<loco::Use>, N> _args;
-};
-
-enum class FusedActFunc
-{
- UNDEFINED, // This is not defined by TFLite. This was added to prevent programming error.
- NONE,
- RELU,
- RELU6
-};
-
-enum class Padding
-{
- UNDEFINED, // This is not defined by TFLite. This was added to prevent programming error.
- SAME,
- VALID,
-};
-
-class Filter final
-{
-public:
- Filter() : _w(1), _h(1) {}
-
- int32_t w() const { return _w; }
- void w(int32_t w) { _w = w; }
-
- int32_t h() const { return _h; }
- void h(int32_t h) { _h = h; }
-
-private:
- int32_t _w;
- int32_t _h;
-};
-
-class Stride final
-{
-public:
- Stride() : _w(1), _h(1) {}
-
- int32_t w() const { return _w; }
- void w(int32_t w) { _w = w; }
-
- int32_t h() const { return _h; }
- void h(int32_t h) { _h = h; }
-
-private:
- int32_t _w;
- int32_t _h;
-};
-
-/**
- * @brief ADD in TensorFlow Lite
- */
-class TFLAdd final : public FixedArityNode<2, TFLNodeImpl<TFLOpcode::ADD>>
-{
-public:
- TFLAdd() = default;
-
-public:
- loco::Node *x(void) const { return at(0)->node(); }
- void x(loco::Node *node) { at(0)->node(node); }
-
- loco::Node *y(void) const { return at(1)->node(); }
- void y(loco::Node *node) { at(1)->node(node); }
-};
-
-/**
- * @brief AVERAGE_POOL_2D in TensorFlow Lite
- */
-class TFLAveragePool2D final : public FixedArityNode<1, TFLNodeImpl<TFLOpcode::AVERAGE_POOL_2D>>
-{
-public:
- TFLAveragePool2D()
- : _fused_act_fun(FusedActFunc::UNDEFINED), _padding(Padding::UNDEFINED) { /* empty */}
-
-public:
- loco::Node *value(void) const { return at(0)->node(); }
- void value(loco::Node *node) { at(0)->node(node); }
-
- FusedActFunc fusedActivationFunction() const { return _fused_act_fun; }
- void fusedActivationFunction(FusedActFunc fused_act_fun) { _fused_act_fun = fused_act_fun; }
-
- Padding padding() const { return _padding; }
- void padding(Padding padding) { _padding = padding; }
-
- const Filter *filter(void) const { return &_filter; }
- Filter *filter(void) { return &_filter; }
-
- const Stride *stride(void) const { return &_stride; }
- Stride *stride(void) { return &_stride; }
-
-private:
- FusedActFunc _fused_act_fun;
- Padding _padding;
- Stride _stride;
- Filter _filter;
-};
-
-// TODO TFLConcatenation
-
-/**
- * @brief Class to build tensor data
- * @note This will not be exported as a specific op
- */
-class TFLConst final : public FixedArityNode<0, TFLNodeImpl<TFLOpcode::NOP_CONSTGEN>>,
- public loco::NodeMixin<loco::NodeTrait::DataType>,
- public loco::NodeMixin<loco::NodeTrait::TensorShape>
-{
-public:
- TFLConst() = default;
-
-public:
- template <loco::DataType DT> uint32_t size(void) const;
- template <loco::DataType DT> void size(uint32_t size);
- template <loco::DataType DT> const typename loco::DataTypeImpl<DT>::Type &at(uint32_t n) const;
- template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &at(uint32_t n);
-
-private:
- std::vector<uint8_t> _data;
-};
-
-// TODO TFLConv2D
-
-// TODO TFLDepthwiseConv2D
-
-/**
- * @brief DIV in TensorFlow Lite
- */
-class TFLDiv final : public FixedArityNode<2, TFLNodeImpl<TFLOpcode::DIV>>
-{
-public:
- TFLDiv() = default;
-
-public:
- loco::Node *x(void) const { return at(0)->node(); }
- void x(loco::Node *node) { at(0)->node(node); }
-
- loco::Node *y(void) const { return at(1)->node(); }
- void y(loco::Node *node) { at(1)->node(node); }
-};
-
-/**
- * @brief MAX_POOL_2D in TensorFlow Lite
- */
-class TFLMaxPool2D final : public FixedArityNode<1, TFLNodeImpl<TFLOpcode::MAX_POOL_2D>>
-{
-public:
- TFLMaxPool2D()
- : _fused_act_fun(FusedActFunc::UNDEFINED), _padding(Padding::UNDEFINED) { /* empty */}
-
-public:
- loco::Node *value(void) const { return at(0)->node(); }
- void value(loco::Node *node) { at(0)->node(node); }
-
- FusedActFunc fusedActivationFunction() const { return _fused_act_fun; }
- void fusedActivationFunction(FusedActFunc fused_act_fun) { _fused_act_fun = fused_act_fun; }
-
- Padding padding() const { return _padding; }
- void padding(Padding padding) { _padding = padding; }
-
- const Filter *filter(void) const { return &_filter; }
- Filter *filter(void) { return &_filter; }
-
- const Stride *stride(void) const { return &_stride; }
- Stride *stride(void) { return &_stride; }
-
-private:
- FusedActFunc _fused_act_fun;
- Padding _padding;
- Stride _stride;
- Filter _filter;
-};
-
-/**
- * @brief MUL in TensorFlow Lite
- */
-class TFLMul final : public FixedArityNode<2, TFLNodeImpl<TFLOpcode::MUL>>
-{
-public:
- TFLMul() = default;
-
-public:
- loco::Node *x(void) const { return at(0)->node(); }
- void x(loco::Node *node) { at(0)->node(node); }
-
- loco::Node *y(void) const { return at(1)->node(); }
- void y(loco::Node *node) { at(1)->node(node); }
-};
-
-class TFLRelu final : public FixedArityNode<1, TFLNodeImpl<TFLOpcode::RELU>>
-{
-public:
- TFLRelu() = default;
-
-public:
- loco::Node *features(void) const { return at(0)->node(); }
- void features(loco::Node *node) { at(0)->node(node); }
-};
-
-// TODO TFLRelu6
-
-// TODO TFLReshape
-
-// TODO TFLSoftmax
-
-// TODO TFLSqrt
-
-/**
- * @brief SUB in TensorFlow Lite
- */
-class TFLSub final : public FixedArityNode<2, TFLNodeImpl<TFLOpcode::SUB>>
-{
-public:
- TFLSub() = default;
-
-public:
- loco::Node *x(void) const { return at(0)->node(); }
- void x(loco::Node *node) { at(0)->node(node); }
-
- loco::Node *y(void) const { return at(1)->node(); }
- void y(loco::Node *node) { at(1)->node(node); }
-};
-
-// TODO TFLTanh
-
-// TODO TFLTranspose
-
-// TODO define more children of TFLNode
-
-} // namespace locoex
-
-#endif // __LOCOEX_IR_TFLNODES_H__
diff --git a/compiler/exo-tflite/src/Dialect/IR/TFLNodes.lst b/compiler/exo-tflite/src/Dialect/IR/TFLNodes.lst
deleted file mode 100644
index 9e9c410f1..000000000
--- a/compiler/exo-tflite/src/Dialect/IR/TFLNodes.lst
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef TFL_NODE
-#error "Define TFL_NODE"
-#endif // TFL_NODE
-
-//
-// PLEASE SORT NODE DECLS IN ALPHABETICAL ORDER
-//
-TFL_NODE(ADD, locoex::TFLAdd)
-TFL_NODE(AVERAGE_POOL_2D, locoex::TFLAveragePool2D)
-// TODO TFLConcatenation
-TFL_NODE(NOP_CONSTGEN, locoex::TFLConst)
-// TODO TFLConv2D
-// TODO TFLDepthwiseConv2D
-TFL_NODE(DIV, locoex::TFLDiv)
-TFL_NODE(MAX_POOL_2D, locoex::TFLMaxPool2D)
-TFL_NODE(MUL, locoex::TFLMul)
-TFL_NODE(RELU, locoex::TFLRelu)
-// TODO TFLRelu6
-// TODO TFLReshape
-// TODO TFLSoftmax
-// TODO TFLSqrt
-TFL_NODE(SUB, locoex::TFLSub)
-// TODO TFLTanh
-// TODO TFLTranspose
diff --git a/compiler/exo-tflite/src/Dialect/IR/TFLNodes.test.cpp b/compiler/exo-tflite/src/Dialect/IR/TFLNodes.test.cpp
deleted file mode 100644
index b52b4525e..000000000
--- a/compiler/exo-tflite/src/Dialect/IR/TFLNodes.test.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TFLNodes.h"
-
-#include "TFLDialect.h"
-#include "TFLOpcode.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFLAddTest, constructor)
-{
- locoex::TFLAdd add_node;
-
- ASSERT_EQ(add_node.dialect(), locoex::TFLDialect::get());
- ASSERT_EQ(add_node.opcode(), locoex::TFLOpcode::ADD);
-
- ASSERT_EQ(add_node.x(), nullptr);
- ASSERT_EQ(add_node.y(), nullptr);
-}
-
-// TODO TFLAveragePool2D
-
-// TODO TFLConcatenation
-
-// TODO TFLConv2D
-
-// TODO TFLDepthwiseConv2D
-
-TEST(TFLDivTest, constructor)
-{
- locoex::TFLDiv div_node;
-
- ASSERT_EQ(div_node.dialect(), locoex::TFLDialect::get());
- ASSERT_EQ(div_node.opcode(), locoex::TFLOpcode::DIV);
-
- ASSERT_EQ(div_node.x(), nullptr);
- ASSERT_EQ(div_node.y(), nullptr);
-}
-
-// TODO TFLMaxPool2D
-
-TEST(TFLMulTest, constructor)
-{
- locoex::TFLMul mul_node;
-
- ASSERT_EQ(mul_node.dialect(), locoex::TFLDialect::get());
- ASSERT_EQ(mul_node.opcode(), locoex::TFLOpcode::MUL);
-
- ASSERT_EQ(mul_node.x(), nullptr);
- ASSERT_EQ(mul_node.y(), nullptr);
-}
-
-TEST(TFLReluTest, constructor)
-{
- locoex::TFLRelu relu_node;
-
- ASSERT_EQ(relu_node.dialect(), locoex::TFLDialect::get());
- ASSERT_EQ(relu_node.opcode(), locoex::TFLOpcode::RELU);
-
- ASSERT_EQ(relu_node.features(), nullptr);
-}
-
-// TODO TFLRelu6
-
-// TODO TFLReshape
-
-// TODO TFLSoftmax
-
-// TODO TFLSqrt
-
-TEST(TFLSubTest, constructor)
-{
- locoex::TFLSub sub_node;
-
- ASSERT_EQ(sub_node.dialect(), locoex::TFLDialect::get());
- ASSERT_EQ(sub_node.opcode(), locoex::TFLOpcode::SUB);
-
- ASSERT_EQ(sub_node.x(), nullptr);
- ASSERT_EQ(sub_node.y(), nullptr);
-}
-
-// TODO TFLTanh
-
-// TODO TFLTranspose
diff --git a/compiler/exo-tflite/src/Dialect/Service/TFLShapeInferenceRule.cpp b/compiler/exo-tflite/src/Dialect/Service/TFLShapeInferenceRule.cpp
deleted file mode 100644
index 7b65705d4..000000000
--- a/compiler/exo-tflite/src/Dialect/Service/TFLShapeInferenceRule.cpp
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TFLShapeInferenceRule.h"
-
-#include "Dialect/IR/TFLNodes.h"
-#include "Dialect/IR/TFLDialect.h"
-#include "Dialect/IR/TFLNodeVisitor.h"
-
-#include "ShapeInference.h"
-
-#include "Check.h"
-
-#include <algorithm>
-#include <cassert>
-#include <stdexcept>
-
-namespace
-{
-
-// Call this for TFLAvgPool2D and TFLMaxPool2D only
-template <class Pool2DType> loco::NodeShape infer_pool_2d_shape(const Pool2DType *node)
-{
- EXO_ASSERT(loco::shape_known(node->value()), "Shape must be known");
-
- auto ifm_shape = loco::shape_get(node->value()).template as<loco::TensorShape>();
-
- uint32_t input_height = ifm_shape.dim(1).value();
- uint32_t input_width = ifm_shape.dim(2).value();
- uint32_t stride_height = node->stride()->h();
- uint32_t stride_width = node->stride()->w();
- uint32_t window_height = node->filter()->h();
- uint32_t window_width = node->filter()->w();
- uint32_t dilation_height = 1; // dilation for TFLAvgPool2D and TFLMaxPool2D is 1
- uint32_t dilation_width = 1;
- uint32_t effective_window_height = dilation_height * (window_height - 1) + 1;
- uint32_t effective_window_width = dilation_width * (window_width - 1) + 1;
-
- uint32_t output_height;
- uint32_t output_width;
-
- if (node->padding() == locoex::Padding::VALID)
- {
- output_height = (input_height + stride_height - effective_window_height) / stride_height;
- output_width = (input_width + stride_width - effective_window_width) / stride_width;
- }
- else if (node->padding() == locoex::Padding::SAME)
- {
- output_height = (input_height + stride_height - 1) / stride_height;
- output_width = (input_width + stride_width - 1) / stride_width;
- }
- else
- EXO_ASSERT(false, "Wrong padding type");
-
- loco::TensorShape ofm_shape;
- ofm_shape.rank(4);
- ofm_shape.dim(0) = ifm_shape.dim(0);
- ofm_shape.dim(1) = output_height;
- ofm_shape.dim(2) = output_width;
- ofm_shape.dim(3) = ifm_shape.dim(3);
-
- return loco::NodeShape{ofm_shape};
-}
-
-/**
- * @brief Create a higher-rank TensorShape following NumPy broadcasting semantics
- *
- * HOW TO USE:
- *
- * auto expanded_tensor_shape = expand(tensor_shape).to(N);
- */
-class TensorShapeExpander
-{
-public:
- TensorShapeExpander(const loco::TensorShape &shape) : _shape{shape}
- {
- // DO NOTHING
- }
-
-public:
- loco::TensorShape to(uint32_t output_rank)
- {
- auto const &input_shape = _shape;
- uint32_t const input_rank = input_shape.rank();
-
- assert(input_rank <= output_rank && "Cannot shrink rank");
- uint32_t const axis_shift = output_rank - input_rank;
-
- loco::TensorShape output_shape;
-
- output_shape.rank(output_rank);
- for (uint32_t axis = 0; axis < output_rank; ++axis)
- {
- output_shape.dim(axis) = (axis < axis_shift) ? 1 : input_shape.dim(axis - axis_shift);
- }
-
- return output_shape;
- }
-
-private:
- const loco::TensorShape _shape;
-};
-
-/**
- * @breif Expand shape x and y to same rank by align right and filling with 1
- */
-void expand_rank(loco::TensorShape &x, loco::TensorShape &y)
-{
- auto x_rank = x.rank();
- auto y_rank = y.rank();
-
- if (x_rank == y_rank)
- return;
-
- TensorShapeExpander x_exp(x);
- TensorShapeExpander y_exp(y);
-
- auto xy_rank = std::max(x_rank, y_rank);
-
- x = x_rank > y_rank ? x : x_exp.to(xy_rank);
- y = y_rank > x_rank ? y : y_exp.to(xy_rank);
-}
-
-/**
- * @breif Returns shape of expanded dimension of input x and y having same rank
- */
-loco::TensorShape expand_dimension(const loco::TensorShape &x, const loco::TensorShape &y)
-{
- assert(x.rank() == y.rank());
-
- auto rank = x.rank();
-
- loco::TensorShape output_shape;
-
- output_shape.rank(rank);
- for (uint32_t axis = 0; axis < rank; ++axis)
- {
- assert(x.dim(axis).known() && y.dim(axis).known());
-
- auto x_dim = x.dim(axis).value();
- auto y_dim = y.dim(axis).value();
-
- // each dimension of x and y should be same or one must be 1 if different
- if (!((x_dim == y_dim) || (x_dim == 1 || y_dim == 1)))
- throw std::runtime_error("Cannot produce expand_dimension of two shapes");
-
- output_shape.dim(axis) = std::max(x_dim, y_dim);
- }
-
- return output_shape;
-}
-
-loco::TensorShape broadcast_shape(const loco::TensorShape &x, const loco::TensorShape &y)
-{
- auto x_match = x;
- auto y_match = y;
-
- expand_rank(x_match, y_match);
-
- auto output_shape = expand_dimension(x_match, y_match);
-
- return output_shape;
-}
-
-/**
- * @brief Class to infer the shape of TFLNode
- *
- * @note All TFLNode's inputs and outouts are always loco::Domain::Tensor
- */
-class ShapeInferenceAlgorithm final : public locoex::TFLNodeVisitor<loco::NodeShape>
-{
-public:
- // TODO Remove this method
- loco::NodeShape visit(const locoex::TFLNode *node) final
- {
- if (loco::shape_known(node)) // if shape was already inferred by inference rule
- {
- assert(loco::shape_get(node).domain() == loco::Domain::Tensor);
-
- return loco::shape_get(node);
- }
- else
- {
- assert(false && "nyi");
- }
- }
-
- loco::NodeShape visit(const locoex::TFLAdd *node) final
- {
- auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
- auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>();
-
- auto output_shape = broadcast_shape(x_shape, y_shape);
-
- return loco::NodeShape{output_shape};
- }
-
- loco::NodeShape visit(const locoex::TFLAveragePool2D *node) final
- {
- return infer_pool_2d_shape(node);
- }
-
- // TODO TFLConcatenation
-
- // TODO TFLConv2D
-
- // TODO TFLDepthwiseConv2D
-
- // TODO TFLDiv
-
- loco::NodeShape visit(const locoex::TFLMaxPool2D *node) final
- {
- return infer_pool_2d_shape(node);
- }
-
- loco::NodeShape visit(const locoex::TFLMul *node) final
- {
- auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
- auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>();
-
- auto output_shape = broadcast_shape(x_shape, y_shape);
-
- return loco::NodeShape{output_shape};
- }
-
- // TODO TFLNop
-
- loco::NodeShape visit(const locoex::TFLRelu *node) final
- {
- auto input_shape = loco::shape_get(node->features()).as<loco::TensorShape>();
-
- return loco::NodeShape{input_shape};
- }
-
- // TODO TFLRelu6
-
- // TODO TFLReshape
-
- // TODO TFLSoftmax
-
- // TODO TFLSqrt
-
- // TODO TFLSub
-
- // TODO TFLTanh
-
- // TODO TFLTranspose
-};
-
-} // namespace
-
-namespace locoex
-{
-
-bool TFLShapeInferenceRule::recognize(const loco::Dialect *d) const
-{
- return TFLDialect::get() == d;
-}
-
-bool TFLShapeInferenceRule::infer(const loco::Node *node, loco::NodeShape &shape) const
-{
- assert(node->dialect() == TFLDialect::get());
- assert(dynamic_cast<const TFLNode *>(node) != nullptr);
-
- ShapeInferenceAlgorithm alg;
- shape = dynamic_cast<const TFLNode *>(node)->accept(&alg);
-
- return true;
-}
-
-} // namespace locoex
diff --git a/compiler/exo-tflite/src/Dialect/Service/TFLShapeInferenceRule.test.cpp b/compiler/exo-tflite/src/Dialect/Service/TFLShapeInferenceRule.test.cpp
deleted file mode 100644
index bdacaf0b5..000000000
--- a/compiler/exo-tflite/src/Dialect/Service/TFLShapeInferenceRule.test.cpp
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestGraph.h"
-
-#include "Dialect/IR/TFLNodes.h"
-#include "Dialect/IR/TFLDialect.h"
-#include "Dialect/Service/TFLShapeInferenceRule.h"
-
-#include "Conversion/ShapeInferencePass.h"
-
-#include <loco.h>
-#include <loco/IR/CanonicalDialect.h>
-#include <loco/Service/ShapeInference.h>
-#include <loco/Service/CanonicalShapeInferenceRule.h>
-#include <loco/Service/MultiDialectShapeInferenceRule.h>
-
-#include <stdex/Memory.h>
-
-#include <gtest/gtest.h>
-
-TEST(TFLShapeInferenceRuleTest, minimal_with_TFLRelu)
-{
- // Create a simple network
- auto g = loco::make_graph();
-
- auto pull_node = g->nodes()->create<loco::Pull>();
- {
- pull_node->rank(2);
- pull_node->dim(0) = 3;
- pull_node->dim(1) = 4;
- }
-
- auto tfl_node = g->nodes()->create<locoex::TFLRelu>();
- tfl_node->features(pull_node);
-
- auto push_node = g->nodes()->create<loco::Push>();
- push_node->from(tfl_node);
-
- auto input = g->inputs()->create();
- {
- input->name("input");
- loco::link(input, pull_node);
- }
- auto output = g->outputs()->create();
- {
- output->name("output");
- loco::link(output, push_node);
- }
-
- // pre-check
- ASSERT_FALSE(loco::shape_known(tfl_node));
-
- // shape inference
- locoex::TFLShapeInferenceRule tfl_rule;
- loco::CanonicalShapeInferenceRule canonical_rule;
- loco::MultiDialectShapeInferenceRule rules;
-
- rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
- .bind(locoex::TFLDialect::get(), &tfl_rule);
-
- loco::apply(&rules).to(g.get());
-
- // Verify
- {
- ASSERT_TRUE(loco::shape_known(tfl_node));
- ASSERT_EQ(loco::shape_get(tfl_node).domain(), loco::Domain::Tensor);
-
- auto shape = loco::shape_get(tfl_node).as<loco::TensorShape>();
- ASSERT_EQ(shape.rank(), 2);
- ASSERT_EQ(shape.dim(0), 3);
- ASSERT_EQ(shape.dim(1), 4);
- }
-}
-
-// based on the case shown in
-// https://www.corvil.com/kb/what-is-the-difference-between-same-and-valid-padding-in-tf-nn-max-pool-of-tensorflow
-TEST(TFLShapeInferenceRuleTest, avgpool2d_valid)
-{
- exo::test::PullPushGraph<locoex::TFLAveragePool2D> test_graph;
- auto pull = test_graph.pull;
- {
- pull->shape({1, 4, 3, 1});
- }
- auto tfl_node = test_graph.middle_node;
- {
- tfl_node->filter()->h(2);
- tfl_node->filter()->w(2);
- tfl_node->stride()->h(2);
- tfl_node->stride()->w(2);
- tfl_node->fusedActivationFunction(locoex::FusedActFunc::NONE);
- tfl_node->padding(locoex::Padding::VALID);
- }
- ASSERT_FALSE(loco::shape_known(tfl_node));
-
- // shape inference
- locoex::TFLShapeInferenceRule tfl_rule;
- loco::CanonicalShapeInferenceRule canonical_rule;
- loco::MultiDialectShapeInferenceRule rules;
-
- rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
- .bind(locoex::TFLDialect::get(), &tfl_rule);
-
- loco::apply(&rules).to(test_graph.g.get());
-
- // Verify
- {
- ASSERT_TRUE(loco::shape_known(tfl_node));
- ASSERT_EQ(loco::shape_get(tfl_node).domain(), loco::Domain::Tensor);
-
- auto shape = loco::shape_get(tfl_node).as<loco::TensorShape>();
- ASSERT_EQ(shape.rank(), 4);
- ASSERT_EQ(shape.dim(0).value(), 1);
- ASSERT_EQ(shape.dim(1).value(), 2);
- ASSERT_EQ(shape.dim(2).value(), 1);
- ASSERT_EQ(shape.dim(3).value(), 1);
- }
-}
-
-TEST(TFLShapeInferenceRuleTest, avgpool2d_same)
-{
- exo::test::PullPushGraph<locoex::TFLAveragePool2D> test_graph;
- auto pull = test_graph.pull;
- {
- pull->shape({1, 4, 3, 1});
- }
-
- auto tfl_node = test_graph.middle_node;
- {
- tfl_node->filter()->h(2);
- tfl_node->filter()->w(2);
- tfl_node->stride()->h(2);
- tfl_node->stride()->w(2);
- tfl_node->fusedActivationFunction(locoex::FusedActFunc::NONE);
- tfl_node->padding(locoex::Padding::SAME);
- }
-
- ASSERT_FALSE(loco::shape_known(tfl_node));
-
- // shape inference
- locoex::TFLShapeInferenceRule tfl_rule;
- loco::CanonicalShapeInferenceRule canonical_rule;
- loco::MultiDialectShapeInferenceRule rules;
-
- rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
- .bind(locoex::TFLDialect::get(), &tfl_rule);
-
- loco::apply(&rules).to(test_graph.g.get());
-
- // Verify
- {
- ASSERT_TRUE(loco::shape_known(tfl_node));
- ASSERT_EQ(loco::shape_get(tfl_node).domain(), loco::Domain::Tensor);
-
- auto shape = loco::shape_get(tfl_node).as<loco::TensorShape>();
- ASSERT_EQ(shape.rank(), 4);
- ASSERT_EQ(shape.dim(0).value(), 1);
- ASSERT_EQ(shape.dim(1).value(), 2);
- ASSERT_EQ(shape.dim(2).value(), 2);
- ASSERT_EQ(shape.dim(3).value(), 1);
- }
-}
-
-/**
- * @note Function to test: Shape inference of two different input shapes
- *
- * Rank expansion to higher input side
- * x(2,1,5) + y(3,5) --> x(2,1,5) + y(1,3,5)
- * Do output shape inference like numpy
- * x(2,1,5) + y(1,3,5) --> output(2,3,5)
- * For each axis, dim value should be same OR one of them should be 1
- */
-TEST(TFLShapeInferenceRuleTest, TFAdd_shapeinf_different)
-{
- auto g = loco::make_graph();
-
- auto x_node = g->nodes()->create<loco::Pull>();
- {
- x_node->rank(3);
- x_node->dim(0) = 2;
- x_node->dim(1) = 1;
- x_node->dim(2) = 5;
- }
- auto y_node = g->nodes()->create<loco::Pull>();
- {
- y_node->rank(2);
- y_node->dim(0) = 3;
- y_node->dim(1) = 5;
- }
- auto tfl_node = g->nodes()->create<locoex::TFLAdd>();
- {
- tfl_node->x(x_node);
- tfl_node->y(y_node);
- }
- auto push_node = g->nodes()->create<loco::Push>();
- {
- push_node->from(tfl_node);
- }
-
- auto x_input = g->inputs()->create();
- {
- x_input->name("x");
- loco::link(x_input, x_node);
- }
- auto y_input = g->inputs()->create();
- {
- y_input->name("y");
- loco::link(y_input, y_node);
- }
- auto output = g->outputs()->create();
- {
- output->name("output");
- loco::link(output, push_node);
- }
-
- // pre-check
- ASSERT_FALSE(loco::shape_known(tfl_node));
-
- exo::ShapeInferencePass pass;
- while (pass.run(g.get()) == true)
- {
- ;
- }
-
- // Verify
- {
- ASSERT_TRUE(loco::shape_known(tfl_node));
- ASSERT_EQ(loco::shape_get(tfl_node).domain(), loco::Domain::Tensor);
-
- auto shape = loco::shape_get(tfl_node).as<loco::TensorShape>();
- ASSERT_EQ(shape.rank(), 3);
- ASSERT_EQ(shape.dim(0), 2);
- ASSERT_EQ(shape.dim(1), 3);
- ASSERT_EQ(shape.dim(2), 5);
- }
-}
diff --git a/compiler/exo-tflite/src/Dialect/Service/TFLTypeInference.test.cpp b/compiler/exo-tflite/src/Dialect/Service/TFLTypeInference.test.cpp
deleted file mode 100644
index 9eef2f699..000000000
--- a/compiler/exo-tflite/src/Dialect/Service/TFLTypeInference.test.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Dialect/IR/TFLNodes.h"
-#include "Dialect/IR/TFLDialect.h"
-#include "Dialect/Service/TFLTypeInferenceRule.h"
-
-#include <loco.h>
-#include <loco/IR/CanonicalDialect.h>
-#include <loco/Service/TypeInference.h>
-
-#include <stdex/Memory.h>
-
-#include <gtest/gtest.h>
-
-TEST(TFLTypeInferenceRuleTest, minimal_with_TFLRelu)
-{
- // Create a simple network
- auto g = loco::make_graph();
-
- auto pull_node = g->nodes()->create<loco::Pull>();
- {
- pull_node->dtype(loco::DataType::S32);
- }
-
- auto tfl_node = g->nodes()->create<locoex::TFLRelu>();
- tfl_node->features(pull_node);
-
- auto push_node = g->nodes()->create<loco::Push>();
- push_node->from(tfl_node);
-
- auto input = g->inputs()->create();
- {
- input->name("input");
- loco::link(input, pull_node);
- }
- auto output = g->outputs()->create();
- {
- output->name("output");
- loco::link(output, push_node);
- }
-
- // pre-check
- ASSERT_FALSE(loco::dtype_known(tfl_node));
-
- // type inference
- locoex::TFLTypeInferenceRule tfl_rule;
- loco::CanonicalTypeInferenceRule canon_rule;
- loco::MultiDialectTypeInferenceRule rules;
-
- rules.bind(loco::CanonicalDialect::get(), &canon_rule);
- rules.bind(locoex::TFLDialect::get(), &tfl_rule);
-
- loco::apply(&rules).to(g.get());
-
- // Verify
- ASSERT_TRUE(loco::dtype_known(tfl_node));
- auto type = loco::dtype_get(tfl_node);
- ASSERT_EQ(type, loco::DataType::S32);
-}
diff --git a/compiler/exo-tflite/src/Dialect/Service/TFLTypeInferenceRule.cpp b/compiler/exo-tflite/src/Dialect/Service/TFLTypeInferenceRule.cpp
deleted file mode 100644
index 5be25b041..000000000
--- a/compiler/exo-tflite/src/Dialect/Service/TFLTypeInferenceRule.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TFLTypeInferenceRule.h"
-
-#include "Dialect/IR/TFLDialect.h"
-#include "Dialect/IR/TFLNodeVisitor.h"
-#include "Dialect/IR/TFLNodes.h"
-
-#include <cassert>
-
-namespace
-{
-
-struct TypeInferenceAlgorithm final : public locoex::TFLNodeVisitor<loco::DataType>
-{
- // TODO Remove this method
- loco::DataType visit(const locoex::TFLNode *tfl_node) final
- {
- if (loco::dtype_known(tfl_node)) // if type was already found by inference rule
- {
- return loco::dtype_get(tfl_node);
- }
- else
- {
- assert(false && "not yet implemented");
- }
- }
-
- loco::DataType visit(const locoex::TFLAdd *node) final { return loco::dtype_get(node->x()); }
-
- loco::DataType visit(const locoex::TFLAveragePool2D *node) final
- {
- return loco::dtype_get(node->value());
- }
-
- // TODO TFLConcatenation
-
- // TODO TFLConv2D
-
- // TODO TFLDepthwiseConv2D
-
- // TODO TFLDiv
-
- loco::DataType visit(const locoex::TFLMaxPool2D *node) final
- {
- return loco::dtype_get(node->value());
- }
-
- loco::DataType visit(const locoex::TFLMul *node) final { return loco::dtype_get(node->x()); }
-
- // TODO TFLNop
-
- loco::DataType visit(const locoex::TFLRelu *node) final
- {
- return loco::dtype_get(node->features());
- }
-
- // TODO TFLRelu6
-
- // TODO TFLReshape
-
- // TODO TFLSoftmax
-
- // TODO TFLSqrt
-
- // TODO TFLSub
-
- // TODO TFLTanh
-
- // TODO TFLTranspose
-};
-
-} // namespace
-
-namespace locoex
-{
-
-bool TFLTypeInferenceRule::recognize(const loco::Dialect *d) const
-{
- return TFLDialect::get() == d;
-}
-
-bool TFLTypeInferenceRule::infer(const loco::Node *node, loco::DataType &dtype) const
-{
- assert(node->dialect() == TFLDialect::get());
-
- TypeInferenceAlgorithm alg;
-
- dtype = dynamic_cast<const TFLNode *>(node)->accept(&alg);
- assert(dtype != loco::DataType::Unknown);
-
- return true;
-}
-
-} // namespace locoex
diff --git a/compiler/exo-tflite/src/ExporterUtils.cpp b/compiler/exo-tflite/src/ExporterUtils.cpp
deleted file mode 100644
index 84143d7e9..000000000
--- a/compiler/exo-tflite/src/ExporterUtils.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ExporterUtils.h"
-
-ShapeDescription to_shape_description(const loco::TensorShape &shape)
-{
- ShapeDescription res;
-
- res._rank_known = true;
-
- res._dims.resize(shape.rank());
- for (uint32_t axis = 0; axis < shape.rank(); ++axis)
- {
- // All the dimensions SHOULD be known
- assert(shape.dim(axis).known());
- res._dims.at(axis) = shape.dim(axis).value();
- }
-
- return res;
-}
-
-ShapeDescription to_shape_description(const loco::FeatureShape &shape)
-{
- ShapeDescription res;
-
- res._rank_known = true;
-
- // T/F Lite encodes a feature map as a NHWC tensor
- res._dims.resize(4);
- res._dims.at(0) = shape.count().value();
- res._dims.at(1) = shape.height().value();
- res._dims.at(2) = shape.width().value();
- res._dims.at(3) = shape.depth().value();
-
- return res;
-}
-
-ShapeDescription to_shape_description(const loco::FilterShape &shape)
-{
- ShapeDescription res;
-
- res._rank_known = true;
-
- // T/F Lite encodes a convolution filter as a NHWC tensor
- res._dims.resize(4);
- res._dims.at(0) = shape.count().value();
- res._dims.at(1) = shape.height().value();
- res._dims.at(2) = shape.width().value();
- res._dims.at(3) = shape.depth().value();
-
- return res;
-}
-
-ShapeDescription to_shape_description(const loco::DepthwiseFilterShape &shape)
-{
- ShapeDescription res;
-
- res._rank_known = true;
-
- // T/F Lite encodes a depthwise convolution filter as a [1, H, W, C*M] tensor
- res._dims.resize(4);
- res._dims.at(0) = 1;
- res._dims.at(1) = shape.height().value();
- res._dims.at(2) = shape.width().value();
- res._dims.at(3) = shape.depth().value() * shape.multiplier().value();
-
- return res;
-}
-
-ShapeDescription to_shape_description(const loco::BiasShape &shape)
-{
- ShapeDescription res;
-
- res._rank_known = true;
-
- res._dims.resize(1);
- res._dims.at(0) = shape.length().value();
-
- return res;
-}
-
-ShapeDescription to_shape_description(const loco::NodeShape &shape)
-{
- switch (shape.domain())
- {
- case loco::Domain::Tensor:
- return to_shape_description(shape.as<loco::TensorShape>());
- case loco::Domain::Feature:
- return to_shape_description(shape.as<loco::FeatureShape>());
- case loco::Domain::Filter:
- return to_shape_description(shape.as<loco::FilterShape>());
- case loco::Domain::DepthwiseFilter:
- return to_shape_description(shape.as<loco::DepthwiseFilterShape>());
- case loco::Domain::Bias:
- return to_shape_description(shape.as<loco::BiasShape>());
- default:
- break;
- }
-
- throw std::runtime_error{"Not implemented yet"};
-}
-
-uint32_t SerializedModelData::registerBuiltinOpcode(tflite::BuiltinOperator builtin_code)
-{
- auto it = _operator_codes.find(OpCode{builtin_code});
- if (it != _operator_codes.end())
- {
- return it->second;
- }
- auto idx = static_cast<uint32_t>(_operator_codes.size());
- _operator_codes.emplace(OpCode{builtin_code}, idx);
- return idx;
-}
-
-uint32_t SerializedModelData::registerCustomOpcode(const std::string &custom_op)
-{
- tflite::BuiltinOperator custom_code = tflite::BuiltinOperator_CUSTOM;
- auto idx = registerBuiltinOpcode(custom_code);
- _custom_operator_codes.emplace(OpCode{custom_code}, custom_op);
- return idx;
-}
-
-tflite::Padding getOpPadding(const loco::Padding2D *pad)
-{
- // VALID padding
- if (pad->top() == 0 && pad->bottom() == 0 && pad->left() == 0 && pad->right() == 0)
- return tflite::Padding_VALID;
-
- // SAME padding
- if ((pad->top() <= pad->bottom()) && (pad->bottom() <= pad->top() + 1) &&
- (pad->left() <= pad->right()) && (pad->right() <= pad->left() + 1))
- return tflite::Padding_SAME;
-
- throw std::runtime_error("NYI for custom PAD");
-}
-
-void registerGraphIOName(loco::Graph *graph, SerializedModelData &gd)
-{
- for (uint32_t in = 0; in < graph->inputs()->size(); ++in)
- {
- auto pull = loco::pull_node(graph, in);
- auto name = graph->inputs()->at(in)->name();
-
- gd._pull_to_name[pull] = name;
- }
- for (uint32_t out = 0; out < graph->outputs()->size(); ++out)
- {
- auto push = loco::push_node(graph, out);
- auto name = graph->outputs()->at(out)->name();
-
- gd._push_to_name[push] = name;
- }
-}
-
-#include <stdex/Memory.h>
-
-#include <cassert>
-
-namespace
-{
-
-class TFLTensorIndexAnnotation final : public loco::NodeAnnotation
-{
-public:
- TFLTensorIndexAnnotation(const TFLTensorIndex &index) : _index{index}
- {
- // DO NOTHING
- }
-
-public:
- const TFLTensorIndex &index(void) const { return _index; }
-
-private:
- TFLTensorIndex _index;
-};
-
-} // namespace
-
-void set_tensor_index(loco::Node *node, const TFLTensorIndex &tensor_id)
-{
- assert(node->annot<TFLTensorIndexAnnotation>() == nullptr);
- node->annot(stdex::make_unique<TFLTensorIndexAnnotation>(tensor_id));
-}
-
-TFLTensorIndex get_tensor_index(loco::Node *node)
-{
- assert(node->annot<TFLTensorIndexAnnotation>() != nullptr);
- return node->annot<TFLTensorIndexAnnotation>()->index();
-}
diff --git a/compiler/exo-tflite/src/ExporterUtils.h b/compiler/exo-tflite/src/ExporterUtils.h
deleted file mode 100644
index fedaaaa8a..000000000
--- a/compiler/exo-tflite/src/ExporterUtils.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __EXPORTER_UTILS_H__
-#define __EXPORTER_UTILS_H__
-
-#include "schema_generated.h"
-#include "loco.h"
-
-#include "loco/IR/PermutingCodec.h"
-#include "loco/IR/NodeShape.h"
-
-#include <unordered_map>
-
-struct OpCode
-{
- tflite::BuiltinOperator opcode;
-
- bool operator==(const OpCode &rhs) const { return opcode == rhs.opcode; }
-};
-
-namespace std
-{
-
-template <> struct hash<OpCode>
-{
- size_t operator()(const OpCode &x) const { return hash<int>()(x.opcode); }
-};
-
-} // namespace std
-
-struct ShapeDescription
-{
- std::vector<int32_t> _dims;
- bool _rank_known;
-};
-
-ShapeDescription to_shape_description(const loco::TensorShape &shape);
-ShapeDescription to_shape_description(const loco::FeatureShape &shape);
-ShapeDescription to_shape_description(const loco::FilterShape &shape);
-ShapeDescription to_shape_description(const loco::BiasShape &shape);
-ShapeDescription to_shape_description(const loco::NodeShape &shape);
-
-/**
- * @breif Record the information of T/F Lite SubGraph and its mapping to loco
- */
-struct SubGraphContext
-{
- /// @brief SubGraph input tensor id
- std::vector<int32_t> _inputs;
- /// @brief SubGraph output tensor id
- std::vector<int32_t> _outputs;
-};
-
-// Prerequisites for tflite::Model object creation
-struct SerializedModelData final : public SubGraphContext
-{
- SerializedModelData() = default;
- SerializedModelData(const SerializedModelData &) = delete;
-
- std::unordered_map<OpCode, uint32_t> _operator_codes;
- std::unordered_map<OpCode, std::string> _custom_operator_codes;
- std::vector<flatbuffers::Offset<tflite::Operator>> _operators;
- std::vector<flatbuffers::Offset<tflite::Tensor>> _tensors;
- std::vector<flatbuffers::Offset<tflite::Buffer>> _buffers;
-
- // Graph input and output names
- std::unordered_map<loco::Pull *, std::string> _pull_to_name;
- std::unordered_map<loco::Push *, std::string> _push_to_name;
-
- /**
- * @brief if opcode is not registered in table of opcodes add it
- * @param builtin_code
- * @return idx of opcode in table of opcodes (see schema)
- */
- uint32_t registerBuiltinOpcode(tflite::BuiltinOperator builtin_code);
- uint32_t registerCustomOpcode(const std::string &custom_op);
-};
-
-template <typename Permutation> inline bool isNHWC(Permutation *perm);
-
-template <> inline bool isNHWC(loco::Permutation<loco::Domain::Feature> *perm)
-{
- return perm->axis(loco::FeatureAxis::Count) == 0 && perm->axis(loco::FeatureAxis::Height) == 1 &&
- perm->axis(loco::FeatureAxis::Width) == 2 && perm->axis(loco::FeatureAxis::Depth) == 3;
-}
-
-template <> inline bool isNHWC(loco::Permutation<loco::Domain::Filter> *perm)
-{
- return perm->axis(loco::FilterAxis::Count) == 0 && perm->axis(loco::FilterAxis::Height) == 1 &&
- perm->axis(loco::FilterAxis::Width) == 2 && perm->axis(loco::FilterAxis::Depth) == 3;
-}
-
-tflite::Padding getOpPadding(const loco::Padding2D *pad);
-
-/// @brief Register graph input and output names to SerializedModelData
-void registerGraphIOName(loco::Graph *graph, SerializedModelData &gd);
-
-using TFLTensorIndex = int32_t;
-
-void set_tensor_index(loco::Node *node, const TFLTensorIndex &tensor_id);
-TFLTensorIndex get_tensor_index(loco::Node *node);
-
-#endif // __EXPORTER_UTILS_H__
diff --git a/compiler/exo-tflite/src/GraphBlock.cpp b/compiler/exo-tflite/src/GraphBlock.cpp
deleted file mode 100644
index e38b1d138..000000000
--- a/compiler/exo-tflite/src/GraphBlock.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "GraphBlock.h"
-
-#include "Check.h"
-
-#include <loco.h>
-#include <stdex/Memory.h>
-
-namespace
-{
-
-template <exo::DefaultLayout T> loco::Permutation<loco::Domain::Feature> perm();
-
-template <> loco::Permutation<loco::Domain::Feature> perm<exo::DefaultLayout::NHWC>()
-{
- // Make NHWC permutation for encoder and decoder
- loco::Permutation<loco::Domain::Feature> NHWC;
-
- NHWC.axis(loco::FeatureAxis::Count) = 0;
- NHWC.axis(loco::FeatureAxis::Height) = 1;
- NHWC.axis(loco::FeatureAxis::Width) = 2;
- NHWC.axis(loco::FeatureAxis::Depth) = 3;
-
- return NHWC;
-}
-
-} // namespace
-
-namespace exo
-{
-
-template <DefaultLayout T> loco::FeatureEncode *make_feature_encode(loco::Node *input_for_encode)
-{
- EXO_ASSERT(input_for_encode != nullptr, "input should not be nullptr");
- loco::Graph *g = input_for_encode->graph();
-
- auto encoder = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>();
-
- encoder->perm(perm<T>());
-
- auto enc = g->nodes()->create<loco::FeatureEncode>();
- enc->input(input_for_encode);
- enc->encoder(std::move(encoder));
-
- return enc;
-}
-
-template <DefaultLayout T> loco::FeatureDecode *make_feature_decode(loco::Node *input_for_decode)
-{
- EXO_ASSERT(input_for_decode != nullptr, "input should not be nullptr");
- loco::Graph *g = input_for_decode->graph();
-
- auto decoder = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>();
-
- decoder->perm(perm<T>());
-
- auto dec = g->nodes()->create<loco::FeatureDecode>();
- dec->input(input_for_decode);
- dec->decoder(std::move(decoder));
-
- return dec;
-}
-
-// template instantiation
-template loco::FeatureEncode *
-make_feature_encode<DefaultLayout::NHWC>(loco::Node *input_for_encode);
-
-template loco::FeatureDecode *
-make_feature_decode<DefaultLayout::NHWC>(loco::Node *input_for_encode);
-
-} // namespace exo
diff --git a/compiler/exo-tflite/src/GraphBlock.h b/compiler/exo-tflite/src/GraphBlock.h
deleted file mode 100644
index 692c7fa50..000000000
--- a/compiler/exo-tflite/src/GraphBlock.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __GRAPH_BLOCK_H__
-#define __GRAPH_BLOCK_H__
-
-#include <loco.h>
-
-namespace exo
-{
-
-/// @brief default layout of TFLITE file
-enum class DefaultLayout
-{
- NHWC,
-};
-
-/// @brief Creates a loco::FeatureEncode of default layout (NHWC for tflite) and add it to graph.
-template <DefaultLayout T> loco::FeatureEncode *make_feature_encode(loco::Node *input_for_encode);
-
-/// @brief Create a loco::FeatureDecode of default layout (NHWC for tflite) and add it to graph.
-template <DefaultLayout T> loco::FeatureDecode *make_feature_decode(loco::Node *input_for_decode);
-
-} // namespace exo
-
-#endif //__CONVERSION_GRAPH_BLOCK_H__
diff --git a/compiler/exo-tflite/src/Knob.cpp b/compiler/exo-tflite/src/Knob.cpp
deleted file mode 100644
index 66c184031..000000000
--- a/compiler/exo-tflite/src/Knob.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Knob.h"
-
-#include <pepper/strcast.h>
-
-#include <iostream>
-#include <string>
-
-// Basic Infrastructure to declare and access Knob values
-namespace
-{
-
-using KnobName = std::string;
-
-/**
- * @brief Load configuration (from somewhere)
- */
-struct KnobLoader
-{
- virtual ~KnobLoader() = default;
-
- virtual bool load(const KnobName &name, bool default_value) const = 0;
-};
-
-// Template-programming helpers
-template <typename T> T knob_load(const KnobLoader &, const KnobName &, const T &);
-
-template <>
-bool knob_load(const KnobLoader &l, const KnobName &knob_name, const bool &default_value)
-{
- return l.load(knob_name, default_value);
-}
-
-/**
- * @brief Load configuration from environment variables
- *
- * Given a prefix P, EnvKnobLoader reads a configuration K from concat(P, K).
- *
- * For example, let us assume that P is "MY_" and K is "CONFIG".
- *
- * Then, EnvKnobLoader reads configuration CONFIG from environment variable MY_CONFIG.
- */
-class EnvKnobLoader final : public KnobLoader
-{
-public:
- EnvKnobLoader(const std::string &prefix) : _prefix{prefix}
- {
- // DO NOTHING
- }
-
-public:
- bool load(const KnobName &knob_name, bool default_value) const override
- {
- auto envvar = _prefix + knob_name;
- auto s = std::getenv(envvar.c_str());
-
- return pepper::safe_strcast<int>(s, default_value ? 1 : 0) != 0;
- }
-
-private:
- /// @brief Environment variable prefix
- std::string _prefix;
-};
-
-} // namespace
-
-namespace
-{
-
-const KnobLoader &knob_loader(void)
-{
- static EnvKnobLoader loader{"EXOTFLITE_"};
- return loader;
-}
-
-} // namespace
-
-namespace exo
-{
-
-#define KNOB_BOOL(NAME, DEFAULT, DESC) \
- template <> typename KnobTrait<Knob::NAME>::ValueType get<Knob::NAME>(void) \
- { \
- static typename KnobTrait<Knob::NAME>::ValueType value = \
- ::knob_load<typename KnobTrait<Knob::NAME>::ValueType>(::knob_loader(), #NAME, DEFAULT); \
- return value; \
- }
-#include "Knob.lst"
-#undef KNOB_BOOL
-
-} // namespace exo
diff --git a/compiler/exo-tflite/src/Knob.h b/compiler/exo-tflite/src/Knob.h
deleted file mode 100644
index 6483c141d..000000000
--- a/compiler/exo-tflite/src/Knob.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __KNOB_H__
-#define __KNOB_H__
-
-namespace exo
-{
-
-enum class Knob
-{
-#define KNOB_BOOL(NAME, DEFAULT, DESC) NAME,
-#include "Knob.lst"
-#undef KNOB_BOOL
-};
-
-template <Knob K> struct KnobTrait;
-
-#define KNOB_BOOL(NAME, DEFAULT, DESC) \
- template <> struct KnobTrait<Knob::NAME> \
- { \
- using ValueType = bool; \
- };
-#include "Knob.lst"
-#undef KNOB_BOOL
-
-template <Knob K> typename KnobTrait<K>::ValueType get(void);
-
-} // namespace exo
-
-#endif // __KNOB_H__
diff --git a/compiler/exo-tflite/src/Knob.lst b/compiler/exo-tflite/src/Knob.lst
deleted file mode 100644
index 07c0eda1e..000000000
--- a/compiler/exo-tflite/src/Knob.lst
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifndef KNOB_BOOL
-#error "KNOB_BOOL is not defined"
-#endif // KNOB_BOOL
-
-// KNOB_BOOL(NAME, DEFAULT_VALUE, DESCRIPTION)
-
-// These are for backward compatibility
-// TO use any knob for operation, EnableTFLDialect must be turned on
-KNOB_BOOL(EnableTFLDialect, false, Convert canonical nodes to TFLNodes)
-
-// operations
-KNOB_BOOL(ConvertAvgPool2D, false, Convert loco::AvgPool2D to TFLAveragePool2D)
-// TODO loco::ConstGen
-// TODO loco::Conv2D
-// TODO loco::DepthwiseConv2D
-// TODO loco::DepthwiseFilterEncode
-KNOB_BOOL(ConvertEltwiseAdd, false, Convert loco::EltwiseAdd to TFLAdd)
-// TODO loco::EltwiseDiv
-KNOB_BOOL(ConvertEltwiseMul, false, Convert loco::EltwiseMul to TFLMul)
-// TODO loco::EltwiseSqrt
-// TODO loco::EltwiseSub
-KNOB_BOOL(ConvertFeatureBiasAdd, false, Convert loco::FeatureBiasAdd to TFLAdd)
-// TODO loco::FixedReshape
-KNOB_BOOL(ConvertMaxPool2D, false, Convert loco::MaxPool2D to TFLMaxPool2D)
-KNOB_BOOL(ConvertRelu, false, Convert loco::Relu to TFLRelu)
-// TODO loco::ReLU6
-// TODO loco::Tanh
-// TODO loco::TensorConcat
-// TODO loco::TensorBiasAdd
-KNOB_BOOL(ConvertTensorBroadcast, false, Resolve loco::TensorBroadcast)
-// TODO loco::TensorSoftmax
diff --git a/compiler/exo-tflite/src/Log.cpp b/compiler/exo-tflite/src/Log.cpp
deleted file mode 100644
index de69b6506..000000000
--- a/compiler/exo-tflite/src/Log.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Log.h"
-
-#include <hermes/ConsoleReporter.h>
-#include <stdex/Memory.h>
-
-#include <cassert>
-#include <cstdlib>
-#include <iostream>
-
-// TODO Extract these lexical conversion routines as a library
-namespace
-{
-
-/**
- * @brief Convert C-string as a value of type T
- *
- * safecast(s, v) returns v if s is nullptr.
- */
-template <typename T> T safecast(const char *, const T &);
-
-template <> bool safecast<bool>(const char *s, const bool &value)
-{
- return (s == nullptr) ? value : (std::stoi(s) != 0);
-}
-
-} // namespace
-
-namespace exo
-{
-
-//
-// Logger
-//
-Logger::Logger(hermes::Context *ctx) { activate(ctx->sources(), ctx->bus()); }
-Logger::~Logger() { deactivate(); }
-
-//
-// LoggerConfig
-//
-LoggerConfig::LoggerConfig()
-{
- // Turn on logging if EXOTFLITE_LOG is set as non-zero value
- _enabled = safecast<bool>(std::getenv("EXOTFLITE_LOG"), false);
-}
-
-void LoggerConfig::configure(const hermes::Source *source, hermes::Source::Setting &setting) const
-{
- // Let's ignore hermes::Sources if that is not a exotflite logger
- if (auto logger = dynamic_cast<const Logger *>(source))
- {
- configure(logger, setting);
- }
-}
-
-void LoggerConfig::configure(const Logger *, hermes::Source::Setting &setting) const
-{
- if (_enabled)
- {
- // Enable all catagories
- setting.accept_all();
- }
- else
- {
- // Disable all catagories
- setting.reject_all();
- }
-}
-
-//
-// LoggingContext
-//
-hermes::Context *LoggingContext::get(void)
-{
- static hermes::Context *ctx = nullptr;
-
- if (ctx == nullptr)
- {
- ctx = new hermes::Context;
- ctx->sinks()->append(stdex::make_unique<hermes::ConsoleReporter>());
- ctx->config(stdex::make_unique<LoggerConfig>());
- }
-
- return ctx;
-}
-
-} // namespace exo
diff --git a/compiler/exo-tflite/src/Log.h b/compiler/exo-tflite/src/Log.h
deleted file mode 100644
index 8b2592116..000000000
--- a/compiler/exo-tflite/src/Log.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __LOG_H__
-#define __LOG_H__
-
-#include <hermes.h>
-
-namespace exo
-{
-
-/**
- * @brief Logger Implementation
- */
-class Logger final : public hermes::Source
-{
-public:
- Logger(hermes::Context *ctx);
- ~Logger();
-};
-
-/**
- * @brief Logger Configuration
- *
- * Users are able to turn logging on/off via EXOTFLITE_LOG environment variable.
- */
-class LoggerConfig final : public hermes::Config
-{
-public:
- LoggerConfig();
-
-public:
- void configure(const hermes::Source *, hermes::Source::Setting &) const final;
- void configure(const Logger *, hermes::Source::Setting &) const;
-
-private:
- bool _enabled;
-};
-
-/**
- * @brief Global logging context
- */
-struct LoggingContext
-{
- static hermes::Context *get(void);
-};
-
-} // namespace exo
-
-/**
- * HOW TO USE:
- *
- * LOGGER(l);
- *
- * INFO(l) << "Hello, World" << std::endl;
- *
- */
-#define LOGGER(name) ::exo::Logger name{::exo::LoggingContext::get()};
-
-// TODO Support FATAL, ERROR, WARN, and VERBOSE
-#define INFO(name) HERMES_INFO(name)
-
-// WARNING!
-//
-// THE CURRENT IMPLEMENTATION IS NOT THREAD SAFE.
-//
-
-#endif // __LOG_H__
diff --git a/compiler/exo-tflite/src/LogHelper.cpp b/compiler/exo-tflite/src/LogHelper.cpp
deleted file mode 100644
index 7c2d6f9f7..000000000
--- a/compiler/exo-tflite/src/LogHelper.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "LogHelper.h"
-
-namespace loco
-{
-
-std::ostream &operator<<(std::ostream &os, const loco::FeatureShape &feature_shape)
-{
- os << "[" << feature_shape.count().value() << "," << feature_shape.height().value() << ","
- << feature_shape.width().value() << "," << feature_shape.depth().value() << "]";
- return os;
-}
-
-std::ostream &operator<<(std::ostream &os, const loco::FilterShape &filter_shape)
-{
- os << "[" << filter_shape.height().value() << "," << filter_shape.width().value() << ","
- << filter_shape.depth().value() << "," << filter_shape.count().value() << "]";
- return os;
-}
-
-std::ostream &operator<<(std::ostream &os, const loco::TensorShape &tensor_shape)
-{
- os << "[";
- for (uint32_t r = 0; r < tensor_shape.rank(); ++r)
- {
- if (r)
- os << ",";
- os << tensor_shape.dim(r).value();
- }
- os << "]";
- return os;
-}
-
-std::ostream &operator<<(std::ostream &os, const loco::Padding2D &pad)
-{
- os << "[TLBR " << pad.top() << "," << pad.left() << "," << pad.bottom() << "," << pad.right()
- << "]";
-
- return os;
-}
-
-} // namespace loco
-
-std::ostream &operator<<(std::ostream &os, const std::vector<int64_t> &vi64)
-{
- for (auto vi : vi64)
- {
- os << vi << " ";
- }
- return os;
-}
-
-#include "TFLFormattedGraph.h"
-
-namespace exo
-{
-
-FormattedGraph fmt(loco::Graph *g)
-{
- auto node_summary_builder = stdex::make_unique<NodeSummaryBuilderFactory>();
- return std::move(locop::fmt<locop::LinearV1>(g).with(std::move(node_summary_builder)));
-}
-
-} // namespace exo
diff --git a/compiler/exo-tflite/src/OperationExporter.cpp b/compiler/exo-tflite/src/OperationExporter.cpp
deleted file mode 100644
index 812aadf9e..000000000
--- a/compiler/exo-tflite/src/OperationExporter.cpp
+++ /dev/null
@@ -1,776 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "OperationExporter.h"
-#include "ExporterUtils.h"
-#include "ShapeInference.h"
-
-#include "Dialect/IR/TFLNode.h"
-#include "Dialect/IR/TFLNodes.h"
-#include "Dialect/IR/TFLNodeVisitor.h"
-
-#include "Check.h"
-
-#include <loco/IR/CanonicalNode.h>
-#include <loco/IR/CanonicalNodeVisitor.h>
-#include <locoex/COpCall.h>
-
-#include <flatbuffers/flexbuffers.h>
-
-using namespace flatbuffers;
-using namespace tflite;
-
-namespace
-{
-
-class OperationExporter final : public locoex::TFLNodeMutableVisitor<void>,
- public loco::CanonicalNodeMutableVisitor<void>
-{
-public:
- OperationExporter(FlatBufferBuilder &fbb, SerializedModelData &ctx) : builder{fbb}, gd{ctx}
- {
- // DO NOTHING
- }
-
-public:
- // FOR TFLNodes
- void visit(locoex::TFLAdd *) final;
- void visit(locoex::TFLAveragePool2D *) final;
- // TODO TFLConcatenation
- // TODO TFLConv2D
- // TODO TFLDepthwiseConv2D
- // TODO TFLDiv
- void visit(locoex::TFLMaxPool2D *) final;
- void visit(locoex::TFLMul *) final;
- void visit(locoex::TFLRelu *) final;
- // TODO TFLRelu6
- // TODO TFLReshape
- // TODO TFLSoftmax
- // TODO TFLSqrt
- // TODO TFLSub
- // TODO TFLTanh
- // TODO TFLTranspose
-
- // FOR canonical nodes. These will be removed later
- void visit(loco::ReLU *) final;
- void visit(loco::ReLU6 *) final;
- void visit(loco::Tanh *) final;
- void visit(loco::Push *) final { /* DO NOTHING */}
- void visit(loco::Pull *) final { /* DO NOTHING */}
- void visit(loco::FeatureEncode *) final;
- void visit(loco::FeatureDecode *) final;
- void visit(loco::FilterEncode *) final;
- void visit(loco::DepthwiseFilterEncode *) final;
- void visit(loco::ConstGen *) final { /* skip, everything is done in exportOpDefinedTensors */}
- void visit(loco::MaxPool2D *) final;
- void visit(loco::AvgPool2D *) final;
- void visit(loco::Conv2D *) final;
- void visit(loco::DepthwiseConv2D *) final;
- void visit(loco::TensorConcat *) final;
- void visit(loco::TensorSoftmax *) final;
- void visit(loco::BiasEncode *) final;
- void visit(loco::TensorBiasAdd *) final;
- void visit(loco::FeatureBiasAdd *) final;
- void visit(loco::EltwiseAdd *) final;
- void visit(loco::EltwiseMul *) final;
- void visit(loco::EltwiseSub *) final;
- void visit(loco::EltwiseDiv *) final;
- void visit(loco::EltwiseSqrt *) final;
- void visit(loco::FixedReshape *) final;
- void visit(loco::TensorBroadcast *) final;
-
- void visit(locoex::COpCall *);
-
-private:
- /**
- * @brief Exports TFLMaxPool2D or TFLAveragePool2D
- *
- * @note TFLPool2D should be one of TFLMaxPool2D or TFLAveragePool2D
- */
- template <class TFLPool2D>
- void export_pool_2d(TFLPool2D *node, tflite::BuiltinOperator builtin_op);
-
-private:
- FlatBufferBuilder &builder;
- SerializedModelData &gd;
-};
-
-void OperationExporter::visit(locoex::TFLAdd *node)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_ADD);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateAddOptions(builder); // dummy option
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- tflite::BuiltinOptions_AddOptions, options.Union());
- gd._operators.push_back(op_offset);
-}
-
-void OperationExporter::visit(locoex::TFLAveragePool2D *node)
-{
- export_pool_2d<locoex::TFLAveragePool2D>(node, tflite::BuiltinOperator_AVERAGE_POOL_2D);
-}
-
-// TODO TFLConcatenation
-
-// TODO TFLConv2D
-
-// TODO TFLDepthwiseConv2D
-
-// TODO TFLDiv
-
-void OperationExporter::visit(locoex::TFLMaxPool2D *node)
-{
- export_pool_2d<locoex::TFLMaxPool2D>(node, tflite::BuiltinOperator_MAX_POOL_2D);
-}
-
-void OperationExporter::visit(locoex::TFLMul *node)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_MUL);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateAddOptions(builder); // dummy option
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- tflite::BuiltinOptions_AddOptions, options.Union());
- gd._operators.push_back(op_offset);
-}
-
-void OperationExporter::visit(locoex::TFLRelu *node)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_RELU);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->features())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
- gd._operators.push_back(op_offset);
-}
-
-// TODO TFLRelu6
-
-// TODO TFLReshape
-
-// TODO TFLSoftmax
-
-// TODO TFLSqrt
-
-// TODO TFLSub
-
-// TODO TFLTanh
-
-// TODO TFLTranspose
-
-template <class TFLPool2D>
-void OperationExporter::export_pool_2d(TFLPool2D *node, tflite::BuiltinOperator builtin_op)
-{
- EXO_ASSERT(builtin_op == tflite::BuiltinOperator_MAX_POOL_2D ||
- builtin_op == tflite::BuiltinOperator_AVERAGE_POOL_2D,
- "should be maxpool or avgpool");
- EXO_ASSERT(node->padding() != locoex::Padding::UNDEFINED, "Padding is not set");
- EXO_ASSERT(node->fusedActivationFunction() != locoex::FusedActFunc::UNDEFINED,
- "fused activation function is not set");
-
- uint32_t op_idx = gd.registerBuiltinOpcode(builtin_op);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->value())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
-
- tflite::Padding padding =
- node->padding() == locoex::Padding::VALID ? tflite::Padding_VALID : tflite::Padding_SAME;
-
- auto options = CreatePool2DOptions(builder, padding, node->stride()->w(), node->stride()->h(),
- node->filter()->w(), node->filter()->h());
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- tflite::BuiltinOptions_Pool2DOptions, options.Union());
- gd._operators.push_back(op_offset);
-}
-
-void OperationExporter::visit(loco::ReLU *node)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_RELU);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
- gd._operators.push_back(op_offset);
-}
-
-void OperationExporter::visit(loco::ReLU6 *node)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_RELU6);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
- gd._operators.push_back(op_offset);
-}
-
-void OperationExporter::visit(loco::Tanh *node)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_TANH);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
- gd._operators.push_back(op_offset);
-}
-
-void OperationExporter::visit(loco::MaxPool2D *node)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_MAX_POOL_2D);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->ifm())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- tflite::Padding padding = getOpPadding(node->pad());
- auto options = CreatePool2DOptions(builder, padding, node->stride()->horizontal(),
- node->stride()->vertical(), node->window()->horizontal(),
- node->window()->vertical());
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- tflite::BuiltinOptions_Pool2DOptions, options.Union());
- gd._operators.push_back(op_offset);
-}
-
-void OperationExporter::visit(loco::AvgPool2D *node)
-{
- // TFlite only support Valid convention of average pooling
- assert(node->convention() == loco::AvgPool2D::Convention::Valid);
-
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_AVERAGE_POOL_2D);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->ifm())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- tflite::Padding padding = getOpPadding(node->pad());
- auto options = CreatePool2DOptions(builder, padding, node->stride()->horizontal(),
- node->stride()->vertical(), node->window()->horizontal(),
- node->window()->vertical());
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- tflite::BuiltinOptions_Pool2DOptions, options.Union());
- gd._operators.push_back(op_offset);
-}
-
-void OperationExporter::visit(loco::Conv2D *node)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_CONV_2D);
-
- // Third input of CONV_2D of tflite should be bias. We will make (and register to gd) dummy zero
- // bias. Bias would be rank 1, have size of output kernel count, and have all zero values, i.e.
- // zero bias.
- auto *ker = dynamic_cast<loco::FilterEncode *>(node->ker());
- assert(ker);
- int32_t bias_vec_size = ShapeInference::get(ker)._dims[0]; // output kernel count
-
- auto bias_vec_shape_offset = builder.CreateVector(std::vector<int32_t>{bias_vec_size});
- size_t raw_bias_vec_size = bias_vec_size * sizeof(int32_t);
-
- std::vector<float> bias_vec_data(bias_vec_size); // initialized as zero vector
-
- auto bias_vec_offset =
- builder.CreateVector(reinterpret_cast<uint8_t *>(bias_vec_data.data()), raw_bias_vec_size);
-
- auto bias_buffer_offset = CreateBuffer(builder, bias_vec_offset);
-
- const auto bias_buffer_id = static_cast<uint32_t>(gd._buffers.size());
-
- gd._buffers.push_back(bias_buffer_offset);
-
- auto bias_tensor_offset =
- CreateTensor(builder, bias_vec_shape_offset, TensorType_FLOAT32, bias_buffer_id);
-
- auto bias_tensor_id = static_cast<int32_t>(gd._tensors.size());
- gd._tensors.push_back(bias_tensor_offset);
-
- // Make input, output and options for operator
- std::vector<int32_t> inputs_vec{get_tensor_index(node->ifm()), get_tensor_index(node->ker()),
- bias_tensor_id};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- tflite::Padding padding = getOpPadding(node->pad());
- auto options = CreateConv2DOptions(builder, padding, node->stride()->horizontal(),
- node->stride()->vertical());
-
- // Make CONV_2D operator
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- tflite::BuiltinOptions_Conv2DOptions, options.Union());
- gd._operators.push_back(op_offset);
-}
-
-void OperationExporter::visit(loco::DepthwiseConv2D *node)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_DEPTHWISE_CONV_2D);
-
- // Third input of DEPTHWISE_CONV2D of tflite should be bias. We will make (and register to gd)
- // dummy zero bias. Bias would be rank 1, have size of output kernel count, and have all zero
- // values, i.e. zero bias.
- auto *ker = dynamic_cast<loco::DepthwiseFilterEncode *>(node->ker());
- assert(ker);
-
- int32_t bias_vec_size = ShapeInference::get(ker)._dims[3]; // output_size(C*M)
- auto bias_vec_shape_offset = builder.CreateVector(std::vector<int32_t>{bias_vec_size});
-
- size_t raw_bias_vec_size = bias_vec_size * sizeof(int32_t);
- std::vector<float> bias_vec_data(bias_vec_size);
- auto bias_vec_offset =
- builder.CreateVector(reinterpret_cast<uint8_t *>(bias_vec_data.data()), raw_bias_vec_size);
-
- auto bias_buffer_offset = CreateBuffer(builder, bias_vec_offset);
-
- const auto bias_buffer_id = static_cast<uint32_t>(gd._buffers.size());
-
- gd._buffers.push_back(bias_buffer_offset);
-
- auto bias_tensor_offset =
- CreateTensor(builder, bias_vec_shape_offset, TensorType_FLOAT32, bias_buffer_id);
- auto bias_tensor_id = static_cast<int32_t>(gd._tensors.size());
- gd._tensors.push_back(bias_tensor_offset);
-
- std::vector<int32_t> inputs_vec{get_tensor_index(node->ifm()), get_tensor_index(node->ker()),
- bias_tensor_id};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- tflite::Padding padding = getOpPadding(node->pad());
-
- int32_t ifm_channel_size = ShapeInference::get(node->ifm())._dims[3];
- // multiplier = bias_vec_size(output_size)/ifm_channel_size
- auto options =
- CreateDepthwiseConv2DOptions(builder, padding, node->stride()->horizontal(),
- node->stride()->vertical(), bias_vec_size / ifm_channel_size);
-
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- tflite::BuiltinOptions_DepthwiseConv2DOptions, options.Union());
- gd._operators.push_back(op_offset);
-}
-
-void OperationExporter::visit(loco::TensorSoftmax *node)
-{
- // TODO Support when the input rank of TensorSoftmax is not 2
- auto rank = ShapeInference::get(node->input())._dims.size();
- assert(rank == 2);
-
- // NOTE TFLite only accepts axis when the value is last dimension
- assert(node->axis() == rank - 1);
-
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_SOFTMAX);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateSoftmaxOptions(builder, 1.0f);
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- tflite::BuiltinOptions_SoftmaxOptions, options.Union());
- gd._operators.push_back(op_offset);
-}
-
-/// @brief Export given node into identity, i.e. CONCATENATION with one input
-template <typename NodeT>
-void exportIdentity(NodeT *node, FlatBufferBuilder &builder, SerializedModelData &gd)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_CONCATENATION);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->arg(0))};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateConcatenationOptions(builder); // use dummy 0 axis and NONE activation
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- tflite::BuiltinOptions_ConcatenationOptions, options.Union());
-
- gd._operators.push_back(op_offset);
-}
-
-/// @brief Export loco nodes as TRANSPOSE
-void exportAsTranspose(loco::Node *node, FlatBufferBuilder &builder,
- std::vector<int32_t> &perm_vec_data, SerializedModelData &gd)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_TRANSPOSE);
-
- auto options = CreateTransposeOptions(builder);
-
- // Create constant tensor with perm vector
- constexpr int perm_vec_size = 4;
- assert(perm_vec_data.size() == perm_vec_size);
- auto perm_vec_shape_offset = builder.CreateVector(std::vector<int32_t>{perm_vec_size});
- constexpr size_t raw_perm_vec_size = perm_vec_size * sizeof(int32_t);
-
- auto perm_vec_offset =
- builder.CreateVector(reinterpret_cast<uint8_t *>(perm_vec_data.data()), raw_perm_vec_size);
-
- auto perm_buffer_offset = CreateBuffer(builder, perm_vec_offset);
-
- const auto perm_buffer_id = static_cast<uint32_t>(gd._buffers.size());
-
- gd._buffers.push_back(perm_buffer_offset);
-
- auto perm_tensor_offset =
- CreateTensor(builder, perm_vec_shape_offset, TensorType_INT32, perm_buffer_id);
-
- auto perm_tensor_id = static_cast<int32_t>(gd._tensors.size());
- gd._tensors.push_back(perm_tensor_offset);
-
- // Create permutation node
-
- std::vector<int32_t> inputs_vec{get_tensor_index(node->arg(0)), perm_tensor_id};
- std::vector<int32_t> outputs_vec{get_tensor_index(node)};
-
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
-
- constexpr auto options_type = tflite::BuiltinOptions::BuiltinOptions_TransposeOptions;
-
- auto transpose_offset =
- CreateOperator(builder, op_idx, inputs, outputs, options_type, options.Union());
- gd._operators.push_back(transpose_offset);
-}
-
-void OperationExporter::visit(loco::FeatureEncode *node)
-{
- auto encoder = dynamic_cast<loco::PermutingEncoder<loco::Domain::Feature> *>(node->encoder());
- auto perm = encoder->perm();
-
- if (isNHWC(perm))
- {
- // Note that tflite represents feature as NHWC
- exportIdentity(node, builder, gd);
- }
- else
- {
- std::vector<int32_t> perm_vec_data(4);
- perm_vec_data[0] = perm->axis(loco::FeatureAxis::Count);
- perm_vec_data[1] = perm->axis(loco::FeatureAxis::Height);
- perm_vec_data[2] = perm->axis(loco::FeatureAxis::Width);
- perm_vec_data[3] = perm->axis(loco::FeatureAxis::Depth);
-
- exportAsTranspose(node, builder, perm_vec_data, gd);
- }
-}
-
-void OperationExporter::visit(loco::FeatureDecode *node)
-{
- auto decoder = dynamic_cast<loco::PermutingDecoder<loco::Domain::Feature> *>(node->decoder());
- auto perm = decoder->perm();
-
- if (isNHWC(perm))
- {
- // Note that tflite represents feature as NHWC
- exportIdentity(node, builder, gd);
- }
- else
- {
- std::vector<int32_t> perm_vec_data(4);
- perm_vec_data[perm->axis(loco::FeatureAxis::Count)] = 0;
- perm_vec_data[perm->axis(loco::FeatureAxis::Height)] = 1;
- perm_vec_data[perm->axis(loco::FeatureAxis::Width)] = 2;
- perm_vec_data[perm->axis(loco::FeatureAxis::Depth)] = 3;
-
- exportAsTranspose(node, builder, perm_vec_data, gd);
- }
-}
-
-void OperationExporter::visit(loco::FilterEncode *node)
-{
- auto encoder = dynamic_cast<loco::PermutingEncoder<loco::Domain::Filter> *>(node->encoder());
- auto perm = encoder->perm();
-
- if (isNHWC(perm))
- {
- // Note that tflite represents filter as NHWC
- exportIdentity(node, builder, gd);
- }
- else
- {
- std::vector<int32_t> perm_vec_data(4);
- // NOTE In tflite, all tensors means NHWC, so 0 = N, 1 = H, 2 = W, 3 = C
- perm_vec_data[0] = perm->axis(loco::FilterAxis::Count);
- perm_vec_data[1] = perm->axis(loco::FilterAxis::Height);
- perm_vec_data[2] = perm->axis(loco::FilterAxis::Width);
- perm_vec_data[3] = perm->axis(loco::FilterAxis::Depth);
-
- exportAsTranspose(node, builder, perm_vec_data, gd);
- }
-}
-
-void exportAsReshape(loco::Node *node, FlatBufferBuilder &builder,
- std::vector<int32_t> &new_shape_vec, SerializedModelData &gd)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_RESHAPE);
-
- std::vector<int32_t> inputs_vec{get_tensor_index(node->arg(0))};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
-
- auto new_shape_vec_offset = builder.CreateVector(new_shape_vec);
- auto options = CreateReshapeOptions(builder, new_shape_vec_offset);
-
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- tflite::BuiltinOptions_ReshapeOptions, options.Union());
-
- gd._operators.push_back(op_offset);
-}
-
-void OperationExporter::visit(loco::DepthwiseFilterEncode *node)
-{
- auto ker = node->input(); // [H, W, C, M]
-
- // tflite represents filter as [1, H, W, C*M] where M is multiplier.
- std::vector<int32_t> new_shape_vec(4);
- new_shape_vec[0] = 1;
- new_shape_vec[1] = ShapeInference::get(ker)._dims[0];
- new_shape_vec[2] = ShapeInference::get(ker)._dims[1];
- new_shape_vec[3] = ShapeInference::get(ker)._dims[2] * ShapeInference::get(ker)._dims[3];
-
- exportAsReshape(node, builder, new_shape_vec, gd);
-}
-
-void OperationExporter::visit(loco::BiasAdd<loco::Domain::Tensor> *node)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_ADD);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->value()), get_tensor_index(node->bias())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateAddOptions(builder); // dummy option
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- tflite::BuiltinOptions_AddOptions, options.Union());
- gd._operators.push_back(op_offset);
-}
-
-void OperationExporter::visit(loco::FeatureBiasAdd *node)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_ADD);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->value()), get_tensor_index(node->bias())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateAddOptions(builder); // dummy option
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- tflite::BuiltinOptions_AddOptions, options.Union());
- gd._operators.push_back(op_offset);
-}
-
-/// @brief Export CONCATENATION of **TWO** tensors only
-void OperationExporter::visit(loco::TensorConcat *node)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_CONCATENATION);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateConcatenationOptions(builder, node->axis());
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- tflite::BuiltinOptions_ConcatenationOptions, options.Union());
-
- gd._operators.push_back(op_offset);
-}
-
-void OperationExporter::visit(loco::BiasEncode *encode) { exportIdentity(encode, builder, gd); }
-
-void OperationExporter::visit(loco::EltwiseAdd *node)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_ADD);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateAddOptions(builder); // dummy option
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- tflite::BuiltinOptions_AddOptions, options.Union());
- gd._operators.push_back(op_offset);
-}
-
-void OperationExporter::visit(loco::EltwiseMul *node)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_MUL);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateMulOptions(builder); // dummy option
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- tflite::BuiltinOptions_MulOptions, options.Union());
- gd._operators.push_back(op_offset);
-}
-
-void OperationExporter::visit(loco::EltwiseSub *node)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_SUB);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateSubOptions(builder); // dummy option
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- tflite::BuiltinOptions_SubOptions, options.Union());
- gd._operators.push_back(op_offset);
-}
-
-void OperationExporter::visit(loco::EltwiseDiv *node)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_DIV);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto options = CreateDivOptions(builder); // dummy option
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- tflite::BuiltinOptions_DivOptions, options.Union());
- gd._operators.push_back(op_offset);
-}
-
-void OperationExporter::visit(loco::EltwiseSqrt *node)
-{
- uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_SQRT);
- std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
- gd._operators.push_back(op_offset);
-}
-
-void OperationExporter::visit(loco::FixedReshape *node)
-{
- std::vector<int32_t> new_shape_vec;
- for (uint32_t axis = 0; axis < node->rank(); ++axis)
- {
- assert(node->dim(axis).known());
- new_shape_vec.push_back(node->dim(axis).value());
- }
-
- exportAsReshape(node, builder, new_shape_vec, gd);
-}
-
-void OperationExporter::visit(loco::TensorBroadcast *)
-{
- throw std::runtime_error("TensorBroadcast should not exist in the graph");
-}
-
-inline flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
-CreateCOpCallOptions(flatbuffers::FlatBufferBuilder &fbb, locoex::COpCall *copCall)
-{
- // read attrs in FlexBuffer format and pass them to FlatBuffer builder
- flexbuffers::Builder flexbuf;
- {
- size_t map_start = flexbuf.StartMap();
-
- // Note: among attrs of COpCall, 'op' and 'name' won't be included into tflite file
- auto names = copCall->attr_names();
- for (auto name : names)
- {
- if (auto int_val = copCall->attr<locoex::COpAttrType::Int>(name))
- flexbuf.Int(name.c_str(), int_val->val());
- else if (auto float_val = copCall->attr<locoex::COpAttrType::Float>(name))
- flexbuf.Float(name.c_str(), float_val->val());
- else
- // TODO Support more attribute types
- throw std::runtime_error("Not supported type while writing flexbuffer");
- }
-
- flexbuf.EndMap(map_start);
- flexbuf.Finish();
- }
-
- auto offset = fbb.CreateVector(flexbuf.GetBuffer());
-
- return offset;
-}
-
-void OperationExporter::visit(locoex::COpCall *call)
-{
- // Registering this custom op name into tflite Operator Codes table
- uint32_t op_idx = gd.registerCustomOpcode(call->op());
-
- std::vector<int32_t> inputs_vec;
- {
- inputs_vec.resize(call->arity());
- for (uint32_t i = 0; i < call->arity(); i++)
- inputs_vec[i] = get_tensor_index(call->arg(i));
- }
-
- std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(call))};
-
- auto inputs = builder.CreateVector(inputs_vec);
- auto outputs = builder.CreateVector(outputs_vec);
-
- auto custom_options = CreateCOpCallOptions(builder, call);
- auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
- tflite::BuiltinOptions_NONE, // builtin_options_type
- 0, // built-in option
- custom_options, // custom options
- tflite::CustomOptionsFormat_FLEXBUFFERS);
-
- gd._operators.push_back(op_offset);
-}
-
-void exportNode(loco::Node *node, flatbuffers::FlatBufferBuilder &builder,
- SerializedModelData &data)
-{
- // TODO Use explicit tagging to prevent possible mistake
- auto isNoOp = [](loco::Node *node) {
- if (node->arity() == 1)
- {
- assert(node->arg(0) != nullptr);
- return get_tensor_index(node) == get_tensor_index(node->arg(0));
- }
- return false;
- };
-
- if (isNoOp(node))
- {
- // Skip if a given node is marked as NoOp (op with no effect) before
- return;
- }
-
- if (auto canonical_node = dynamic_cast<loco::CanonicalNode *>(node))
- { // TODO Consider removing this later
- OperationExporter exporter{builder, data};
- canonical_node->accept(&exporter);
- }
- else if (auto tfl_node = dynamic_cast<locoex::TFLNode *>(node))
- {
- OperationExporter exporter{builder, data};
- tfl_node->accept(&exporter);
- }
- else if (dynamic_cast<locoex::COpNode *>(node))
- {
- OperationExporter exporter{builder, data};
- exporter.visit(dynamic_cast<locoex::COpCall *>(node));
- }
- else
- {
- assert(false && "unsupported node found");
- }
-}
-
-} // namespace
-
-void exportNodes(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &gd)
-{
- for (auto node : loco::postorder_traversal(loco::output_nodes(g)))
- {
- exportNode(node, builder, gd);
- }
-}
diff --git a/compiler/exo-tflite/src/OperationExporter.h b/compiler/exo-tflite/src/OperationExporter.h
deleted file mode 100644
index ac404456c..000000000
--- a/compiler/exo-tflite/src/OperationExporter.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __OPERATION_EXPORTER_H__
-#define __OPERATION_EXPORTER_H__
-
-#include "ExporterUtils.h"
-
-#include <loco/IR/Graph.h>
-
-/**
- * @brief create Operators corresponding to model nodes
- * @param nodes container with nodes
- * @param gd information about serializer parts of model
- */
-void exportNodes(loco::Graph *g, flatbuffers::FlatBufferBuilder &builder, SerializedModelData &gd);
-
-#endif // __OPERATION_EXPORTER_H__
diff --git a/compiler/exo-tflite/src/ShapeInference.cpp b/compiler/exo-tflite/src/ShapeInference.cpp
deleted file mode 100644
index 4ca45990e..000000000
--- a/compiler/exo-tflite/src/ShapeInference.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ShapeInference.h"
-#include "Dialect/IR/TFLDialect.h"
-#include "Dialect/Service/TFLShapeInferenceRule.h"
-
-#include <loco/IR/CanonicalNode.h>
-#include <loco/IR/CanonicalDialect.h>
-#include <loco/IR/CanonicalNodeVisitor.h>
-#include <loco/Service/ShapeInference.h>
-#include <loco/Service/CanonicalShapeInferenceRule.h>
-#include <loco/Service/MultiDialectShapeInferenceRule.h>
-
-#include <locoex/COpCall.h>
-#include <locoex/COpDialect.h>
-#include <locoex/Service/COpShapeInferenceRule.h>
-
-void ShapeInference::run(loco::Graph *g)
-{
- // TODO Adjust indentation level
- {
- loco::CanonicalShapeInferenceRule canonical_rule;
- locoex::TFLShapeInferenceRule tfl_rule;
- locoex::COpShapeInferenceRule cop_rule;
-
- loco::MultiDialectShapeInferenceRule rules;
-
- rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
- .bind(locoex::TFLDialect::get(), &tfl_rule)
- .bind(locoex::COpDialect::get(), &cop_rule);
-
- loco::apply(&rules).to(g);
-
- return;
- }
-}
-
-ShapeDescription ShapeInference::get(loco::Node *node)
-{
- // TODO Adjust indentation level
- {
- assert(loco::shape_known(node));
- return to_shape_description(loco::shape_get(node));
- }
-}
diff --git a/compiler/exo-tflite/src/ShapeInference.h b/compiler/exo-tflite/src/ShapeInference.h
deleted file mode 100644
index 86c0c592a..000000000
--- a/compiler/exo-tflite/src/ShapeInference.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __SHAPE_INFERENCE_H__
-#define __SHAPE_INFERENCE_H__
-
-#include "ExporterUtils.h"
-
-#include <loco/IR/Nodes.h>
-
-/**
- * @brief Annotate the shape of each node as a node annotation
- *
- * HOW TO USE
- *
- * ShapeInference::run(g);
- *
- * ShapeInference::get(g->nodes()->at(..));
- */
-struct ShapeInference
-{
- static void run(loco::Graph *g);
-
- static ShapeDescription get(loco::Node *node);
-};
-
-#endif // __SHAPE_INFERENCE_H__
diff --git a/compiler/exo-tflite/src/TFLExporter.cpp b/compiler/exo-tflite/src/TFLExporter.cpp
deleted file mode 100644
index 006c57d08..000000000
--- a/compiler/exo-tflite/src/TFLExporter.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "exo/TFLExporter.h"
-
-#include "TFLExporterImpl.h"
-
-#include <stdex/Memory.h>
-
-#include <fstream>
-
-namespace exo
-{
-
-TFLExporter::TFLExporter(loco::Graph *graph) : _impl(stdex::make_unique<Impl>(graph))
-{
- // NOTHING TO DO
-}
-
-TFLExporter::~TFLExporter() = default;
-
-void TFLExporter::dumpToFile(const char *path) const
-{
- const char *ptr = _impl->getBufferPointer();
- const size_t size = _impl->getBufferSize();
- assert(ptr && "graph is not serialized for some reason");
- std::ofstream file(path);
- file.write(ptr, size);
-}
-
-} // namespace exo
diff --git a/compiler/exo-tflite/src/TFLExporterImpl.cpp b/compiler/exo-tflite/src/TFLExporterImpl.cpp
deleted file mode 100644
index ebe98cae5..000000000
--- a/compiler/exo-tflite/src/TFLExporterImpl.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TFLExporterImpl.h"
-
-#include "Convert.h"
-
-#include "TypeInference.h"
-#include "ShapeInference.h"
-#include "TensorExporter.h"
-#include "OperationExporter.h"
-#include "ExporterUtils.h"
-
-#include "Log.h"
-#include "Knob.h"
-
-#include <cassert>
-#include <unordered_map>
-#include <string>
-#include <stdexcept>
-
-namespace
-{
-
-void registerGraphInputTensors(loco::Graph *graph, SubGraphContext &ctx)
-{
- for (uint32_t n = 0; n < graph->inputs()->size(); ++n)
- {
- auto node = loco::pull_node(graph, n);
- assert(node != nullptr);
- ctx._inputs.push_back(get_tensor_index(node));
- }
-}
-
-void registerGraphOutputTensors(loco::Graph *graph, SubGraphContext &ctx)
-{
- for (uint32_t n = 0; n < graph->outputs()->size(); ++n)
- {
- auto push = loco::push_node(graph, n);
- assert(push != nullptr);
- auto node = push->from();
- assert(node != nullptr);
- ctx._outputs.push_back(get_tensor_index(node));
- }
-}
-
-} // namespace
-
-namespace exo
-{
-using namespace tflite;
-using namespace flatbuffers;
-
-TFLExporter::Impl::Impl(loco::Graph *graph) { exportGraph(graph); }
-
-Offset<Vector<Offset<OperatorCode>>>
-encodeOperatorCodes(FlatBufferBuilder &builder, std::unordered_map<OpCode, uint32_t> &opcodes,
- std::unordered_map<OpCode, std::string> &custom_opcodes)
-{
- std::vector<Offset<OperatorCode>> operator_codes_vec(opcodes.size());
- for (auto it : opcodes)
- {
- uint32_t idx = it.second;
- if (it.first.opcode != BuiltinOperator_CUSTOM)
- {
- operator_codes_vec[idx] = CreateOperatorCode(builder, it.first.opcode);
- }
- else // custom op
- {
- auto opCode = it.first;
- auto custom_code = custom_opcodes.find(opCode);
- if (custom_code == custom_opcodes.end())
- throw std::runtime_error("Cannot find code for custom op");
-
- operator_codes_vec[idx] =
- CreateOperatorCode(builder, it.first.opcode, builder.CreateString(custom_code->second));
- }
- }
- return builder.CreateVector(operator_codes_vec);
-}
-
-flatbuffers::Offset<tflite::SubGraph> TFLExporter::Impl::exportSubgraph(SerializedModelData &gd)
-{
- auto tensors = _builder.CreateVector(gd._tensors);
- auto inputs = _builder.CreateVector(gd._inputs);
- auto outputs = _builder.CreateVector(gd._outputs);
- auto operators = _builder.CreateVector(gd._operators);
- auto subgraph = CreateSubGraph(_builder, tensors, inputs, outputs, operators);
- return subgraph;
-}
-
-void TFLExporter::Impl::exportGraph(loco::Graph *graph)
-{
- LOGGER(l);
-
- INFO(l) << "Knob::EnableTFLDialect is " << (get<Knob::EnableTFLDialect>() ? "set" : "unset");
-
- if (get<Knob::EnableTFLDialect>()) // guard for backward compatibility
- {
- convert_to_TFLNodes(graph);
- }
-
- // Infer the type of each node
- TypeInference::run(graph);
- // TypeInference::get(node) now works
-
- // Infer the shape of each node
- ShapeInference::run(graph);
- // ShapeInference::get(node) now works
-
- _builder.Clear();
-
- SerializedModelData gd;
-
- // This version is taken from comment in fbs
- constexpr uint32_t version = 3;
-
- registerGraphIOName(graph, gd);
-
- // parse graph into SerializedModelData structure
- exportOpDefinedTensors(graph, _builder, gd);
-
- // NOTE Invoke these register functions only after each node is annotated with its tensor_index
- registerGraphInputTensors(graph, gd);
- registerGraphOutputTensors(graph, gd);
-
- exportNodes(graph, _builder, gd);
-
- // excode operator codes
- auto operator_codes =
- encodeOperatorCodes(_builder, gd._operator_codes, gd._custom_operator_codes);
-
- // Subgraphs
- Offset<SubGraph> subgraph = exportSubgraph(gd);
- auto subgraphs = _builder.CreateVector(std::vector<Offset<SubGraph>>{subgraph});
-
- // Description
- std::string description_str = "nnpackage";
- auto description = _builder.CreateString(description_str);
-
- // create array of buffers
- auto buffers = _builder.CreateVector(gd._buffers);
-
- // empty metadata
- std::vector<int> metadata_buffer_vec;
- auto metadata_buffer = _builder.CreateVector(metadata_buffer_vec);
-
- // Model
- auto model_offset = CreateModel(_builder, version, operator_codes, subgraphs, description,
- buffers, metadata_buffer);
- FinishModelBuffer(_builder, model_offset);
-}
-
-const char *TFLExporter::Impl::getBufferPointer() const
-{
- return reinterpret_cast<const char *>(_builder.GetBufferPointer());
-}
-
-size_t TFLExporter::Impl::getBufferSize() const { return _builder.GetSize(); }
-
-} // namespace exo
diff --git a/compiler/exo-tflite/src/TFLExporterImpl.h b/compiler/exo-tflite/src/TFLExporterImpl.h
deleted file mode 100644
index 9e7508637..000000000
--- a/compiler/exo-tflite/src/TFLExporterImpl.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __TFL_EXPORTER_IMPL_H__
-#define __TFL_EXPORTER_IMPL_H__
-
-#include "exo/TFLExporter.h"
-#include "schema_generated.h"
-
-#include <loco.h>
-
-struct SerializedModelData;
-
-namespace exo
-{
-
-/**
- * internal implementation of interface exporter class
- */
-class TFLExporter::Impl
-{
-public:
- Impl() = delete;
- ~Impl() = default;
-
- explicit Impl(loco::Graph *graph);
-
- /**
- * @return pointer to buffer with serialized graph
- */
- const char *getBufferPointer() const;
-
- /**
- * @return size of buffer with serialized graph
- */
- size_t getBufferSize() const;
-
-private:
- /**
- * @brief create Subgraph using data stored in SerializedModelData
- * @param gd information about serializer parts of model
- * @return offset in buffer corresponding to serialized subgraph
- */
- flatbuffers::Offset<tflite::SubGraph> exportSubgraph(SerializedModelData &gd);
-
- /**
- * @brief root function that writes graph into internal buffer
- * @param graph
- */
- void exportGraph(loco::Graph *graph);
-
-private:
- flatbuffers::FlatBufferBuilder _builder;
-};
-
-} // namespace loco_exporter
-
-#endif // __TFL_EXPORTER_IMPL_H__
diff --git a/compiler/exo-tflite/src/TFLExporterImpl.test.cpp b/compiler/exo-tflite/src/TFLExporterImpl.test.cpp
deleted file mode 100644
index e1d339e0a..000000000
--- a/compiler/exo-tflite/src/TFLExporterImpl.test.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TFLExporterImpl.h"
-
-#include "schema_generated.h"
-
-#include "GraphBlock.h"
-
-#include <loco/IR/PermutingCodec.h>
-#include <stdex/Memory.h>
-
-#include <gtest/gtest.h>
-
-namespace
-{
-
-class TFLExporterImplTests : public ::testing::Test
-{
-public:
- TFLExporterImplTests() { _graph = loco::make_graph(); }
-
-public:
- virtual ~TFLExporterImplTests() = default;
-
-protected:
- loco::Graph *graph(void) { return _graph.get(); }
-
- template <typename NodeT> NodeT *make_node(void);
-
-private:
- std::unique_ptr<loco::Graph> _graph;
-};
-
-template <typename NodeT> NodeT *TFLExporterImplTests::make_node(void)
-{
- return graph()->nodes()->create<NodeT>();
-}
-
-template <> loco::FeatureEncode *TFLExporterImplTests::make_node(void)
-{
- loco::FeatureEncode *encode_layer = graph()->nodes()->create<loco::FeatureEncode>();
-
- auto encoder = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>();
- (*encoder->perm())[loco::FeatureAxis::Count] = 0;
- (*encoder->perm())[loco::FeatureAxis::Depth] = 1;
- (*encoder->perm())[loco::FeatureAxis::Height] = 2;
- (*encoder->perm())[loco::FeatureAxis::Width] = 3;
- encode_layer->encoder(std::move(encoder));
-
- return encode_layer;
-}
-
-template <> loco::FeatureDecode *TFLExporterImplTests::make_node(void)
-{
- loco::FeatureDecode *decode_layer = graph()->nodes()->create<loco::FeatureDecode>();
-
- auto decoder = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>();
- (*decoder->perm())[loco::FeatureAxis::Count] = 0;
- (*decoder->perm())[loco::FeatureAxis::Depth] = 1;
- (*decoder->perm())[loco::FeatureAxis::Height] = 2;
- (*decoder->perm())[loco::FeatureAxis::Width] = 3;
- decode_layer->decoder(std::move(decoder));
-
- return decode_layer;
-}
-
-} // namespace
-
-// TODO TFLAdd
-
-// TODO TFLAveragePool2D
-
-// TODO TFLConcatenation
-
-// TODO TFLConv2D
-
-// TODO TFLDepthwiseConv2D
-
-// TODO TFLDiv
-
-// TODO TFLMaxPool2D
-
-// TODO TFLMul
-
-TEST_F(TFLExporterImplTests, Relu6)
-{
- auto pull = make_node<loco::Pull>();
- {
- pull->dtype(loco::DataType::FLOAT32);
- pull->shape({1, 8, 8, 3});
- }
- auto relu6 = make_node<loco::ReLU6>();
- {
- relu6->input(pull);
- }
- auto push = make_node<loco::Push>();
- {
- push->from(relu6);
- }
-
- auto input = graph()->inputs()->create();
- {
- input->name("input");
- loco::link(input, pull);
- }
- auto output = graph()->outputs()->create();
- {
- output->name("output");
- loco::link(output, push);
- }
-
- exo::TFLExporter::Impl exporter{graph()};
-
- // TODO Add more checks
- SUCCEED();
-}
-
-// TODO TFLRelu6
-
-// TODO TFLReshape
-
-// TODO TFLSoftmax
-
-// TODO TFLSqrt
-
-// TODO TFLSub
-
-// TODO TFLTanh
-
-// TODO TFLTranspose
-
-/**
- * What happens when there is a mismatch between generation and execution order!?
- */
-TEST_F(TFLExporterImplTests, Regression_0000)
-{
- // Execution Order: MaxPool2D -> ReLU
- // Generation Order: ReLU -> MaxPool2D
- auto pull = make_node<loco::Pull>();
- {
- pull->dtype(loco::DataType::FLOAT32);
- pull->shape({1, 8, 8, 3});
- }
- auto relu = make_node<loco::ReLU>();
- auto encode = exo::make_feature_encode<exo::DefaultLayout::NHWC>(pull);
- auto maxpool = make_node<loco::MaxPool2D>();
- auto decode = exo::make_feature_decode<exo::DefaultLayout::NHWC>(relu);
- auto push = make_node<loco::Push>();
-
- ASSERT_EQ(maxpool->window()->vertical(), 1);
- ASSERT_EQ(maxpool->window()->horizontal(), 1);
-
- maxpool->ifm(encode);
- relu->input(maxpool);
- push->from(decode);
-
- auto input = graph()->inputs()->create();
- {
- input->name("input");
- loco::link(input, pull);
- }
- auto output = graph()->outputs()->create();
- {
- output->name("output");
- loco::link(output, push);
- }
-
- exo::TFLExporter::Impl exporter{graph()};
- {
- int64_t maxpool_execution_index = -1;
- int64_t relu_exeuction_index = -1;
-
- auto model = tflite::GetModel(exporter.getBufferPointer());
- auto operators = model->subgraphs()->Get(0)->operators();
-
- for (uint32_t n = 0; n < operators->Length(); ++n)
- {
- auto opcode_index = operators->Get(n)->opcode_index();
-
- switch (model->operator_codes()->Get(opcode_index)->builtin_code())
- {
- case tflite::BuiltinOperator_RELU:
- ASSERT_EQ(relu_exeuction_index, -1);
- relu_exeuction_index = static_cast<int64_t>(n);
- break;
- case tflite::BuiltinOperator_MAX_POOL_2D:
- ASSERT_EQ(maxpool_execution_index, -1);
- maxpool_execution_index = static_cast<int64_t>(n);
- break;
- default:
- break;
- }
- }
-
- ASSERT_NE(maxpool_execution_index, -1);
- ASSERT_NE(relu_exeuction_index, -1);
- // maxpool SHOULD precede ReLU
- ASSERT_LT(maxpool_execution_index, relu_exeuction_index);
- }
-}
-
-/**
- * @brief Test exporter buffer generation
- */
-TEST_F(TFLExporterImplTests, Regression_0001)
-{
- auto cgen = make_node<loco::ConstGen>();
- cgen->rank(1);
- cgen->dim(0) = 2;
- cgen->dtype(loco::DataType::FLOAT32);
- cgen->size<loco::DataType::FLOAT32>(2);
- cgen->at<loco::DataType::FLOAT32>(0) = 3.3f;
- cgen->at<loco::DataType::FLOAT32>(1) = 1.1f;
-
- auto push = make_node<loco::Push>();
- push->from(cgen);
-
- auto output = graph()->outputs()->create();
- {
- output->name("output");
- loco::link(output, push);
- }
-
- exo::TFLExporter::Impl exporter{graph()};
- {
- auto model = tflite::GetModel(exporter.getBufferPointer());
- auto buffers = model->buffers();
-
- // 0'th empty buffer + ConstGen data + ConstGen node output
- ASSERT_EQ(buffers->Length(), 3);
-
- // 0'th should be empty buffer
- auto buffer_0 = (*buffers)[0];
- auto array_0 = buffer_0->data();
- ASSERT_EQ(array_0, nullptr);
-
- // 1'st should be ConstGen data which is two float
- auto buffer_1 = (*buffers)[1];
- auto array_1 = buffer_1->data();
- size_t size_1 = array_1->size();
- ASSERT_EQ(size_1, 2 * sizeof(float));
- }
-}
diff --git a/compiler/exo-tflite/src/TFLFormattedGraph.cpp b/compiler/exo-tflite/src/TFLFormattedGraph.cpp
deleted file mode 100644
index d01fe7bcc..000000000
--- a/compiler/exo-tflite/src/TFLFormattedGraph.cpp
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TFLFormattedGraph.h"
-
-#include "Dialect/IR/TFLDialect.h"
-#include "Dialect/IR/TFLNodes.h"
-
-#include <locoex/Service/COpFormattedGraph.h>
-
-#include <sstream>
-
-namespace
-{
-
-std::string opname(uint32_t opnum)
-{
- static std::string prefix{"tfl."};
-
- switch (static_cast<locoex::TFLOpcode>(opnum))
- {
-#define TFL_NODE(OPCODE, CLASS) \
- case locoex::TFLOpcode::OPCODE: \
- return prefix + #OPCODE;
-#include "Dialect/IR/TFLNodes.lst"
-#undef TFL_NODE
- default:
- break;
- };
-
- return prefix + "Invalid";
-}
-
-// TFLNodeSummaryBuilder with default implementation
-class TFLNodeSummaryBuilderBase : public locop::NodeSummaryBuilder
-{
-public:
- TFLNodeSummaryBuilderBase(const locop::SymbolTable *tbl) : _tbl{tbl}
- {
- // DO NOTHING
- }
-
-public:
- bool build(const loco::Node *, locop::NodeSummary &s) const final;
-
-protected:
-#define TFL_NODE(OPCODE, CLASS) \
- virtual bool summary(const CLASS *, locop::NodeSummary &s) const \
- { \
- s.comments().append("Emitted by Default TFLNodeSummaryBuilder"); \
- s.state(locop::NodeSummary::State::PartiallyKnown); \
- return true; \
- }
-#include "Dialect/IR/TFLNodes.lst"
-#undef TFL_NODE
-
-protected:
- const locop::SymbolTable *tbl(void) const { return _tbl; }
-
- // Please do not use _tbl directly and use tbl().
- // This will be changed to private in near future.
-protected:
- const locop::SymbolTable *_tbl;
-};
-
-class TFLNodeSummaryBuilder final : public TFLNodeSummaryBuilderBase
-{
-public:
- TFLNodeSummaryBuilder(const locop::SymbolTable *tbl) : TFLNodeSummaryBuilderBase(tbl)
- {
- // DO NOTHING
- }
-
-private:
-#define TFL_NODE(OPCODE, CLASS) bool summary(const CLASS *, locop::NodeSummary &) const final;
-#include "Dialect/IR/TFLNodes.lst"
-#undef TFL_NODE
-};
-
-bool TFLNodeSummaryBuilderBase::build(const loco::Node *node, locop::NodeSummary &s) const
-{
- (void)s; // TODO remove this when TFL_NODE(..) is defined in TFLNodes.lst
-
- if (node->dialect() != locoex::TFLDialect::get())
- return false;
-
-#define TFL_NODE(OPCODE, CLASS) \
- if (dynamic_cast<const CLASS *>(node)) \
- { \
- s.opname(opname(node->opnum())); \
- return summary(dynamic_cast<const CLASS *>(node), s); \
- }
-#include "Dialect/IR/TFLNodes.lst"
-#undef TFL_NODE
-
- return false;
-}
-
-bool TFLNodeSummaryBuilder::summary(const locoex::TFLAdd *node, locop::NodeSummary &s) const
-{
- s.args().append("x", tbl()->lookup(node->x()));
- s.args().append("y", tbl()->lookup(node->y()));
- s.state(locop::NodeSummary::State::Complete);
- return true;
-}
-
-bool TFLNodeSummaryBuilder::summary(const locoex::TFLAveragePool2D *node,
- locop::NodeSummary &s) const
-{
- s.args().append("input", tbl()->lookup(node->value()));
- s.state(locop::NodeSummary::State::PartiallyKnown);
- return true;
-}
-
-// TODO TFLConcatenation
-
-bool TFLNodeSummaryBuilder::summary(const locoex::TFLConst *, locop::NodeSummary &s) const
-{
- s.state(locop::NodeSummary::State::PartiallyKnown);
- return true;
-}
-
-// TODO TFLConv2D
-
-// TODO TFLDepthwiseConv2D
-
-bool TFLNodeSummaryBuilder::summary(const locoex::TFLDiv *node, locop::NodeSummary &s) const
-{
- s.args().append("x", tbl()->lookup(node->x()));
- s.args().append("y", tbl()->lookup(node->y()));
- s.state(locop::NodeSummary::State::Complete);
- return true;
-}
-
-bool TFLNodeSummaryBuilder::summary(const locoex::TFLMaxPool2D *node, locop::NodeSummary &s) const
-{
- s.args().append("input", tbl()->lookup(node->value()));
- s.state(locop::NodeSummary::State::PartiallyKnown);
- return true;
-}
-
-bool TFLNodeSummaryBuilder::summary(const locoex::TFLMul *node, locop::NodeSummary &s) const
-{
- s.args().append("x", tbl()->lookup(node->x()));
- s.args().append("y", tbl()->lookup(node->y()));
- s.state(locop::NodeSummary::State::Complete);
- return true;
-}
-
-bool TFLNodeSummaryBuilder::summary(const locoex::TFLRelu *node, locop::NodeSummary &s) const
-{
- s.args().append("input", tbl()->lookup(node->features()));
- s.state(locop::NodeSummary::State::Complete);
- return true;
-}
-
-// TODO TFLRelu6
-
-// TODO TFLReshape
-
-// TODO TFLSoftmax
-
-// TODO TFLSqrt
-
-bool TFLNodeSummaryBuilder::summary(const locoex::TFLSub *node, locop::NodeSummary &s) const
-{
- s.args().append("x", tbl()->lookup(node->x()));
- s.args().append("y", tbl()->lookup(node->y()));
- s.state(locop::NodeSummary::State::Complete);
- return true;
-}
-
-// TODO TFLTanh
-
-// TODO TFLTranspose
-
-} // namespace
-
-namespace exo
-{
-
-bool NodeSummaryBuilder::build(const loco::Node *node, locop::NodeSummary &s) const
-{
- if (locop::CanonicalNodeSummaryBuilder(_tbl).build(node, s))
- {
- return true;
- }
-
- if (TFLNodeSummaryBuilder(_tbl).build(node, s))
- {
- return true;
- }
-
- if (locoex::COpNodeSummaryBuilder(_tbl).build(node, s))
- {
- return true;
- }
-
- return false;
-}
-
-} // namespace exo
diff --git a/compiler/exo-tflite/src/TFLFormattedGraph.h b/compiler/exo-tflite/src/TFLFormattedGraph.h
deleted file mode 100644
index 5b890f53e..000000000
--- a/compiler/exo-tflite/src/TFLFormattedGraph.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __TFL_FORMATTED_GRAPH_H__
-#define __TFL_FORMATTED_GRAPH_H__
-
-#include <locop/FormattedGraph.h>
-
-#include <stdex/Memory.h>
-
-namespace exo
-{
-
-class NodeSummaryBuilder final : public locop::NodeSummaryBuilder
-{
-public:
- NodeSummaryBuilder(const locop::SymbolTable *tbl) : _tbl{tbl}
- {
- // DO NOTHING
- }
-
-public:
- bool build(const loco::Node *node, locop::NodeSummary &s) const final;
-
-private:
- const locop::SymbolTable *_tbl;
-};
-
-class NodeSummaryBuilderFactory final : public locop::NodeSummaryBuilderFactory
-{
-public:
- NodeSummaryBuilderFactory() = default;
-
-public:
- std::unique_ptr<locop::NodeSummaryBuilder> create(const locop::SymbolTable *tlb) const final
- {
- return stdex::make_unique<NodeSummaryBuilder>(tlb);
- }
-};
-
-} // namespace exo
-
-#endif // __TFL_FORMATTED_GRAPH_H__
diff --git a/compiler/exo-tflite/src/TensorExporter.cpp b/compiler/exo-tflite/src/TensorExporter.cpp
deleted file mode 100644
index e329db1da..000000000
--- a/compiler/exo-tflite/src/TensorExporter.cpp
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TensorExporter.h"
-#include "TypeInference.h"
-#include "ShapeInference.h"
-
-// TODO Fix include style
-#include "loco/IR/Algorithm.h"
-#include "loco/IR/CanonicalNode.h"
-#include "loco/IR/CanonicalNodeVisitor.h"
-
-using namespace tflite;
-using namespace flatbuffers;
-
-namespace
-{
-
-class TFLTensorInfo
-{
-public:
- TFLTensorInfo() = default;
-
-public:
- void name(const std::string &name) { _name = name; }
- const std::string &name(void) const { return _name; }
-
-public:
- const tflite::TensorType &dtype(void) const { return _dtype; }
- void dtype(const tflite::TensorType &dtype) { _dtype = dtype; }
-
- const ShapeDescription &shape(void) const { return _shape; }
- void shape(const ShapeDescription &shape) { _shape = shape; }
-
-public:
- loco::ConstGen *content(void) const { return _content; }
- void content(loco::ConstGen *c) { _content = c; }
-
-private:
- std::string _name;
-
- tflite::TensorType _dtype;
- ShapeDescription _shape;
-
- // TODO Find a better design
- loco::ConstGen *_content = nullptr;
-};
-
-using TFLTensorContext = std::vector<TFLTensorInfo>;
-
-struct NoOpDetector final : public loco::CanonicalNodeMutableVisitor<bool>
-{
- bool visit(loco::BiasEncode *) final
- {
- // BiasEncode is always noop
- return true;
- }
-
- bool visit(loco::FilterEncode *node) final
- {
- auto encoder = dynamic_cast<loco::PermutingEncoder<loco::Domain::Filter> *>(node->encoder());
- auto perm = encoder->perm();
-
- return isNHWC(perm);
- }
-
- bool visit(loco::FeatureEncode *node) final
- {
- auto encoder = dynamic_cast<loco::PermutingEncoder<loco::Domain::Feature> *>(node->encoder());
- auto perm = encoder->perm();
- return isNHWC(perm);
- }
-
- bool visit(loco::FeatureDecode *node) final
- {
- auto decoder = dynamic_cast<loco::PermutingDecoder<loco::Domain::Feature> *>(node->decoder());
- auto perm = decoder->perm();
- return isNHWC(perm);
- }
-
- // Return false by default
- bool visit(loco::Node *) final { return false; }
-};
-
-bool isNoOp(loco::Node *node)
-{
- if (auto canonical_node = dynamic_cast<loco::CanonicalNode *>(node))
- {
- NoOpDetector d;
- return canonical_node->accept(&d);
- }
- return false;
-}
-
-void allocateTFLiteTensor(loco::Node *node, TFLTensorContext &ctx)
-{
- if (isNoOp(node))
- {
- assert(node->arity() == 1 && node->arg(0) != nullptr);
- set_tensor_index(node, get_tensor_index(node->arg(0)));
- return;
- }
-
- auto tensor_index = static_cast<TFLTensorIndex>(ctx.size());
- // TODO Use Graph-level metadata for Input & Output
- auto tensor_name = "t_" + std::to_string(tensor_index);
-
- TFLTensorInfo tensor_info;
-
- tensor_info.name(tensor_name);
- tensor_info.dtype(TypeInference::get(node));
- tensor_info.shape(ShapeInference::get(node));
- tensor_info.content(dynamic_cast<loco::ConstGen *>(node));
-
- set_tensor_index(node, tensor_index);
-
- ctx.emplace_back(tensor_info);
-}
-
-} // namespace
-
-namespace
-{
-
-flatbuffers::Offset<Vector<int32_t>> encodeShape(FlatBufferBuilder &builder,
- const ShapeDescription &shape)
-{
- assert(shape._rank_known && "unknown number of dimensions is not supported");
- return builder.CreateVector(shape._dims);
-}
-
-flatbuffers::Offset<tflite::Buffer> encodeOpBuffer(FlatBufferBuilder &builder)
-{
- return CreateBuffer(builder);
-}
-
-template <typename NodeT>
-flatbuffers::Offset<tflite::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, NodeT *)
-{
- return CreateBuffer(builder);
-}
-
-template <>
-flatbuffers::Offset<tflite::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, loco::ConstGen *c)
-{
- assert(c->dtype() == loco::DataType::FLOAT32);
- std::vector<float> raw_data;
- const uint32_t size = c->size<loco::DataType::FLOAT32>();
- raw_data.reserve(size);
- for (uint32_t i = 0; i < size; ++i)
- {
- raw_data.push_back(c->at<loco::DataType::FLOAT32>(i));
- }
- const size_t raw_size = size * sizeof(float);
- auto array_offset = builder.CreateVector(reinterpret_cast<uint8_t *>(raw_data.data()), raw_size);
- return CreateBuffer(builder, array_offset);
-}
-
-} // namespace
-
-void exportOpDefinedTensor(const TFLTensorInfo &info, FlatBufferBuilder &builder,
- SerializedModelData &gd)
-{
- // Create and register output tensor shape
- auto shape_offset = encodeShape(builder, info.shape());
-
- // encode and register output tensor buffer
- auto buffer = (info.content() != nullptr) ? encodeOpBuffer(builder, info.content())
- : encodeOpBuffer(builder);
- auto buffer_id = static_cast<uint32_t>(gd._buffers.size());
- gd._buffers.push_back(buffer);
-
- auto name_offset = builder.CreateString(info.name());
- auto tensor_offset = CreateTensor(builder, shape_offset, info.dtype(), buffer_id, name_offset,
- /*quantization*/ 0, /*is_variable*/ false);
- gd._tensors.push_back(tensor_offset);
-}
-
-void exportOpDefinedTensors(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &gd)
-{
- TFLTensorContext tensor_ctx;
-
- for (auto node : loco::postorder_traversal(loco::output_nodes(g)))
- {
- allocateTFLiteTensor(node, tensor_ctx);
- }
-
- // add one empty buffer
- // note: there's a comment in tflite fbs file
- // - Note the 0th entry of this array must be an empty buffer (sentinel).
- // - This is a convention so that tensors without a buffer can provide 0 as
- // - their buffer.
- auto buffer = encodeOpBuffer(builder);
- gd._buffers.push_back(buffer);
-
- for (const auto &tensor_info : tensor_ctx)
- {
- exportOpDefinedTensor(tensor_info, builder, gd);
- }
-}
diff --git a/compiler/exo-tflite/src/TensorExporter.h b/compiler/exo-tflite/src/TensorExporter.h
deleted file mode 100644
index 4c1902aa0..000000000
--- a/compiler/exo-tflite/src/TensorExporter.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __TENSOR_EXPORTER_H__
-#define __TENSOR_EXPORTER_H__
-
-#include "ExporterUtils.h"
-
-#include <loco/IR/Graph.h>
-
-#include <flatbuffers/flatbuffers.h>
-
-/**
- * @brief create Tensors corresponding to results of all nodes in graph
- * @param computational graph
- * @param gd information about serialized parts of model
- */
-void exportOpDefinedTensors(loco::Graph *g, flatbuffers::FlatBufferBuilder &builder,
- SerializedModelData &gd);
-
-#endif // __TENSOR_EXPORTER_H__
diff --git a/compiler/exo-tflite/src/TestGraph.h b/compiler/exo-tflite/src/TestGraph.h
deleted file mode 100644
index 79d4d4b0e..000000000
--- a/compiler/exo-tflite/src/TestGraph.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __TEST_GRAPH_H__
-#define __TEST_GRAPH_H__
-
-#include "Dialect/IR/TFLNodes.h"
-
-#include <loco.h>
-
-#include <stdex/Memory.h>
-
-#include <cassert>
-
-namespace exo
-{
-namespace test
-{
-
-// THIS WILL BE DEPRECATED. USE TestGraph instead.
-// graph to build [Pull - some node of type T - Push]
-template <typename T> struct PullPushGraph
-{
-public:
- std::unique_ptr<loco::Graph> g;
- loco::Pull *pull;
- loco::Push *push;
- T *middle_node;
-
- PullPushGraph()
- {
- // g = Pull - T - Push
- g = loco::make_graph();
-
- pull = g->nodes()->create<loco::Pull>();
-
- middle_node = g->nodes()->create<T>();
- {
- setInput();
- }
-
- push = g->nodes()->create<loco::Push>();
- {
- push->from(middle_node);
- }
-
- auto input = g->inputs()->create();
- {
- input->name("input");
- loco::link(input, pull);
- }
- auto output = g->outputs()->create();
- {
- output->name("output");
- loco::link(output, push);
- }
- }
-
-private:
- void setInput(); // set the input of T
-};
-
-// setInput of TFL nodes
-template <> void PullPushGraph<locoex::TFLAveragePool2D>::setInput() { middle_node->value(pull); }
-
-class TestGraph
-{
-public:
- std::unique_ptr<loco::Graph> g;
- loco::Pull *pull;
- loco::Push *push;
-
- TestGraph() // creates Pull and Push
- {
- g = loco::make_graph();
-
- pull = g->nodes()->create<loco::Pull>();
-
- push = g->nodes()->create<loco::Push>();
-
- auto input = g->inputs()->create();
- {
- input->name("input");
- loco::link(input, pull);
- }
- auto output = g->outputs()->create();
- {
- output->name("output");
- loco::link(output, push);
- }
-
- _next_input = pull;
- }
-
- /// @brief Creates node with NO arg and appends it to graph
- template <class T> T *append()
- {
- auto node = g->nodes()->create<T>();
- _next_input = node;
-
- return node;
- }
-
- /// @brief Creates op T (arity=1) with arg1 as an input and appends it to graph
- template <class T> T *append(loco::Node *arg1)
- {
- auto node = g->nodes()->create<T>();
- setInput(node, arg1);
- _next_input = node;
-
- return node;
- }
-
- /// @brief Creates op T (arity=2) with arg1, arg2 as inputs and appends it to graph
- template <class T> T *append(loco::Node *arg1, loco::Node *arg2)
- {
- auto node = g->nodes()->create<T>();
- setInput(node, arg1, arg2);
- _next_input = node;
-
- return node;
- }
-
- // push will get the last appended node
- void complete() { push->from(_next_input); }
-
- void complete(loco::Node *last_node) { push->from(last_node); }
-
-private:
- // arity 1
- void setInput(loco::Node *node, loco::Node *) { assert(false && "NYI"); };
-
- void setInput(loco::AvgPool2D *node, loco::Node *input) { node->ifm(input); }
- void setInput(loco::BiasDecode *node, loco::Node *input) { node->input(input); };
- void setInput(loco::BiasEncode *node, loco::Node *input) { node->input(input); };
- void setInput(loco::FeatureDecode *node, loco::Node *input) { node->input(input); };
- void setInput(loco::FeatureEncode *node, loco::Node *input) { node->input(input); };
- void setInput(loco::MaxPool2D *node, loco::Node *input) { node->ifm(input); }
- void setInput(loco::Push *node, loco::Node *input) { node->from(input); };
- void setInput(loco::ReLU *node, loco::Node *input) { node->input(input); };
- void setInput(loco::ReLU6 *node, loco::Node *input) { node->input(input); };
- void setInput(loco::Tanh *node, loco::Node *input) { node->input(input); };
-
- void setInput(locoex::TFLAveragePool2D *node, loco::Node *input) { node->value(input); };
- void setInput(locoex::TFLMaxPool2D *node, loco::Node *input) { node->value(input); };
- void setInput(locoex::TFLRelu *node, loco::Node *input) { node->features(input); };
-
- // arity 2
- void setInput(loco::Node *node, loco::Node *, loco::Node *) { assert(false && "NYI"); };
-
- void setInput(loco::EltwiseAdd *node, loco::Node *arg1, loco::Node *arg2)
- {
- node->lhs(arg1);
- node->rhs(arg2);
- };
-
- void setInput(loco::FeatureBiasAdd *node, loco::Node *arg1, loco::Node *arg2)
- {
- node->value(arg1);
- node->bias(arg2);
- };
-
- void setInput(locoex::TFLAdd *node, loco::Node *arg1, loco::Node *arg2)
- {
- node->x(arg1);
- node->y(arg2);
- };
-
- void setInput(locoex::TFLMul *node, loco::Node *arg1, loco::Node *arg2)
- {
- node->x(arg1);
- node->y(arg2);
- };
-
-private:
- loco::Node *_next_input;
-};
-
-} // namespace test
-} // namespace exo
-
-#endif // __TEST_GRAPH_H__
diff --git a/compiler/exo-tflite/src/TestHelper.h b/compiler/exo-tflite/src/TestHelper.h
deleted file mode 100644
index 19097df22..000000000
--- a/compiler/exo-tflite/src/TestHelper.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __TEST_HELPER_H__
-#define __TEST_HELPER_H__
-
-#include "Check.h"
-#include "ProgressReporter.h"
-#include "Conversion/TypeInferencePass.h"
-#include "Conversion/ShapeInferencePass.h"
-
-#include <logo/Pass.h>
-#include <logo/Phase.h>
-
-#include <loco.h>
-
-#include <stdex/Memory.h>
-
-#include <gtest/gtest.h>
-
-/**
- * @brief Check the number of nodes in a graph starting from OUTPUTS
- */
-#define EXO_TEST_ASSERT_NODE_COUNT(OUTPUTS, COUNT) \
- { \
- auto v = loco::postorder_traversal(OUTPUTS); \
- ASSERT_EQ(v.size(), (COUNT)); \
- }
-
-namespace exo
-{
-namespace test
-{
-
-/**
- * @brief Phase for test, that is used to test pass. This phase initially adds TypeInferencePass
- * and ShapeInferencePass
- */
-class TypeShapeReadyPhase
-{
-public:
- TypeShapeReadyPhase()
- {
- // Type and Shape inference is prerequisite for run other test
- _phase.emplace_back(stdex::make_unique<::exo::TypeInferencePass>());
- _phase.emplace_back(stdex::make_unique<::exo::ShapeInferencePass>());
- }
-
- template <typename PassT> void add_pass() { _phase.emplace_back(stdex::make_unique<PassT>()); }
-
- void run(loco::Graph *g)
- {
- logo::PhaseRunner<logo::PhaseStrategy::Saturate> phase_runner{g};
-
- ::exo::ProgressReporter prog(g, logo::PhaseStrategy::Saturate);
- phase_runner.attach(&prog);
- phase_runner.run(_phase);
- }
-
-private:
- logo::Phase _phase;
-};
-
-/**
- * @brief Get the only succ object of type LocoNodeT. (The name `only succ` comes from English word
- * `only child`.)
- * parent must have 1 succ only.
- * When there is no succ of type LocoNodeT, nullptr will be returned.
- */
-template <typename LocoNodeT> inline LocoNodeT *get_only_succ(loco::Node *parent)
-{
- auto succs = loco::succs(parent);
- EXO_ASSERT(succs.size() == 1, "parent has more than 1 succs.");
-
- return dynamic_cast<LocoNodeT *>(*succs.begin());
-}
-
-template <typename T> inline T *find_first_node_bytype(loco::Graph *g)
-{
- T *first_node = nullptr;
- loco::Graph::NodeContext *nodes = g->nodes();
- uint32_t count = nodes->size();
-
- for (uint32_t i = 0; i < count; ++i)
- {
- first_node = dynamic_cast<T *>(nodes->at(i));
- if (first_node != nullptr)
- break;
- }
-
- return first_node;
-}
-
-} // namespace test
-} // namespace exo
-
-#endif // __TEST_HELPER_H__
diff --git a/compiler/exo-tflite/src/TypeInference.cpp b/compiler/exo-tflite/src/TypeInference.cpp
deleted file mode 100644
index 69a8a74f1..000000000
--- a/compiler/exo-tflite/src/TypeInference.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TypeInference.h"
-
-#include "schema_generated.h"
-
-#include "Dialect/Service/TFLTypeInferenceRule.h"
-#include "Dialect/IR/TFLDialect.h"
-
-#include <loco/IR/CanonicalNode.h>
-#include <loco/IR/CanonicalNodeVisitor.h>
-#include <loco/IR/CanonicalDialect.h>
-#include <loco/Service/TypeInference.h>
-
-#include <locoex/COpDialect.h>
-#include <locoex/Service/COpTypeInference.h>
-
-#include <stdex/Memory.h>
-
-#include <stdexcept>
-#include <type_traits>
-
-namespace
-{
-
-tflite::TensorType translateLocoTypeToTFLite(loco::DataType dtype)
-{
- switch (dtype)
- {
- case loco::DataType::U8:
- return tflite::TensorType_UINT8;
- // case loco::DataType::U16: unsupported
- // case loco::DataType::U32: unsupported
- // case loco::DataType::U64: unsupported
- case loco::DataType::S8:
- return tflite::TensorType_INT8;
- case loco::DataType::S16:
- return tflite::TensorType_INT16;
- case loco::DataType::S32:
- return tflite::TensorType_INT32;
- case loco::DataType::S64:
- return tflite::TensorType_INT64;
- case loco::DataType::FLOAT16:
- return tflite::TensorType_FLOAT16;
- case loco::DataType::FLOAT32:
- return tflite::TensorType_FLOAT32;
- // case loco::DataType::FLOAT64: unsupported
- default:
- break;
- }
-
- throw std::invalid_argument{"dtype"};
-}
-
-} // namespace
-
-void TypeInference::run(loco::Graph *g)
-{
- loco::CanonicalTypeInferenceRule canonical_rule;
- locoex::COpTypeInferenceRule cop_rule; // rule for custom op
- locoex::TFLTypeInferenceRule tfl_rule;
-
- loco::MultiDialectTypeInferenceRule rules;
-
- rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
- .bind(locoex::COpDialect::get(), &cop_rule)
- .bind(locoex::TFLDialect::get(), &tfl_rule);
-
- loco::apply(&rules).to(g);
-}
-
-tflite::TensorType TypeInference::get(loco::Node *node)
-{
- assert(loco::dtype_known(node));
- return translateLocoTypeToTFLite(loco::dtype_get(node));
-}
diff --git a/compiler/exo-tflite/src/TypeInference.h b/compiler/exo-tflite/src/TypeInference.h
deleted file mode 100644
index 848549e0a..000000000
--- a/compiler/exo-tflite/src/TypeInference.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __TYPE_INFERENCE_H__
-#define __TYPE_INFERENCE_H__
-
-#include "ExporterUtils.h"
-
-#include <loco/IR/Nodes.h>
-
-/**
- * @brief Annotate the type of each node as NodeAnnotation
- *
- * HOW TO USE
- *
- * TypeInference::run(g);
- *
- * TypeInference::get(g->nodes()->at(0));
- * TypeInference::get(g->nodes()->at(...));
- */
-struct TypeInference
-{
- static void run(loco::Graph *g);
-
- static tflite::TensorType get(loco::Node *node);
-};
-
-#endif // __TYPE_INFERENCE_H__
diff --git a/compiler/exo-tflite/src/TypeInference.test.cpp b/compiler/exo-tflite/src/TypeInference.test.cpp
deleted file mode 100644
index 10cad8fdb..000000000
--- a/compiler/exo-tflite/src/TypeInference.test.cpp
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TypeInference.h"
-
-#include <loco/IR/PermutingCodec.h>
-#include <stdex/Memory.h>
-
-#include <gtest/gtest.h>
-
-using stdex::make_unique;
-
-namespace
-{
-
-class Sequential
-{
-public:
- loco::Pull *addPullLayer(const loco::DataType &dtype = loco::DataType::FLOAT32)
- {
- loco::Pull *pull = _graph.nodes()->create<loco::Pull>();
-
- auto graph_input = _graph.inputs()->create();
- graph_input->name("graph_input");
- loco::link(graph_input, pull);
-
- pull->dtype(dtype);
- setSampleShape(pull);
-
- return last(pull);
- }
-
- loco::ReLU *addReLULayer(void)
- {
- loco::ReLU *relu = _graph.nodes()->create<loco::ReLU>();
-
- relu->input(_last);
-
- return last(relu);
- }
-
- loco::Push *addPushLayer(void)
- {
- loco::Push *push = _graph.nodes()->create<loco::Push>();
-
- auto graph_output = _graph.outputs()->create();
- graph_output->name("graph_output");
- loco::link(graph_output, push);
-
- push->from(_last);
-
- return last(push);
- }
-
- loco::Graph *graph() { return &_graph; }
-
-private:
- template <typename T> uint32_t setSampleShape(T *op)
- {
- const uint32_t n = 1;
- const uint32_t h = 100;
- const uint32_t w = 100;
- const uint32_t c = 3;
- op->rank(4);
- op->dim(0).set(n);
- op->dim(1).set(c);
- op->dim(2).set(h);
- op->dim(3).set(w);
- return n * h * w * c;
- }
-
- template <typename T> T *last(T *node)
- {
- _last = node;
- return node;
- }
-
-private:
- loco::Graph _graph;
- loco::Node *_last;
-};
-
-struct TypeInferenceTest : public Sequential, public ::testing::Test
-{
- virtual ~TypeInferenceTest() = default;
-};
-
-} // namespace
-
-// TypeInference SHOULD PROPAGATE type information properly
-TEST_F(TypeInferenceTest, Regression_0000)
-{
- auto pull = addPullLayer(loco::DataType::S8);
- auto relu = addReLULayer();
- auto push = addPushLayer();
-
- TypeInference::run(graph());
-
- ASSERT_EQ(TypeInference::get(relu), tflite::TensorType_INT8);
- ASSERT_EQ(TypeInference::get(push), tflite::TensorType_INT8);
-}
diff --git a/compiler/exo/CMakeLists.txt b/compiler/exo/CMakeLists.txt
new file mode 100644
index 000000000..79c75ef2e
--- /dev/null
+++ b/compiler/exo/CMakeLists.txt
@@ -0,0 +1,73 @@
+nnas_find_package(FlatBuffers QUIET)
+
+if(NOT FlatBuffers_FOUND)
+ message(STATUS "Build exo: FALSE (missing FlatBuffers)")
+ return()
+endif(NOT FlatBuffers_FOUND)
+
+nnas_find_package(TensorFlowSource EXACT 1.14 QUIET)
+
+if(NOT TensorFlowSource_FOUND)
+ message(STATUS "Build exo: FALSE (missing TensorFlowSource)")
+ return()
+endif(NOT TensorFlowSource_FOUND)
+
+message(STATUS "Build exo: TRUE")
+
+set(TFLITE_SCHEMA_DIR "${TensorFlowSource_DIR}/tensorflow/lite/schema")
+set(CIRCLE_SCHEMA_DIR "${NNAS_PROJECT_SOURCE_DIR}/nnpackage/schema")
+
+FlatBuffers_Target(exo_tflite_fbs
+ OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen"
+ SCHEMA_DIR "${TFLITE_SCHEMA_DIR}"
+ SCHEMA_FILES schema.fbs
+)
+
+FlatBuffers_Target(exo_circle_fbs
+ OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/gen"
+ SCHEMA_DIR "${CIRCLE_SCHEMA_DIR}"
+ SCHEMA_FILES circle_schema.fbs
+)
+
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(exo SHARED ${SOURCES})
+target_include_directories(exo PUBLIC include)
+target_include_directories(exo PRIVATE src)
+target_link_libraries(exo PUBLIC exo_tflite_fbs)
+target_link_libraries(exo PUBLIC exo_circle_fbs)
+target_link_libraries(exo PUBLIC loco)
+target_link_libraries(exo PRIVATE stdex)
+target_link_libraries(exo PRIVATE pepper_str)
+target_link_libraries(exo PRIVATE pepper_strcast)
+target_link_libraries(exo PRIVATE locoex_customop)
+target_link_libraries(exo PRIVATE locop)
+target_link_libraries(exo PRIVATE hermes_std)
+target_link_libraries(exo PRIVATE logo)
+target_link_libraries(exo PRIVATE oops)
+install(TARGETS exo DESTINATION lib)
+
+# Let's apply nncc common compile options
+#
+# NOTE This will enable strict compilation (warnings as error).
+# Please refer to the top-level CMakeLists.txt for details
+target_link_libraries(exo PRIVATE nncc_common)
+
+if (NOT ENABLE_TEST)
+ return()
+endif (NOT ENABLE_TEST)
+
+# Google Test is mandatory for internal testing
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(exo_test ${TESTS})
+target_include_directories(exo_test PRIVATE src)
+target_link_libraries(exo_test stdex)
+target_link_libraries(exo_test pepper_str)
+target_link_libraries(exo_test exo)
+target_link_libraries(exo_test hermes_std)
+target_link_libraries(exo_test logo)
+target_link_libraries(exo_test oops)
+target_link_libraries(exo_test locoex_customop)
diff --git a/compiler/exo/README.md b/compiler/exo/README.md
new file mode 100644
index 000000000..bfa7fcfd3
--- /dev/null
+++ b/compiler/exo/README.md
@@ -0,0 +1,12 @@
+# exo
+
+_exo_ includes _loco_-to-_T/F Lite_ exporter (as a library).
+
+## How to add a new TFL node
+
+1. Add a new TFL node into `TFLNodes.lst` and `TFLNodes.h`
+1. Define a knob in `Knob.lst` if you need a knob.
+1. Add appropriate methods in `TFLShapeInferenceRule.cpp` and `TFLTypeInferenceRule.cpp`
+1. Add a new converter under `Conversion` directory
+1. Add an appropriate method in `OperationExporter.cpp`
+1. Register the converter into `Convert.cpp`
diff --git a/compiler/exo/include/exo/CircleExporter.h b/compiler/exo/include/exo/CircleExporter.h
new file mode 100644
index 000000000..7ec159303
--- /dev/null
+++ b/compiler/exo/include/exo/CircleExporter.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EXO_CIRCLE_EXPORTER_H__
+#define __EXO_CIRCLE_EXPORTER_H__
+
+#include <loco.h>
+
+#include <memory>
+
+namespace exo
+{
+
+/**
+ * HOW TO USE:
+ *
+ * loco::Graph *g = ...;
+ *
+ * CircleExporter e(g);
+ * e.dumpToFile("model.circle");
+ *
+ * HOW TO USE (simplified):
+ *
+ * CircleExporter(g).dumpToFile("model.circle");
+ *
+ */
+class CircleExporter
+{
+public:
+ class Impl;
+
+public:
+ explicit CircleExporter(loco::Graph *graph);
+ ~CircleExporter();
+
+ /**
+ * @brief write to a file
+ * @param path path to file where to write data
+ * @throws any file related exceptions
+ */
+ void dumpToFile(const char *path) const;
+
+private:
+ std::unique_ptr<Impl> _impl;
+};
+
+} // namespace exo
+
+#endif // __EXO_CIRCLE_EXPORTER_H__
diff --git a/compiler/exo/include/exo/LoggingContext.h b/compiler/exo/include/exo/LoggingContext.h
new file mode 100644
index 000000000..5f10ceb93
--- /dev/null
+++ b/compiler/exo/include/exo/LoggingContext.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EXO_LOGGING_CONTEXT_H__
+#define __EXO_LOGGING_CONTEXT_H__
+
+#include <hermes.h>
+
+namespace exo
+{
+
+/**
+ * @brief Global logging context
+ */
+struct LoggingContext
+{
+ static hermes::Context *get(void);
+};
+
+} // namespace exo
+
+#endif // __EXO_LOGGING_CONTEXT_H__
diff --git a/compiler/exo-tflite/include/exo/TFLExporter.h b/compiler/exo/include/exo/TFLExporter.h
index 49cce2af5..49cce2af5 100644
--- a/compiler/exo-tflite/include/exo/TFLExporter.h
+++ b/compiler/exo/include/exo/TFLExporter.h
diff --git a/compiler/exo/requires.cmake b/compiler/exo/requires.cmake
new file mode 100644
index 000000000..6378b942d
--- /dev/null
+++ b/compiler/exo/requires.cmake
@@ -0,0 +1,6 @@
+require("stdex")
+require("loco")
+require("locoex-customop")
+require("logo")
+require("pepper-str")
+require("oops")
diff --git a/compiler/exo/src/Check.h b/compiler/exo/src/Check.h
new file mode 100644
index 000000000..79dac50dd
--- /dev/null
+++ b/compiler/exo/src/Check.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CHECK_H__
+#define __CHECK_H__
+
+#include <pepper/str.h>
+
+#include <stdexcept>
+#include <cassert>
+#include <iostream>
+
+// TODO Add macro for Release version
+
+#define EXO_ASSERT(condition, msg) \
+ { \
+ if (!(condition)) \
+ { \
+ std::cerr << "[assert failed] " << (msg) << ". " << std::endl; \
+ assert((condition)); \
+ } \
+ }
+
+#endif // __CHECK_H__
diff --git a/compiler/exo/src/Circle/CircleExporter.cpp b/compiler/exo/src/Circle/CircleExporter.cpp
new file mode 100644
index 000000000..797749090
--- /dev/null
+++ b/compiler/exo/src/Circle/CircleExporter.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "exo/CircleExporter.h"
+
+#include "CircleExporterImpl.h"
+
+#include <stdex/Memory.h>
+
+#include <oops/InternalExn.h>
+
+#include <fstream>
+
+namespace exo
+{
+
+CircleExporter::CircleExporter(loco::Graph *graph) : _impl(stdex::make_unique<Impl>(graph))
+{
+ // NOTHING TO DO
+}
+
+CircleExporter::~CircleExporter() = default;
+
+void CircleExporter::dumpToFile(const char *path) const
+{
+ const char *ptr = _impl->getBufferPointer();
+ const size_t size = _impl->getBufferSize();
+
+ if (!ptr)
+ INTERNAL_EXN("Graph was not serialized by FlatBuffer for some reason");
+
+ std::ofstream file(path, std::ofstream::binary);
+ file.write(ptr, size);
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Circle/CircleExporterImpl.cpp b/compiler/exo/src/Circle/CircleExporterImpl.cpp
new file mode 100644
index 000000000..4cba33da1
--- /dev/null
+++ b/compiler/exo/src/Circle/CircleExporterImpl.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CircleExporterImpl.h"
+
+#include "Convert.h"
+#include "ExoOptimize.h"
+
+#include "CircleTensorExporter.h"
+#include "CircleOperationExporter.h"
+#include "CircleExporterUtils.h"
+
+#include "Log.h"
+#include "Knob.h"
+
+#include <oops/InternalExn.h>
+
+#include <cassert>
+#include <unordered_map>
+#include <string>
+#include <stdexcept>
+
+namespace
+{
+
+using namespace exo::circle_detail;
+
+void registerGraphInputTensors(loco::Graph *graph, SubGraphContext &ctx)
+{
+ for (uint32_t n = 0; n < graph->inputs()->size(); ++n)
+ {
+ auto node = loco::pull_node(graph, n);
+ assert(node != nullptr);
+ ctx._inputs.push_back(get_tensor_index(node));
+ }
+}
+
+void registerGraphOutputTensors(loco::Graph *graph, SubGraphContext &ctx)
+{
+ for (uint32_t n = 0; n < graph->outputs()->size(); ++n)
+ {
+ auto push = loco::push_node(graph, n);
+ assert(push != nullptr);
+ auto node = push->from();
+ assert(node != nullptr);
+ ctx._outputs.push_back(get_tensor_index(node));
+ }
+}
+
+} // namespace
+
+namespace
+{
+
+using namespace circle;
+using namespace flatbuffers;
+
+Offset<Vector<Offset<OperatorCode>>>
+encodeOperatorCodes(FlatBufferBuilder &builder, std::unordered_map<OpCode, uint32_t> &opcodes,
+ std::unordered_map<OpCode, std::string> &custom_opcodes)
+{
+ std::vector<Offset<OperatorCode>> operator_codes_vec(opcodes.size());
+ for (auto it : opcodes)
+ {
+ uint32_t idx = it.second;
+ if (it.first.opcode != BuiltinOperator_CUSTOM)
+ {
+ operator_codes_vec[idx] = CreateOperatorCode(builder, it.first.opcode);
+ }
+ else // custom op
+ {
+ auto opCode = it.first;
+ auto custom_code = custom_opcodes.find(opCode);
+ if (custom_code == custom_opcodes.end())
+ INTERNAL_EXN("Cannot find code for customop even though opcode is BuiltinOperator_CUSTOM");
+
+ operator_codes_vec[idx] =
+ CreateOperatorCode(builder, it.first.opcode, builder.CreateString(custom_code->second));
+ }
+ }
+ return builder.CreateVector(operator_codes_vec);
+}
+
+} // namespace
+
+namespace exo
+{
+
+using namespace exo::circle_detail;
+using namespace circle;
+using namespace flatbuffers;
+
+CircleExporter::Impl::Impl(loco::Graph *graph) { exportGraph(graph); }
+
+::flatbuffers::Offset<::circle::SubGraph>
+CircleExporter::Impl::exportSubgraph(SerializedModelData &gd)
+{
+ auto tensors = _builder.CreateVector(gd._tensors);
+ auto inputs = _builder.CreateVector(gd._inputs);
+ auto outputs = _builder.CreateVector(gd._outputs);
+ auto operators = _builder.CreateVector(gd._operators);
+ auto df = gd._data_format;
+ auto subgraph = CreateSubGraph(_builder, tensors, inputs, outputs, operators, df);
+ return subgraph;
+}
+
+void CircleExporter::Impl::exportGraph(loco::Graph *graph)
+{
+ LOGGER(l);
+
+ // IR-level conversion and optimization
+ {
+ convert_to_TFLNodes(graph);
+ set(Dialect::CIRCLE);
+ optimize(graph);
+ }
+
+ _builder.Clear();
+
+ SerializedModelData gd;
+
+ // This version is taken from comment in fbs
+ constexpr uint32_t version = 0;
+
+ registerGraphIOName(graph, gd);
+
+ // parse graph into SerializedModelData structure
+ exportOpDefinedTensors(graph, _builder, gd);
+
+ // NOTE Invoke these register functions only after each node is annotated with its tensor_index
+ registerGraphInputTensors(graph, gd);
+ registerGraphOutputTensors(graph, gd);
+
+ exportNodes(graph, _builder, gd);
+
+ // encode operator codes
+ auto operator_codes =
+ encodeOperatorCodes(_builder, gd._operator_codes, gd._custom_operator_codes);
+
+ // Subgraphs
+ Offset<SubGraph> subgraph = exportSubgraph(gd);
+ auto subgraphs = _builder.CreateVector(std::vector<Offset<SubGraph>>{subgraph});
+
+ // Description
+ std::string description_str = "nnpackage";
+ auto description = _builder.CreateString(description_str);
+
+ // create array of buffers
+ auto buffers = _builder.CreateVector(gd._buffers);
+
+ // empty metadata
+ std::vector<int> metadata_buffer_vec;
+ auto metadata_buffer = _builder.CreateVector(metadata_buffer_vec);
+
+ // Model
+ auto model_offset = CreateModel(_builder, version, operator_codes, subgraphs, description,
+ buffers, metadata_buffer);
+ FinishModelBuffer(_builder, model_offset);
+}
+
+const char *CircleExporter::Impl::getBufferPointer() const
+{
+ return reinterpret_cast<const char *>(_builder.GetBufferPointer());
+}
+
+size_t CircleExporter::Impl::getBufferSize() const { return _builder.GetSize(); }
+
+} // namespace exo
diff --git a/compiler/exo/src/Circle/CircleExporterImpl.h b/compiler/exo/src/Circle/CircleExporterImpl.h
new file mode 100644
index 000000000..b1138fbad
--- /dev/null
+++ b/compiler/exo/src/Circle/CircleExporterImpl.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_EXPORTER_IMPL_H__
+#define __CIRCLE_EXPORTER_IMPL_H__
+
+#include "exo/CircleExporter.h"
+#include "circle_schema_generated.h"
+
+#include <loco.h>
+
+namespace exo
+{
+
+namespace circle_detail
+{
+
+struct SerializedModelData;
+
+} // namespace circle_detail
+
+using namespace circle_detail;
+
+/**
+ * internal implementation of interface exporter class
+ */
+class CircleExporter::Impl
+{
+public:
+ Impl() = delete;
+ ~Impl() = default;
+
+ explicit Impl(loco::Graph *graph);
+
+ /**
+ * @return pointer to buffer with serialized graph
+ */
+ const char *getBufferPointer() const;
+
+ /**
+ * @return size of buffer with serialized graph
+ */
+ size_t getBufferSize() const;
+
+private:
+ /**
+ * @brief create Subgraph using data stored in SerializedModelData
+ * @param gd information about serializer parts of model
+ * @return offset in buffer corresponding to serialized subgraph
+ */
+ flatbuffers::Offset<circle::SubGraph> exportSubgraph(SerializedModelData &gd);
+
+ /**
+ * @brief root function that writes graph into internal buffer
+ * @param graph
+ */
+ void exportGraph(loco::Graph *graph);
+
+private:
+ flatbuffers::FlatBufferBuilder _builder;
+};
+
+} // namespace exo
+
+#endif // __CIRCLE_EXPORTER_IMPL_H__
diff --git a/compiler/exo/src/Circle/CircleExporterUtils.cpp b/compiler/exo/src/Circle/CircleExporterUtils.cpp
new file mode 100644
index 000000000..12b204ce7
--- /dev/null
+++ b/compiler/exo/src/Circle/CircleExporterUtils.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CircleExporterUtils.h"
+
+#include <oops/InternalExn.h>
+
+namespace exo
+{
+
+circle::ActivationFunctionType to_circle_actfunc(locoex::FusedActFunc func)
+{
+ switch (func)
+ {
+ case locoex::FusedActFunc::NONE:
+ return circle::ActivationFunctionType_NONE;
+ case locoex::FusedActFunc::RELU:
+ return circle::ActivationFunctionType_RELU;
+ case locoex::FusedActFunc::RELU6:
+ return circle::ActivationFunctionType_RELU6;
+ default:
+ INTERNAL_EXN_V("trying to convert unsupported locoex::FusedActFunc", oops::to_uint32(func));
+ }
+}
+
+} // namespace exo
+
+namespace exo
+{
+namespace circle_detail
+{
+
+uint32_t SerializedModelData::registerBuiltinOpcode(circle::BuiltinOperator builtin_code)
+{
+ auto it = _operator_codes.find(OpCode{builtin_code});
+ if (it != _operator_codes.end())
+ {
+ return it->second;
+ }
+ auto idx = static_cast<uint32_t>(_operator_codes.size());
+ _operator_codes.emplace(OpCode{builtin_code}, idx);
+ return idx;
+}
+
+uint32_t SerializedModelData::registerCustomOpcode(const std::string &custom_op)
+{
+ circle::BuiltinOperator custom_code = circle::BuiltinOperator_CUSTOM;
+ auto idx = registerBuiltinOpcode(custom_code);
+ _custom_operator_codes.emplace(OpCode{custom_code}, custom_op);
+ return idx;
+}
+
+circle::Padding getOpPadding(const loco::Padding2D *pad, const loco::Stride<2> *stride,
+ const ShapeDescription &ifm, const ShapeDescription &ofm)
+{
+ // VALID padding
+ if (pad->top() == 0 && pad->bottom() == 0 && pad->left() == 0 && pad->right() == 0)
+ return circle::Padding_VALID;
+
+ // SAME padding
+ //
+ // For same padding, by definition, following equation should hold:
+ // O = floor((I - 1) / S) + 1
+ // where input size I, output size O, stride S
+ //
+ // NOTE input and output 'feature' map are shape of NHWC
+ bool same_padding_criterion_1 =
+ (static_cast<uint32_t>(ofm._dims[1]) == (ifm._dims[1] - 1) / stride->vertical() + 1) &&
+ (static_cast<uint32_t>(ofm._dims[2]) == (ifm._dims[2] - 1) / stride->horizontal() + 1);
+
+ // For same padding, rear padding is same or bigger than front padding by at most 1
+ bool same_padding_criterion_2 =
+ (pad->top() <= pad->bottom()) && (pad->bottom() <= pad->top() + 1) &&
+ (pad->left() <= pad->right()) && (pad->right() <= pad->left() + 1);
+
+ if (same_padding_criterion_1 && same_padding_criterion_2)
+ return circle::Padding_SAME;
+
+ INTERNAL_EXN("Unsupported padding criteria");
+}
+
+circle::Padding getOpPadding(const locoex::Padding pad)
+{
+ if (pad == locoex::Padding::VALID)
+ return circle::Padding_VALID;
+ if (pad == locoex::Padding::SAME)
+ return circle::Padding_SAME;
+
+ INTERNAL_EXN_V("Unsupported locoex::Padding", oops::to_uint32(pad));
+}
+
+void registerGraphIOName(loco::Graph *graph, SerializedModelData &gd)
+{
+ for (uint32_t in = 0; in < graph->inputs()->size(); ++in)
+ {
+ auto pull = loco::pull_node(graph, in);
+ auto name = graph->inputs()->at(in)->name();
+
+ gd._pull_to_name[pull] = name;
+ }
+ for (uint32_t out = 0; out < graph->outputs()->size(); ++out)
+ {
+ auto push = loco::push_node(graph, out);
+ auto name = graph->outputs()->at(out)->name();
+
+ gd._push_to_name[push] = name;
+ }
+
+ // TODO set this value properly
+ gd._data_format = circle::DataFormat::DataFormat_CHANNELS_LAST;
+}
+
+#include <stdex/Memory.h>
+
+#include <cassert>
+
+namespace
+{
+
+class TFLTensorIndexAnnotation final : public loco::NodeAnnotation
+{
+public:
+ TFLTensorIndexAnnotation(const TFLTensorIndex &index) : _index{index}
+ {
+ // DO NOTHING
+ }
+
+public:
+ const TFLTensorIndex &index(void) const { return _index; }
+
+private:
+ TFLTensorIndex _index;
+};
+
+} // namespace
+
+void set_tensor_index(loco::Node *node, const TFLTensorIndex &tensor_id)
+{
+ assert(node->annot<TFLTensorIndexAnnotation>() == nullptr);
+ node->annot(stdex::make_unique<TFLTensorIndexAnnotation>(tensor_id));
+}
+
+TFLTensorIndex get_tensor_index(loco::Node *node)
+{
+ assert(node->annot<TFLTensorIndexAnnotation>() != nullptr);
+ return node->annot<TFLTensorIndexAnnotation>()->index();
+}
+
+} // namespace circle_detail
+} // namespace exo
diff --git a/compiler/exo/src/Circle/CircleExporterUtils.h b/compiler/exo/src/Circle/CircleExporterUtils.h
new file mode 100644
index 000000000..fdd162bae
--- /dev/null
+++ b/compiler/exo/src/Circle/CircleExporterUtils.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_EXPORTER_UTILS_H__
+#define __CIRCLE_EXPORTER_UTILS_H__
+
+#include "ExporterUtils.h"
+
+#include "circle_schema_generated.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+#include <loco.h>
+
+#include <unordered_map>
+
+namespace exo
+{
+namespace circle_detail
+{
+
+struct OpCode
+{
+ circle::BuiltinOperator opcode;
+
+ bool operator==(const OpCode &rhs) const { return opcode == rhs.opcode; }
+};
+
+} // namespace circle_detail
+} // namespace exo
+
+namespace exo
+{
+
+circle::ActivationFunctionType to_circle_actfunc(locoex::FusedActFunc func);
+
+} // namespace exo
+
+namespace std
+{
+
+template <> struct hash<exo::circle_detail::OpCode>
+{
+ size_t operator()(const exo::circle_detail::OpCode &x) const { return hash<int>()(x.opcode); }
+};
+
+} // namespace std
+
+namespace exo
+{
+namespace circle_detail
+{
+
+/**
+ * @breif Record the information of T/F Lite SubGraph and its mapping to loco
+ */
+struct SubGraphContext
+{
+ /// @brief SubGraph input tensor id
+ std::vector<int32_t> _inputs;
+ /// @brief SubGraph output tensor id
+ std::vector<int32_t> _outputs;
+ /// @DataFormat for SubGraph
+ circle::DataFormat _data_format{circle::DataFormat::DataFormat_CHANNELS_LAST};
+};
+
+// Prerequisites for circle::Model object creation
+struct SerializedModelData final : public SubGraphContext
+{
+ SerializedModelData() = default;
+ SerializedModelData(const SerializedModelData &) = delete;
+
+ std::unordered_map<OpCode, uint32_t> _operator_codes;
+ std::unordered_map<OpCode, std::string> _custom_operator_codes;
+ std::vector<flatbuffers::Offset<circle::Operator>> _operators;
+ std::vector<flatbuffers::Offset<circle::Tensor>> _tensors;
+ std::vector<flatbuffers::Offset<circle::Buffer>> _buffers;
+
+ // Graph input and output names
+ std::unordered_map<loco::Pull *, std::string> _pull_to_name;
+ std::unordered_map<loco::Push *, std::string> _push_to_name;
+
+ /**
+ * @brief if opcode is not registered in table of opcodes add it
+ * @param builtin_code
+ * @return idx of opcode in table of opcodes (see schema)
+ */
+ uint32_t registerBuiltinOpcode(circle::BuiltinOperator builtin_code);
+ uint32_t registerCustomOpcode(const std::string &custom_op);
+};
+
+circle::Padding getOpPadding(const loco::Padding2D *pad, const loco::Stride<2> *stride,
+ const ShapeDescription &ifm, const ShapeDescription &ofm);
+circle::Padding getOpPadding(const locoex::Padding pad);
+
+/// @brief Register graph input and output names to SerializedModelData
+void registerGraphIOName(loco::Graph *graph, SerializedModelData &gd);
+
+using TFLTensorIndex = int32_t;
+
+void set_tensor_index(loco::Node *node, const TFLTensorIndex &tensor_id);
+TFLTensorIndex get_tensor_index(loco::Node *node);
+
+} // namespace circle_detail
+} // namespace exo
+
+#endif // __TFL_EXPORTER_UTILS_H__
diff --git a/compiler/exo/src/Circle/CircleOperationExporter.cpp b/compiler/exo/src/Circle/CircleOperationExporter.cpp
new file mode 100644
index 000000000..390e2ec99
--- /dev/null
+++ b/compiler/exo/src/Circle/CircleOperationExporter.cpp
@@ -0,0 +1,1228 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CircleOperationExporter.h"
+#include "CircleExporterUtils.h"
+#include "ShapeInference.h"
+
+#include "Dialect/IR/TFLNode.h"
+#include "Dialect/IR/TFLNodes.h"
+#include "Dialect/IR/TFLNodeVisitor.h"
+
+#include "Dialect/IR/CircleNode.h"
+#include "Dialect/IR/CircleNodes.h"
+#include "Dialect/IR/CircleNodeVisitor.h"
+
+#include "Check.h"
+
+#include <loco/IR/CanonicalNode.h>
+#include <loco/IR/CanonicalNodeVisitor.h>
+#include <loco/Service/ShapeInference.h>
+#include <locoex/COpCall.h>
+
+#include <oops/InternalExn.h>
+
+#include <flatbuffers/flexbuffers.h>
+
+using namespace flatbuffers;
+using namespace circle;
+
+namespace
+{
+
+using namespace exo;
+using namespace exo::circle_detail;
+
+class OperationExporter final : public locoex::TFLNodeMutableVisitor<void>,
+ public locoex::CircleNodeMutableVisitor<void>,
+ public loco::CanonicalNodeMutableVisitor<void>
+{
+public:
+ OperationExporter(FlatBufferBuilder &fbb, SerializedModelData &ctx) : builder{fbb}, gd{ctx}
+ {
+ // DO NOTHING
+ }
+
+public:
+ // FOR TFLNodes
+ void visit(locoex::TFLAdd *) final;
+ void visit(locoex::TFLAveragePool2D *) final;
+ void visit(locoex::TFLConcatenation *) final;
+ void visit(locoex::TFLConst *) final{/* skip, everything is done in exportOpDefinedTensors */};
+ void visit(locoex::TFLConv2D *) final;
+ void visit(locoex::TFLDepthwiseConv2D *) final;
+ void visit(locoex::TFLDiv *) final;
+ void visit(locoex::TFLFullyConnected *) final;
+ void visit(locoex::TFLMaximum *) final;
+ void visit(locoex::TFLMaxPool2D *) final;
+ void visit(locoex::TFLMean *) final;
+ void visit(locoex::TFLMul *) final;
+ void visit(locoex::TFLRelu *) final;
+ void visit(locoex::TFLRelu6 *) final;
+ // TODO TFLReshape
+ void visit(locoex::TFLRsqrt *) final;
+ // TODO TFLSoftmax
+ void visit(locoex::TFLSqrt *) final;
+ void visit(locoex::TFLSquaredDifference *) final;
+ void visit(locoex::TFLSub *) final;
+ // TODO TFLTanh
+ void visit(locoex::TFLTranspose *) final;
+ void visit(locoex::TFLTransposeConv *) final;
+
+ // FOR CircleNodes
+ void visit(locoex::CircleInstanceNorm *) final;
+
+ // FOR canonical nodes. These will be removed later
+ void visit(loco::ReLU *) final;
+ void visit(loco::ReLU6 *) final;
+ void visit(loco::Tanh *) final;
+ void visit(loco::Push *) final { /* DO NOTHING */}
+ void visit(loco::Pull *) final { /* DO NOTHING */}
+ void visit(loco::FeatureEncode *) final;
+ void visit(loco::FeatureDecode *) final;
+ void visit(loco::FilterEncode *) final;
+ void visit(loco::DepthwiseFilterEncode *) final;
+ void visit(loco::ConstGen *) final { /* skip, everything is done in exportOpDefinedTensors */}
+ void visit(loco::MaxPool2D *) final;
+ void visit(loco::AvgPool2D *) final;
+ void visit(loco::Conv2D *) final;
+ void visit(loco::TransposedConv2D *) final;
+ void visit(loco::DepthwiseConv2D *) final;
+ void visit(loco::TensorConcat *) final;
+ void visit(loco::TensorReduce *) final;
+ void visit(loco::TensorSoftmax *) final;
+ void visit(loco::BiasEncode *) final;
+ void visit(loco::TensorBiasAdd *) final;
+ void visit(loco::FeatureBiasAdd *) final;
+ void visit(loco::EltwiseAdd *) final;
+ void visit(loco::EltwiseMax *) final;
+ void visit(loco::EltwiseMul *) final;
+ void visit(loco::EltwiseSub *) final;
+ void visit(loco::EltwiseDiv *) final;
+ void visit(loco::EltwiseSqrt *) final;
+ void visit(loco::FixedReshape *) final;
+ void visit(loco::TensorBroadcast *) final;
+ void visit(loco::TensorConstantPad *) final;
+
+ void visit(locoex::COpCall *);
+
+private:
+ /**
+ * @brief Exports TFLMaxPool2D or TFLAveragePool2D
+ *
+ * @note TFLPool2D should be one of TFLMaxPool2D or TFLAveragePool2D
+ */
+ template <class TFLPool2D>
+ void export_pool_2d(TFLPool2D *node, circle::BuiltinOperator builtin_op);
+
+private:
+ FlatBufferBuilder &builder;
+ SerializedModelData &gd;
+};
+
+void OperationExporter::visit(locoex::TFLAdd *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_ADD);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateAddOptions(builder, to_circle_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_AddOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLAveragePool2D *node)
+{
+ export_pool_2d<locoex::TFLAveragePool2D>(node, circle::BuiltinOperator_AVERAGE_POOL_2D);
+}
+
+void OperationExporter::visit(locoex::TFLConcatenation *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_CONCATENATION);
+ std::vector<int32_t> inputs_vec;
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+
+ for (uint32_t i = 0; i < node->numValues(); ++i)
+ inputs_vec.push_back(get_tensor_index(node->values(i)));
+
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateConcatenationOptions(builder, node->axis(),
+ to_circle_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_ConcatenationOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLConv2D *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_CONV_2D);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->filter()),
+ get_tensor_index(node->bias())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ circle::Padding padding = getOpPadding(node->padding());
+ auto options = CreateConv2DOptions(builder, padding, node->stride()->w(), node->stride()->h(),
+ to_circle_actfunc(node->fusedActivationFunction()));
+
+ // Make CONV_2D operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_Conv2DOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLDepthwiseConv2D *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_DEPTHWISE_CONV_2D);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->filter()),
+ get_tensor_index(node->bias())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ circle::Padding padding = getOpPadding(node->padding());
+ auto options = CreateDepthwiseConv2DOptions(builder, padding, node->stride()->w(),
+ node->stride()->h(), node->depthMultiplier(),
+ to_circle_actfunc(node->fusedActivationFunction()));
+
+ // Make DEPTHWISE_CONV_2D operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_DepthwiseConv2DOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLDiv *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_DIV);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateDivOptions(builder, to_circle_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_DivOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLFullyConnected *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_FULLY_CONNECTED);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()),
+ get_tensor_index(node->weights()),
+ get_tensor_index(node->bias())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options =
+ CreateFullyConnectedOptions(builder, to_circle_actfunc(node->fusedActivationFunction()));
+
+ // Make FULLY_CONNECTED operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_FullyConnectedOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLMaximum *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_MAXIMUM);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateMaximumMinimumOptions(builder);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_MaximumMinimumOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLMaxPool2D *node)
+{
+ export_pool_2d<locoex::TFLMaxPool2D>(node, circle::BuiltinOperator_MAX_POOL_2D);
+}
+
+void OperationExporter::visit(locoex::TFLMean *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_MEAN);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()),
+ get_tensor_index(node->reduction_indices())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateReducerOptions(builder, node->keep_dims());
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_ReducerOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLMul *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_MUL);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateMulOptions(builder, to_circle_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_MulOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLRelu *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_RELU);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->features())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLRelu6 *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_RELU6);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->features())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+// TODO TFLReshape
+
+void OperationExporter::visit(locoex::TFLRsqrt *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_RSQRT);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+// TODO TFLSoftmax
+
+void OperationExporter::visit(locoex::TFLSqrt *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_SQRT);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLSquaredDifference *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_SQUARED_DIFFERENCE);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateSquaredDifferenceOptions(builder);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_SquaredDifferenceOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLSub *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_SUB);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateSubOptions(builder, to_circle_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_SubOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+// TODO TFLTanh
+
+void OperationExporter::visit(locoex::TFLTranspose *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_TRANSPOSE);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->arg(0)), get_tensor_index(node->arg(1))};
+ std::vector<int32_t> outputs_vec{get_tensor_index(node)};
+
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateTransposeOptions(builder);
+
+ auto op_offset =
+ CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions::BuiltinOptions_TransposeOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLTransposeConv *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_TRANSPOSE_CONV);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->inputSizes()),
+ get_tensor_index(node->filter()),
+ get_tensor_index(node->outBackprop())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ circle::Padding padding = getOpPadding(node->padding());
+ auto options =
+ CreateTransposeConvOptions(builder, padding, node->stride()->w(), node->stride()->h());
+
+ // Make TRANSPOSE_CONV operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_TransposeConvOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+template <class TFLPool2D>
+void OperationExporter::export_pool_2d(TFLPool2D *node, circle::BuiltinOperator builtin_op)
+{
+ EXO_ASSERT(builtin_op == circle::BuiltinOperator_MAX_POOL_2D ||
+ builtin_op == circle::BuiltinOperator_AVERAGE_POOL_2D,
+ "should be maxpool or avgpool");
+ EXO_ASSERT(node->padding() != locoex::Padding::UNDEFINED, "Padding is not set");
+
+ uint32_t op_idx = gd.registerBuiltinOpcode(builtin_op);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->value())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+
+ circle::Padding padding = getOpPadding(node->padding());
+
+ auto options = CreatePool2DOptions(builder, padding, node->stride()->w(), node->stride()->h(),
+ node->filter()->w(), node->filter()->h(),
+ to_circle_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_Pool2DOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::CircleInstanceNorm *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_INSTANCE_NORM);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->gamma()),
+ get_tensor_index(node->beta())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateInstanceNormOptions(builder, node->epsilon(),
+ to_circle_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_InstanceNormOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::ReLU *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_RELU);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::ReLU6 *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_RELU6);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::Tanh *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_TANH);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::MaxPool2D *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_MAX_POOL_2D);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->ifm())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ circle::Padding padding = getOpPadding(
+ node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node));
+ auto options = CreatePool2DOptions(builder, padding, node->stride()->horizontal(),
+ node->stride()->vertical(), node->window()->horizontal(),
+ node->window()->vertical());
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_Pool2DOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::AvgPool2D *node)
+{
+ // Circle only support Valid convention of average pooling
+ assert(node->convention() == loco::AvgPool2D::Convention::Valid);
+
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_AVERAGE_POOL_2D);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->ifm())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ circle::Padding padding = getOpPadding(
+ node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node));
+ auto options = CreatePool2DOptions(builder, padding, node->stride()->horizontal(),
+ node->stride()->vertical(), node->window()->horizontal(),
+ node->window()->vertical());
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_Pool2DOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::Conv2D *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_CONV_2D);
+
+ // Third input of CONV_2D of Circle should be bias. We will make (and register to gd) dummy zero
+ // bias. Bias would be rank 1, have size of output kernel count, and have all zero values, i.e.
+ // zero bias.
+ auto *ker = dynamic_cast<loco::FilterEncode *>(node->ker());
+ assert(ker);
+ int32_t bias_vec_size = ShapeInference::get(ker)._dims[0]; // output kernel count
+
+ auto bias_vec_shape_offset = builder.CreateVector(std::vector<int32_t>{bias_vec_size});
+ size_t raw_bias_vec_size = bias_vec_size * sizeof(int32_t);
+
+ std::vector<float> bias_vec_data(bias_vec_size); // initialized as zero vector
+
+ auto bias_vec_offset =
+ builder.CreateVector(reinterpret_cast<uint8_t *>(bias_vec_data.data()), raw_bias_vec_size);
+
+ auto bias_buffer_offset = CreateBuffer(builder, bias_vec_offset);
+
+ const auto bias_buffer_id = static_cast<uint32_t>(gd._buffers.size());
+
+ gd._buffers.push_back(bias_buffer_offset);
+
+ auto bias_tensor_id = static_cast<int32_t>(gd._tensors.size());
+ auto name_offset = builder.CreateString("t_" + std::to_string(bias_tensor_id));
+
+ auto bias_tensor_offset =
+ CreateTensor(builder, bias_vec_shape_offset, TensorType_FLOAT32, bias_buffer_id, name_offset);
+ gd._tensors.push_back(bias_tensor_offset);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->ifm()), get_tensor_index(node->ker()),
+ bias_tensor_id};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ circle::Padding padding = getOpPadding(
+ node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node));
+ auto options = CreateConv2DOptions(builder, padding, node->stride()->horizontal(),
+ node->stride()->vertical());
+
+ // Make CONV_2D operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_Conv2DOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::TransposedConv2D *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_TRANSPOSE_CONV);
+
+ // TRANSPOSE_CONV's first input is output shape array.
+ const int32_t outshape_vec_size = 4;
+ auto outshape_vec_shape_offset = builder.CreateVector(std::vector<int32_t>{outshape_vec_size});
+ size_t raw_outshape_vec_size = outshape_vec_size * sizeof(int32_t);
+
+ std::vector<int32_t> outshape_vec_data(outshape_vec_size);
+ {
+ // Copy inferred output shape of node
+ auto out_feature_shape = loco::shape_get(node).as<loco::FeatureShape>();
+
+ // Feature tensor in Circle is NHWC
+ outshape_vec_data.at(0) = out_feature_shape.count().value();
+ outshape_vec_data.at(1) = out_feature_shape.height().value();
+ outshape_vec_data.at(2) = out_feature_shape.width().value();
+ outshape_vec_data.at(3) = out_feature_shape.depth().value();
+ }
+
+ auto outshape_vec_offset = builder.CreateVector(
+ reinterpret_cast<uint8_t *>(outshape_vec_data.data()), raw_outshape_vec_size);
+
+ auto outshape_buffer_offset = CreateBuffer(builder, outshape_vec_offset);
+
+ const auto outshape_buffer_id = static_cast<uint32_t>(gd._buffers.size());
+
+ gd._buffers.push_back(outshape_buffer_offset);
+
+ auto outshape_tensor_id = static_cast<int32_t>(gd._tensors.size());
+ auto name_offset = builder.CreateString("t_" + std::to_string(outshape_tensor_id));
+
+ auto outshape_tensor_offset = CreateTensor(builder, outshape_vec_shape_offset, TensorType_INT32,
+ outshape_buffer_id, name_offset);
+ gd._tensors.push_back(outshape_tensor_offset);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{outshape_tensor_id, get_tensor_index(node->ker()),
+ get_tensor_index(node->ifm())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ // NOTE input and output is inversed to use this function
+ circle::Padding padding = getOpPadding(node->pad(), node->stride(), ShapeInference::get(node),
+ ShapeInference::get(node->ifm()));
+ auto options = CreateTransposeConvOptions(builder, padding, node->stride()->horizontal(),
+ node->stride()->vertical());
+
+ // Make TRANSPOSE_CONV operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_TransposeConvOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::DepthwiseConv2D *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_DEPTHWISE_CONV_2D);
+
+ // Third input of DEPTHWISE_CONV2D of Circle should be bias. We will make (and register to gd)
+ // dummy zero bias. Bias would be rank 1, have size of output kernel count, and have all zero
+ // values, i.e. zero bias.
+ auto *ker = dynamic_cast<loco::DepthwiseFilterEncode *>(node->ker());
+ assert(ker);
+
+ int32_t bias_vec_size = ShapeInference::get(ker)._dims[3]; // output_size(C*M)
+ auto bias_vec_shape_offset = builder.CreateVector(std::vector<int32_t>{bias_vec_size});
+
+ size_t raw_bias_vec_size = bias_vec_size * sizeof(int32_t);
+ std::vector<float> bias_vec_data(bias_vec_size);
+ auto bias_vec_offset =
+ builder.CreateVector(reinterpret_cast<uint8_t *>(bias_vec_data.data()), raw_bias_vec_size);
+
+ auto bias_buffer_offset = CreateBuffer(builder, bias_vec_offset);
+
+ const auto bias_buffer_id = static_cast<uint32_t>(gd._buffers.size());
+
+ gd._buffers.push_back(bias_buffer_offset);
+
+ auto bias_tensor_id = static_cast<int32_t>(gd._tensors.size());
+ auto name_offset = builder.CreateString("t_" + std::to_string(bias_tensor_id));
+
+ auto bias_tensor_offset =
+ CreateTensor(builder, bias_vec_shape_offset, TensorType_FLOAT32, bias_buffer_id, name_offset);
+ gd._tensors.push_back(bias_tensor_offset);
+
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->ifm()), get_tensor_index(node->ker()),
+ bias_tensor_id};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ circle::Padding padding = getOpPadding(
+ node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node));
+
+ int32_t ifm_channel_size = ShapeInference::get(node->ifm())._dims[3];
+ // multiplier = bias_vec_size(output_size)/ifm_channel_size
+ auto options =
+ CreateDepthwiseConv2DOptions(builder, padding, node->stride()->horizontal(),
+ node->stride()->vertical(), bias_vec_size / ifm_channel_size);
+
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_DepthwiseConv2DOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::TensorReduce *node)
+{
+ uint32_t op_idx;
+
+ switch (node->func())
+ {
+ case loco::ReduceFunc::Mean:
+ op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_MEAN);
+ break;
+
+ // TODO Support more reduce type operation
+ default:
+ INTERNAL_EXN_V("Unsupported reduce type", oops::to_uint32(node->func()));
+ }
+
+ // Create a vector for axes data
+ std::vector<int32_t> axes_vec;
+ auto rank = ShapeInference::get(node->input())._dims.size();
+ for (uint32_t i = 0; i < rank; ++i)
+ if (node->axes()->defined(i))
+ axes_vec.push_back(i);
+
+ int32_t axes_vec_size = axes_vec.size();
+ auto axes_vec_shape_offset = builder.CreateVector(std::vector<int32_t>{axes_vec_size});
+
+ size_t raw_axes_vec_size = axes_vec_size * sizeof(int32_t);
+ auto axes_vec_offset =
+ builder.CreateVector(reinterpret_cast<uint8_t *>(axes_vec.data()), raw_axes_vec_size);
+
+ auto axes_buffer_offset = CreateBuffer(builder, axes_vec_offset);
+
+ const auto axes_buffer_id = static_cast<uint32_t>(gd._buffers.size());
+
+ gd._buffers.push_back(axes_buffer_offset);
+
+ auto axes_tensor_id = static_cast<int32_t>(gd._tensors.size());
+ auto name_offset = builder.CreateString("t_" + std::to_string(axes_tensor_id));
+
+ auto axes_tensor_offset =
+ CreateTensor(builder, axes_vec_shape_offset, TensorType_INT32, axes_buffer_id, name_offset);
+ gd._tensors.push_back(axes_tensor_offset);
+
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), axes_tensor_id};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateReducerOptions(builder, true); // true is for keep_dims option
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_ReducerOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::TensorSoftmax *node)
+{
+ // TODO Support when the input rank of TensorSoftmax is not 2
+ assert(ShapeInference::get(node->input())._dims.size() == 2);
+
+ // NOTE Circle only accepts axis when the value is last dimension
+ assert(node->axis() == ShapeInference::get(node->input())._dims.size() - 1);
+
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_SOFTMAX);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateSoftmaxOptions(builder, 1.0f);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_SoftmaxOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+/// @brief Export given node into identity, i.e. CONCATENATION with one input
+template <typename NodeT>
+void exportIdentity(NodeT *node, FlatBufferBuilder &builder, SerializedModelData &gd)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_CONCATENATION);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->arg(0))};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateConcatenationOptions(builder); // use dummy 0 axis and NONE activation
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_ConcatenationOptions, options.Union());
+
+ gd._operators.push_back(op_offset);
+}
+
+/// @brief Export loco nodes as TRANSPOSE
+void exportAsTranspose(loco::Node *node, FlatBufferBuilder &builder,
+ std::vector<int32_t> &perm_vec_data, SerializedModelData &gd)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_TRANSPOSE);
+
+ auto options = CreateTransposeOptions(builder);
+
+ // Create constant tensor with perm vector
+ constexpr int perm_vec_size = 4;
+ assert(perm_vec_data.size() == perm_vec_size);
+ auto perm_vec_shape_offset = builder.CreateVector(std::vector<int32_t>{perm_vec_size});
+ constexpr size_t raw_perm_vec_size = perm_vec_size * sizeof(int32_t);
+
+ auto perm_vec_offset =
+ builder.CreateVector(reinterpret_cast<uint8_t *>(perm_vec_data.data()), raw_perm_vec_size);
+
+ auto perm_buffer_offset = CreateBuffer(builder, perm_vec_offset);
+
+ const auto perm_buffer_id = static_cast<uint32_t>(gd._buffers.size());
+
+ gd._buffers.push_back(perm_buffer_offset);
+
+ auto perm_tensor_id = static_cast<int32_t>(gd._tensors.size());
+ auto name_offset = builder.CreateString("t_" + std::to_string(perm_tensor_id));
+
+ auto perm_tensor_offset =
+ CreateTensor(builder, perm_vec_shape_offset, TensorType_INT32, perm_buffer_id, name_offset);
+ gd._tensors.push_back(perm_tensor_offset);
+
+ // Create permutation node
+
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->arg(0)), perm_tensor_id};
+ std::vector<int32_t> outputs_vec{get_tensor_index(node)};
+
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+
+ constexpr auto options_type = circle::BuiltinOptions::BuiltinOptions_TransposeOptions;
+
+ auto transpose_offset =
+ CreateOperator(builder, op_idx, inputs, outputs, options_type, options.Union());
+ gd._operators.push_back(transpose_offset);
+}
+
+void OperationExporter::visit(loco::FeatureEncode *node)
+{
+ auto encoder = dynamic_cast<loco::PermutingEncoder<loco::Domain::Feature> *>(node->encoder());
+ auto perm = encoder->perm();
+
+ if (isNHWC(perm))
+ {
+ // Note that Circle represents feature as NHWC
+ exportIdentity(node, builder, gd);
+ }
+ else
+ {
+ std::vector<int32_t> perm_vec_data(4);
+ perm_vec_data[0] = perm->axis(loco::FeatureAxis::Count);
+ perm_vec_data[1] = perm->axis(loco::FeatureAxis::Height);
+ perm_vec_data[2] = perm->axis(loco::FeatureAxis::Width);
+ perm_vec_data[3] = perm->axis(loco::FeatureAxis::Depth);
+
+ exportAsTranspose(node, builder, perm_vec_data, gd);
+ }
+}
+
+void OperationExporter::visit(loco::FeatureDecode *node)
+{
+ auto decoder = dynamic_cast<loco::PermutingDecoder<loco::Domain::Feature> *>(node->decoder());
+ auto perm = decoder->perm();
+
+ if (isNHWC(perm))
+ {
+ // Note that Circle represents feature as NHWC
+ exportIdentity(node, builder, gd);
+ }
+ else
+ {
+ std::vector<int32_t> perm_vec_data(4);
+ perm_vec_data[perm->axis(loco::FeatureAxis::Count)] = 0;
+ perm_vec_data[perm->axis(loco::FeatureAxis::Height)] = 1;
+ perm_vec_data[perm->axis(loco::FeatureAxis::Width)] = 2;
+ perm_vec_data[perm->axis(loco::FeatureAxis::Depth)] = 3;
+
+ exportAsTranspose(node, builder, perm_vec_data, gd);
+ }
+}
+
+void OperationExporter::visit(loco::FilterEncode *node)
+{
+ auto encoder = dynamic_cast<loco::PermutingEncoder<loco::Domain::Filter> *>(node->encoder());
+ auto perm = encoder->perm();
+
+ if (isNHWC(perm))
+ {
+ // Note that Circle represents filter as NHWC
+ exportIdentity(node, builder, gd);
+ }
+ else
+ {
+ std::vector<int32_t> perm_vec_data(4);
+ // NOTE In Circle, all tensors means NHWC, so 0 = N, 1 = H, 2 = W, 3 = C
+ perm_vec_data[0] = perm->axis(loco::FilterAxis::Count);
+ perm_vec_data[1] = perm->axis(loco::FilterAxis::Height);
+ perm_vec_data[2] = perm->axis(loco::FilterAxis::Width);
+ perm_vec_data[3] = perm->axis(loco::FilterAxis::Depth);
+
+ exportAsTranspose(node, builder, perm_vec_data, gd);
+ }
+}
+
+void exportAsReshape(loco::Node *node, FlatBufferBuilder &builder,
+ std::vector<int32_t> &new_shape_vec, SerializedModelData &gd)
+{
+ // NOTE Circle currently follows TFLite for this.
+ // NOTE TFLite has two ways to get new shape paramter,
+ // one is by attribute 'new_shape' and the other is by input 'shape'.
+ // Therefore TFLite interpreter calculates Reshape operation correctly
+ // if one of them is valid.
+ // However, since NN runtime usually get new shape parameter by input 'shape',
+ // passing new shape only by attribute can cause some problems.
+ // Of course, the opposite situation can be occurred in the future.
+ // To prevent those problems, we pass new shape parameter not only by attribute
+ // but also by input.
+
+ auto input_shape_shape_vec_offset =
+ builder.CreateVector(std::vector<int32_t>{(int32_t)new_shape_vec.size()});
+
+ size_t input_shape_vec_size = new_shape_vec.size() * sizeof(int32_t);
+ auto input_shape_input_vec_offset =
+ builder.CreateVector(reinterpret_cast<uint8_t *>(new_shape_vec.data()), input_shape_vec_size);
+ auto input_shape_buffer_offset = CreateBuffer(builder, input_shape_input_vec_offset);
+
+ const auto input_shape_buffer_id = static_cast<uint32_t>(gd._buffers.size());
+ gd._buffers.push_back(input_shape_buffer_offset);
+
+ auto input_shape_tensor_id = static_cast<int32_t>(gd._tensors.size());
+ auto name_offset = builder.CreateString("t_" + std::to_string(input_shape_tensor_id));
+ auto input_shape_tensor_offset = CreateTensor(
+ builder, input_shape_shape_vec_offset, TensorType_INT32, input_shape_buffer_id, name_offset);
+ gd._tensors.push_back(input_shape_tensor_offset);
+
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_RESHAPE);
+
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->arg(0)), input_shape_tensor_id};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+
+ auto new_shape_vec_offset = builder.CreateVector(new_shape_vec);
+ auto options = CreateReshapeOptions(builder, new_shape_vec_offset);
+
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_ReshapeOptions, options.Union());
+
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::DepthwiseFilterEncode *node)
+{
+ auto ker = node->input(); // [H, W, C, M]
+
+ // Circle represents filter as [1, H, W, C*M] where M is multiplier.
+ std::vector<int32_t> new_shape_vec(4);
+ new_shape_vec[0] = 1;
+ new_shape_vec[1] = ShapeInference::get(ker)._dims[0];
+ new_shape_vec[2] = ShapeInference::get(ker)._dims[1];
+ new_shape_vec[3] = ShapeInference::get(ker)._dims[2] * ShapeInference::get(ker)._dims[3];
+
+ exportAsReshape(node, builder, new_shape_vec, gd);
+}
+
+void OperationExporter::visit(loco::BiasAdd<loco::Domain::Tensor> *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_ADD);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->value()), get_tensor_index(node->bias())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateAddOptions(builder); // dummy option
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_AddOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::FeatureBiasAdd *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_ADD);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->value()), get_tensor_index(node->bias())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateAddOptions(builder); // dummy option
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_AddOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+/// @brief Export CONCATENATION of **TWO** tensors only
+void OperationExporter::visit(loco::TensorConcat *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_CONCATENATION);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateConcatenationOptions(builder, node->axis());
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_ConcatenationOptions, options.Union());
+
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::BiasEncode *encode) { exportIdentity(encode, builder, gd); }
+
+void OperationExporter::visit(loco::EltwiseAdd *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_ADD);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateAddOptions(builder); // dummy option
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_AddOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::EltwiseMax *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_MAXIMUM);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateMaximumMinimumOptions(builder); // dummy option
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_MaximumMinimumOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::EltwiseMul *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_MUL);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateMulOptions(builder); // dummy option
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_MulOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::EltwiseSub *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_SUB);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateSubOptions(builder); // dummy option
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_SubOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::EltwiseDiv *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_DIV);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateDivOptions(builder); // dummy option
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_DivOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::EltwiseSqrt *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_SQRT);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::FixedReshape *node)
+{
+ std::vector<int32_t> new_shape_vec;
+ for (uint32_t axis = 0; axis < node->rank(); ++axis)
+ {
+ assert(node->dim(axis).known());
+ new_shape_vec.push_back(node->dim(axis).value());
+ }
+
+ exportAsReshape(node, builder, new_shape_vec, gd);
+}
+
+void OperationExporter::visit(loco::TensorBroadcast *)
+{
+ INTERNAL_EXN("loco graph has loco::TensorBroadcast, which should not exist in the graph");
+}
+
+void OperationExporter::visit(loco::TensorConstantPad *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_PAD);
+
+ // make padding attribute an input
+ auto padding = node->padding();
+ // get padding vector size
+ int32_t padding_vec_size = padding->rank();
+ // get byte size of vector
+ size_t padding_vec_byte_size = padding_vec_size * sizeof(int32_t) * 2; // [rank, 2]
+ // create vector for data
+ std::vector<int32_t> padding_vec_data(padding_vec_size * 2);
+ // set data
+ for (int32_t i = 0; i < padding_vec_size; i++)
+ {
+ padding_vec_data.at(i * 2) = padding->front(i);
+ padding_vec_data.at(i * 2 + 1) = padding->back(i);
+ }
+ // create FlatBuffer vector
+ auto padding_vec_ptr = builder.CreateVector(reinterpret_cast<uint8_t *>(padding_vec_data.data()),
+ padding_vec_byte_size);
+
+ // create buffer
+ auto padding_buffer_ptr = CreateBuffer(builder, padding_vec_ptr);
+ // get buffer id
+ const auto padding_buffer_id = static_cast<uint32_t>(gd._buffers.size());
+
+ gd._buffers.push_back(padding_buffer_ptr);
+
+ // create padding shape vector
+ auto padding_shape_vec_ptr = builder.CreateVector(std::vector<int32_t>{padding_vec_size, 2});
+ // create tensor
+ auto padding_tensor_ptr =
+ CreateTensor(builder, padding_shape_vec_ptr, TensorType_INT32, padding_buffer_id);
+ // get tensor id
+ const auto padding_tensor_id = static_cast<int32_t>(gd._tensors.size());
+
+ gd._tensors.push_back(padding_tensor_ptr);
+
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), padding_tensor_id};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+inline flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
+CreateCOpCallOptions(flatbuffers::FlatBufferBuilder &fbb, locoex::COpCall *copCall)
+{
+ // read attrs in FlexBuffer format and pass them to FlatBuffer builder
+ flexbuffers::Builder flexbuf;
+ {
+ size_t map_start = flexbuf.StartMap();
+
+ // Note: among attrs of COpCall, 'op' and 'name' won't be included into tflite file
+ auto names = copCall->attr_names();
+ for (auto name : names)
+ {
+ if (auto int_val = copCall->attr<locoex::COpAttrType::Int>(name))
+ flexbuf.Int(name.c_str(), int_val->val());
+ else if (auto float_val = copCall->attr<locoex::COpAttrType::Float>(name))
+ flexbuf.Float(name.c_str(), float_val->val());
+ else
+ // TODO Support more attribute types
+ INTERNAL_EXN_V("Unsupported dtype while writing flexbuffer for customop attr", name);
+ }
+
+ flexbuf.EndMap(map_start);
+ flexbuf.Finish();
+ }
+
+ auto offset = fbb.CreateVector(flexbuf.GetBuffer());
+
+ return offset;
+}
+
+void OperationExporter::visit(locoex::COpCall *call)
+{
+ // Registering this custom op name into tflite Operator Codes table
+ uint32_t op_idx = gd.registerCustomOpcode(call->op());
+
+ std::vector<int32_t> inputs_vec;
+ {
+ inputs_vec.resize(call->arity());
+ for (uint32_t i = 0; i < call->arity(); i++)
+ inputs_vec[i] = get_tensor_index(call->arg(i));
+ }
+
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(call))};
+
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+
+ auto custom_options = CreateCOpCallOptions(builder, call);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_NONE, // builtin_options_type
+ 0, // built-in option
+ custom_options, // custom options
+ circle::CustomOptionsFormat_FLEXBUFFERS);
+
+ gd._operators.push_back(op_offset);
+}
+
+void exportNode(loco::Node *node, flatbuffers::FlatBufferBuilder &builder,
+ SerializedModelData &data)
+{
+ // TODO Use explicit tagging to prevent possible mistake
+ auto isNoOp = [](loco::Node *node) {
+ if (node->arity() == 1)
+ {
+ assert(node->arg(0) != nullptr);
+ return get_tensor_index(node) == get_tensor_index(node->arg(0));
+ }
+ return false;
+ };
+
+ if (isNoOp(node))
+ {
+ // Skip if a given node is marked as NoOp (op with no effect) before
+ return;
+ }
+
+ if (auto canonical_node = dynamic_cast<loco::CanonicalNode *>(node))
+ { // TODO Consider removing this later
+ OperationExporter exporter{builder, data};
+ canonical_node->accept(&exporter);
+ }
+ else if (auto tfl_node = dynamic_cast<locoex::TFLNode *>(node))
+ {
+ OperationExporter exporter{builder, data};
+ tfl_node->accept(&exporter);
+ }
+ else if (auto circle_node = dynamic_cast<locoex::CircleNode *>(node))
+ {
+ OperationExporter exporter{builder, data};
+ circle_node->accept(&exporter);
+ }
+ else if (dynamic_cast<locoex::COpNode *>(node))
+ {
+ OperationExporter exporter{builder, data};
+ exporter.visit(dynamic_cast<locoex::COpCall *>(node));
+ }
+ else
+ {
+ INTERNAL_EXN("Node with unsupported dialect found");
+ }
+}
+
+} // namespace
+
+namespace exo
+{
+namespace circle_detail
+{
+
+void exportNodes(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &gd)
+{
+ for (auto node : loco::postorder_traversal(loco::output_nodes(g)))
+ {
+ exportNode(node, builder, gd);
+ }
+}
+
+} // namespace circle_detail
+} // namespace exo
diff --git a/compiler/exo/src/Circle/CircleOperationExporter.h b/compiler/exo/src/Circle/CircleOperationExporter.h
new file mode 100644
index 000000000..19dadbfd1
--- /dev/null
+++ b/compiler/exo/src/Circle/CircleOperationExporter.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_OPERATION_EXPORTER_H__
+#define __CIRCLE_OPERATION_EXPORTER_H__
+
+#include "CircleExporterUtils.h"
+
+#include <loco/IR/Graph.h>
+
+namespace exo
+{
+namespace circle_detail
+{
+
+/**
+ * @brief create Operators corresponding to model nodes
+ * @param nodes container with nodes
+ * @param gd information about serializer parts of model
+ */
+void exportNodes(loco::Graph *g, flatbuffers::FlatBufferBuilder &builder, SerializedModelData &gd);
+
+} // namespace circle_detail
+} // namespace exo
+
+#endif // __CIRCLE_OPERATION_EXPORTER_H__
diff --git a/compiler/exo/src/Circle/CircleTensorExporter.cpp b/compiler/exo/src/Circle/CircleTensorExporter.cpp
new file mode 100644
index 000000000..6adc31616
--- /dev/null
+++ b/compiler/exo/src/Circle/CircleTensorExporter.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CircleTensorExporter.h"
+#include "CircleTypeInference.h"
+#include "ShapeInference.h"
+
+// TODO Fix include style
+#include "loco/IR/Algorithm.h"
+#include "loco/IR/CanonicalNode.h"
+#include "loco/IR/CanonicalNodeVisitor.h"
+#include "loco/IR/DataTypeTraits.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+#include <oops/InternalExn.h>
+
+using namespace circle;
+using namespace flatbuffers;
+
+namespace
+{
+
+using namespace exo;
+using namespace exo::circle_detail;
+
+class TFLTensorInfo
+{
+public:
+ TFLTensorInfo() = default;
+
+public:
+ void name(const std::string &name) { _name = name; }
+ const std::string &name(void) const { return _name; }
+
+public:
+ const circle::TensorType &dtype(void) const { return _dtype; }
+ void dtype(const circle::TensorType &dtype) { _dtype = dtype; }
+
+ const ShapeDescription &shape(void) const { return _shape; }
+ void shape(const ShapeDescription &shape) { _shape = shape; }
+
+public:
+ locoex::TFLConst *tfl_content(void) const { return _tfl_content; }
+ void tfl_content(locoex::TFLConst *c) { _tfl_content = c; }
+
+private:
+ std::string _name;
+
+ circle::TensorType _dtype{circle::TensorType_FLOAT32};
+ ShapeDescription _shape{};
+
+ // TODO Find a better design
+ loco::ConstGen *_content = nullptr; // TODO deprecate
+ locoex::TFLConst *_tfl_content = nullptr;
+};
+
+using TFLTensorContext = std::vector<TFLTensorInfo>;
+
+struct NoOpDetector final : public loco::CanonicalNodeMutableVisitor<bool>
+{
+ bool visit(loco::BiasEncode *) final
+ {
+ // BiasEncode is always noop
+ return true;
+ }
+
+ bool visit(loco::FilterEncode *node) final
+ {
+ auto encoder = dynamic_cast<loco::PermutingEncoder<loco::Domain::Filter> *>(node->encoder());
+ if (encoder != nullptr)
+ {
+ auto perm = encoder->perm();
+ return isNHWC(perm);
+ }
+ return false;
+ }
+
+ bool visit(loco::FeatureEncode *node) final
+ {
+ auto encoder = dynamic_cast<loco::PermutingEncoder<loco::Domain::Feature> *>(node->encoder());
+ if (encoder != nullptr)
+ {
+ auto perm = encoder->perm();
+ return isNHWC(perm);
+ }
+ return false;
+ }
+
+ bool visit(loco::FeatureDecode *node) final
+ {
+ auto decoder = dynamic_cast<loco::PermutingDecoder<loco::Domain::Feature> *>(node->decoder());
+ if (decoder != nullptr)
+ {
+ auto perm = decoder->perm();
+ return isNHWC(perm);
+ }
+ return false;
+ }
+
+ // Return false by default
+ bool visit(loco::Node *) final { return false; }
+};
+
+bool isNoOp(loco::Node *node)
+{
+ if (auto canonical_node = dynamic_cast<loco::CanonicalNode *>(node))
+ {
+ NoOpDetector d;
+ return canonical_node->accept(&d);
+ }
+ return false;
+}
+
+void allocateCircleTensor(loco::Node *node, TFLTensorContext &ctx)
+{
+ if (isNoOp(node))
+ {
+ assert(node->arity() == 1 && node->arg(0) != nullptr);
+ set_tensor_index(node, get_tensor_index(node->arg(0)));
+ return;
+ }
+
+ auto tensor_index = static_cast<TFLTensorIndex>(ctx.size());
+ // TODO Use Graph-level metadata for Input & Output
+ auto tensor_name = "t_" + std::to_string(tensor_index);
+
+ TFLTensorInfo tensor_info;
+
+ tensor_info.name(tensor_name);
+ tensor_info.dtype(TypeInference::get(node));
+ tensor_info.shape(ShapeInference::get(node));
+
+ tensor_info.tfl_content(dynamic_cast<locoex::TFLConst *>(node));
+
+ set_tensor_index(node, tensor_index);
+
+ ctx.emplace_back(tensor_info);
+}
+
+} // namespace
+
+namespace
+{
+
+flatbuffers::Offset<Vector<int32_t>> encodeShape(FlatBufferBuilder &builder,
+ const ShapeDescription &shape)
+{
+ assert(shape._rank_known && "unknown number of dimensions is not supported");
+ return builder.CreateVector(shape._dims);
+}
+
+flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder)
+{
+ return CreateBuffer(builder);
+}
+
+template <typename NodeT>
+flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, NodeT *)
+{
+ return CreateBuffer(builder);
+}
+
+template <loco::DataType DT>
+flatbuffers::Offset<circle::Buffer> encodeOpBufferByDType(FlatBufferBuilder &builder,
+ locoex::TFLConst *c)
+{
+ using NativeType = typename loco::DataTypeImpl<DT>::Type;
+
+ std::vector<NativeType> raw_data;
+ const uint32_t size = c->size<DT>();
+ raw_data.reserve(size);
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ raw_data.push_back(c->at<DT>(i));
+ }
+ const size_t raw_size = size * sizeof(NativeType);
+ auto array_offset = builder.CreateVector(reinterpret_cast<uint8_t *>(raw_data.data()), raw_size);
+ return CreateBuffer(builder, array_offset);
+}
+
+template <>
+flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, locoex::TFLConst *c)
+{
+ if (c->dtype() == loco::DataType::FLOAT32)
+ {
+ return encodeOpBufferByDType<loco::DataType::FLOAT32>(builder, c);
+ }
+ else if (c->dtype() == loco::DataType::S32)
+ {
+ return encodeOpBufferByDType<loco::DataType::S32>(builder, c);
+ }
+
+ INTERNAL_EXN_V("Unsupported datatype", oops::to_uint32(c->dtype()));
+}
+
+} // namespace
+
+namespace exo
+{
+namespace circle_detail
+{
+
+void exportOpDefinedTensor(const TFLTensorInfo &info, FlatBufferBuilder &builder,
+ SerializedModelData &gd)
+{
+ // Create and register output tensor shape
+ auto shape_offset = encodeShape(builder, info.shape());
+
+ // encode and register output tensor buffer
+ auto buffer = info.tfl_content() == nullptr ? encodeOpBuffer(builder)
+ : encodeOpBuffer(builder, info.tfl_content());
+
+ auto buffer_id = static_cast<uint32_t>(gd._buffers.size());
+ gd._buffers.push_back(buffer);
+
+ auto name_offset = builder.CreateString(info.name());
+ auto tensor_offset = CreateTensor(builder, shape_offset, info.dtype(), buffer_id, name_offset,
+ /*quantization*/ 0, /*is_variable*/ false);
+ gd._tensors.push_back(tensor_offset);
+}
+
+void exportOpDefinedTensors(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &gd)
+{
+ TFLTensorContext tensor_ctx;
+
+ for (auto node : loco::postorder_traversal(loco::output_nodes(g)))
+ {
+ allocateCircleTensor(node, tensor_ctx);
+ }
+
+ // add one empty buffer
+ // note: this follows TFLite
+ // note: there's a comment in tflite fbs file
+ // - Note the 0th entry of this array must be an empty buffer (sentinel).
+ // - This is a convention so that tensors without a buffer can provide 0 as
+ // - their buffer.
+ auto buffer = encodeOpBuffer(builder);
+ gd._buffers.push_back(buffer);
+
+ for (const auto &tensor_info : tensor_ctx)
+ {
+ exportOpDefinedTensor(tensor_info, builder, gd);
+ }
+}
+
+} // namespace circle_detail
+} // namespace exo
diff --git a/compiler/exo/src/Circle/CircleTensorExporter.h b/compiler/exo/src/Circle/CircleTensorExporter.h
new file mode 100644
index 000000000..39d8e1b86
--- /dev/null
+++ b/compiler/exo/src/Circle/CircleTensorExporter.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_TENSOR_EXPORTER_H__
+#define __CIRCLE_TENSOR_EXPORTER_H__
+
+#include "CircleExporterUtils.h"
+
+#include <loco/IR/Graph.h>
+
+#include <flatbuffers/flatbuffers.h>
+
+namespace exo
+{
+namespace circle_detail
+{
+
+/**
+ * @brief create Tensors corresponding to results of all nodes in graph
+ * @param computational graph
+ * @param gd information about serialized parts of model
+ */
+void exportOpDefinedTensors(loco::Graph *g, flatbuffers::FlatBufferBuilder &builder,
+ SerializedModelData &gd);
+
+} // namespace circle_detail
+} // namespace exo
+
+#endif // __CIRCLE_TENSOR_EXPORTER_H__
diff --git a/compiler/exo/src/Circle/CircleTypeInference.cpp b/compiler/exo/src/Circle/CircleTypeInference.cpp
new file mode 100644
index 000000000..a1e92b884
--- /dev/null
+++ b/compiler/exo/src/Circle/CircleTypeInference.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CircleTypeInference.h"
+
+#include "circle_schema_generated.h"
+
+#include "Dialect/Service/TFLTypeInferenceRule.h"
+#include "Dialect/IR/TFLDialect.h"
+
+#include <loco/IR/CanonicalNode.h>
+#include <loco/IR/CanonicalNodeVisitor.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/Service/TypeInference.h>
+
+#include <locoex/COpDialect.h>
+#include <locoex/Service/COpTypeInference.h>
+
+#include <oops/InternalExn.h>
+
+#include <stdex/Memory.h>
+
+#include <stdexcept>
+#include <type_traits>
+
+namespace
+{
+
+circle::TensorType translateLocoTypeToCircle(loco::DataType dtype)
+{
+ switch (dtype)
+ {
+ case loco::DataType::U8:
+ return circle::TensorType_UINT8;
+ // case loco::DataType::U16: unsupported
+ // case loco::DataType::U32: unsupported
+ // case loco::DataType::U64: unsupported
+ case loco::DataType::S8:
+ return circle::TensorType_INT8;
+ case loco::DataType::S16:
+ return circle::TensorType_INT16;
+ case loco::DataType::S32:
+ return circle::TensorType_INT32;
+ case loco::DataType::S64:
+ return circle::TensorType_INT64;
+ case loco::DataType::FLOAT16:
+ return circle::TensorType_FLOAT16;
+ case loco::DataType::FLOAT32:
+ return circle::TensorType_FLOAT32;
+ // case loco::DataType::FLOAT64: unsupported
+ default:
+ break;
+ }
+
+ INTERNAL_EXN_V("Invalid loco dtype", oops::to_uint32(dtype));
+}
+
+} // namespace
+
+namespace exo
+{
+namespace circle_detail
+{
+
+circle::TensorType TypeInference::get(loco::Node *node)
+{
+ assert(loco::dtype_known(node));
+ return translateLocoTypeToCircle(loco::dtype_get(node));
+}
+
+} // namespace circle_detail
+} // namespace exo
diff --git a/compiler/exo/src/Circle/CircleTypeInference.h b/compiler/exo/src/Circle/CircleTypeInference.h
new file mode 100644
index 000000000..9c1730233
--- /dev/null
+++ b/compiler/exo/src/Circle/CircleTypeInference.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_TYPE_INFERENCE_H__
+#define __CIRCLE_TYPE_INFERENCE_H__
+
+#include "CircleExporterUtils.h"
+
+#include <loco/IR/Nodes.h>
+
+namespace exo
+{
+namespace circle_detail
+{
+
+/**
+ * @brief Get the type of each node as NodeAnnotation
+ *
+ * HOW TO USE
+ *
+ * TypeInference::get(g->nodes()->at(0));
+ * TypeInference::get(g->nodes()->at(...));
+ */
+struct TypeInference
+{
+ static circle::TensorType get(loco::Node *node);
+};
+
+} // namespace circle_detail
+} // namespace exo
+
+#endif // __CIRCLE_TYPE_INFERENCE_H__
diff --git a/compiler/exo/src/Conversion/AvgPool2DConverter.cpp b/compiler/exo/src/Conversion/AvgPool2DConverter.cpp
new file mode 100644
index 000000000..a95518ac6
--- /dev/null
+++ b/compiler/exo/src/Conversion/AvgPool2DConverter.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AvgPool2DConverter.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+#include "GraphBlock.h"
+#include "Check.h"
+
+#include <loco.h>
+
+namespace exo
+{
+/**
+ * @brief Converts loco::AvgPool2D to locoex::TFLAveragePool2D
+ *
+ * How it works: (note: ten->fea means input: tensor, output: feature)
+ *
+ * Before:
+ * Foo ---- FeatureEncode ---- AvgPool2D ---- FeatureDecode ---- Bar
+ * ten->ten ten->fea fea->fea fea->ten ten->ten
+ *
+ * After: AvgPool2D
+ * /
+ * Foo -- FeatureEncode - FeatureDecode - TFLAvgPool2D - FeatureEncode - FeatureDecode -- Bar
+ * ten->ten ten->fea fea->ten ten->ten ten->fea fea->ten ten->ten
+ *
+ * @note This method replaces AvgPool2D with "FeatureDecode -- TFLAvgPool2D -- FeatureEncode".
+ * Redundant nodes will be removed during transforms.
+ */
+bool AvgPool2DConverter::convert(loco::AvgPool2D *origin)
+{
+ auto *graph = origin->graph();
+
+ auto dec = make_feature_decode<FeatureLayout::NHWC>(origin->ifm());
+ auto tfl_average = graph->nodes()->create<locoex::TFLAveragePool2D>();
+ {
+ tfl_average->value(dec);
+
+ // set attributes
+ tfl_average->stride()->w(origin->stride()->horizontal());
+ tfl_average->stride()->h(origin->stride()->vertical());
+
+ tfl_average->filter()->w(origin->window()->horizontal());
+ tfl_average->filter()->h(origin->window()->vertical());
+
+ auto pad = origin->pad();
+ if (pad->bottom() == 0 && pad->top() == 0 && pad->left() == 0 && pad->right() == 0)
+ tfl_average->padding(locoex::Padding::VALID);
+ else
+ // TODO This is necessary, but not sufficient condition. More rigorous check required
+ tfl_average->padding(locoex::Padding::SAME);
+
+ tfl_average->fusedActivationFunction(locoex::FusedActFunc::NONE);
+ }
+ auto enc = make_feature_encode<FeatureLayout::NHWC>(tfl_average);
+
+ // replace canonical node
+ loco::replace(origin).with(enc);
+ origin->ifm(nullptr);
+
+ return true;
+}
+
+} // namespace exo
diff --git a/compiler/exo-tflite/src/Conversion/AvgPool2DConverter.h b/compiler/exo/src/Conversion/AvgPool2DConverter.h
index f66d02eb6..f66d02eb6 100644
--- a/compiler/exo-tflite/src/Conversion/AvgPool2DConverter.h
+++ b/compiler/exo/src/Conversion/AvgPool2DConverter.h
diff --git a/compiler/exo/src/Conversion/CanonicalNodeConverter.cpp b/compiler/exo/src/Conversion/CanonicalNodeConverter.cpp
new file mode 100644
index 000000000..4daf905f8
--- /dev/null
+++ b/compiler/exo/src/Conversion/CanonicalNodeConverter.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CanonicalNodeConverter.h"
+
+// This file is to make sure compilation of "CanonicalNodeConverter.h"
diff --git a/compiler/exo/src/Conversion/CanonicalNodeConverter.h b/compiler/exo/src/Conversion/CanonicalNodeConverter.h
new file mode 100644
index 000000000..76f73d888
--- /dev/null
+++ b/compiler/exo/src/Conversion/CanonicalNodeConverter.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONVERSION_CANONICAL_NODE_CONVERTER_H__
+#define __CONVERSION_CANONICAL_NODE_CONVERTER_H__
+
+#include "Convert.h"
+
+#include <loco.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <logo/Pass.h>
+
+namespace exo
+{
+
+/**
+ * @brief Class to convert a canonical node to TFL node
+ *
+ * TODO Find a better name
+ */
+template <typename CanonicalType> class CanonicalNodeConverter : public logo::Pass
+{
+public:
+ virtual const char *name(void) const { return nullptr; }
+
+public:
+ bool run(loco::Graph *graph);
+
+protected:
+ virtual bool convert(CanonicalType *node) = 0;
+};
+
+template <typename CanonicalType>
+bool CanonicalNodeConverter<CanonicalType>::run(loco::Graph *graph)
+{
+ auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
+ bool changed = false;
+
+ for (auto node : active_nodes)
+ {
+ // TODO Generalize this to all loco dialects
+ if (node->dialect() == loco::CanonicalDialect::get())
+ {
+ auto the_node = dynamic_cast<CanonicalType *>(node);
+ if (the_node != nullptr)
+ {
+ if (convert(the_node))
+ changed = true;
+ }
+ }
+ }
+
+ return changed;
+}
+
+} // namespace exo
+
+#endif //__CONVERSION_CANONICAL_NODE_CONVERTER_H__
diff --git a/compiler/exo/src/Conversion/ConstGenConverter.cpp b/compiler/exo/src/Conversion/ConstGenConverter.cpp
new file mode 100644
index 000000000..b2e2b4bdb
--- /dev/null
+++ b/compiler/exo/src/Conversion/ConstGenConverter.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ConstGenConverter.h"
+
+#include "Dialect/IR/TFLNodes.h"
+#include "Check.h"
+
+#include <loco.h>
+
+#include <oops/InternalExn.h>
+
+namespace exo
+{
+
+bool ConstGenConverter::convert(loco::ConstGen *constgen)
+{
+ auto *graph = constgen->graph();
+
+ auto tfl_const = graph->nodes()->create<locoex::TFLConst>();
+ {
+ if (constgen->dtype() == loco::DataType::FLOAT32)
+ {
+ tfl_const->dtype(loco::DataType::FLOAT32);
+
+ tfl_const->rank(constgen->rank());
+ for (uint32_t axis = 0; axis < constgen->rank(); axis++)
+ tfl_const->dim(axis) = constgen->dim(axis);
+
+ auto size = constgen->size<loco::DataType::FLOAT32>();
+ tfl_const->size<loco::DataType::FLOAT32>(size);
+
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ tfl_const->at<loco::DataType::FLOAT32>(i) = constgen->at<loco::DataType::FLOAT32>(i);
+ }
+ }
+ else
+ INTERNAL_EXN_V("Unsupported DataType", oops::to_uint32(constgen->dtype()));
+ }
+
+ loco::replace(constgen).with(tfl_const);
+
+ return true;
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Conversion/ConstGenConverter.h b/compiler/exo/src/Conversion/ConstGenConverter.h
new file mode 100644
index 000000000..613ccd0e6
--- /dev/null
+++ b/compiler/exo/src/Conversion/ConstGenConverter.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONVERSION_CONSTGEN_CONVERTER_H__
+#define __CONVERSION_CONSTGEN_CONVERTER_H__
+
+#include "CanonicalNodeConverter.h"
+
+#include <loco.h>
+
+namespace exo
+{
+
+class ConstGenConverter : public CanonicalNodeConverter<loco::ConstGen>
+{
+public:
+ const char *name(void) const final { return "exo::ConstGenConverter"; }
+
+public:
+ bool convert(loco::ConstGen *constgen) final;
+};
+
+} // namespace exo
+
+#endif // __CONVERSION_CONSTGEN_CONVERTER_H__
diff --git a/compiler/exo/src/Conversion/ConstGenConverter.test.cpp b/compiler/exo/src/Conversion/ConstGenConverter.test.cpp
new file mode 100644
index 000000000..f7a577242
--- /dev/null
+++ b/compiler/exo/src/Conversion/ConstGenConverter.test.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ConstGenConverter.h"
+#include "ReluConverter.h"
+
+#include "Dialect/IR/TFLNodes.h"
+#include "TestGraph.h"
+#include "TestHelper.h"
+
+#include <loco.h>
+
+#include <gtest/gtest.h>
+
+TEST(TFLConstGenConverterTest, ConstGen_Relu)
+{
+ exo::test::ExampleGraph<exo::test::ExampleGraphType::ConstGen_ReLU> g;
+
+ // set constgen
+ {
+ g.constgen->dtype(loco::DataType::FLOAT32);
+ g.constgen->shape({2, 1});
+ g.constgen->size<loco::DataType::FLOAT32>(2);
+
+ g.constgen->at<loco::DataType::FLOAT32>(0) = 0.5;
+ g.constgen->at<loco::DataType::FLOAT32>(1) = -0.5;
+ }
+
+ // let's convert
+ {
+ exo::test::TypeShapeReadyPhase test_phase;
+
+ test_phase.add_pass<exo::ConstGenConverter>();
+ test_phase.add_pass<exo::ReluConverter>();
+
+ test_phase.run(g.graph());
+ }
+
+ auto tfl_const = exo::test::find_first_node_bytype<locoex::TFLConst>(g.graph());
+ auto tfl_relu = exo::test::find_first_node_bytype<locoex::TFLRelu>(g.graph());
+
+ ASSERT_TRUE(tfl_const != nullptr and tfl_relu != nullptr);
+ ASSERT_TRUE(tfl_relu->features() == tfl_const);
+
+ ASSERT_TRUE(tfl_const->rank() == g.constgen->rank());
+ ASSERT_TRUE(tfl_const->dim(0) == g.constgen->dim(0));
+ ASSERT_TRUE(tfl_const->dim(1) == g.constgen->dim(1));
+ ASSERT_TRUE(tfl_const->at<loco::DataType::FLOAT32>(0) ==
+ g.constgen->at<loco::DataType::FLOAT32>(0));
+ ASSERT_TRUE(tfl_const->at<loco::DataType::FLOAT32>(1) ==
+ g.constgen->at<loco::DataType::FLOAT32>(1));
+}
diff --git a/compiler/exo/src/Conversion/Conv2DConverter.cpp b/compiler/exo/src/Conversion/Conv2DConverter.cpp
new file mode 100644
index 000000000..c8120171d
--- /dev/null
+++ b/compiler/exo/src/Conversion/Conv2DConverter.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Conv2DConverter.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+#include "GraphBlock.h"
+#include "Check.h"
+
+#include <loco.h>
+#include <loco/Service/TypeInference.h>
+#include <loco/Service/ShapeInference.h>
+
+namespace exo
+{
+/**
+ * @brief Converts loco::Conv2D to locoex::TFLConv2D
+ * @note Because TFLConv2D accepts input and filter of loco::Domain::Tensor,
+ * loco::FeatureDecode and loco::FilterDecode will be inserted as an inputs
+ * to meet domain invariant.
+ * Please refer to the comment in AvgPool2DConvert.
+ */
+bool Conv2DConverter::convert(loco::Conv2D *origin)
+{
+ auto *graph = origin->graph();
+
+ assert(origin->ifm());
+ assert(origin->ker());
+
+ auto tfl_conv2d = graph->nodes()->create<locoex::TFLConv2D>();
+ {
+ tfl_conv2d->stride()->w(origin->stride()->horizontal());
+ tfl_conv2d->stride()->h(origin->stride()->vertical());
+
+ auto pad = origin->pad();
+ if (pad->bottom() == 0 && pad->top() == 0 && pad->left() == 0 && pad->right() == 0)
+ tfl_conv2d->padding(locoex::Padding::VALID);
+ else
+ // TODO This is necessary, but not sufficient condition. More rigorous check required
+ tfl_conv2d->padding(locoex::Padding::SAME);
+
+ tfl_conv2d->fusedActivationFunction(locoex::FusedActFunc::NONE);
+ }
+
+ // let's create a new graph connection with tfl_conv2d
+ {
+ // input
+ auto feature_dec = make_feature_decode<FeatureLayout::NHWC>(origin->ifm());
+ tfl_conv2d->input(feature_dec);
+
+ // filter
+ auto filter_dec = make_filter_decode<FilterLayout::OHWI>(origin->ker());
+ tfl_conv2d->filter(filter_dec);
+
+ // bias
+ auto zero_const = graph->nodes()->create<locoex::TFLConst>();
+ {
+ assert(loco::shape_known(origin));
+ assert(loco::dtype_known(origin) && loco::dtype_get(origin) == loco::DataType::FLOAT32);
+
+ auto output_depth = loco::shape_get(origin->ker()).as<loco::FilterShape>().count();
+
+ zero_const->dtype(loco::DataType::FLOAT32);
+ zero_const->rank(1);
+ zero_const->dim(0) = output_depth;
+ zero_const->size<loco::DataType::FLOAT32>(output_depth.value());
+ for (uint32_t x = 0; x < output_depth.value(); x++)
+ zero_const->at<loco::DataType::FLOAT32>(x) = 0.0;
+ }
+ tfl_conv2d->bias(zero_const);
+
+ // output
+ auto feature_enc = make_feature_encode<FeatureLayout::NHWC>(tfl_conv2d);
+
+ // replace canonical node
+ loco::replace(origin).with(feature_enc);
+ origin->ifm(nullptr);
+ }
+
+ return true;
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Conversion/Conv2DConverter.h b/compiler/exo/src/Conversion/Conv2DConverter.h
new file mode 100644
index 000000000..95b3fbfae
--- /dev/null
+++ b/compiler/exo/src/Conversion/Conv2DConverter.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONVERSION_CONV2D_CONVERTER__
+#define __CONVERSION_CONV2D_CONVERTER__
+
+#include "CanonicalNodeConverter.h"
+
+#include <loco.h>
+
+namespace exo
+{
+
+/**
+ * @brief Convert loco::Conv2D to locoex::TFLConv2D
+ */
+class Conv2DConverter : public CanonicalNodeConverter<loco::Conv2D>
+{
+public:
+ const char *name(void) const final { return "exo::Conv2DConverter"; }
+
+public:
+ bool convert(loco::Conv2D *origin) final;
+};
+
+} // namespace exo
+
+#endif // __CONVERSION_CONV2D_CONVERTER__
diff --git a/compiler/exo/src/Conversion/DepthwiseConv2DConverter.cpp b/compiler/exo/src/Conversion/DepthwiseConv2DConverter.cpp
new file mode 100644
index 000000000..e3884c3cc
--- /dev/null
+++ b/compiler/exo/src/Conversion/DepthwiseConv2DConverter.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DepthwiseConv2DConverter.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+#include "GraphBlock.h"
+#include "Check.h"
+
+#include <loco.h>
+#include <loco/Service/TypeInference.h>
+#include <loco/Service/ShapeInference.h>
+
+namespace exo
+{
+
+bool DepthwiseConv2DConverter::convert(loco::DepthwiseConv2D *origin)
+{
+ // Filter shape is required
+ if (not loco::shape_known(origin->ker()))
+ return false;
+
+ auto filter_shape = loco::shape_get(origin->ker()).as<loco::DepthwiseFilterShape>();
+
+ if ((origin->ifm() == nullptr) or (origin->ker() == nullptr))
+ return false;
+
+ auto *graph = origin->graph();
+
+ auto tfl_dw_conv2d = graph->nodes()->create<locoex::TFLDepthwiseConv2D>();
+ {
+ tfl_dw_conv2d->stride()->w(origin->stride()->horizontal());
+ tfl_dw_conv2d->stride()->h(origin->stride()->vertical());
+
+ auto pad = origin->pad();
+ if (pad->bottom() == 0 && pad->top() == 0 && pad->left() == 0 && pad->right() == 0)
+ tfl_dw_conv2d->padding(locoex::Padding::VALID);
+ else
+ // TODO This is necessary, but not sufficient condition. More rigorous check required
+ tfl_dw_conv2d->padding(locoex::Padding::SAME);
+
+ tfl_dw_conv2d->fusedActivationFunction(locoex::FusedActFunc::NONE);
+
+ uint32_t multiplier = filter_shape.multiplier().value();
+ EXO_ASSERT(multiplier < static_cast<uint32_t>(std::numeric_limits<int32_t>::max()),
+ "Multiplier is too big that casting may occur unintended behavior")
+
+ tfl_dw_conv2d->depthMultiplier(static_cast<int32_t>(multiplier));
+ }
+
+ // let's create a new graph connection with tfl_dw_conv2d
+ {
+ // ifm --- feature_dec --- tfl_dw_conv2d
+ auto feature_dec = make_feature_decode<FeatureLayout::NHWC>(origin->ifm());
+ tfl_dw_conv2d->input(feature_dec);
+
+ // ker --- filter_dec(H x W x C x M) --- reshape(1 x H x W x CM) --- tfl_dw_conv2d
+ auto filter_dec = make_dw_filter_decode<DepthwiseFilterLayout::HWCM>(origin->ker());
+
+ auto reshape = graph->nodes()->create<locoex::TFLReshape>();
+ reshape->tensor(filter_dec);
+
+ int32_t new_shape[4] = {
+ 1, static_cast<int32_t>(filter_shape.height().value()),
+ static_cast<int32_t>(filter_shape.width().value()),
+ static_cast<int32_t>(filter_shape.depth().value() * filter_shape.multiplier().value())};
+ locoex::set_new_shape(reshape, new_shape, 4);
+
+ tfl_dw_conv2d->filter(reshape);
+
+ // bias
+ auto zero_const = graph->nodes()->create<locoex::TFLConst>();
+ {
+ assert(loco::shape_known(origin));
+ assert(loco::dtype_known(origin) && loco::dtype_get(origin) == loco::DataType::FLOAT32);
+
+ // bias size is C * M
+ uint32_t bias_size = filter_shape.depth().value() * filter_shape.multiplier().value();
+
+ zero_const->dtype(loco::DataType::FLOAT32);
+ zero_const->rank(1);
+ zero_const->dim(0) = bias_size;
+ zero_const->size<loco::DataType::FLOAT32>(bias_size);
+ for (uint32_t x = 0; x < bias_size; x++)
+ zero_const->at<loco::DataType::FLOAT32>(x) = 0.0;
+ }
+ tfl_dw_conv2d->bias(zero_const);
+
+ // output
+ auto feature_enc = make_feature_encode<FeatureLayout::NHWC>(tfl_dw_conv2d);
+
+ // replace canonical node
+ loco::replace(origin).with(feature_enc);
+ origin->ifm(nullptr);
+ }
+
+ return true;
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Conversion/DepthwiseConv2DConverter.h b/compiler/exo/src/Conversion/DepthwiseConv2DConverter.h
new file mode 100644
index 000000000..57cc01e5e
--- /dev/null
+++ b/compiler/exo/src/Conversion/DepthwiseConv2DConverter.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONVERSION_DEPTHWISECONV2D_CONVERTER__
+#define __CONVERSION_DEPTHWISECONV2D_CONVERTER__
+
+#include "CanonicalNodeConverter.h"
+
+#include <loco.h>
+
+namespace exo
+{
+
+/**
+ * @brief Convert loco::DepthwiseConv2D to locoex::TFLDepthwiseConv2D and auxiliary
+ *
+ *
+ * <BEFORE>
+ *
+ * IFM -------- DepthwiseConv2D --- Out
+ * [Feature] / [Feature]
+ * /
+ * KER -------
+ * [DWFilter]
+ *
+ *
+ * <AFTER>
+ * TFLConst (bias) ---------------------------
+ * \
+ * IFM ------ FeatureDecode ------------------ TFLDepthwiseConv2D --- FeatureEncode --- Out
+ * [Feature] [Tensor] / [Tensor] [Feature]
+ * /
+ * KER ------- DepthwiseFilterDecode --- TFLReshape
+ * [DWFilter] [Tensor / H W C M] [Tensor / 1 H W CM]
+ *
+ */
+class DepthwiseConv2DConverter : public CanonicalNodeConverter<loco::DepthwiseConv2D>
+{
+public:
+ const char *name(void) const final { return "exo::DepthwiseConv2DConverter"; }
+
+public:
+ bool convert(loco::DepthwiseConv2D *origin) final;
+};
+
+} // namespace exo
+
+#endif // __CONVERSION_DEPTHWISECONV2D_CONVERTER__
diff --git a/compiler/exo/src/Conversion/EltwiseAddConverter.cpp b/compiler/exo/src/Conversion/EltwiseAddConverter.cpp
new file mode 100644
index 000000000..557f47944
--- /dev/null
+++ b/compiler/exo/src/Conversion/EltwiseAddConverter.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EltwiseAddConverter.h"
+
+#include "EltwiseBinaryConverter.h"
+
+namespace exo
+{
+
+bool EltwiseAddConverter::convert(loco::EltwiseAdd *origin)
+{
+ return EltwiseBinaryConvert<loco::EltwiseAdd, locoex::TFLAdd>(origin);
+}
+
+} // namespace exo
diff --git a/compiler/exo-tflite/src/Conversion/EltwiseAddConverter.h b/compiler/exo/src/Conversion/EltwiseAddConverter.h
index 97e1071b5..97e1071b5 100644
--- a/compiler/exo-tflite/src/Conversion/EltwiseAddConverter.h
+++ b/compiler/exo/src/Conversion/EltwiseAddConverter.h
diff --git a/compiler/exo/src/Conversion/EltwiseBinaryConverter.h b/compiler/exo/src/Conversion/EltwiseBinaryConverter.h
new file mode 100644
index 000000000..095da9e5c
--- /dev/null
+++ b/compiler/exo/src/Conversion/EltwiseBinaryConverter.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONVERSION_ELTWISEBINARY_CONVERTER_H__
+#define __CONVERSION_ELTWISEBINARY_CONVERTER_H__
+
+#include "GraphBlock.h"
+#include "Check.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+#include <loco/IR/Nodes.h>
+
+#include <loco/Service/ShapeInference.h>
+
+namespace
+{
+
+template <class ELTWISEBIN, class TFLBIN>
+class EltwiseBinInputHandler : public exo::InputHandler<ELTWISEBIN, TFLBIN>
+{
+public:
+ void handover(ELTWISEBIN *origin, TFLBIN *replacer) override
+ {
+ assert(origin && replacer);
+ replacer->x(origin->lhs());
+ replacer->y(origin->rhs());
+ }
+
+ std::vector<loco::Node *> getInputsToConvert(ELTWISEBIN *origin) override
+ {
+ assert(origin);
+ std::vector<loco::Node *> inputs({origin->lhs(), origin->rhs()});
+ return inputs;
+ }
+
+ void set(TFLBIN *replacer, std::vector<loco::Node *> &to) override
+ {
+ assert(to.size() == 2);
+
+ replacer->x(to.at(0));
+ replacer->y(to.at(1));
+ }
+
+ void nullify(ELTWISEBIN *origin) override
+ {
+ assert(origin);
+ origin->lhs(nullptr);
+ origin->rhs(nullptr);
+ }
+};
+
+template <class TFLBIN> void init_fused_act_func(TFLBIN *);
+
+template <> inline void init_fused_act_func(locoex::TFLAdd *node)
+{
+ node->fusedActivationFunction(locoex::FusedActFunc::NONE);
+}
+
+template <> inline void init_fused_act_func(locoex::TFLMul *node)
+{
+ node->fusedActivationFunction(locoex::FusedActFunc::NONE);
+}
+
+template <> inline void init_fused_act_func(locoex::TFLSub *node)
+{
+ node->fusedActivationFunction(locoex::FusedActFunc::NONE);
+}
+
+template <> inline void init_fused_act_func(locoex::TFLDiv *node)
+{
+ node->fusedActivationFunction(locoex::FusedActFunc::NONE);
+}
+
+} // namespace
+
+namespace exo
+{
+
+template <class ELTWISEBIN, class TFLBIN> bool EltwiseBinaryConvert(ELTWISEBIN *origin)
+{
+ EltwiseBinInputHandler<ELTWISEBIN, TFLBIN> input_handler;
+ exo::DomainConverter<ELTWISEBIN, TFLBIN> domain_converter;
+
+ auto tfl_node = domain_converter.template convert<FeatureLayout::NHWC>(origin, input_handler);
+
+ if (tfl_node == nullptr)
+ return false;
+
+ init_fused_act_func(tfl_node);
+
+ return true;
+}
+
+} // namespace exo
+
+#endif // __CONVERSION_ELTWISEBINARY_CONVERTER_H__
diff --git a/compiler/exo/src/Conversion/EltwiseDivConverter.cpp b/compiler/exo/src/Conversion/EltwiseDivConverter.cpp
new file mode 100644
index 000000000..dc8eae461
--- /dev/null
+++ b/compiler/exo/src/Conversion/EltwiseDivConverter.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EltwiseDivConverter.h"
+
+#include "EltwiseBinaryConverter.h"
+
+namespace exo
+{
+
+bool EltwiseDivConverter::convert(loco::EltwiseDiv *origin)
+{
+ return EltwiseBinaryConvert<loco::EltwiseDiv, locoex::TFLDiv>(origin);
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Conversion/EltwiseDivConverter.h b/compiler/exo/src/Conversion/EltwiseDivConverter.h
new file mode 100644
index 000000000..06b2d685b
--- /dev/null
+++ b/compiler/exo/src/Conversion/EltwiseDivConverter.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONVERSION_ELTWISEDIV_CONVERTER_H__
+#define __CONVERSION_ELTWISEDIV_CONVERTER_H__
+
+#include "CanonicalNodeConverter.h"
+
+#include <loco.h>
+
+namespace exo
+{
+
+/**
+ * @brief Convert loco::EltwiseDiv to TFLDiv
+ */
+class EltwiseDivConverter : public CanonicalNodeConverter<loco::EltwiseDiv>
+{
+public:
+ const char *name(void) const final { return "exo::EltwiseDivConverter"; }
+
+public:
+ bool convert(loco::EltwiseDiv *origin) final;
+};
+
+} // namespace exo
+
+#endif // __CONVERSION_ELTWISEDIV_CONVERTER_H__
diff --git a/compiler/exo/src/Conversion/EltwiseMaxConverter.cpp b/compiler/exo/src/Conversion/EltwiseMaxConverter.cpp
new file mode 100644
index 000000000..dd7d34440
--- /dev/null
+++ b/compiler/exo/src/Conversion/EltwiseMaxConverter.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EltwiseMaxConverter.h"
+
+#include "GraphBlock.h"
+#include "Check.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+#include <loco/Service/ShapeInference.h>
+
+namespace
+{
+
+class EltwiseMaxInputHandler : public exo::InputHandler<loco::EltwiseMax, locoex::TFLMaximum>
+{
+public:
+ void handover(loco::EltwiseMax *origin, locoex::TFLMaximum *replacer) override
+ {
+ replacer->x(origin->lhs());
+ replacer->y(origin->rhs());
+ }
+
+ std::vector<loco::Node *> getInputsToConvert(loco::EltwiseMax *origin) override
+ {
+ std::vector<loco::Node *> inputs({origin->lhs(), origin->rhs()});
+ return inputs;
+ }
+
+ void set(locoex::TFLMaximum *replacer, std::vector<loco::Node *> &to) override
+ {
+ assert(to.size() == 2);
+
+ replacer->x(to.at(0));
+ replacer->y(to.at(1));
+ }
+
+ void nullify(loco::EltwiseMax *origin) override
+ {
+ assert(origin);
+ origin->lhs(nullptr);
+ origin->rhs(nullptr);
+ }
+};
+
+} // namespace
+
+namespace exo
+{
+
+bool EltwiseMaxConverter::convert(loco::EltwiseMax *origin)
+{
+ EltwiseMaxInputHandler input_handler;
+ exo::DomainConverter<loco::EltwiseMax, locoex::TFLMaximum> domain_converter;
+
+ auto tfl_new = domain_converter.convert<FeatureLayout::NHWC>(origin, input_handler);
+
+ return (tfl_new != nullptr);
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Conversion/EltwiseMaxConverter.h b/compiler/exo/src/Conversion/EltwiseMaxConverter.h
new file mode 100644
index 000000000..708745419
--- /dev/null
+++ b/compiler/exo/src/Conversion/EltwiseMaxConverter.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONVERSION_ELTWISEMAX_CONVERTER_H__
+#define __CONVERSION_ELTWISEMAX_CONVERTER_H__
+
+#include "CanonicalNodeConverter.h"
+
+#include <loco.h>
+
+namespace exo
+{
+
+/**
+ * @brief Convert loco::EltwiseMax to TFLMaximum
+ */
+class EltwiseMaxConverter : public CanonicalNodeConverter<loco::EltwiseMax>
+{
+public:
+ const char *name(void) const final { return "exo::EltwiseMaxConverter"; }
+
+public:
+ bool convert(loco::EltwiseMax *origin) final;
+};
+
+} // namespace exo
+
+#endif // __CONVERSION_ELTWISEMAX_CONVERTER_H__
diff --git a/compiler/exo/src/Conversion/EltwiseMulConverter.cpp b/compiler/exo/src/Conversion/EltwiseMulConverter.cpp
new file mode 100644
index 000000000..f7a4b8298
--- /dev/null
+++ b/compiler/exo/src/Conversion/EltwiseMulConverter.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EltwiseMulConverter.h"
+
+#include "EltwiseBinaryConverter.h"
+
+namespace exo
+{
+
+bool EltwiseMulConverter::convert(loco::EltwiseMul *origin)
+{
+ return EltwiseBinaryConvert<loco::EltwiseMul, locoex::TFLMul>(origin);
+}
+
+} // namespace exo
diff --git a/compiler/exo-tflite/src/Conversion/EltwiseMulConverter.h b/compiler/exo/src/Conversion/EltwiseMulConverter.h
index 4f73484c0..4f73484c0 100644
--- a/compiler/exo-tflite/src/Conversion/EltwiseMulConverter.h
+++ b/compiler/exo/src/Conversion/EltwiseMulConverter.h
diff --git a/compiler/exo/src/Conversion/EltwiseSqrtConverter.cpp b/compiler/exo/src/Conversion/EltwiseSqrtConverter.cpp
new file mode 100644
index 000000000..6dead7dc6
--- /dev/null
+++ b/compiler/exo/src/Conversion/EltwiseSqrtConverter.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EltwiseSqrtConverter.h"
+
+#include "GraphBlock.h"
+#include "Check.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+#include <loco/Service/ShapeInference.h>
+
+namespace
+{
+
+class EltwiseSqrtInputHandler : public exo::InputHandler<loco::EltwiseSqrt, locoex::TFLSqrt>
+{
+public:
+ void handover(loco::EltwiseSqrt *origin, locoex::TFLSqrt *replacer) override
+ {
+ replacer->x(origin->input());
+ }
+
+ std::vector<loco::Node *> getInputsToConvert(loco::EltwiseSqrt *origin) override
+ {
+ std::vector<loco::Node *> inputs({origin->input()});
+ return inputs;
+ }
+
+ void set(locoex::TFLSqrt *replacer, std::vector<loco::Node *> &to) override
+ {
+ assert(to.size() == 1);
+
+ replacer->x(to.at(0));
+ }
+
+ void nullify(loco::EltwiseSqrt *origin) override { origin->input(nullptr); }
+};
+
+} // namespace
+
+namespace exo
+{
+
+bool EltwiseSqrtConverter::convert(loco::EltwiseSqrt *origin)
+{
+ EltwiseSqrtInputHandler input_handler;
+ exo::DomainConverter<loco::EltwiseSqrt, locoex::TFLSqrt> domain_converter;
+
+ auto tfl_new = domain_converter.convert<FeatureLayout::NHWC>(origin, input_handler);
+
+ return (tfl_new != nullptr);
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Conversion/EltwiseSqrtConverter.h b/compiler/exo/src/Conversion/EltwiseSqrtConverter.h
new file mode 100644
index 000000000..5ee3185ff
--- /dev/null
+++ b/compiler/exo/src/Conversion/EltwiseSqrtConverter.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ELTWISE_SQRT_CONVERTER_H__
+#define __ELTWISE_SQRT_CONVERTER_H__
+
+#include "CanonicalNodeConverter.h"
+
+#include <loco.h>
+
+namespace exo
+{
+
+/**
+ * @brief Convert loco::EltwiseSqrt to TFLSqrt
+ */
+class EltwiseSqrtConverter : public CanonicalNodeConverter<loco::EltwiseSqrt>
+{
+public:
+ const char *name(void) const final { return "exo::EltwiseSqrtConverter"; }
+
+public:
+ bool convert(loco::EltwiseSqrt *origin) final;
+};
+
+} // namespace exo
+
+#endif // __ELTWISE_SQRT_CONVERTER_H__
diff --git a/compiler/exo/src/Conversion/EltwiseSubConverter.cpp b/compiler/exo/src/Conversion/EltwiseSubConverter.cpp
new file mode 100644
index 000000000..5647c47a2
--- /dev/null
+++ b/compiler/exo/src/Conversion/EltwiseSubConverter.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EltwiseSubConverter.h"
+
+#include "EltwiseBinaryConverter.h"
+
+namespace exo
+{
+
+bool EltwiseSubConverter::convert(loco::EltwiseSub *origin)
+{
+ return EltwiseBinaryConvert<loco::EltwiseSub, locoex::TFLSub>(origin);
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Conversion/EltwiseSubConverter.h b/compiler/exo/src/Conversion/EltwiseSubConverter.h
new file mode 100644
index 000000000..d61b76ec0
--- /dev/null
+++ b/compiler/exo/src/Conversion/EltwiseSubConverter.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONVERSION_ELTWISESUB_CONVERTER_H__
+#define __CONVERSION_ELTWISESUB_CONVERTER_H__
+
+#include "CanonicalNodeConverter.h"
+
+#include <loco.h>
+
+namespace exo
+{
+
+/**
+ * @brief Convert loco::EltwiseSub to TFLSub
+ */
+class EltwiseSubConverter : public CanonicalNodeConverter<loco::EltwiseSub>
+{
+public:
+ const char *name(void) const final { return "exo::EltwiseSubConverter"; }
+
+public:
+ bool convert(loco::EltwiseSub *origin) final;
+};
+
+} // namespace exo
+
+#endif // __CONVERSION_ELTWISESUB_CONVERTER_H__
diff --git a/compiler/exo/src/Conversion/FeatureBiasAddConverter.cpp b/compiler/exo/src/Conversion/FeatureBiasAddConverter.cpp
new file mode 100644
index 000000000..b9aaf140b
--- /dev/null
+++ b/compiler/exo/src/Conversion/FeatureBiasAddConverter.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FeatureBiasAddConverter.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+#include "GraphBlock.h"
+
+#include <loco.h>
+#include <loco/Service/ShapeInference.h>
+
+#include <cassert>
+
+namespace
+{
+
+inline void init_fused_act_func(locoex::TFLAdd *node)
+{
+ node->fusedActivationFunction(locoex::FusedActFunc::NONE);
+}
+
+} // namespace
+
+namespace exo
+{
+
+/**
+ * @brief Converts loco::FeatureBiasAdd to locoex::TFLAdd
+ *
+ * Before:
+ * Foo ---+
+ * |
+ * loco::FeatureBiasAdd - FeatureDecode - ...
+ * |
+ * Bar - BiasEncode --+
+ *
+ * After:
+ *
+ * Foo - loco::FeatureDecode --+ loco::FeatureBiasAdd
+ * |(x)
+ * TFLAdd -- loco::FeatureEncode - FeatureDecode - ...
+ * |(y)
+ * Bar - BiasEncode - loco::BiasDecode --+
+ */
+bool FeatureBiasAddConverter::convert(loco::FeatureBiasAdd *origin)
+{
+ auto *graph = origin->graph();
+
+ auto tfl_add = graph->nodes()->create<locoex::TFLAdd>();
+
+ // handling input x
+ assert(loco::shape_get(origin->value()).domain() == loco::Domain::Feature);
+
+ auto fea_dec = make_feature_decode<FeatureLayout::NHWC>(origin->value());
+ tfl_add->x(fea_dec);
+
+ // handling input y
+ auto bias_dec = graph->nodes()->create<loco::BiasDecode>();
+ assert(bias_dec != nullptr);
+
+ bias_dec->input(origin->bias());
+
+ tfl_add->y(bias_dec);
+
+ // fused activation function
+ init_fused_act_func(tfl_add);
+
+ // handling output
+ auto fea_enc = make_feature_encode<FeatureLayout::NHWC>(tfl_add);
+
+ loco::replace(origin).with(fea_enc);
+ origin->value(nullptr);
+
+ return true;
+}
+
+} // namespace exo
diff --git a/compiler/exo-tflite/src/Conversion/FeatureBiasAddConverter.h b/compiler/exo/src/Conversion/FeatureBiasAddConverter.h
index 5c4f10213..5c4f10213 100644
--- a/compiler/exo-tflite/src/Conversion/FeatureBiasAddConverter.h
+++ b/compiler/exo/src/Conversion/FeatureBiasAddConverter.h
diff --git a/compiler/exo/src/Conversion/FeatureBiasAddConverter.test.cpp b/compiler/exo/src/Conversion/FeatureBiasAddConverter.test.cpp
new file mode 100644
index 000000000..f3c4a5f81
--- /dev/null
+++ b/compiler/exo/src/Conversion/FeatureBiasAddConverter.test.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FeatureBiasAddConverter.h"
+
+#include "GraphBlock.h"
+#include "Dialect/IR/TFLNodes.h"
+
+#include "TestGraph.h"
+#include "TestHelper.h"
+
+#include <loco.h>
+
+#include <gtest/gtest.h>
+
+TEST(FeatureBiasAddConverterTest, basic_test)
+{
+ exo::test::ExampleGraph<exo::test::ExampleGraphType::FeatureBiasAdd> g;
+
+ { // attrib setting
+ // pull
+ g.pull->dtype(loco::DataType::FLOAT32);
+ g.pull->shape({1, 2, 2, 3});
+
+ // bias value
+ g.constgen->dtype(loco::DataType::FLOAT32);
+ g.constgen->shape({3});
+ g.constgen->size<loco::DataType::FLOAT32>(3);
+
+ g.constgen->at<loco::DataType::FLOAT32>(0) = 0.5;
+ g.constgen->at<loco::DataType::FLOAT32>(1) = 1;
+ g.constgen->at<loco::DataType::FLOAT32>(2) = 1.5;
+ }
+
+ EXO_TEST_ASSERT_NODE_COUNT({g.push}, 7); // sanity check
+
+ // let's convert!!
+ {
+ exo::test::TypeShapeReadyPhase test_phase;
+
+ test_phase.add_pass<exo::FeatureBiasAddConverter>();
+
+ test_phase.run(g.graph());
+
+ /*
+ Expected:
+
+ Pull - FeatureEncoder - FeatureDecode - TFLAdd - FeatureEncode - FeatureDecode - Push
+ |
+ ConstGen - BiasEncode - BiasDecode ---+
+ */
+ }
+
+ // check surroundings
+ auto tfl_add = exo::test::find_first_node_bytype<locoex::TFLAdd>(g.graph());
+ {
+ ASSERT_TRUE(tfl_add != nullptr);
+
+ // input x and its pred
+ {
+ auto actual_fea_dec = dynamic_cast<loco::FeatureDecode *>(tfl_add->x());
+ ASSERT_TRUE(actual_fea_dec != nullptr);
+
+ auto actual_fea_enc = dynamic_cast<loco::FeatureEncode *>(actual_fea_dec->input());
+ ASSERT_TRUE(actual_fea_enc != nullptr);
+ ASSERT_TRUE(actual_fea_enc == g.fea_enc);
+ }
+
+ // input y and its pred
+ {
+ auto actual_bias_dec = dynamic_cast<loco::BiasDecode *>(tfl_add->y());
+ ASSERT_TRUE(actual_bias_dec != nullptr);
+
+ auto actual_bias_enc = dynamic_cast<loco::BiasEncode *>(actual_bias_dec->input());
+ ASSERT_TRUE(actual_bias_enc != nullptr);
+ ASSERT_TRUE(actual_bias_enc == g.bias_enc);
+ }
+
+ // output check
+ {
+ auto actual_fea_enc = exo::test::get_only_succ<loco::FeatureEncode>(tfl_add);
+ ASSERT_TRUE(actual_fea_enc != nullptr);
+
+ auto actual_fea_dec = exo::test::get_only_succ<loco::FeatureDecode>(actual_fea_enc);
+ ASSERT_TRUE(actual_fea_dec != nullptr);
+ ASSERT_TRUE(actual_fea_dec == g.fea_dec);
+ }
+ }
+}
diff --git a/compiler/exo/src/Conversion/MatMulConverter.cpp b/compiler/exo/src/Conversion/MatMulConverter.cpp
new file mode 100644
index 000000000..b1158b73d
--- /dev/null
+++ b/compiler/exo/src/Conversion/MatMulConverter.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MatMulConverter.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+#include "GraphBlock.h"
+#include "Check.h"
+
+#include <loco.h>
+#include <loco/Service/TypeInference.h>
+#include <loco/Service/ShapeInference.h>
+
+namespace exo
+{
+/**
+ * @brief Converts loco::MatMul to locoex::TFLFullyConnected
+ * @note Because TFLFullyConnected accepts input and weights of loco::Domain::Matrix,
+ * loco::MatrixDecode will be inserted as an input and weights
+ * to meet domain invariant.
+ *
+ * How it works:
+ *
+ * Before:
+ * Foo1 ---- MatrixEncode ---- MatMul ---- MatrixDecode ---- Bar
+ * Foo2 ---- MatrixEncode ----/
+ *
+ * After:
+ *
+ * Foo1 - MatrixEncode - MatrixDecode - TFLFullyConnected - MatrixEncode - MatrixDecode - Bar
+ * Foo2 - MatrixEncode - MatrixDecode -/
+ *
+ * @note This method replaces MatMul with "- MatrixDecode - TFLFullyConnected - MatrixEncode -".
+ * - MatrixDecode -/
+ * Redundant nodes will be removed during transforms.
+ *
+ * @ref
+ * https://github.com/tensorflow/tensorflow/blob/v1.13.1/tensorflow/lite/kernels/internal/reference/fully_connected.h
+ */
+bool MatMulConverter::convert(loco::MatMul *origin)
+{
+ auto *graph = origin->graph();
+
+ assert(origin->lhs());
+ assert(origin->rhs());
+
+ auto tfl_fc = graph->nodes()->create<locoex::TFLFullyConnected>();
+ tfl_fc->fusedActivationFunction(locoex::FusedActFunc::NONE);
+
+ // let's create a new graph connection with tfl_fc
+ {
+ // input
+ auto lhs_matrix_dec = make_matrix_decode<MatrixLayout::HW>(origin->lhs());
+ tfl_fc->input(lhs_matrix_dec);
+
+ // weights (WH format on TFLite)
+ auto rhs_matrix_dec = make_matrix_decode<MatrixLayout::WH>(origin->rhs());
+ tfl_fc->weights(rhs_matrix_dec);
+
+ // bias
+ auto zero_const = graph->nodes()->create<locoex::TFLConst>();
+ { // TODO Create optimization pass which fuse additional Add into bias of Conv or FC
+ assert(loco::shape_known(origin));
+ assert(loco::dtype_known(origin) && loco::dtype_get(origin) == loco::DataType::FLOAT32);
+
+ auto output_depth = loco::shape_get(origin->rhs()).as<loco::MatrixShape>().width();
+ // TODO Fix it with type inference
+ zero_const->dtype(loco::DataType::FLOAT32);
+ zero_const->rank(1);
+ zero_const->dim(0) = output_depth;
+ zero_const->size<loco::DataType::FLOAT32>(output_depth.value());
+ for (uint32_t x = 0; x < output_depth.value(); x++)
+ zero_const->at<loco::DataType::FLOAT32>(x) = 0.0;
+ }
+ tfl_fc->bias(zero_const);
+
+ // output
+ auto matrix_enc = make_matrix_encode<MatrixLayout::HW>(tfl_fc);
+
+ // replace canonical node
+ loco::replace(origin).with(matrix_enc);
+ origin->lhs(nullptr);
+ origin->rhs(nullptr);
+ }
+
+ return true;
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Conversion/MatMulConverter.h b/compiler/exo/src/Conversion/MatMulConverter.h
new file mode 100644
index 000000000..e64c4a0f2
--- /dev/null
+++ b/compiler/exo/src/Conversion/MatMulConverter.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONVERSION_FULLY_CONNECTED_CONVERTER__
+#define __CONVERSION_FULLY_CONNECTED_CONVERTER__
+
+#include "CanonicalNodeConverter.h"
+
+#include <loco.h>
+
+namespace exo
+{
+
+/**
+ * @brief Convert loco::MatMul to locoex::TFLFullyConnected
+ */
+class MatMulConverter : public CanonicalNodeConverter<loco::MatMul>
+{
+public:
+ const char *name(void) const final { return "exo::MatMulConverter"; }
+
+public:
+ bool convert(loco::MatMul *origin) final;
+};
+
+} // namespace exo
+
+#endif // __CONVERSION_FULLY_CONNECTED_CONVERTER__
diff --git a/compiler/exo/src/Conversion/MaxPool2DConverter.cpp b/compiler/exo/src/Conversion/MaxPool2DConverter.cpp
new file mode 100644
index 000000000..67e5ab833
--- /dev/null
+++ b/compiler/exo/src/Conversion/MaxPool2DConverter.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MaxPool2DConverter.h"
+
+#include "Dialect/IR/TFLNodes.h"
+#include "GraphBlock.h"
+
+#include <loco.h>
+
+namespace exo
+{
+
+/**
+ * @brief Converts loco::MaxPool2D to locoex::TFLMaxPool2D
+ *
+ * @note This works similar to AvgPool2DConverter. Please refer to the comment in
+ * AvgPool2DConverter.
+ */
+bool MaxPool2DConverter::convert(loco::MaxPool2D *origin)
+{
+ auto *graph = origin->graph();
+
+ auto dec = make_feature_decode<FeatureLayout::NHWC>(origin->ifm());
+ auto tfl_max = graph->nodes()->create<locoex::TFLMaxPool2D>();
+ {
+ tfl_max->value(dec);
+
+ // set attributes
+ tfl_max->stride()->w(origin->stride()->horizontal());
+ tfl_max->stride()->h(origin->stride()->vertical());
+
+ tfl_max->filter()->w(origin->window()->horizontal());
+ tfl_max->filter()->h(origin->window()->vertical());
+
+ auto pad = origin->pad();
+ if (pad->bottom() == 0 && pad->top() == 0 && pad->left() == 0 && pad->right() == 0)
+ tfl_max->padding(locoex::Padding::VALID);
+ else
+ // TODO This is necessary, but not sufficient condition. More rigorous check required
+ tfl_max->padding(locoex::Padding::SAME);
+
+ tfl_max->fusedActivationFunction(locoex::FusedActFunc::NONE);
+ }
+
+ auto enc = make_feature_encode<FeatureLayout::NHWC>(tfl_max);
+
+ loco::replace(origin).with(enc);
+ origin->ifm(nullptr);
+
+ return true;
+}
+
+} // namespace exo
diff --git a/compiler/exo-tflite/src/Conversion/MaxPool2DConverter.h b/compiler/exo/src/Conversion/MaxPool2DConverter.h
index 3f526d88f..3f526d88f 100644
--- a/compiler/exo-tflite/src/Conversion/MaxPool2DConverter.h
+++ b/compiler/exo/src/Conversion/MaxPool2DConverter.h
diff --git a/compiler/exo/src/Conversion/Relu6Converter.cpp b/compiler/exo/src/Conversion/Relu6Converter.cpp
new file mode 100644
index 000000000..b694511f5
--- /dev/null
+++ b/compiler/exo/src/Conversion/Relu6Converter.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Relu6Converter.h"
+
+#include "GraphBlock.h"
+#include "Check.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+#include <loco/Service/ShapeInference.h>
+
+namespace
+{
+
+class Relu6InputHandler : public exo::InputHandler<loco::ReLU6, locoex::TFLRelu6>
+{
+public:
+ void handover(loco::ReLU6 *origin, locoex::TFLRelu6 *replacer) override
+ {
+ replacer->features(origin->input());
+ }
+
+ std::vector<loco::Node *> getInputsToConvert(loco::ReLU6 *origin) override
+ {
+ std::vector<loco::Node *> inputs({origin->input()});
+ return inputs;
+ }
+
+ void set(locoex::TFLRelu6 *replacer, std::vector<loco::Node *> &to) override
+ {
+ assert(to.size() == 1);
+
+ replacer->features(to.at(0));
+ }
+
+ void nullify(loco::ReLU6 *origin) override { origin->input(nullptr); }
+};
+
+} // namespace
+
+namespace exo
+{
+
+bool Relu6Converter::convert(loco::ReLU6 *origin)
+{
+ Relu6InputHandler input_handler;
+ exo::DomainConverter<loco::ReLU6, locoex::TFLRelu6> domain_converter;
+
+ auto tfl_node = domain_converter.convert<FeatureLayout::NHWC>(origin, input_handler);
+
+ return (tfl_node != nullptr);
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Conversion/Relu6Converter.h b/compiler/exo/src/Conversion/Relu6Converter.h
new file mode 100644
index 000000000..d987b42d0
--- /dev/null
+++ b/compiler/exo/src/Conversion/Relu6Converter.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONVERSION_RELU6_CONVERTER_H__
+#define __CONVERSION_RELU6_CONVERTER_H__
+
+#include "CanonicalNodeConverter.h"
+
+#include <loco.h>
+
+namespace exo
+{
+
+/**
+ * @brief Convert loco::Relu6 to TFLRelu6
+ */
+class Relu6Converter : public CanonicalNodeConverter<loco::ReLU6>
+{
+public:
+ const char *name(void) const final { return "exo::Relu6Converter"; }
+
+public:
+ bool convert(loco::ReLU6 *origin) final;
+};
+
+} // namespace exo
+
+#endif // __CONVERSION_RELU6_CONVERTER_H__
diff --git a/compiler/exo/src/Conversion/ReluConverter.cpp b/compiler/exo/src/Conversion/ReluConverter.cpp
new file mode 100644
index 000000000..92adef94d
--- /dev/null
+++ b/compiler/exo/src/Conversion/ReluConverter.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReluConverter.h"
+
+#include "GraphBlock.h"
+#include "Check.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+#include <loco/Service/ShapeInference.h>
+
+namespace
+{
+
+class ReluInputHandler : public exo::InputHandler<loco::ReLU, locoex::TFLRelu>
+{
+public:
+ void handover(loco::ReLU *origin, locoex::TFLRelu *replacer) override
+ {
+ replacer->features(origin->input());
+ }
+
+ std::vector<loco::Node *> getInputsToConvert(loco::ReLU *origin) override
+ {
+ std::vector<loco::Node *> inputs({origin->input()});
+ return inputs;
+ }
+
+ void set(locoex::TFLRelu *replacer, std::vector<loco::Node *> &to) override
+ {
+ assert(to.size() == 1);
+
+ replacer->features(to.at(0));
+ }
+
+ void nullify(loco::ReLU *origin) override { origin->input(nullptr); }
+};
+
+} // namespace
+
+namespace exo
+{
+
+bool ReluConverter::convert(loco::ReLU *origin)
+{
+ ReluInputHandler input_handler;
+ exo::DomainConverter<loco::ReLU, locoex::TFLRelu> domain_converter;
+
+ auto tfl_node = domain_converter.convert<FeatureLayout::NHWC>(origin, input_handler);
+
+ return (tfl_node != nullptr);
+}
+
+} // namespace exo
diff --git a/compiler/exo-tflite/src/Conversion/ReluConverter.h b/compiler/exo/src/Conversion/ReluConverter.h
index e1e82ae4b..e1e82ae4b 100644
--- a/compiler/exo-tflite/src/Conversion/ReluConverter.h
+++ b/compiler/exo/src/Conversion/ReluConverter.h
diff --git a/compiler/exo/src/Conversion/ReluConverter.test.cpp b/compiler/exo/src/Conversion/ReluConverter.test.cpp
new file mode 100644
index 000000000..f53d656b4
--- /dev/null
+++ b/compiler/exo/src/Conversion/ReluConverter.test.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReluConverter.h"
+
+#include "GraphBlock.h"
+#include "Dialect/IR/TFLNodes.h"
+
+#include "TestHelper.h"
+#include "TestGraph.h"
+
+#include <gtest/gtest.h>
+
+TEST(ReluConverterTest, relu_tensor_inout)
+{
+ exo::test::TestGraph graph;
+ {
+ auto tanh = graph.append<loco::Tanh>(graph.pull);
+ auto relu = graph.append<loco::ReLU>(tanh);
+ auto relu6 = graph.append<loco::ReLU6>(relu);
+ graph.complete();
+
+ auto pull = graph.pull;
+ {
+ pull->dtype(loco::DataType::FLOAT32);
+ pull->shape({2, 2});
+ }
+ }
+
+ // let's convert
+ exo::test::TypeShapeReadyPhase test_phase;
+ {
+ test_phase.add_pass<exo::ReluConverter>();
+ test_phase.run(graph.g.get());
+ }
+
+ loco::Node *node = exo::test::find_first_node_bytype<loco::Tanh>(graph.g.get());
+ ASSERT_TRUE(node != nullptr);
+ node = exo::test::get_only_succ<locoex::TFLRelu>(node);
+ ASSERT_TRUE(node != nullptr);
+ node = exo::test::get_only_succ<loco::ReLU6>(node);
+ ASSERT_TRUE(node != nullptr);
+}
+
+TEST(ReluConverterTest, relu_feature_inout)
+{
+ // g = Pull - FeatureEncode - Relu - FeatureDecode - Push
+ exo::test::TestGraph graph;
+ {
+ auto enc = exo::make_feature_encode<exo::FeatureLayout::NHWC>(graph.pull);
+ auto relu = graph.append<loco::ReLU>(enc);
+ auto dec = exo::make_feature_decode<exo::FeatureLayout::NHWC>(relu);
+ graph.complete(dec);
+ }
+
+ auto pull = graph.pull;
+ {
+ pull->dtype(loco::DataType::FLOAT32);
+ pull->shape({1, 2, 3, 4});
+ }
+
+ exo::test::TypeShapeReadyPhase test_phase;
+ {
+ test_phase.add_pass<exo::ReluConverter>();
+ test_phase.run(graph.g.get());
+ }
+
+ // now, g = Pull - FeatureEncode - FeatureDecode - TFLRelu - FeatureEncode - FeatureDecode - Push
+
+ // Check
+ EXO_TEST_ASSERT_NODE_COUNT({graph.push}, 7);
+
+ // Check [FeatureEncode - FeatureDecode - TFLRelu - FeatureEncode - FeatureDecode] chunk
+ loco::Node *node = exo::test::find_first_node_bytype<loco::FeatureEncode>(graph.g.get());
+ ASSERT_TRUE(node != nullptr);
+ node = exo::test::get_only_succ<loco::FeatureDecode>(node);
+ ASSERT_TRUE(node != nullptr);
+ node = exo::test::get_only_succ<locoex::TFLRelu>(node);
+ ASSERT_TRUE(node != nullptr);
+ node = exo::test::get_only_succ<loco::FeatureEncode>(node);
+ ASSERT_TRUE(node != nullptr);
+ node = exo::test::get_only_succ<loco::FeatureDecode>(node);
+ ASSERT_TRUE(node != nullptr);
+}
diff --git a/compiler/exo/src/Conversion/TensorBroadcastConverter.cpp b/compiler/exo/src/Conversion/TensorBroadcastConverter.cpp
new file mode 100644
index 000000000..daccbe688
--- /dev/null
+++ b/compiler/exo/src/Conversion/TensorBroadcastConverter.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TensorBroadcastConverter.h"
+
+#include "Dialect/IR/TFLDialect.h"
+#include "Dialect/IR/TFLNodeVisitor.h"
+
+#include <loco.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/IR/CanonicalNode.h>
+
+#include <set>
+
+namespace
+{
+
+template <class T> loco::TensorBroadcast *input_as_tbc(T *node)
+{
+ loco::TensorBroadcast *tbc = dynamic_cast<loco::TensorBroadcast *>(node->x());
+ if (tbc == nullptr)
+ tbc = dynamic_cast<loco::TensorBroadcast *>(node->y());
+
+ return tbc;
+}
+
+struct Collector final : public locoex::TFLNodeMutableVisitor<void>
+{
+ using NodePair = std::pair<loco::TensorBroadcast *, loco::Node *>;
+
+ void visit(locoex::TFLAdd *node) final
+ {
+ if (auto tbc = input_as_tbc<locoex::TFLAdd>(node))
+ {
+ NodePair pair(tbc, node);
+ candidates.insert(pair);
+ }
+ }
+
+ void visit(locoex::TFLDiv *node) final
+ {
+ if (auto tbc = input_as_tbc<locoex::TFLDiv>(node))
+ {
+ NodePair pair(tbc, node);
+ candidates.insert(pair);
+ }
+ }
+
+ void visit(locoex::TFLMul *node) final
+ {
+ if (auto tbc = input_as_tbc<locoex::TFLMul>(node))
+ {
+ NodePair pair(tbc, node);
+ candidates.insert(pair);
+ }
+ }
+
+ void visit(locoex::TFLSub *node) final
+ {
+ if (auto tbc = input_as_tbc<locoex::TFLSub>(node))
+ {
+ NodePair pair(tbc, node);
+ candidates.insert(pair);
+ }
+ }
+
+ void visit(locoex::TFLMaximum *node) final
+ {
+ if (auto tbc = input_as_tbc<locoex::TFLMaximum>(node))
+ {
+ NodePair pair(tbc, node);
+ candidates.insert(pair);
+ }
+ }
+
+ void visit(locoex::TFLNode *) final { return; }
+
+ std::set<NodePair> candidates;
+};
+
+bool mapping_condition(Collector::NodePair &)
+{
+ // TODO fill condition
+
+ return true;
+}
+
+template <class T> void jump_connection(loco::TensorBroadcast *tbc, T *tflnode)
+{
+ if (tflnode->x() == tbc)
+ tflnode->x(tbc->input());
+ else if (tflnode->y() == tbc)
+ tflnode->y(tbc->input());
+ else
+ assert(false);
+
+ tbc->input(nullptr);
+}
+
+} // namespace
+
+namespace exo
+{
+
+/**
+ * @brief Disconnects loco::TensorBroadcast from the graph if following node
+ * is one of binary node: TFLAdd, TFLSub, TFLMul, TFLDiv, TFLMaximum
+ * and meets condition (TBA)
+ * @note
+ * Before:
+ * x --- TensorBroadcast --- TFLXXX --- output
+ * y ----------------------/
+ *
+ * After:
+ * --- TensorBroadcast ---
+ * x --- TFLXXX --- output
+ * y --/
+ */
+bool TensorBroadcastConverter::run(loco::Graph *graph)
+{
+ Collector collector;
+
+ auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
+
+ for (auto node : active_nodes)
+ {
+ if (node->dialect() == locoex::TFLDialect::get())
+ {
+ auto tfl_node = loco::must_cast<locoex::TFLNode *>(node);
+ tfl_node->accept(&collector);
+ }
+ }
+
+ bool changed = false;
+
+ for (auto pair : collector.candidates)
+ {
+ if (mapping_condition(pair))
+ {
+ loco::TensorBroadcast *tensorbroadcast = pair.first;
+ if (auto tfladd = dynamic_cast<locoex::TFLAdd *>(pair.second))
+ {
+ jump_connection<locoex::TFLAdd>(tensorbroadcast, tfladd);
+ changed = true;
+ }
+ else if (auto tfldiv = dynamic_cast<locoex::TFLDiv *>(pair.second))
+ {
+ jump_connection<locoex::TFLDiv>(tensorbroadcast, tfldiv);
+ changed = true;
+ }
+ else if (auto tflmul = dynamic_cast<locoex::TFLMul *>(pair.second))
+ {
+ jump_connection<locoex::TFLMul>(tensorbroadcast, tflmul);
+ changed = true;
+ }
+ else if (auto tflsub = dynamic_cast<locoex::TFLSub *>(pair.second))
+ {
+ jump_connection<locoex::TFLSub>(tensorbroadcast, tflsub);
+ changed = true;
+ }
+ else if (auto tflmaximum = dynamic_cast<locoex::TFLMaximum *>(pair.second))
+ {
+ jump_connection<locoex::TFLMaximum>(tensorbroadcast, tflmaximum);
+ changed = true;
+ }
+ else
+ {
+ assert(false);
+ }
+ }
+ }
+
+ return changed;
+}
+
+} // namespace exo
diff --git a/compiler/exo-tflite/src/Conversion/TensorBroadcastConverter.h b/compiler/exo/src/Conversion/TensorBroadcastConverter.h
index 3cf79b0ba..3cf79b0ba 100644
--- a/compiler/exo-tflite/src/Conversion/TensorBroadcastConverter.h
+++ b/compiler/exo/src/Conversion/TensorBroadcastConverter.h
diff --git a/compiler/exo/src/Conversion/TensorConcatConverter.cpp b/compiler/exo/src/Conversion/TensorConcatConverter.cpp
new file mode 100644
index 000000000..1c36b11f8
--- /dev/null
+++ b/compiler/exo/src/Conversion/TensorConcatConverter.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TensorConcatConverter.h"
+
+#include "GraphBlock.h"
+#include "Check.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+#include <loco/Service/ShapeInference.h>
+
+namespace exo
+{
+/**
+ * @brief Converts loco::TensorConcat to locoex::TFLConcatenate
+ *
+ * Before:
+ * input:0 ----- loco::TensorConcat ------- C
+ * input:1 ----/
+ *
+ * After:
+ * input:0 ----- locoex::TFLConcatenate --- C
+ * input:1 ----/
+ *
+ * input:0 ----- loco::TensorConcat ---
+ * input:1 ----/
+ *
+ */
+bool TensorConcatConverter::convert(loco::TensorConcat *origin)
+{
+ assert(loco::shape_get(origin).domain() == loco::Domain::Tensor);
+
+ if (!loco::shape_known(origin))
+ {
+ return false;
+ }
+
+ auto tfl_concat = origin->graph()->nodes()->create<locoex::TFLConcatenation>(2);
+ tfl_concat->values(0, origin->lhs());
+ tfl_concat->values(1, origin->rhs());
+ tfl_concat->axis(origin->axis());
+ tfl_concat->fusedActivationFunction(locoex::FusedActFunc::NONE);
+
+ loco::replace(origin).with(tfl_concat);
+
+ origin->lhs(nullptr);
+ origin->rhs(nullptr);
+
+ return true;
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Conversion/TensorConcatConverter.h b/compiler/exo/src/Conversion/TensorConcatConverter.h
new file mode 100644
index 000000000..6b90f4731
--- /dev/null
+++ b/compiler/exo/src/Conversion/TensorConcatConverter.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONVERSION_TENSORCONCAT_CONVERTER_H__
+#define __CONVERSION_TENSORCONCAT_CONVERTER_H__
+
+#include "CanonicalNodeConverter.h"
+
+#include <loco.h>
+
+namespace exo
+{
+
+/**
+ * @brief Convert loco::TensorConcat to TFLConcatenate
+ */
+class TensorConcatConverter : public CanonicalNodeConverter<loco::TensorConcat>
+{
+public:
+ const char *name(void) const final { return "exo::TensorConcatConverter"; }
+
+public:
+ bool convert(loco::TensorConcat *origin) final;
+};
+
+} // namespace exo
+
+#endif // __CONVERSION_TENSORCONCAT_CONVERTER_H__
diff --git a/compiler/exo/src/Conversion/TensorReduceConverter.cpp b/compiler/exo/src/Conversion/TensorReduceConverter.cpp
new file mode 100644
index 000000000..8fcb1682d
--- /dev/null
+++ b/compiler/exo/src/Conversion/TensorReduceConverter.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TensorReduceConverter.h"
+
+#include "Dialect/IR/TFLNodes.h"
+#include "Check.h"
+
+#include <oops/InternalExn.h>
+
+#include <loco.h>
+#include <loco/Service/ShapeInference.h>
+
+namespace
+{
+
+/**
+ * @brief Convert given TensorReduce as TFLMean
+ *
+ * <Before>
+ * In --- loco::TensorReduce --- Out(s)
+ *
+ * <After>
+ * In -------- locoex::TFLMean --- Out(s)
+ * /
+ * TFLConst ---
+ * (reduction indices)
+ */
+bool convert_as_mean(loco::TensorReduce *origin)
+{
+ EXO_ASSERT(origin->func() == loco::ReduceFunc::Mean, "func should be Mean for this helper");
+ EXO_ASSERT(origin->input(), "TensorReduce has no input");
+
+ auto *graph = origin->graph();
+
+ // Make reduction indicies TFLConst node
+ auto reduction = graph->nodes()->create<locoex::TFLConst>();
+ {
+ auto input_rank = loco::shape_get(origin->input()).as<loco::TensorShape>().rank();
+
+ std::vector<int32_t> red_vec;
+ for (uint32_t axis = 0; axis < input_rank; ++axis)
+ if (origin->axes()->defined(axis))
+ red_vec.push_back(static_cast<int32_t>(axis));
+
+ const loco::DataType S32 = loco::DataType::S32;
+
+ reduction->dtype(S32);
+ reduction->rank(1);
+ reduction->dim(0) = red_vec.size();
+ reduction->size<S32>(red_vec.size());
+ for (uint32_t i = 0; i < red_vec.size(); ++i)
+ reduction->at<S32>(i) = red_vec.at(i);
+ }
+
+ // Make TFLMean node to replace
+ auto mean = graph->nodes()->create<locoex::TFLMean>();
+ mean->input(origin->input());
+ mean->reduction_indices(reduction);
+ mean->keep_dims(true); // Canonical TensorReduce always keep dimensions
+
+ // replace canonical node
+ loco::replace(origin).with(mean);
+ origin->input(nullptr);
+
+ return true;
+}
+
+} // namespace
+
+namespace exo
+{
+
+bool TensorReduceConverter::convert(loco::TensorReduce *origin)
+{
+ if (origin->func() == loco::ReduceFunc::Mean)
+ return convert_as_mean(origin);
+ else
+ INTERNAL_EXN_V("Unsupported ReduceFunc", oops::to_uint32(origin->func()));
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Conversion/TensorReduceConverter.h b/compiler/exo/src/Conversion/TensorReduceConverter.h
new file mode 100644
index 000000000..dfd65ad2d
--- /dev/null
+++ b/compiler/exo/src/Conversion/TensorReduceConverter.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TENSOR_REDUCE_CONVERTER__
+#define __TENSOR_REDUCE_CONVERTER__
+
+#include "CanonicalNodeConverter.h"
+
+#include <loco.h>
+
+namespace exo
+{
+
+/**
+ * @brief Convert loco::TensorReduce to appropriate TFL reduce operation
+ * @note loco::TensorReduce always keep dimensions
+ *
+ * Currently support:
+ * - When loco::TensorReduce::func() == Mean, convert to TFLMean + TFLConst
+ * - TODO Support other cases
+ */
+class TensorReduceConverter : public CanonicalNodeConverter<loco::TensorReduce>
+{
+public:
+ const char *name(void) const final { return "exo::TensorReduceConverter"; }
+
+public:
+ bool convert(loco::TensorReduce *origin) final;
+};
+
+} // namespace exo
+
+#endif // __TENSOR_REDUCE_CONVERTER__
diff --git a/compiler/exo/src/Conversion/TensorTransposeConverter.cpp b/compiler/exo/src/Conversion/TensorTransposeConverter.cpp
new file mode 100644
index 000000000..25c27fe7e
--- /dev/null
+++ b/compiler/exo/src/Conversion/TensorTransposeConverter.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TensorTransposeConverter.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+#include <loco.h>
+#include <loco/Service/ShapeInference.h>
+
+#include <oops/InternalExn.h>
+
+#include <algorithm>
+#include <cassert>
+#include <vector>
+
+namespace
+{
+
+void validate_perm(loco::TensorTranspose *origin)
+{
+ // check perm values are correct
+ std::vector<uint32_t> base_perms; // such as {0, 1, 2, 3, ... }
+ std::vector<uint32_t> perms; // perm values in TensorTranspose
+
+ base_perms.resize(origin->perm()->size());
+ perms.resize(origin->perm()->size());
+ for (loco::TensorAxis x = 0; x < origin->perm()->size(); x++)
+ {
+ base_perms[x] = x;
+ perms[x] = origin->perm()->axis(x);
+ }
+
+ if (!std::is_permutation(base_perms.begin(), base_perms.end(), perms.begin()))
+ INTERNAL_EXN("wrong perm value");
+}
+
+} // namespace
+
+namespace exo
+{
+/**
+ * @brief Converts loco::TensorTranspose to locoex::TFLTranspose
+ */
+bool TensorTransposeConverter::convert(loco::TensorTranspose *origin)
+{
+ auto *graph = origin->graph();
+
+ auto tfl_transpose = graph->nodes()->create<locoex::TFLTranspose>();
+ {
+ // validation
+ {
+ assert(origin->input() != nullptr);
+
+ auto input_rank = loco::shape_get(origin->input()).as<loco::TensorShape>().rank();
+ if (input_rank != origin->perm()->size())
+ INTERNAL_EXN_V("perm size should be same with input rank",
+ oops::to_uint32(origin->perm()->size()));
+
+ validate_perm(origin);
+ }
+
+ tfl_transpose->a(origin->input());
+
+ // perm : set TFLConst
+ auto perm_const = graph->nodes()->create<locoex::TFLConst>();
+ {
+ perm_const->dtype(loco::DataType::S32);
+ perm_const->rank(1);
+ perm_const->dim(0) = origin->perm()->size();
+ perm_const->size<loco::DataType::S32>(origin->perm()->size());
+
+ // add perm values into perm TFLConst
+ for (loco::TensorAxis x = 0; x < origin->perm()->size(); x++)
+ {
+ perm_const->at<loco::DataType::S32>(x) = origin->perm()->axis(x);
+ }
+ }
+ tfl_transpose->perm(perm_const);
+ }
+
+ // replace canonical node
+ loco::replace(origin).with(tfl_transpose);
+ origin->input(nullptr);
+
+ return true;
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Conversion/TensorTransposeConverter.h b/compiler/exo/src/Conversion/TensorTransposeConverter.h
new file mode 100644
index 000000000..9b61ff38d
--- /dev/null
+++ b/compiler/exo/src/Conversion/TensorTransposeConverter.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONVERSION_TENSORTRANSPOSE_CONVERTER__
+#define __CONVERSION_TENSORTRANSPOSE_CONVERTER__
+
+#include "CanonicalNodeConverter.h"
+
+#include <loco.h>
+
+namespace exo
+{
+
+/**
+ * @brief Convert loco::TensorTranspose to locoex::TFLTranspose
+ */
+class TensorTransposeConverter : public CanonicalNodeConverter<loco::TensorTranspose>
+{
+public:
+ const char *name(void) const final { return "exo::TensorTransposeConverter"; }
+
+public:
+ bool convert(loco::TensorTranspose *origin) final;
+};
+
+} // namespace exo
+
+#endif // __CONVERSION_TENSORTRANSPOSE_CONVERTER__
diff --git a/compiler/exo/src/Conversion/TransposedConv2DConverter.cpp b/compiler/exo/src/Conversion/TransposedConv2DConverter.cpp
new file mode 100644
index 000000000..c03b64f48
--- /dev/null
+++ b/compiler/exo/src/Conversion/TransposedConv2DConverter.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TransposedConv2DConverter.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+#include "GraphBlock.h"
+
+#include <loco.h>
+#include <loco/Service/ShapeInference.h>
+
+namespace exo
+{
+
+bool TransposedConv2DConverter::convert(loco::TransposedConv2D *origin)
+{
+ // Shape is required to set origin->inputSizes()
+ if (not loco::shape_known(origin))
+ return false;
+
+ if ((origin->ifm() == nullptr) or (origin->ker() == nullptr))
+ return false;
+
+ auto *graph = origin->graph();
+
+ auto tfl_tr_conv = graph->nodes()->create<locoex::TFLTransposeConv>();
+ {
+ tfl_tr_conv->stride()->w(origin->stride()->horizontal());
+ tfl_tr_conv->stride()->h(origin->stride()->vertical());
+
+ auto pad = origin->pad();
+ if (pad->bottom() == 0 && pad->top() == 0 && pad->left() == 0 && pad->right() == 0)
+ tfl_tr_conv->padding(locoex::Padding::VALID);
+ else
+ // TODO This is necessary, but not sufficient condition. More rigorous check required
+ tfl_tr_conv->padding(locoex::Padding::SAME);
+ }
+
+ // let's create a new graph connection with tfl_tr_conv
+ {
+ // Make inputSizes from shape of origin
+ auto input_sizes_const = graph->nodes()->create<locoex::TFLConst>();
+ auto origin_shape = loco::shape_get(origin).as<loco::FeatureShape>();
+
+ const loco::DataType S32 = loco::DataType::S32;
+
+ input_sizes_const->dtype(S32);
+ input_sizes_const->rank(1);
+ input_sizes_const->dim(0) = 4;
+ input_sizes_const->size<S32>(4);
+ // Note that NHWC is layout for inputSizes determined by tflite format
+ input_sizes_const->at<S32>(0) = origin_shape.count().value(); // N
+ input_sizes_const->at<S32>(1) = origin_shape.height().value(); // H
+ input_sizes_const->at<S32>(2) = origin_shape.width().value(); // W
+ input_sizes_const->at<S32>(3) = origin_shape.depth().value(); // C
+
+ tfl_tr_conv->inputSizes(input_sizes_const);
+
+ // filter
+ auto filter_dec = make_filter_decode<FilterLayout::OHWI>(origin->ker());
+ tfl_tr_conv->filter(filter_dec);
+
+ // outBackprop
+ auto feature_dec = make_feature_decode<FeatureLayout::NHWC>(origin->ifm());
+ tfl_tr_conv->outBackprop(feature_dec);
+
+ // output
+ auto feature_enc = make_feature_encode<FeatureLayout::NHWC>(tfl_tr_conv);
+
+ // replace canonical node
+ loco::replace(origin).with(feature_enc);
+ origin->ifm(nullptr);
+ }
+
+ return true;
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Conversion/TransposedConv2DConverter.h b/compiler/exo/src/Conversion/TransposedConv2DConverter.h
new file mode 100644
index 000000000..f51e0a5bc
--- /dev/null
+++ b/compiler/exo/src/Conversion/TransposedConv2DConverter.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONVERSION_TRANSPOSEDCONV2D_CONVERTER__
+#define __CONVERSION_TRANSPOSEDCONV2D_CONVERTER__
+
+#include "CanonicalNodeConverter.h"
+
+#include <loco.h>
+
+namespace exo
+{
+
+/**
+ * @brief Convert loco::TransposedConv2D to locoex::TFLTransposeConv and auxiliary
+ *
+ *
+ * <BEFORE>
+ *
+ * IFM ------- TransposedConv2D --- OFM
+ * (Feature) / (Feature)
+ * /
+ * KER ------
+ * (Filter)
+ *
+ *
+ * <AFTER>
+ *
+ * out_backprop : IFM ------- FeatureDecode --- TFLTransposeConv --- FeatureEncode --- OFM
+ * [Feature] [Tensor] / / [Tensor] [Feature]
+ * / /
+ * filter: KER ------- FilterDecode --- /
+ * [Filter] [Tensor] /
+ * /
+ * input_sizes : TFLConst (new) ------------
+ * [Tensor]
+ */
+class TransposedConv2DConverter : public CanonicalNodeConverter<loco::TransposedConv2D>
+{
+public:
+ const char *name(void) const final { return "exo::TransposedConv2DConverter"; }
+
+public:
+ bool convert(loco::TransposedConv2D *origin) final;
+};
+
+} // namespace exo
+
+#endif // __CONVERSION_TRANSPOSEDCONV2D_CONVERTER__
diff --git a/compiler/exo/src/Conversions.h b/compiler/exo/src/Conversions.h
new file mode 100644
index 000000000..8eb4ed2e4
--- /dev/null
+++ b/compiler/exo/src/Conversions.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONVERSIONS_H__
+#define __CONVERSIONS_H__
+
+#include "Conversion/AvgPool2DConverter.h"
+#include "Conversion/ConstGenConverter.h"
+#include "Conversion/Conv2DConverter.h"
+#include "Conversion/DepthwiseConv2DConverter.h"
+// TODO loco::DepthwiseFilterEncode
+#include "Conversion/EltwiseAddConverter.h"
+#include "Conversion/EltwiseDivConverter.h"
+#include "Conversion/EltwiseMaxConverter.h"
+#include "Conversion/EltwiseMulConverter.h"
+#include "Conversion/EltwiseSqrtConverter.h"
+#include "Conversion/EltwiseSubConverter.h"
+#include "Conversion/FeatureBiasAddConverter.h"
+// TODO loco::FixedReshape
+#include "Conversion/MatMulConverter.h"
+#include "Conversion/MaxPool2DConverter.h"
+#include "Conversion/ReluConverter.h"
+#include "Conversion/Relu6Converter.h"
+// TODO loco::Tanh
+#include "Conversion/TensorConcatConverter.h"
+// TODO loco::TensorBiasAdd
+#include "Conversion/TensorBroadcastConverter.h"
+#include "Conversion/TensorReduceConverter.h"
+// TODO loco::TensorSoftmax
+#include "Conversion/TensorTransposeConverter.h"
+#include "Conversion/TransposedConv2DConverter.h"
+
+#endif // __CONVERSIONS_H__
diff --git a/compiler/exo/src/Convert.cpp b/compiler/exo/src/Convert.cpp
new file mode 100644
index 000000000..45f0481f4
--- /dev/null
+++ b/compiler/exo/src/Convert.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Convert.h"
+
+#include "Conversions.h"
+#include "Pass/ShapeInferencePass.h"
+#include "Pass/TypeInferencePass.h"
+#include "ProgressReporter.h"
+#include "Knob.h"
+
+#include <loco.h>
+#include <loco/Service/ShapeInference.h>
+#include <loco/Service/CanonicalShapeInferenceRule.h>
+#include <loco/Service/TypeInference.h>
+
+#include <logo/SimplifyDomainConversionPass.h>
+#include <logo/RemoveDeadNodePass.h>
+#include <logo/RemoveForwardNodePass.h>
+
+#include <logo/Phase.h>
+#include <stdex/Memory.h>
+
+namespace exo
+{
+
+void convert_to_TFLNodes(loco::Graph *graph)
+{
+ // run Shape and Type inference must be run before conversion
+ loco::CanonicalShapeInferenceRule shape_rule;
+ loco::apply(&shape_rule).to(graph);
+
+ loco::CanonicalTypeInferenceRule type_rule;
+ loco::apply(&type_rule).to(graph);
+
+ logo::Phase phase;
+ {
+ // prepare type and shape before conversion
+ phase.emplace_back(stdex::make_unique<TypeInferencePass>());
+ phase.emplace_back(stdex::make_unique<ShapeInferencePass>());
+
+ // Add converters for canonical nodes. Note: Not all loco canonical nodes are listed.
+ phase.emplace_back(stdex::make_unique<AvgPool2DConverter>());
+ phase.emplace_back(stdex::make_unique<ConstGenConverter>());
+ phase.emplace_back(stdex::make_unique<Conv2DConverter>());
+ phase.emplace_back(stdex::make_unique<DepthwiseConv2DConverter>());
+ // TODO loco::DepthwiseFilterEncode
+ phase.emplace_back(stdex::make_unique<EltwiseAddConverter>());
+ phase.emplace_back(stdex::make_unique<EltwiseDivConverter>());
+ phase.emplace_back(stdex::make_unique<EltwiseMaxConverter>());
+ phase.emplace_back(stdex::make_unique<EltwiseMulConverter>());
+ phase.emplace_back(stdex::make_unique<EltwiseSqrtConverter>());
+ phase.emplace_back(stdex::make_unique<EltwiseSubConverter>());
+ phase.emplace_back(stdex::make_unique<FeatureBiasAddConverter>());
+ // TODO loco::FixedReshape
+ phase.emplace_back(stdex::make_unique<MatMulConverter>());
+ phase.emplace_back(stdex::make_unique<MaxPool2DConverter>());
+ phase.emplace_back(stdex::make_unique<ReluConverter>());
+ phase.emplace_back(stdex::make_unique<Relu6Converter>());
+ // TODO loco::Tanh
+ phase.emplace_back(stdex::make_unique<TensorConcatConverter>());
+ // TODO loco::TensorBiasAdd
+ phase.emplace_back(stdex::make_unique<TensorBroadcastConverter>());
+ phase.emplace_back(stdex::make_unique<TensorReduceConverter>());
+ // TODO loco::TensorSoftmax
+ phase.emplace_back(stdex::make_unique<TensorTransposeConverter>());
+ phase.emplace_back(stdex::make_unique<TransposedConv2DConverter>());
+
+ // Add optimization below
+ phase.emplace_back(stdex::make_unique<logo::SimplifyDomainConversionPass>());
+ phase.emplace_back(stdex::make_unique<logo::RemoveForwardNodePass>());
+ phase.emplace_back(stdex::make_unique<logo::RemoveDeadNodePass>());
+ }
+
+ logo::PhaseRunner<logo::PhaseStrategy::Restart> phase_runner{graph};
+
+ ProgressReporter prog(graph, logo::PhaseStrategy::Restart);
+ phase_runner.attach(&prog);
+ phase_runner.run(phase);
+
+ // TODO Assert if all canonical nodes are converted to TFL node
+}
+
+} // namespace exo
diff --git a/compiler/exo-tflite/src/Convert.h b/compiler/exo/src/Convert.h
index 7038f9cf7..7038f9cf7 100644
--- a/compiler/exo-tflite/src/Convert.h
+++ b/compiler/exo/src/Convert.h
diff --git a/compiler/exo/src/Dialect/IR/CircleDialect.cpp b/compiler/exo/src/Dialect/IR/CircleDialect.cpp
new file mode 100644
index 000000000..ecd43b0a3
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/CircleDialect.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CircleDialect.h"
+
+namespace locoex
+{
+
+loco::Dialect *CircleDialect::get(void)
+{
+ static CircleDialect d;
+ return &d;
+}
+
+} // namespace locoex
diff --git a/compiler/exo/src/Dialect/IR/CircleDialect.h b/compiler/exo/src/Dialect/IR/CircleDialect.h
new file mode 100644
index 000000000..9857d9e6d
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/CircleDialect.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOCOEX_IR_CIRCLEDIALECT_H__
+#define __LOCOEX_IR_CIRCLEDIALECT_H__
+
+#include <loco/IR/Dialect.h>
+
+namespace locoex
+{
+
+class CircleDialect final : public loco::Dialect
+{
+private:
+ CircleDialect() = default;
+
+public:
+ CircleDialect(const CircleDialect &) = delete;
+ CircleDialect(CircleDialect &&) = delete;
+
+public:
+ static loco::Dialect *get(void);
+};
+
+} // namespace locoex
+
+#endif // __LOCOEX_IR_CIRCLEDIALECT_H__
diff --git a/compiler/exo/src/Dialect/IR/CircleDialect.test.cpp b/compiler/exo/src/Dialect/IR/CircleDialect.test.cpp
new file mode 100644
index 000000000..6c85b67a1
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/CircleDialect.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleDialectTest, get)
+{
+ using locoex::CircleDialect;
+
+ auto d = CircleDialect::get();
+
+ // get() SHOULD return a valid(non-null) pointer
+ ASSERT_NE(d, nullptr);
+ // The return value SHOULD be stable across multiple invocations
+ ASSERT_EQ(CircleDialect::get(), d);
+}
diff --git a/compiler/exo/src/Dialect/IR/CircleNode.cpp b/compiler/exo/src/Dialect/IR/CircleNode.cpp
new file mode 100644
index 000000000..cdcd434ea
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/CircleNode.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CircleNode.h"
+
+#include "CircleDialect.h"
+
+namespace locoex
+{
+
+const loco::Dialect *CircleNode::dialect(void) const { return CircleDialect::get(); }
+
+} // namespace locoex
diff --git a/compiler/exo/src/Dialect/IR/CircleNode.h b/compiler/exo/src/Dialect/IR/CircleNode.h
new file mode 100644
index 000000000..1ae9d38bd
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/CircleNode.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOCOEX_IR_CIRCLENODE_H__
+#define __LOCOEX_IR_CIRCLENODE_H__
+
+#include "CircleNodeDecl.h"
+#include "CircleNodeImpl.h"
+
+#endif // __LOCOEX_IR_CIRCLENODE_H__
diff --git a/compiler/exo/src/Dialect/IR/CircleNodeDecl.h b/compiler/exo/src/Dialect/IR/CircleNodeDecl.h
new file mode 100644
index 000000000..358b1f0ce
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/CircleNodeDecl.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOCOEX_IR_CIRCLENODEDECL_H__
+#define __LOCOEX_IR_CIRCLENODEDECL_H__
+
+#include <loco/IR/Node.h>
+#include <loco/IR/Dialect.h>
+
+#include "CircleOpcode.h"
+#include "CircleNodeVisitor.forward.h"
+
+namespace locoex
+{
+
+struct CircleNode : public loco::Node
+{
+ virtual ~CircleNode() = default;
+
+ const loco::Dialect *dialect(void) const final;
+ virtual CircleOpcode opcode(void) const = 0;
+
+ template <typename T> T accept(CircleNodeVisitorBase<T> *) const;
+ template <typename T> T accept(CircleNodeMutableVisitorBase<T> *);
+};
+
+template <CircleOpcode Code> struct CircleNodeImpl : public CircleNode
+{
+ virtual ~CircleNodeImpl() = default;
+
+ uint32_t opnum(void) const final { return static_cast<uint32_t>(Code); }
+ CircleOpcode opcode(void) const final { return Code; }
+};
+
+} // namespace locoex
+
+#endif // __LOCOEX_IR_CIRCLENODEDECL_H__
diff --git a/compiler/exo/src/Dialect/IR/CircleNodeImpl.h b/compiler/exo/src/Dialect/IR/CircleNodeImpl.h
new file mode 100644
index 000000000..8c8b99b4f
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/CircleNodeImpl.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOCOEX_IR_CIRCLENODEIMPL_H__
+#define __LOCOEX_IR_CIRCLENODEIMPL_H__
+
+#include "CircleNodes.h"
+
+#include <oops/InternalExn.h>
+
+#include <cassert>
+
+namespace locoex
+{
+
+template <typename T> T CircleNode::accept(CircleNodeVisitorBase<T> *v) const
+{
+ switch (this->opcode())
+ {
+#define CIRCLE_NODE(OPCODE, CLASS) \
+ \
+ case CircleOpcode::OPCODE: \
+ return v->visit(dynamic_cast<const CLASS *>(this));
+
+#include "CircleNodes.lst"
+#undef CIRCLE_NODE
+
+ default:
+ break;
+ }
+
+ INTERNAL_EXN("CircleNode::accept(CircleNodeVisitorBase) not handled");
+}
+
+template <typename T> T CircleNode::accept(CircleNodeMutableVisitorBase<T> *v)
+{
+ switch (this->opcode())
+ {
+#define CIRCLE_NODE(OPCODE, CLASS) \
+ \
+ case CircleOpcode::OPCODE: \
+ return v->visit(dynamic_cast<CLASS *>(this));
+
+#include "CircleNodes.lst"
+#undef CIRCLE_NODE
+
+ default:
+ break;
+ }
+
+ INTERNAL_EXN("CircleNode::accept(CircleNodeMutableVisitorBase) not handled");
+}
+
+} // namespace locoex
+
+#endif // __LOCOEX_IR_CIRCLENODEIMPL_H__
diff --git a/compiler/exo/src/Dialect/IR/CircleNodeVisitor.forward.h b/compiler/exo/src/Dialect/IR/CircleNodeVisitor.forward.h
new file mode 100644
index 000000000..8ae28abf3
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/CircleNodeVisitor.forward.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOCOEX_IR_CIRCLENODE_VISITOR_FORWARD_H__
+#define __LOCOEX_IR_CIRCLENODE_VISITOR_FORWARD_H__
+
+namespace locoex
+{
+
+// NOTE These forward declarations SHOULD BE aligned with Node delcarations in
+// "CircleNodeVisitor.h"
+template <typename T> struct CircleNodeVisitorBase;
+template <typename T> struct CircleNodeMutableVisitorBase;
+
+} // namespace locoex
+
+#endif // __LOCOEX_IR_CIRCLENODE_VISITOR_FORWARD_H__
diff --git a/compiler/exo/src/Dialect/IR/CircleNodeVisitor.h b/compiler/exo/src/Dialect/IR/CircleNodeVisitor.h
new file mode 100644
index 000000000..f5c28a184
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/CircleNodeVisitor.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOCOEX_IR_CIRCLENODE_VISITOR_H__
+#define __LOCOEX_IR_CIRCLENODE_VISITOR_H__
+
+#include "CircleNode.h"
+
+#include <oops/InternalExn.h>
+
+namespace locoex
+{
+
+/**
+ * DO NOT use this class. Use CircleNodeVisitor instead.
+ */
+template <typename T> struct CircleNodeVisitorBase
+{
+ virtual ~CircleNodeVisitorBase() = default;
+
+#define CIRCLE_NODE(OPCODE, Circle_CLASS) virtual T visit(const Circle_CLASS *) = 0;
+
+#include "CircleNodes.lst"
+#undef CIRCLE_NODE
+};
+
+template <typename T> struct CircleNodeVisitor : public CircleNodeVisitorBase<T>
+{
+ virtual ~CircleNodeVisitor() = default;
+
+#define CIRCLE_NODE(OPCODE, Circle_CLASS) \
+ \
+ virtual T visit(const Circle_CLASS *node) { return visit(static_cast<const CircleNode *>(node)); }
+
+#include "CircleNodes.lst"
+#undef CIRCLE_NODE
+
+ /// @brief Default fallback
+ virtual T visit(const CircleNode *) { INTERNAL_EXN("CircleNodeVisistor: NYI node"); }
+};
+
+/**
+ * DO NOT use this class. Use CircleNodeMutableVisitor instead.
+ */
+template <typename T> struct CircleNodeMutableVisitorBase
+{
+ virtual ~CircleNodeMutableVisitorBase() = default;
+
+#define CIRCLE_NODE(OPCODE, Circle_CLASS) virtual T visit(Circle_CLASS *) = 0;
+
+#include "CircleNodes.lst"
+#undef CIRCLE_NODE
+};
+
+template <typename T> struct CircleNodeMutableVisitor : public CircleNodeMutableVisitorBase<T>
+{
+ virtual ~CircleNodeMutableVisitor() = default;
+
+#define CIRCLE_NODE(OPCODE, Circle_CLASS) \
+ \
+ virtual T visit(Circle_CLASS *node) { return visit(static_cast<CircleNode *>(node)); }
+
+#include "CircleNodes.lst"
+#undef CIRCLE_NODE
+
+ /// @brief Default fallback
+ virtual T visit(CircleNode *) { INTERNAL_EXN("CircleMutableNodeVisistor: NYI node"); }
+};
+
+} // namespace locoex
+
+#endif // __LOCOEX_IR_CIRCLENODE_VISITOR_H__
diff --git a/compiler/exo/src/Dialect/IR/CircleNodes.cpp b/compiler/exo/src/Dialect/IR/CircleNodes.cpp
new file mode 100644
index 000000000..bba59ff4d
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/CircleNodes.cpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This is to validate CircleNodes.h
+#include "CircleNodes.h"
diff --git a/compiler/exo/src/Dialect/IR/CircleNodes.h b/compiler/exo/src/Dialect/IR/CircleNodes.h
new file mode 100644
index 000000000..7be093103
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/CircleNodes.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOCOEX_IR_CIRCLENODES_H__
+#define __LOCOEX_IR_CIRCLENODES_H__
+
+#include "CircleNodeDecl.h"
+#include "CircleOpcode.h"
+
+#include "FusedActFunc.h"
+#include "NodeMixins.h" // FixedArityNode
+
+#include <loco/IR/Node.h>
+
+namespace locoex
+{
+
+/// @brief enumeration of mixin class
+enum class CircleNodeTrait
+{
+ FusedActFunc,
+};
+
+template <CircleNodeTrait T> class CircleNodeMixin;
+
+template <> class CircleNodeMixin<CircleNodeTrait::FusedActFunc>
+{
+public:
+ CircleNodeMixin() = default;
+
+public:
+ FusedActFunc fusedActivationFunction() const { return _fused_act_fun; }
+ void fusedActivationFunction(FusedActFunc fused_act_fun) { _fused_act_fun = fused_act_fun; }
+
+private:
+ FusedActFunc _fused_act_fun = FusedActFunc::UNDEFINED;
+};
+
+/**
+ * @brief INSTANCE_NORM in circle
+ */
+class CircleInstanceNorm final
+ : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::INSTANCE_NORM>>,
+ public CircleNodeMixin<CircleNodeTrait::FusedActFunc>
+{
+public:
+ /// @note Currently only support FLOAT32 as input node
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *gamma(void) const { return at(1)->node(); }
+ void gamma(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *beta(void) const { return at(2)->node(); }
+ void beta(loco::Node *node) { at(2)->node(node); }
+
+ float epsilon() const { return _epsilon; }
+ void epsilon(float epsilon) { _epsilon = epsilon; }
+
+private:
+ float _epsilon = 1e-05;
+};
+
+} // namespace locoex
+
+#endif // __LOCOEX_IR_CIRCLENODES_H__
diff --git a/compiler/exo/src/Dialect/IR/CircleNodes.lst b/compiler/exo/src/Dialect/IR/CircleNodes.lst
new file mode 100644
index 000000000..96baf2917
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/CircleNodes.lst
@@ -0,0 +1,8 @@
+#ifndef CIRCLE_NODE
+#error "Define CIRCLE_NODE"
+#endif // CIRCLE_NODE
+
+//
+// PLEASE SORT NODE DECLS IN ALPHABETICAL ORDER
+//
+CIRCLE_NODE(INSTANCE_NORM, locoex::CircleInstanceNorm)
diff --git a/compiler/exo/src/Dialect/IR/CircleNodes.test.cpp b/compiler/exo/src/Dialect/IR/CircleNodes.test.cpp
new file mode 100644
index 000000000..3011bdc0a
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/CircleNodes.test.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CircleNodes.h"
+
+#include "CircleDialect.h"
+#include "CircleOpcode.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleInstanceNormTest, constructor)
+{
+ locoex::CircleInstanceNorm instance_norm;
+
+ ASSERT_EQ(locoex::CircleDialect::get(), instance_norm.dialect());
+ ASSERT_EQ(locoex::CircleOpcode::INSTANCE_NORM, instance_norm.opcode());
+
+ ASSERT_EQ(nullptr, instance_norm.input());
+ ASSERT_EQ(nullptr, instance_norm.gamma());
+ ASSERT_EQ(nullptr, instance_norm.beta());
+ ASSERT_FLOAT_EQ(1e-05, instance_norm.epsilon());
+ ASSERT_EQ(locoex::FusedActFunc::UNDEFINED, instance_norm.fusedActivationFunction());
+}
diff --git a/compiler/exo/src/Dialect/IR/CircleOpcode.h b/compiler/exo/src/Dialect/IR/CircleOpcode.h
new file mode 100644
index 000000000..264304049
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/CircleOpcode.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOCOEX_IR_CIRCLEOPCODE_H__
+#define __LOCOEX_IR_CIRCLEOPCODE_H__
+
+namespace locoex
+{
+
+enum class CircleOpcode
+{
+#define CIRCLE_NODE(OPCODE, CLASS) OPCODE,
+#include "CircleNodes.lst"
+#undef CIRCLE_NODE
+};
+
+} // namespace locoex
+
+#endif // __LOCOEX_IR_CIRCLEOPCODE_H__
diff --git a/compiler/exo/src/Dialect/IR/FusedActFunc.h b/compiler/exo/src/Dialect/IR/FusedActFunc.h
new file mode 100644
index 000000000..b73a0799e
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/FusedActFunc.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __DIALECT_IR_FUSEDACTFUNC_H__
+#define __DIALECT_IR_FUSEDACTFUNC_H__
+
+namespace locoex
+{
+
+// TODO Divide into TFL version and Circle version when they go different approach
+enum class FusedActFunc
+{
+ UNDEFINED, // This is not defined by TFLite or Circle. This was added to
+ // prevent programming error.
+ NONE,
+ RELU,
+ RELU6
+};
+
+} // namespace locoex
+
+#endif // __DIALECT_IR_FUSEDACTFUNC_H__
diff --git a/compiler/exo/src/Dialect/IR/NodeMixins.cpp b/compiler/exo/src/Dialect/IR/NodeMixins.cpp
new file mode 100644
index 000000000..cdfe0d8d1
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/NodeMixins.cpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This is to validate NodeMixins.h
+#include "NodeMixins.h"
diff --git a/compiler/exo/src/Dialect/IR/NodeMixins.h b/compiler/exo/src/Dialect/IR/NodeMixins.h
new file mode 100644
index 000000000..9490ea1bc
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/NodeMixins.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __DIALECT_IR_NODEMIXINS_H__
+#define __DIALECT_IR_NODEMIXINS_H__
+
+#include <loco/IR/Node.h>
+
+namespace locoex
+{
+
+/**
+ * @brief Nodes with the fixed number of inputs
+ *
+ * TODO Deprecated this class, and use loco::FixedArity instead
+ */
+template <unsigned N, typename Base> class FixedArityNode : public Base
+{
+public:
+ FixedArityNode()
+ {
+ for (uint32_t n = 0; n < N; ++n)
+ {
+ _args[n] = std::unique_ptr<loco::Use>(new loco::Use{this});
+ }
+ }
+
+ virtual ~FixedArityNode() = default;
+
+public:
+ unsigned arity(void) const final { return N; }
+
+ loco::Node *arg(uint32_t n) const final { return _args.at(n)->node(); }
+
+ void drop(void) final
+ {
+ for (uint32_t n = 0; n < N; ++n)
+ {
+ _args.at(n)->node(nullptr);
+ }
+ }
+
+protected:
+ // This API allows inherited classes to access "_args" field.
+ loco::Use *at(unsigned n) const { return _args.at(n).get(); }
+
+private:
+ std::array<std::unique_ptr<loco::Use>, N> _args{};
+};
+
+} // namespace locoex
+
+#endif // __DIALECT_IR_NODEMIXINS_H__
diff --git a/compiler/exo-tflite/src/Dialect/IR/TFLDialect.cpp b/compiler/exo/src/Dialect/IR/TFLDialect.cpp
index 8cbf9a364..8cbf9a364 100644
--- a/compiler/exo-tflite/src/Dialect/IR/TFLDialect.cpp
+++ b/compiler/exo/src/Dialect/IR/TFLDialect.cpp
diff --git a/compiler/exo-tflite/src/Dialect/IR/TFLDialect.h b/compiler/exo/src/Dialect/IR/TFLDialect.h
index 96463a9f9..96463a9f9 100644
--- a/compiler/exo-tflite/src/Dialect/IR/TFLDialect.h
+++ b/compiler/exo/src/Dialect/IR/TFLDialect.h
diff --git a/compiler/exo/src/Dialect/IR/TFLDialect.test.cpp b/compiler/exo/src/Dialect/IR/TFLDialect.test.cpp
new file mode 100644
index 000000000..1267540af
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/TFLDialect.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TFLDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFLDialectTest, get)
+{
+ using locoex::TFLDialect;
+
+ auto d = TFLDialect::get();
+
+ // get() SHOULD return a valid(non-null) pointer
+ ASSERT_NE(d, nullptr);
+ // The return value SHOULD be stable across multiple invocations
+ ASSERT_EQ(TFLDialect::get(), d);
+}
diff --git a/compiler/exo-tflite/src/Dialect/IR/TFLNode.cpp b/compiler/exo/src/Dialect/IR/TFLNode.cpp
index 82d5f1eba..82d5f1eba 100644
--- a/compiler/exo-tflite/src/Dialect/IR/TFLNode.cpp
+++ b/compiler/exo/src/Dialect/IR/TFLNode.cpp
diff --git a/compiler/exo-tflite/src/Dialect/IR/TFLNode.h b/compiler/exo/src/Dialect/IR/TFLNode.h
index eff69b1a5..eff69b1a5 100644
--- a/compiler/exo-tflite/src/Dialect/IR/TFLNode.h
+++ b/compiler/exo/src/Dialect/IR/TFLNode.h
diff --git a/compiler/exo-tflite/src/Dialect/IR/TFLNodeDecl.h b/compiler/exo/src/Dialect/IR/TFLNodeDecl.h
index d13900ab3..d13900ab3 100644
--- a/compiler/exo-tflite/src/Dialect/IR/TFLNodeDecl.h
+++ b/compiler/exo/src/Dialect/IR/TFLNodeDecl.h
diff --git a/compiler/exo/src/Dialect/IR/TFLNodeImpl.h b/compiler/exo/src/Dialect/IR/TFLNodeImpl.h
new file mode 100644
index 000000000..2ec94a268
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/TFLNodeImpl.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOCOEX_IR_TFLNODEIMPL_H__
+#define __LOCOEX_IR_TFLNODEIMPL_H__
+
+#include "TFLNodes.h"
+
+#include <oops/InternalExn.h>
+
+#include <cassert>
+
+namespace locoex
+{
+
+template <typename T> T TFLNode::accept(TFLNodeVisitorBase<T> *v) const
+{
+ switch (this->opcode())
+ {
+#define TFL_NODE(OPCODE, CLASS) \
+ \
+ case TFLOpcode::OPCODE: \
+ return v->visit(dynamic_cast<const CLASS *>(this));
+
+#include "TFLNodes.lst"
+#undef TFL_NODE
+
+ default:
+ break;
+ }
+
+ INTERNAL_EXN("TFLNode::accept(TFLNodeVisitorBase) not handled");
+}
+
+template <typename T> T TFLNode::accept(TFLNodeMutableVisitorBase<T> *v)
+{
+ switch (this->opcode())
+ {
+#define TFL_NODE(OPCODE, CLASS) \
+ \
+ case TFLOpcode::OPCODE: \
+ return v->visit(dynamic_cast<CLASS *>(this));
+
+#include "TFLNodes.lst"
+#undef TFL_NODE
+
+ default:
+ break;
+ }
+
+ INTERNAL_EXN("TFLNode::accept(TFLNodeMutableVisitorBase) not handled");
+}
+
+} // namespace locoex
+
+#endif // __LOCOEX_IR_TFLNODEIMPL_H__
diff --git a/compiler/exo-tflite/src/Dialect/IR/TFLNodeVisitor.forward.h b/compiler/exo/src/Dialect/IR/TFLNodeVisitor.forward.h
index e98057bc3..e98057bc3 100644
--- a/compiler/exo-tflite/src/Dialect/IR/TFLNodeVisitor.forward.h
+++ b/compiler/exo/src/Dialect/IR/TFLNodeVisitor.forward.h
diff --git a/compiler/exo/src/Dialect/IR/TFLNodeVisitor.h b/compiler/exo/src/Dialect/IR/TFLNodeVisitor.h
new file mode 100644
index 000000000..147b67398
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/TFLNodeVisitor.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOCOEX_IR_TFLNODE_VISITOR_H__
+#define __LOCOEX_IR_TFLNODE_VISITOR_H__
+
+#include "TFLNode.h"
+
+#include <oops/InternalExn.h>
+
+namespace locoex
+{
+
+/**
+ * DO NOT use this class. Use TFLNodeVisitor instead.
+ */
+template <typename T> struct TFLNodeVisitorBase
+{
+ virtual ~TFLNodeVisitorBase() = default;
+
+#define TFL_NODE(OPCODE, TFL_CLASS) virtual T visit(const TFL_CLASS *) = 0;
+
+#include "TFLNodes.lst"
+#undef TFL_NODE
+};
+
+template <typename T> struct TFLNodeVisitor : public TFLNodeVisitorBase<T>
+{
+ virtual ~TFLNodeVisitor() = default;
+
+#define TFL_NODE(OPCODE, TFL_CLASS) \
+ \
+ virtual T visit(const TFL_CLASS *node) { return visit(static_cast<const TFLNode *>(node)); }
+
+#include "TFLNodes.lst"
+#undef TFL_NODE
+
+ /// @brief Default fallback
+ virtual T visit(const TFLNode *) { INTERNAL_EXN("TFLNodeVisitor: NYI node"); }
+};
+
+/**
+ * DO NOT use this class. Use TFLNodeMutableVisitor instead.
+ */
+template <typename T> struct TFLNodeMutableVisitorBase
+{
+ virtual ~TFLNodeMutableVisitorBase() = default;
+
+#define TFL_NODE(OPCODE, TFL_CLASS) virtual T visit(TFL_CLASS *) = 0;
+
+#include "TFLNodes.lst"
+#undef TFL_NODE
+};
+
+template <typename T> struct TFLNodeMutableVisitor : public TFLNodeMutableVisitorBase<T>
+{
+ virtual ~TFLNodeMutableVisitor() = default;
+
+#define TFL_NODE(OPCODE, TFL_CLASS) \
+ \
+ virtual T visit(TFL_CLASS *node) { return visit(static_cast<TFLNode *>(node)); }
+
+#include "TFLNodes.lst"
+#undef TFL_NODE
+
+ /// @brief Default fallback
+ virtual T visit(TFLNode *) { INTERNAL_EXN("TFLNodeMutableVisitor: NYI node"); }
+};
+
+} // namespace locoex
+
+#endif // __LOCOEX_IR_TFLNODE_VISITOR_H__
diff --git a/compiler/exo/src/Dialect/IR/TFLNodes.cpp b/compiler/exo/src/Dialect/IR/TFLNodes.cpp
new file mode 100644
index 000000000..f385ce0d9
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/TFLNodes.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TFLNodes.h"
+
+#include "Check.h"
+
+#include <loco.h>
+
+#include <cassert>
+
+namespace locoex
+{
+
+template <loco::DataType DT> uint32_t TFLConst::size(void) const
+{
+ assert(dtype() == DT);
+ assert(_data.size() % sizeof(typename loco::DataTypeImpl<DT>::Type) == 0);
+ return _data.size() / sizeof(typename loco::DataTypeImpl<DT>::Type);
+}
+
+template <loco::DataType DT> void TFLConst::size(uint32_t l)
+{
+ assert(dtype() == DT);
+ _data.resize(l * sizeof(typename loco::DataTypeImpl<DT>::Type));
+}
+
+template <loco::DataType DT>
+const typename loco::DataTypeImpl<DT>::Type &TFLConst::at(uint32_t n) const
+{
+ assert(dtype() == DT);
+ assert(n < size<DT>());
+ return *(reinterpret_cast<const typename loco::DataTypeImpl<DT>::Type *>(_data.data()) + n);
+}
+
+template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &TFLConst::at(uint32_t n)
+{
+ assert(dtype() == DT);
+ assert(n < size<DT>());
+ return *(reinterpret_cast<typename loco::DataTypeImpl<DT>::Type *>(_data.data()) + n);
+}
+
+#define INSTANTIATE(DT) \
+ template uint32_t TFLConst::size<DT>(void) const; \
+ template void TFLConst::size<DT>(uint32_t); \
+ template const typename loco::DataTypeImpl<DT>::Type &TFLConst::at<DT>(uint32_t) const; \
+ template typename loco::DataTypeImpl<DT>::Type &TFLConst::at<DT>(uint32_t);
+
+INSTANTIATE(loco::DataType::S32);
+INSTANTIATE(loco::DataType::FLOAT32);
+
+#undef INSTANTIATE
+
+void set_new_shape(locoex::TFLReshape *node, int32_t *base, uint32_t size)
+{
+ // Check node does not have both of new shape infos
+ EXO_ASSERT(node->shape() == nullptr, "node already has shape input");
+ EXO_ASSERT(node->newShape()->rank() == 0, "node already has newShape attribute");
+
+ const loco::DataType S32 = loco::DataType::S32;
+
+ // Set 2nd input as TFLConst
+ auto const_shape_node = node->graph()->nodes()->create<locoex::TFLConst>();
+ const_shape_node->rank(1);
+ const_shape_node->dim(0) = size;
+ const_shape_node->dtype(S32);
+ const_shape_node->size<S32>(size);
+ for (uint32_t axis = 0; axis < size; ++axis)
+ const_shape_node->at<S32>(axis) = base[axis];
+ node->shape(const_shape_node);
+
+ // Set newShape attribute
+ node->newShape()->rank(size);
+ for (uint32_t axis = 0; axis < size; ++axis)
+ node->newShape()->dim(axis) = base[axis];
+}
+
+} // namespace locoex
diff --git a/compiler/exo/src/Dialect/IR/TFLNodes.h b/compiler/exo/src/Dialect/IR/TFLNodes.h
new file mode 100644
index 000000000..41a11e7c0
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/TFLNodes.h
@@ -0,0 +1,551 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOCOEX_IR_TFLNODES_H__
+#define __LOCOEX_IR_TFLNODES_H__
+
+#include "TFLNodeDecl.h"
+#include "TFLOpcode.h"
+
+#include "FusedActFunc.h"
+#include "NodeMixins.h"
+
+#include <loco/IR/Node.h>
+#include <loco/IR/NodeMixins.h>
+#include <loco/IR/DataTypeTraits.h>
+
+#include <locoex/VariadicArityNode.h>
+
+#include <array>
+
+namespace locoex
+{
+
+enum class Padding
+{
+ UNDEFINED, // This is not defined by TFLite. This was added to prevent programming error.
+ SAME,
+ VALID,
+};
+
+class Filter final
+{
+public:
+ Filter() : _w(1), _h(1) {}
+
+ int32_t w() const { return _w; }
+ void w(int32_t w) { _w = w; }
+
+ int32_t h() const { return _h; }
+ void h(int32_t h) { _h = h; }
+
+private:
+ int32_t _w;
+ int32_t _h;
+};
+
+class Stride final
+{
+public:
+ Stride() : _w(1), _h(1) {}
+
+ int32_t w() const { return _w; }
+ void w(int32_t w) { _w = w; }
+
+ int32_t h() const { return _h; }
+ void h(int32_t h) { _h = h; }
+
+private:
+ int32_t _w;
+ int32_t _h;
+};
+
+/// @brief enumeration of mixin class
+enum class TFLNodeTrait
+{
+ FusedActFunc,
+ Bias
+};
+
+template <TFLNodeTrait T> class TFLNodeMixin;
+
+template <> class TFLNodeMixin<TFLNodeTrait::FusedActFunc>
+{
+public:
+ TFLNodeMixin() = default;
+
+public:
+ FusedActFunc fusedActivationFunction() const { return _fused_act_fun; }
+ void fusedActivationFunction(FusedActFunc fused_act_fun) { _fused_act_fun = fused_act_fun; }
+
+private:
+ FusedActFunc _fused_act_fun = FusedActFunc::UNDEFINED;
+};
+
+/**
+ * @brief Mixin class for nodes that has a bias input
+ */
+template <> class TFLNodeMixin<TFLNodeTrait::Bias>
+{
+public:
+ TFLNodeMixin() = default;
+
+public:
+ virtual loco::Node *bias(void) const = 0; /// @brief get the input for bias.
+ virtual void bias(loco::Node *node) = 0; /// @brief set the input for bias.
+};
+
+/**
+ * @brief ADD in TensorFlow Lite
+ */
+class TFLAdd final : public FixedArityNode<2, TFLNodeImpl<TFLOpcode::ADD>>,
+ public TFLNodeMixin<TFLNodeTrait::FusedActFunc>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+/**
+ * @brief AVERAGE_POOL_2D in TensorFlow Lite
+ */
+class TFLAveragePool2D final : public FixedArityNode<1, TFLNodeImpl<TFLOpcode::AVERAGE_POOL_2D>>,
+ public TFLNodeMixin<TFLNodeTrait::FusedActFunc>
+{
+public:
+ TFLAveragePool2D() : _padding(Padding::UNDEFINED) { /* empty */}
+
+public:
+ loco::Node *value(void) const { return at(0)->node(); }
+ void value(loco::Node *node) { at(0)->node(node); }
+
+ Padding padding() const { return _padding; }
+ void padding(Padding padding) { _padding = padding; }
+
+ const Filter *filter(void) const { return &_filter; }
+ Filter *filter(void) { return &_filter; }
+
+ const Stride *stride(void) const { return &_stride; }
+ Stride *stride(void) { return &_stride; }
+
+private:
+ Padding _padding;
+ Stride _stride;
+ Filter _filter;
+};
+
+/**
+ * @brief CONCATENATION in TensorFlow Lite
+ */
+class TFLConcatenation final : public VariadicArityNode<TFLNodeImpl<TFLOpcode::CONCATENATION>>,
+ public TFLNodeMixin<TFLNodeTrait::FusedActFunc>
+{
+public:
+ TFLConcatenation(uint32_t arity) : VariadicArityNode<TFLNodeImpl<TFLOpcode::CONCATENATION>>(arity)
+ {
+ // TODO Support when arity is 0
+ assert(arity >= 1);
+ }
+
+public:
+ uint32_t numValues(void) const { return arity(); }
+
+public:
+ Node *values(uint32_t index) const
+ {
+ assert(index < numValues());
+ return at(index)->node();
+ }
+ void values(uint32_t index, Node *node)
+ {
+ assert(index < numValues());
+ at(index)->node(node);
+ }
+
+public:
+ uint32_t axis(void) const { return _axis; }
+ void axis(uint32_t axis) { _axis = axis; }
+
+private:
+ uint32_t _axis{0};
+};
+
+/**
+ * @brief Class to build tensor data
+ * @note This will not be exported as a specific op
+ */
+class TFLConst final : public FixedArityNode<0, TFLNodeImpl<TFLOpcode::CONST>>,
+ public loco::NodeMixin<loco::NodeTrait::DataType>,
+ public loco::NodeMixin<loco::NodeTrait::TensorShape>
+{
+public:
+ TFLConst() = default;
+
+public:
+ template <loco::DataType DT> uint32_t size(void) const;
+ template <loco::DataType DT> void size(uint32_t size);
+ template <loco::DataType DT> const typename loco::DataTypeImpl<DT>::Type &at(uint32_t n) const;
+ template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &at(uint32_t n);
+
+private:
+ std::vector<uint8_t> _data;
+};
+
+/**
+ * @brief CONV_2D in TensorFlow Lite
+ */
+class TFLConv2D final : public FixedArityNode<3, TFLNodeImpl<TFLOpcode::CONV_2D>>,
+ public TFLNodeMixin<TFLNodeTrait::FusedActFunc>,
+ public TFLNodeMixin<TFLNodeTrait::Bias>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *filter(void) const { return at(1)->node(); }
+ void filter(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *bias(void) const override { return at(2)->node(); }
+ void bias(loco::Node *node) override { at(2)->node(node); }
+
+public:
+ Padding padding() const { return _padding; }
+ void padding(Padding padding) { _padding = padding; }
+
+ const Stride *stride(void) const { return &_stride; }
+ Stride *stride(void) { return &_stride; }
+
+private:
+ Padding _padding = Padding::UNDEFINED;
+ Stride _stride;
+};
+
+/**
+ * @brief DEPTHWISE_CONV_2D in TensorFlow Lite
+ */
+class TFLDepthwiseConv2D final
+ : public FixedArityNode<3, TFLNodeImpl<TFLOpcode::DEPTHWISE_CONV_2D>>,
+ public TFLNodeMixin<TFLNodeTrait::FusedActFunc>,
+ public TFLNodeMixin<TFLNodeTrait::Bias>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *filter(void) const { return at(1)->node(); }
+ void filter(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *bias(void) const override { return at(2)->node(); }
+ void bias(loco::Node *node) override { at(2)->node(node); }
+
+public:
+ Padding padding() const { return _padding; }
+ void padding(Padding padding) { _padding = padding; }
+
+ const Stride *stride(void) const { return &_stride; }
+ Stride *stride(void) { return &_stride; }
+
+ int32_t depthMultiplier(void) const { return _depth_multiplier; }
+ void depthMultiplier(int32_t arg) { _depth_multiplier = arg; }
+
+private:
+ Padding _padding = Padding::UNDEFINED;
+ Stride _stride;
+ int32_t _depth_multiplier = 0;
+};
+
+/**
+ * @brief DIV in TensorFlow Lite
+ */
+class TFLDiv final : public FixedArityNode<2, TFLNodeImpl<TFLOpcode::DIV>>,
+ public TFLNodeMixin<TFLNodeTrait::FusedActFunc>
+{
+public:
+ TFLDiv() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+/**
+ * @brief FULLY_CONNECTED in TensorFlow Lite
+ */
+class TFLFullyConnected final : public FixedArityNode<3, TFLNodeImpl<TFLOpcode::FULLY_CONNECTED>>,
+ public TFLNodeMixin<TFLNodeTrait::FusedActFunc>,
+ public TFLNodeMixin<TFLNodeTrait::Bias>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *weights(void) const { return at(1)->node(); }
+ void weights(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *bias(void) const override { return at(2)->node(); }
+ void bias(loco::Node *node) override { at(2)->node(node); }
+};
+
+/**
+ * @brief MAXIMUM in TensorFlow Lite
+ */
+class TFLMaximum final : public FixedArityNode<2, TFLNodeImpl<TFLOpcode::MAXIMUM>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+/**
+ * @brief MAX_POOL_2D in TensorFlow Lite
+ */
+class TFLMaxPool2D final : public FixedArityNode<1, TFLNodeImpl<TFLOpcode::MAX_POOL_2D>>,
+ public TFLNodeMixin<TFLNodeTrait::FusedActFunc>
+{
+public:
+ TFLMaxPool2D() : _padding(Padding::UNDEFINED) { /* empty */}
+
+public:
+ loco::Node *value(void) const { return at(0)->node(); }
+ void value(loco::Node *node) { at(0)->node(node); }
+
+ Padding padding() const { return _padding; }
+ void padding(Padding padding) { _padding = padding; }
+
+ const Filter *filter(void) const { return &_filter; }
+ Filter *filter(void) { return &_filter; }
+
+ const Stride *stride(void) const { return &_stride; }
+ Stride *stride(void) { return &_stride; }
+
+private:
+ Padding _padding;
+ Stride _stride;
+ Filter _filter;
+};
+
+class TFLMean final : public FixedArityNode<2, TFLNodeImpl<TFLOpcode::MEAN>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *reduction_indices(void) const { return at(1)->node(); }
+ void reduction_indices(loco::Node *node) { at(1)->node(node); }
+
+public:
+ bool keep_dims(void) const { return _keep_dims; }
+ void keep_dims(bool keep_dims) { _keep_dims = keep_dims; }
+
+private:
+ bool _keep_dims = false;
+};
+
+/**
+ * @brief MUL in TensorFlow Lite
+ */
+class TFLMul final : public FixedArityNode<2, TFLNodeImpl<TFLOpcode::MUL>>,
+ public TFLNodeMixin<TFLNodeTrait::FusedActFunc>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+class TFLRelu final : public FixedArityNode<1, TFLNodeImpl<TFLOpcode::RELU>>
+{
+public:
+ TFLRelu() = default;
+
+public:
+ loco::Node *features(void) const { return at(0)->node(); }
+ void features(loco::Node *node) { at(0)->node(node); }
+};
+
+class TFLRelu6 final : public FixedArityNode<1, TFLNodeImpl<TFLOpcode::RELU6>>
+{
+public:
+ TFLRelu6() = default;
+
+public:
+ loco::Node *features(void) const { return at(0)->node(); }
+ void features(loco::Node *node) { at(0)->node(node); }
+};
+
+class TFLReshape final : public FixedArityNode<2, TFLNodeImpl<TFLOpcode::RESHAPE>>
+{
+public:
+ TFLReshape() = default;
+
+public:
+ loco::Node *tensor(void) const { return at(0)->node(); }
+ void tensor(loco::Node *node) { at(0)->node(node); }
+
+ // TODO Make this input optional. That is, loco system does not emit error
+ // with this input being null
+ loco::Node *shape(void) const { return at(1)->node(); }
+ void shape(loco::Node *node) { at(1)->node(node); }
+
+public:
+ class Shape
+ {
+ public:
+ uint32_t rank(void) const { return _shape.size(); }
+ void rank(uint32_t rank) { _shape.resize(rank); }
+
+ int32_t dim(uint32_t n) const { return _shape.at(n); }
+ int32_t &dim(uint32_t n) { return _shape.at(n); }
+
+ private:
+ std::vector<int32_t> _shape;
+ };
+
+ const Shape *newShape(void) const { return &_new_shape; }
+ Shape *newShape(void) { return &_new_shape; }
+
+private:
+ Shape _new_shape;
+};
+
+/**
+ * @brief Set both TFLReshape's 2nd input as TFLConst, and newShape attribute
+ * with same value
+ * @note Shape inference for TFLReshape forces them to be same
+ * TODO find better place for this helper
+ */
+void set_new_shape(locoex::TFLReshape *node, int32_t *base, uint32_t size);
+
+class TFLRsqrt final : public FixedArityNode<1, TFLNodeImpl<TFLOpcode::RSQRT>>
+{
+public:
+ TFLRsqrt() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+// TODO TFLSoftmax
+
+class TFLSqrt final : public FixedArityNode<1, TFLNodeImpl<TFLOpcode::SQRT>>
+{
+public:
+ TFLSqrt() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+class TFLSquaredDifference final
+ : public FixedArityNode<2, TFLNodeImpl<TFLOpcode::SQUARED_DIFFERENCE>>
+{
+public:
+ TFLSquaredDifference() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+/**
+ * @brief SUB in TensorFlow Lite
+ */
+class TFLSub final : public FixedArityNode<2, TFLNodeImpl<TFLOpcode::SUB>>,
+ public TFLNodeMixin<TFLNodeTrait::FusedActFunc>
+{
+public:
+ TFLSub() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+// TODO TFLTanh
+
+/**
+ * @brief TRANSPOSE in TensorFlow Lite
+ */
+class TFLTranspose final : public FixedArityNode<2, TFLNodeImpl<TFLOpcode::TRANSPOSE>>
+{
+public:
+ TFLTranspose() = default;
+
+public:
+ /// @brief Get the input node to transpose
+ loco::Node *a(void) const { return at(0)->node(); }
+
+ /// @brief Set the input node to transpose
+ void a(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *perm(void) const { return at(1)->node(); }
+ void perm(loco::Node *node) { at(1)->node(node); }
+};
+
+/**
+ * @brief TRANSPOSE_CONV in TensorFlow Lite
+ *
+ * @note Argument node function names are from TensorFlow. So refering 'in' and
+ * 'out' acutally means 'out' and 'in' of the this node.
+ */
+class TFLTransposeConv final : public FixedArityNode<3, TFLNodeImpl<TFLOpcode::TRANSPOSE_CONV>>
+{
+public:
+ loco::Node *inputSizes(void) const { return at(0)->node(); }
+ void inputSizes(Node *node) { at(0)->node(node); }
+
+ loco::Node *filter(void) const { return at(1)->node(); }
+ void filter(Node *node) { at(1)->node(node); }
+
+ loco::Node *outBackprop(void) const { return at(2)->node(); }
+ void outBackprop(Node *node) { at(2)->node(node); }
+
+public:
+ const Padding &padding(void) const { return _padding; }
+ void padding(const Padding &padding) { _padding = padding; }
+
+ const Stride *stride(void) const { return &_stride; }
+ Stride *stride(void) { return &_stride; }
+
+private:
+ Padding _padding{Padding::UNDEFINED};
+ Stride _stride;
+};
+
+// TODO define more children of TFLNode
+
+} // namespace locoex
+
+#endif // __LOCOEX_IR_TFLNODES_H__
diff --git a/compiler/exo/src/Dialect/IR/TFLNodes.lst b/compiler/exo/src/Dialect/IR/TFLNodes.lst
new file mode 100644
index 000000000..225e2be3b
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/TFLNodes.lst
@@ -0,0 +1,30 @@
+#ifndef TFL_NODE
+#error "Define TFL_NODE"
+#endif // TFL_NODE
+
+//
+// PLEASE SORT NODE DECLS IN ALPHABETICAL ORDER
+//
+TFL_NODE(ADD, locoex::TFLAdd)
+TFL_NODE(AVERAGE_POOL_2D, locoex::TFLAveragePool2D)
+TFL_NODE(CONCATENATION, locoex::TFLConcatenation)
+TFL_NODE(CONST, locoex::TFLConst)
+TFL_NODE(CONV_2D, locoex::TFLConv2D)
+TFL_NODE(DEPTHWISE_CONV_2D, locoex::TFLDepthwiseConv2D)
+TFL_NODE(DIV, locoex::TFLDiv)
+TFL_NODE(FULLY_CONNECTED, locoex::TFLFullyConnected)
+TFL_NODE(MAXIMUM, locoex::TFLMaximum)
+TFL_NODE(MAX_POOL_2D, locoex::TFLMaxPool2D)
+TFL_NODE(MEAN, locoex::TFLMean)
+TFL_NODE(MUL, locoex::TFLMul)
+TFL_NODE(RELU, locoex::TFLRelu)
+TFL_NODE(RELU6, locoex::TFLRelu6)
+TFL_NODE(RESHAPE, locoex::TFLReshape)
+TFL_NODE(RSQRT, locoex::TFLRsqrt)
+// TODO TFLSoftmax
+TFL_NODE(SQRT, locoex::TFLSqrt)
+TFL_NODE(SQUARED_DIFFERENCE, locoex::TFLSquaredDifference)
+TFL_NODE(SUB, locoex::TFLSub)
+// TODO TFLTanh
+TFL_NODE(TRANSPOSE, locoex::TFLTranspose)
+TFL_NODE(TRANSPOSE_CONV, locoex::TFLTransposeConv)
diff --git a/compiler/exo/src/Dialect/IR/TFLNodes.test.cpp b/compiler/exo/src/Dialect/IR/TFLNodes.test.cpp
new file mode 100644
index 000000000..f17898b37
--- /dev/null
+++ b/compiler/exo/src/Dialect/IR/TFLNodes.test.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TFLNodes.h"
+
+#include "TFLDialect.h"
+#include "TFLOpcode.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFLAddTest, constructor)
+{
+ locoex::TFLAdd add_node;
+
+ ASSERT_EQ(locoex::TFLDialect::get(), add_node.dialect());
+ ASSERT_EQ(locoex::TFLOpcode::ADD, add_node.opcode());
+
+ ASSERT_EQ(nullptr, add_node.x());
+ ASSERT_EQ(nullptr, add_node.y());
+}
+
+// TODO TFLAveragePool2D
+
+TEST(TFLConcatTest, constructor)
+{
+ locoex::TFLConcatenation concat_node(3);
+
+ ASSERT_EQ(locoex::TFLDialect::get(), concat_node.dialect());
+ ASSERT_EQ(locoex::TFLOpcode::CONCATENATION, concat_node.opcode());
+
+ ASSERT_EQ(3, concat_node.numValues());
+ ASSERT_EQ(nullptr, concat_node.values(0));
+ ASSERT_EQ(nullptr, concat_node.values(1));
+ ASSERT_EQ(nullptr, concat_node.values(2));
+ ASSERT_EQ(locoex::FusedActFunc::UNDEFINED, concat_node.fusedActivationFunction());
+}
+
+// TODO TFLConv2D
+
+TEST(TFLDepthwiseConv2DTest, constructor)
+{
+ locoex::TFLDepthwiseConv2D dw_conv2d_node;
+
+ ASSERT_EQ(locoex::TFLDialect::get(), dw_conv2d_node.dialect());
+ ASSERT_EQ(locoex::TFLOpcode::DEPTHWISE_CONV_2D, dw_conv2d_node.opcode());
+
+ ASSERT_EQ(nullptr, dw_conv2d_node.input());
+ ASSERT_EQ(nullptr, dw_conv2d_node.filter());
+ ASSERT_EQ(nullptr, dw_conv2d_node.bias());
+ ASSERT_EQ(locoex::Padding::UNDEFINED, dw_conv2d_node.padding());
+ ASSERT_EQ(1, dw_conv2d_node.stride()->h());
+ ASSERT_EQ(1, dw_conv2d_node.stride()->w());
+ ASSERT_EQ(0, dw_conv2d_node.depthMultiplier());
+ ASSERT_EQ(locoex::FusedActFunc::UNDEFINED, dw_conv2d_node.fusedActivationFunction());
+}
+
+TEST(TFLDivTest, constructor)
+{
+ locoex::TFLDiv div_node;
+
+ ASSERT_EQ(locoex::TFLDialect::get(), div_node.dialect());
+ ASSERT_EQ(locoex::TFLOpcode::DIV, div_node.opcode());
+
+ ASSERT_EQ(nullptr, div_node.x());
+ ASSERT_EQ(nullptr, div_node.y());
+}
+
+// TODO TFLMaxPool2D
+
+TEST(TFLMulTest, constructor)
+{
+ locoex::TFLMul mul_node;
+
+ ASSERT_EQ(locoex::TFLDialect::get(), mul_node.dialect());
+ ASSERT_EQ(locoex::TFLOpcode::MUL, mul_node.opcode());
+
+ ASSERT_EQ(nullptr, mul_node.x());
+ ASSERT_EQ(nullptr, mul_node.y());
+}
+
+TEST(TFLReluTest, constructor)
+{
+ locoex::TFLRelu relu_node;
+
+ ASSERT_EQ(locoex::TFLDialect::get(), relu_node.dialect());
+ ASSERT_EQ(locoex::TFLOpcode::RELU, relu_node.opcode());
+
+ ASSERT_EQ(nullptr, relu_node.features());
+}
+
+// TODO TFLRelu6
+
+TEST(TFLReshapeTest, constructor)
+{
+ locoex::TFLReshape reshape;
+
+ ASSERT_EQ(locoex::TFLDialect::get(), reshape.dialect());
+ ASSERT_EQ(locoex::TFLOpcode::RESHAPE, reshape.opcode());
+
+ ASSERT_EQ(nullptr, reshape.tensor());
+ ASSERT_EQ(nullptr, reshape.shape());
+ ASSERT_EQ(0, reshape.newShape()->rank());
+}
+
+TEST(TFLReshapeTest, alloc_new_shape)
+{
+ locoex::TFLReshape reshape;
+
+ reshape.newShape()->rank(2);
+ ASSERT_EQ(2, reshape.newShape()->rank());
+
+ reshape.newShape()->dim(0) = 0;
+ reshape.newShape()->dim(1) = 1;
+
+ auto &const_reshape = const_cast<const locoex::TFLReshape &>(reshape);
+ ASSERT_EQ(0, const_reshape.newShape()->dim(0));
+ ASSERT_EQ(1, const_reshape.newShape()->dim(1));
+}
+
+// TODO TFLSoftmax
+
+// TODO TFLSqrt
+
+TEST(TFLSubTest, constructor)
+{
+ locoex::TFLSub sub_node;
+
+ ASSERT_EQ(locoex::TFLDialect::get(), sub_node.dialect());
+ ASSERT_EQ(locoex::TFLOpcode::SUB, sub_node.opcode());
+
+ ASSERT_EQ(nullptr, sub_node.x());
+ ASSERT_EQ(nullptr, sub_node.y());
+}
+
+// TODO TFLTanh
+
+TEST(TFLTransposeTest, constructor)
+{
+ locoex::TFLTranspose tr_node;
+
+ ASSERT_EQ(locoex::TFLDialect::get(), tr_node.dialect());
+ ASSERT_EQ(locoex::TFLOpcode::TRANSPOSE, tr_node.opcode());
+
+ ASSERT_EQ(nullptr, tr_node.a());
+ ASSERT_EQ(nullptr, tr_node.perm());
+}
diff --git a/compiler/exo-tflite/src/Dialect/IR/TFLOpcode.h b/compiler/exo/src/Dialect/IR/TFLOpcode.h
index 0c0ab64bd..0c0ab64bd 100644
--- a/compiler/exo-tflite/src/Dialect/IR/TFLOpcode.h
+++ b/compiler/exo/src/Dialect/IR/TFLOpcode.h
diff --git a/compiler/exo/src/Dialect/Service/CircleShapeInferenceRule.cpp b/compiler/exo/src/Dialect/Service/CircleShapeInferenceRule.cpp
new file mode 100644
index 000000000..2e71aa000
--- /dev/null
+++ b/compiler/exo/src/Dialect/Service/CircleShapeInferenceRule.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CircleShapeInferenceRule.h"
+
+#include "Dialect/IR/CircleNodes.h"
+#include "Dialect/IR/CircleDialect.h"
+#include "Dialect/IR/CircleNodeVisitor.h"
+
+#include "Check.h"
+
+#include <cassert>
+
+namespace
+{
+
+/**
+ * @brief Class to infer the shape of CircleNode
+ *
+ * @note All CircleNode's inputs and outputs are always loco::Domain::Tensor
+ */
+class ShapeInferenceAlgorithm final : public locoex::CircleNodeVisitor<loco::NodeShape>
+{
+public:
+ loco::NodeShape visit(const locoex::CircleInstanceNorm *node) final
+ {
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+
+ return loco::NodeShape{input_shape};
+ }
+};
+
+} // namespace
+
+namespace locoex
+{
+
+bool CircleShapeInferenceRule::recognize(const loco::Dialect *d) const
+{
+ return CircleDialect::get() == d;
+}
+
+bool CircleShapeInferenceRule::infer(const loco::Node *node, loco::NodeShape &shape) const
+{
+ assert(node->dialect() == CircleDialect::get());
+ assert(dynamic_cast<const CircleNode *>(node) != nullptr);
+
+ ShapeInferenceAlgorithm alg;
+ shape = dynamic_cast<const CircleNode *>(node)->accept(&alg);
+
+ return true;
+}
+
+} // namespace locoex
diff --git a/compiler/exo/src/Dialect/Service/CircleShapeInferenceRule.h b/compiler/exo/src/Dialect/Service/CircleShapeInferenceRule.h
new file mode 100644
index 000000000..92f23c9dd
--- /dev/null
+++ b/compiler/exo/src/Dialect/Service/CircleShapeInferenceRule.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOCOEX_SERVICE_CIRCLESHAPE_INFERENCE_RULE_H__
+#define __LOCOEX_SERVICE_CIRCLESHAPE_INFERENCE_RULE_H__
+
+#include <loco/Service/ShapeInference.h>
+
+namespace locoex
+{
+
+struct CircleShapeInferenceRule final : public loco::ShapeInferenceRule
+{
+ bool recognize(const loco::Dialect *) const final;
+ bool infer(const loco::Node *, loco::NodeShape &) const final;
+};
+
+} // namespace locoex
+
+#endif // __LOCOEX_SERVICE_CIRCLESHAPE_INFERENCE_RULE_H__
diff --git a/compiler/exo/src/Dialect/Service/CircleTypeInferenceRule.cpp b/compiler/exo/src/Dialect/Service/CircleTypeInferenceRule.cpp
new file mode 100644
index 000000000..dd64eacc7
--- /dev/null
+++ b/compiler/exo/src/Dialect/Service/CircleTypeInferenceRule.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CircleTypeInferenceRule.h"
+
+#include "Dialect/IR/CircleDialect.h"
+#include "Dialect/IR/CircleNodeVisitor.h"
+#include "Dialect/IR/CircleNodes.h"
+
+#include <cassert>
+
+namespace
+{
+
+struct TypeInferenceAlgorithm final : public locoex::CircleNodeVisitor<loco::DataType>
+{
+ loco::DataType visit(const locoex::CircleInstanceNorm *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+};
+
+} // namespace
+
+namespace locoex
+{
+
+bool CircleTypeInferenceRule::recognize(const loco::Dialect *d) const
+{
+ return CircleDialect::get() == d;
+}
+
+bool CircleTypeInferenceRule::infer(const loco::Node *node, loco::DataType &dtype) const
+{
+ assert(node->dialect() == CircleDialect::get());
+
+ TypeInferenceAlgorithm alg;
+
+ dtype = loco::must_cast<const CircleNode *>(node)->accept(&alg);
+ assert(dtype != loco::DataType::Unknown);
+
+ return true;
+}
+
+} // namespace locoex
diff --git a/compiler/exo/src/Dialect/Service/CircleTypeInferenceRule.h b/compiler/exo/src/Dialect/Service/CircleTypeInferenceRule.h
new file mode 100644
index 000000000..c073dfc54
--- /dev/null
+++ b/compiler/exo/src/Dialect/Service/CircleTypeInferenceRule.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOCOEX_SERVICE_CIRCLETYPE_INFERENCE_RULE_H__
+#define __LOCOEX_SERVICE_CIRCLETYPE_INFERENCE_RULE_H__
+
+#include <loco/Service/TypeInference.h>
+
+namespace locoex
+{
+
+/**
+ * @brief Type Inference Rule for CircleDialect
+ */
+struct CircleTypeInferenceRule final : public loco::TypeInferenceRule
+{
+ bool recognize(const loco::Dialect *) const final;
+ bool infer(const loco::Node *, loco::DataType &) const final;
+};
+
+} // namespace locoex
+
+#endif // __LOCOEX_SERVICE_CIRCLETYPE_INFERENCE_RULE_H__
diff --git a/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.cpp b/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.cpp
new file mode 100644
index 000000000..f4bb10364
--- /dev/null
+++ b/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.cpp
@@ -0,0 +1,627 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TFLShapeInferenceRule.h"
+
+#include "Dialect/IR/TFLNodes.h"
+#include "Dialect/IR/TFLDialect.h"
+#include "Dialect/IR/TFLNodeVisitor.h"
+
+#include "Check.h"
+
+#include <oops/InternalExn.h>
+
+#include <algorithm>
+#include <cassert>
+#include <stdexcept>
+
+namespace
+{
+
+// Call this for TFLAvgPool2D and TFLMaxPool2D only
+template <class Pool2DType> loco::NodeShape infer_pool_2d_shape(const Pool2DType *node)
+{
+ EXO_ASSERT(loco::shape_known(node->value()), "Shape must be known");
+
+ auto ifm_shape = loco::shape_get(node->value()).template as<loco::TensorShape>();
+ assert(ifm_shape.rank() == 4);
+
+ uint32_t input_height = ifm_shape.dim(1).value();
+ uint32_t input_width = ifm_shape.dim(2).value();
+ uint32_t stride_height = node->stride()->h();
+ uint32_t stride_width = node->stride()->w();
+ uint32_t window_height = node->filter()->h();
+ uint32_t window_width = node->filter()->w();
+ uint32_t dilation_height = 1; // dilation for TFLAvgPool2D and TFLMaxPool2D is 1
+ uint32_t dilation_width = 1;
+ uint32_t effective_window_height = dilation_height * (window_height - 1) + 1;
+ uint32_t effective_window_width = dilation_width * (window_width - 1) + 1;
+
+ uint32_t output_height = 0;
+ uint32_t output_width = 0;
+
+ if (node->padding() == locoex::Padding::VALID)
+ {
+ output_height = (input_height + stride_height - effective_window_height) / stride_height;
+ output_width = (input_width + stride_width - effective_window_width) / stride_width;
+ }
+ else if (node->padding() == locoex::Padding::SAME)
+ {
+ output_height = (input_height + stride_height - 1) / stride_height;
+ output_width = (input_width + stride_width - 1) / stride_width;
+ }
+ else
+ EXO_ASSERT(false, "Wrong padding type");
+
+ loco::TensorShape ofm_shape;
+ ofm_shape.rank(4);
+ ofm_shape.dim(0) = ifm_shape.dim(0);
+ ofm_shape.dim(1) = output_height;
+ ofm_shape.dim(2) = output_width;
+ ofm_shape.dim(3) = ifm_shape.dim(3);
+
+ return loco::NodeShape{ofm_shape};
+}
+
+/**
+ * @brief Create a higher-rank TensorShape following NumPy broadcasting semantics
+ *
+ * HOW TO USE:
+ *
+ * auto expanded_tensor_shape = expand(tensor_shape).to(N);
+ */
+class TensorShapeExpander
+{
+public:
+ TensorShapeExpander(const loco::TensorShape &shape) : _shape{shape}
+ {
+ // DO NOTHING
+ }
+
+public:
+ loco::TensorShape to(uint32_t output_rank)
+ {
+ auto const &input_shape = _shape;
+ uint32_t const input_rank = input_shape.rank();
+
+ assert(input_rank <= output_rank && "Cannot shrink rank");
+ uint32_t const axis_shift = output_rank - input_rank;
+
+ loco::TensorShape output_shape;
+
+ output_shape.rank(output_rank);
+ for (uint32_t axis = 0; axis < output_rank; ++axis)
+ {
+ output_shape.dim(axis) = (axis < axis_shift) ? 1 : input_shape.dim(axis - axis_shift);
+ }
+
+ return output_shape;
+ }
+
+private:
+ const loco::TensorShape _shape;
+};
+
+/**
+ * @breif Expand shape x and y to same rank by align right and filling with 1
+ */
+void expand_rank(loco::TensorShape &x, loco::TensorShape &y)
+{
+ auto x_rank = x.rank();
+ auto y_rank = y.rank();
+
+ if (x_rank == y_rank)
+ return;
+
+ TensorShapeExpander x_exp(x);
+ TensorShapeExpander y_exp(y);
+
+ auto xy_rank = std::max(x_rank, y_rank);
+
+ x = x_rank > y_rank ? x : x_exp.to(xy_rank);
+ y = y_rank > x_rank ? y : y_exp.to(xy_rank);
+}
+
+/**
+ * @breif Returns shape of expanded dimension of input x and y having same rank
+ */
+loco::TensorShape expand_dimension(const loco::TensorShape &x, const loco::TensorShape &y)
+{
+ assert(x.rank() == y.rank());
+
+ auto rank = x.rank();
+
+ loco::TensorShape output_shape;
+
+ output_shape.rank(rank);
+ for (uint32_t axis = 0; axis < rank; ++axis)
+ {
+ assert(x.dim(axis).known() && y.dim(axis).known());
+
+ auto x_dim = x.dim(axis).value();
+ auto y_dim = y.dim(axis).value();
+
+ // each dimension of x and y should be same or one must be 1 if different
+ if (!((x_dim == y_dim) || (x_dim == 1 || y_dim == 1)))
+ INTERNAL_EXN("Cannot produce expand_dimension of two shapes");
+
+ output_shape.dim(axis) = std::max(x_dim, y_dim);
+ }
+
+ return output_shape;
+}
+
+loco::TensorShape broadcast_shape(const loco::TensorShape &x, const loco::TensorShape &y)
+{
+ auto x_match = x;
+ auto y_match = y;
+
+ expand_rank(x_match, y_match);
+
+ auto output_shape = expand_dimension(x_match, y_match);
+
+ return output_shape;
+}
+
+/**
+ * @brief Class to infer the shape of TFLNode
+ *
+ * @note All TFLNode's inputs and outputs are always loco::Domain::Tensor
+ */
+class ShapeInferenceAlgorithm final : public locoex::TFLNodeVisitor<loco::NodeShape>
+{
+public:
+ loco::NodeShape visit(const locoex::TFLAdd *node) final
+ {
+ auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+ auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>();
+
+ auto output_shape = broadcast_shape(x_shape, y_shape);
+
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const locoex::TFLAveragePool2D *node) final
+ {
+ return infer_pool_2d_shape(node);
+ }
+
+ loco::NodeShape visit(const locoex::TFLConcatenation *node) final
+ {
+ // TODO Support when TFLConcatenation has 0 input
+ assert(node->numValues() > 0);
+
+ auto axis = node->axis();
+ auto first_shape = loco::shape_get(node->values(0)).as<loco::TensorShape>();
+
+ loco::TensorShape output_shape;
+
+ output_shape.rank(first_shape.rank());
+ for (uint32_t i = 0; i < output_shape.rank(); ++i)
+ output_shape.dim(i) = first_shape.dim(i);
+
+ for (uint32_t i = 1; i < node->numValues(); ++i)
+ {
+ auto input_shape = loco::shape_get(node->values(i)).as<loco::TensorShape>();
+
+ for (uint32_t j = 0; j < output_shape.rank(); ++j)
+ {
+ if (j == axis)
+ output_shape.dim(j) = output_shape.dim(j).value() + input_shape.dim(j).value();
+ else
+ assert(output_shape.dim(j) == input_shape.dim(j));
+ }
+ }
+
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const locoex::TFLConst *node) final
+ {
+ loco::TensorShape shape;
+
+ shape.rank(node->rank());
+ for (uint32_t axis = 0; axis < node->rank(); axis++)
+ shape.dim(axis) = node->dim(axis);
+
+ return loco::NodeShape{shape};
+ }
+
+ loco::NodeShape visit(const locoex::TFLConv2D *node) final
+ {
+ auto ifm_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); // in NHWC
+ auto ker_shape = loco::shape_get(node->filter()).as<loco::TensorShape>(); // in OHWI
+
+ assert(ifm_shape.rank() == 4);
+ assert(ker_shape.rank() == 4);
+ assert(ifm_shape.dim(3) == ker_shape.dim(3));
+
+ uint32_t input_height = ifm_shape.dim(1).value();
+ uint32_t input_width = ifm_shape.dim(2).value();
+ uint32_t stride_height = node->stride()->h();
+ uint32_t stride_width = node->stride()->w();
+ uint32_t ker_height = ker_shape.dim(1).value();
+ uint32_t ker_width = ker_shape.dim(2).value();
+ uint32_t dilation_height = 1;
+ uint32_t dilation_width = 1;
+ uint32_t effective_ker_height = dilation_height * (ker_height - 1) + 1;
+ uint32_t effective_ker_width = dilation_width * (ker_width - 1) + 1;
+
+ uint32_t output_height = 0;
+ uint32_t output_width = 0;
+
+ if (node->padding() == locoex::Padding::VALID)
+ {
+ output_height = (input_height + stride_height - effective_ker_height) / stride_height;
+ output_width = (input_width + stride_width - effective_ker_width) / stride_width;
+ }
+ else if (node->padding() == locoex::Padding::SAME)
+ {
+ output_height = (input_height + stride_height - 1) / stride_height;
+ output_width = (input_width + stride_width - 1) / stride_width;
+ }
+ else
+ EXO_ASSERT(false, "Wrong padding type");
+
+ loco::TensorShape ofm_shape;
+ ofm_shape.rank(4);
+ ofm_shape.dim(0) = ifm_shape.dim(0);
+ ofm_shape.dim(1) = output_height;
+ ofm_shape.dim(2) = output_width;
+ ofm_shape.dim(3) = ker_shape.dim(0);
+
+ return loco::NodeShape{ofm_shape};
+ }
+
+ loco::NodeShape visit(const locoex::TFLDepthwiseConv2D *node) final
+ {
+ auto ifm_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); // in NHWC
+ auto ker_shape = loco::shape_get(node->filter()).as<loco::TensorShape>(); // in 1 H W CM
+
+ assert(ifm_shape.rank() == 4);
+ assert(ker_shape.rank() == 4);
+ assert(ker_shape.dim(0).value() == 1);
+
+ uint32_t input_height = ifm_shape.dim(1).value();
+ uint32_t input_width = ifm_shape.dim(2).value();
+ uint32_t stride_height = node->stride()->h();
+ uint32_t stride_width = node->stride()->w();
+ uint32_t ker_height = ker_shape.dim(1).value();
+ uint32_t ker_width = ker_shape.dim(2).value();
+ uint32_t dilation_height = 1;
+ uint32_t dilation_width = 1;
+ uint32_t effective_ker_height = dilation_height * (ker_height - 1) + 1;
+ uint32_t effective_ker_width = dilation_width * (ker_width - 1) + 1;
+
+ uint32_t output_height = 0;
+ uint32_t output_width = 0;
+
+ if (node->padding() == locoex::Padding::VALID)
+ {
+ output_height = (input_height + stride_height - effective_ker_height) / stride_height;
+ output_width = (input_width + stride_width - effective_ker_width) / stride_width;
+ }
+ else if (node->padding() == locoex::Padding::SAME)
+ {
+ output_height = (input_height + stride_height - 1) / stride_height;
+ output_width = (input_width + stride_width - 1) / stride_width;
+ }
+ else
+ EXO_ASSERT(false, "Wrong padding type");
+
+ loco::TensorShape ofm_shape;
+ ofm_shape.rank(4);
+ ofm_shape.dim(0) = ifm_shape.dim(0);
+ ofm_shape.dim(1) = output_height;
+ ofm_shape.dim(2) = output_width;
+ ofm_shape.dim(3) = ker_shape.dim(3);
+
+ return loco::NodeShape{ofm_shape};
+ }
+
+ loco::NodeShape visit(const locoex::TFLDiv *node) final
+ {
+ auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+ auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>();
+
+ auto output_shape = broadcast_shape(x_shape, y_shape);
+
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const locoex::TFLFullyConnected *node) final
+ {
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ auto weights_shape = loco::shape_get(node->weights()).as<loco::TensorShape>();
+
+ // Checking shape capability for multiplication
+ EXO_ASSERT(input_shape.rank() == 2, "NYI for input shape rank > 2");
+ EXO_ASSERT(weights_shape.rank() == 2, "Incompatible weights rank for fully connected");
+ EXO_ASSERT(input_shape.dim(1) == weights_shape.dim(1),
+ "Incompatible shapes for fully connected");
+
+ loco::TensorShape out_shape;
+ out_shape.rank(2);
+
+ out_shape.dim(0) = input_shape.dim(0);
+ out_shape.dim(1) = weights_shape.dim(0);
+
+ return loco::NodeShape{out_shape};
+ }
+
+ loco::NodeShape visit(const locoex::TFLMaximum *node) final
+ {
+ auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+ auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>();
+
+ auto output_shape = broadcast_shape(x_shape, y_shape);
+
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const locoex::TFLMaxPool2D *node) final
+ {
+ return infer_pool_2d_shape(node);
+ }
+
+ loco::NodeShape visit(const locoex::TFLMean *node) final
+ {
+ const loco::DataType S32 = loco::DataType::S32;
+
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ auto reduction_indices = dynamic_cast<locoex::TFLConst *>(node->reduction_indices());
+
+ { // Exceptions
+ // TODO support non-const case
+ EXO_ASSERT(reduction_indices, "Only support constant reduction_indices");
+ // TODO support other data type
+ EXO_ASSERT(reduction_indices->dtype() == S32, "Only support int 32");
+ }
+
+ std::vector<int32_t> reduction_values;
+
+ for (uint32_t i = 0; i < reduction_indices->size<S32>(); ++i)
+ {
+ int32_t axis = reduction_indices->at<S32>(i);
+ if (axis < 0)
+ axis += input_shape.rank();
+ if (not(0 <= axis and axis < static_cast<int32_t>(input_shape.rank())))
+ INTERNAL_EXN_V("Invalid reduction axis for MEAN", oops::to_uint32(axis));
+ reduction_values.push_back(axis);
+ }
+
+ loco::TensorShape output_shape;
+
+ if (node->keep_dims())
+ {
+ output_shape.rank(input_shape.rank());
+ for (uint32_t i = 0; i < input_shape.rank(); ++i)
+ output_shape.dim(i) = input_shape.dim(i);
+ for (uint32_t i = 0; i < reduction_values.size(); ++i)
+ output_shape.dim(reduction_values.at(i)) = 1;
+ }
+ else
+ {
+ std::vector<bool> check_reduce(input_shape.rank(), false);
+ for (uint32_t i = 0; i < reduction_values.size(); ++i)
+ check_reduce.at(reduction_values.at(i)) = true;
+
+ uint32_t reduce_cnt = 0;
+ for (uint32_t i = 0; i < check_reduce.size(); ++i)
+ if (check_reduce.at(i))
+ ++reduce_cnt;
+
+ output_shape.rank(input_shape.rank() - reduce_cnt);
+ for (uint32_t i = 0, j = 0; i < check_reduce.size(); ++i)
+ if (check_reduce.at(i) == false)
+ output_shape.dim(j++) = i;
+ }
+
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const locoex::TFLMul *node) final
+ {
+ auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+ auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>();
+
+ auto output_shape = broadcast_shape(x_shape, y_shape);
+
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const locoex::TFLRelu *node) final
+ {
+ auto input_shape = loco::shape_get(node->features()).as<loco::TensorShape>();
+
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const locoex::TFLRelu6 *node) final
+ {
+ auto input_shape = loco::shape_get(node->features()).as<loco::TensorShape>();
+
+ return loco::NodeShape{input_shape};
+ }
+
+ /**
+ * @note TFLReshape has new shape info in two places: 2nd input and attribute.
+ * This shape inference forces both to exist, and match each other.
+ * When this condition satisfied, it return the inferred shape
+ *
+ * TODO Change this policy when not appropriate
+ */
+ loco::NodeShape visit(const locoex::TFLReshape *node) final
+ {
+ const loco::DataType S32 = loco::DataType::S32;
+
+ loco::TensorShape shape_by_input;
+ {
+ EXO_ASSERT(node->shape(), "2nd input shape() should not be nullptr");
+
+ // Only support node's shape() is TFLConst with S32
+ // TODO support other node with other types
+ auto const_shape_node = dynamic_cast<locoex::TFLConst *>(node->shape());
+ EXO_ASSERT(const_shape_node, "Only support TFLConst for shape of TFLReshape");
+ EXO_ASSERT(const_shape_node->dtype() == S32, "Only support int32 TFLConst");
+
+ if (const_shape_node->rank() != 1)
+ INTERNAL_EXN_V("Only support rank 1 TFLConst", oops::to_uint32(const_shape_node->rank()));
+
+ shape_by_input.rank(const_shape_node->dim(0).value());
+
+ for (uint32_t axis = 0; axis < shape_by_input.rank(); ++axis)
+ {
+ EXO_ASSERT(const_shape_node->at<S32>(axis) > 0, "Dimension should be > 0")
+ shape_by_input.dim(axis) = const_shape_node->at<S32>(axis);
+ }
+ }
+
+ loco::TensorShape shape_by_attr;
+ {
+ shape_by_attr.rank(node->newShape()->rank());
+
+ for (uint32_t axis = 0; axis < shape_by_attr.rank(); ++axis)
+ {
+ EXO_ASSERT(node->newShape()->dim(axis) > 0, "Dimension should be > 0")
+ shape_by_attr.dim(axis) = node->newShape()->dim(axis);
+ }
+ }
+
+ EXO_ASSERT(shape_by_input == shape_by_attr,
+ "Warning: Two new shape information mismatched for TFLReshape");
+
+ return loco::NodeShape{shape_by_input};
+ }
+
+ loco::NodeShape visit(const locoex::TFLRsqrt *node) final
+ {
+ auto input_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+
+ return loco::NodeShape{input_shape};
+ }
+
+ // TODO TFLSoftmax
+
+ loco::NodeShape visit(const locoex::TFLSqrt *node) final
+ {
+ auto input_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const locoex::TFLSquaredDifference *node) final
+ {
+ auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+ auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>();
+
+ auto output_shape = broadcast_shape(x_shape, y_shape);
+
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const locoex::TFLSub *node) final
+ {
+ auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+ auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>();
+
+ auto output_shape = broadcast_shape(x_shape, y_shape);
+
+ return loco::NodeShape{output_shape};
+ }
+
+ // TODO TFLTanh
+
+ /// @brief Returns output shape of transpose. Use loco::ConstGen and locoex::TFLConst for ConstT.
+ template <class ConstT>
+ loco::TensorShape output_shape_of_transpose(loco::TensorShape input_shape,
+ const ConstT *perm_node)
+ {
+ loco::TensorShape output_shape;
+ output_shape.rank(input_shape.rank());
+
+ assert(perm_node->dtype() == loco::DataType::S32);
+ assert(input_shape.rank() == perm_node->template size<loco::DataType::S32>());
+
+ for (uint32_t out_axis = 0; out_axis < output_shape.rank(); out_axis++)
+ {
+ auto new_dim = perm_node->template at<loco::DataType::S32>(out_axis);
+ output_shape.dim(new_dim) = input_shape.dim(out_axis);
+ }
+
+ return output_shape;
+ }
+
+ loco::NodeShape visit(const locoex::TFLTranspose *node) final
+ {
+ auto input_shape = loco::shape_get(node->a()).as<loco::TensorShape>();
+
+ auto canon_perm = dynamic_cast<loco::ConstGen *>(node->perm());
+ auto tfl_perm = dynamic_cast<locoex::TFLConst *>(node->perm());
+
+ if (canon_perm)
+ {
+ return loco::NodeShape{output_shape_of_transpose(input_shape, canon_perm)};
+ }
+ else if (tfl_perm)
+ {
+ return loco::NodeShape{output_shape_of_transpose(input_shape, tfl_perm)};
+ }
+ else
+ INTERNAL_EXN("perm of TFLTranspose should be either ConstGen or TFLConst");
+ }
+
+ loco::NodeShape visit(const locoex::TFLTransposeConv *node) final
+ {
+ // TransposeConv's output shape is written in its 'inputSizes' argument
+ auto input_sizes_const = dynamic_cast<locoex::TFLConst *>(node->inputSizes());
+ EXO_ASSERT(input_sizes_const, "Only support when TFLTransposeConv's inputSizes is TFLConst")
+ EXO_ASSERT(input_sizes_const->dtype() == loco::DataType::S32, "Only support S32 dtype")
+ EXO_ASSERT(input_sizes_const->rank() == 1 && input_sizes_const->dim(0).value() == 4,
+ "Only support rank 1 with 4 entries")
+
+ loco::TensorShape shape;
+
+ shape.rank(4);
+ for (uint32_t axis = 0; axis < 4; ++axis)
+ shape.dim(axis) = input_sizes_const->at<loco::DataType::S32>(axis);
+
+ return loco::NodeShape{shape};
+ }
+};
+
+} // namespace
+
+namespace locoex
+{
+
+bool TFLShapeInferenceRule::recognize(const loco::Dialect *d) const
+{
+ return TFLDialect::get() == d;
+}
+
+bool TFLShapeInferenceRule::infer(const loco::Node *node, loco::NodeShape &shape) const
+{
+ assert(node->dialect() == TFLDialect::get());
+ assert(dynamic_cast<const TFLNode *>(node) != nullptr);
+
+ ShapeInferenceAlgorithm alg;
+ shape = dynamic_cast<const TFLNode *>(node)->accept(&alg);
+
+ return true;
+}
+
+} // namespace locoex
diff --git a/compiler/exo-tflite/src/Dialect/Service/TFLShapeInferenceRule.h b/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.h
index 434a145cc..434a145cc 100644
--- a/compiler/exo-tflite/src/Dialect/Service/TFLShapeInferenceRule.h
+++ b/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.h
diff --git a/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.test.cpp b/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.test.cpp
new file mode 100644
index 000000000..b68728b47
--- /dev/null
+++ b/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.test.cpp
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestGraph.h"
+
+#include "Dialect/IR/TFLNodes.h"
+#include "Dialect/IR/TFLDialect.h"
+#include "Dialect/Service/TFLShapeInferenceRule.h"
+
+#include <loco.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/Service/ShapeInference.h>
+#include <loco/Service/CanonicalShapeInferenceRule.h>
+#include <loco/Service/MultiDialectShapeInferenceRule.h>
+
+#include <stdex/Memory.h>
+
+#include <gtest/gtest.h>
+
+TEST(TFLShapeInferenceRuleTest, minimal_with_TFLRelu)
+{
+ // Create a simple network
+ exo::test::TestGraph graph;
+ auto tfl_node = graph.append<locoex::TFLRelu>(graph.pull);
+ graph.complete(tfl_node);
+
+ // set shape
+ {
+ graph.pull->rank(2);
+ graph.pull->dim(0) = 3;
+ graph.pull->dim(1) = 4;
+ }
+
+ // pre-check
+ ASSERT_FALSE(loco::shape_known(tfl_node));
+
+ // shape inference
+ locoex::TFLShapeInferenceRule tfl_rule;
+ loco::CanonicalShapeInferenceRule canonical_rule;
+ loco::MultiDialectShapeInferenceRule rules;
+
+ rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
+ .bind(locoex::TFLDialect::get(), &tfl_rule);
+
+ loco::apply(&rules).to(graph.g.get());
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(tfl_node));
+ ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(tfl_node).domain());
+
+ auto shape = loco::shape_get(tfl_node).as<loco::TensorShape>();
+ ASSERT_EQ(2, shape.rank());
+ ASSERT_EQ(3, shape.dim(0));
+ ASSERT_EQ(4, shape.dim(1));
+ }
+}
+
+// based on the case shown in
+// https://www.corvil.com/kb/what-is-the-difference-between-same-and-valid-padding-in-tf-nn-max-pool-of-tensorflow
+TEST(TFLShapeInferenceRuleTest, avgpool2d_valid)
+{
+ exo::test::TestGraph graph;
+ auto tfl_node = graph.append<locoex::TFLAveragePool2D>(graph.pull);
+ graph.complete();
+
+ auto pull = graph.pull;
+ {
+ pull->shape({1, 4, 3, 1});
+ }
+ // setting TFLAveragePool2D
+ {
+ tfl_node->filter()->h(2);
+ tfl_node->filter()->w(2);
+ tfl_node->stride()->h(2);
+ tfl_node->stride()->w(2);
+ tfl_node->fusedActivationFunction(locoex::FusedActFunc::NONE);
+ tfl_node->padding(locoex::Padding::VALID);
+ }
+ ASSERT_FALSE(loco::shape_known(tfl_node));
+
+ // shape inference
+ locoex::TFLShapeInferenceRule tfl_rule;
+ loco::CanonicalShapeInferenceRule canonical_rule;
+ loco::MultiDialectShapeInferenceRule rules;
+
+ rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
+ .bind(locoex::TFLDialect::get(), &tfl_rule);
+
+ loco::apply(&rules).to(graph.g.get());
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(tfl_node));
+ ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(tfl_node).domain());
+
+ auto shape = loco::shape_get(tfl_node).as<loco::TensorShape>();
+ ASSERT_EQ(4, shape.rank());
+ ASSERT_EQ(1, shape.dim(0).value());
+ ASSERT_EQ(2, shape.dim(1).value());
+ ASSERT_EQ(1, shape.dim(2).value());
+ ASSERT_EQ(1, shape.dim(3).value());
+ }
+}
+
+TEST(TFLShapeInferenceRuleTest, avgpool2d_same)
+{
+ exo::test::TestGraph graph;
+ auto tfl_node = graph.append<locoex::TFLAveragePool2D>(graph.pull);
+ graph.complete();
+
+ auto pull = graph.pull;
+ {
+ pull->shape({1, 4, 3, 1});
+ }
+
+ // setting TFLAveragePool2D
+ {
+ tfl_node->filter()->h(2);
+ tfl_node->filter()->w(2);
+ tfl_node->stride()->h(2);
+ tfl_node->stride()->w(2);
+ tfl_node->fusedActivationFunction(locoex::FusedActFunc::NONE);
+ tfl_node->padding(locoex::Padding::SAME);
+ }
+
+ ASSERT_FALSE(loco::shape_known(tfl_node));
+
+ // shape inference
+ locoex::TFLShapeInferenceRule tfl_rule;
+ loco::CanonicalShapeInferenceRule canonical_rule;
+ loco::MultiDialectShapeInferenceRule rules;
+
+ rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
+ .bind(locoex::TFLDialect::get(), &tfl_rule);
+
+ loco::apply(&rules).to(graph.g.get());
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(tfl_node));
+ ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(tfl_node).domain());
+
+ auto shape = loco::shape_get(tfl_node).as<loco::TensorShape>();
+ ASSERT_EQ(4, shape.rank());
+ ASSERT_EQ(1, shape.dim(0).value());
+ ASSERT_EQ(2, shape.dim(1).value());
+ ASSERT_EQ(2, shape.dim(2).value());
+ ASSERT_EQ(1, shape.dim(3).value());
+ }
+}
+
+/**
+ * @note Function to test: Shape inference of two different input shapes
+ *
+ * Rank expansion to higher input side
+ * x(2,1,5) + y(3,5) --> x(2,1,5) + y(1,3,5)
+ * Do output shape inference like numpy
+ * x(2,1,5) + y(1,3,5) --> output(2,3,5)
+ * For each axis, dim value should be same OR one of them should be 1
+ */
+TEST(TFLShapeInferenceRuleTest, TFAdd_shapeinf_different)
+{
+ auto g = loco::make_graph();
+
+ auto x_node = g->nodes()->create<loco::Pull>();
+ {
+ x_node->rank(3);
+ x_node->dim(0) = 2;
+ x_node->dim(1) = 1;
+ x_node->dim(2) = 5;
+ }
+ auto y_node = g->nodes()->create<loco::Pull>();
+ {
+ y_node->rank(2);
+ y_node->dim(0) = 3;
+ y_node->dim(1) = 5;
+ }
+ auto tfl_node = g->nodes()->create<locoex::TFLAdd>();
+ {
+ tfl_node->x(x_node);
+ tfl_node->y(y_node);
+ }
+ auto push_node = g->nodes()->create<loco::Push>();
+ {
+ push_node->from(tfl_node);
+ }
+
+ auto x_input = g->inputs()->create();
+ {
+ x_input->name("x");
+ loco::link(x_input, x_node);
+ }
+ auto y_input = g->inputs()->create();
+ {
+ y_input->name("y");
+ loco::link(y_input, y_node);
+ }
+ auto output = g->outputs()->create();
+ {
+ output->name("output");
+ loco::link(output, push_node);
+ }
+
+ // pre-check
+ ASSERT_FALSE(loco::shape_known(tfl_node));
+
+ exo::ShapeInferencePass pass;
+ while (pass.run(g.get()) == true)
+ {
+ ;
+ }
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(tfl_node));
+ ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(tfl_node).domain());
+
+ auto shape = loco::shape_get(tfl_node).as<loco::TensorShape>();
+ ASSERT_EQ(3, shape.rank());
+ ASSERT_EQ(2, shape.dim(0));
+ ASSERT_EQ(3, shape.dim(1));
+ ASSERT_EQ(5, shape.dim(2));
+ }
+}
+
+TEST(TFLShapeInferenceRuleTest, TFLTranspose_simple)
+{
+ exo::test::ExampleGraph<exo::test::ExampleGraphType::TFLTranspose> g;
+
+ g.pull->rank(4);
+ g.pull->dim(0) = 10;
+ g.pull->dim(1) = 20;
+ g.pull->dim(2) = 30;
+ g.pull->dim(3) = 40;
+
+ g.const_perm->dtype(loco::DataType::S32);
+ g.const_perm->rank(1);
+ g.const_perm->dim(0) = 4;
+ g.const_perm->size<loco::DataType::S32>(4);
+ g.const_perm->at<loco::DataType::S32>(0) = 2;
+ g.const_perm->at<loco::DataType::S32>(1) = 3;
+ g.const_perm->at<loco::DataType::S32>(2) = 0;
+ g.const_perm->at<loco::DataType::S32>(3) = 1;
+
+ // pre-check
+ ASSERT_FALSE(loco::shape_known(g.tfl_transpose));
+
+ exo::ShapeInferencePass pass;
+ while (pass.run(g.graph()) == true)
+ ;
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(g.tfl_transpose));
+
+ auto shape = loco::shape_get(g.tfl_transpose).as<loco::TensorShape>();
+ ASSERT_EQ(4, shape.rank());
+ ASSERT_EQ(30, shape.dim(0));
+ ASSERT_EQ(40, shape.dim(1));
+ ASSERT_EQ(10, shape.dim(2));
+ ASSERT_EQ(20, shape.dim(3));
+ }
+}
diff --git a/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.cpp b/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.cpp
new file mode 100644
index 000000000..b0fecffe5
--- /dev/null
+++ b/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TFLTypeInferenceRule.h"
+
+#include "Dialect/IR/TFLDialect.h"
+#include "Dialect/IR/TFLNodeVisitor.h"
+#include "Dialect/IR/TFLNodes.h"
+
+#include <cassert>
+
+namespace
+{
+
+struct TypeInferenceAlgorithm final : public locoex::TFLNodeVisitor<loco::DataType>
+{
+ loco::DataType visit(const locoex::TFLAdd *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const locoex::TFLAveragePool2D *node) final
+ {
+ return loco::dtype_get(node->value());
+ }
+
+ loco::DataType visit(const locoex::TFLConcatenation *node) final
+ {
+ // TODO Support when TFLConcatenation has 0 input
+ assert(node->numValues() > 0);
+
+ for (uint32_t i = 1; i < node->numValues(); ++i)
+ assert(loco::dtype_get(node->values(i - 1)) == loco::dtype_get(node->values(i)));
+
+ return loco::dtype_get(node->values(0));
+ }
+
+ loco::DataType visit(const locoex::TFLConst *node) final { return node->dtype(); }
+
+ loco::DataType visit(const locoex::TFLConv2D *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const locoex::TFLDepthwiseConv2D *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const locoex::TFLDiv *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const locoex::TFLFullyConnected *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const locoex::TFLMaximum *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const locoex::TFLMaxPool2D *node) final
+ {
+ return loco::dtype_get(node->value());
+ }
+
+ loco::DataType visit(const locoex::TFLMean *node) final { return loco::dtype_get(node->input()); }
+
+ loco::DataType visit(const locoex::TFLMul *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const locoex::TFLRelu *node) final
+ {
+ return loco::dtype_get(node->features());
+ }
+
+ loco::DataType visit(const locoex::TFLRelu6 *node) final
+ {
+ return loco::dtype_get(node->features());
+ }
+
+ loco::DataType visit(const locoex::TFLReshape *node) final
+ {
+ return loco::dtype_get(node->tensor());
+ }
+
+ loco::DataType visit(const locoex::TFLRsqrt *node) final { return loco::dtype_get(node->x()); }
+
+ // TODO TFLSoftmax
+
+ loco::DataType visit(const locoex::TFLSqrt *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const locoex::TFLSquaredDifference *node) final
+ {
+ return loco::dtype_get(node->x());
+ }
+
+ loco::DataType visit(const locoex::TFLSub *node) final { return loco::dtype_get(node->x()); }
+
+ // TODO TFLTanh
+
+ loco::DataType visit(const locoex::TFLTranspose *node) final
+ {
+ return loco::dtype_get(node->a());
+ }
+
+ loco::DataType visit(const locoex::TFLTransposeConv *node) final
+ {
+ return loco::dtype_get(node->outBackprop());
+ }
+};
+
+} // namespace
+
+namespace locoex
+{
+
+bool TFLTypeInferenceRule::recognize(const loco::Dialect *d) const
+{
+ return TFLDialect::get() == d;
+}
+
+bool TFLTypeInferenceRule::infer(const loco::Node *node, loco::DataType &dtype) const
+{
+ assert(node->dialect() == TFLDialect::get());
+
+ TypeInferenceAlgorithm alg;
+
+ dtype = loco::must_cast<const TFLNode *>(node)->accept(&alg);
+ assert(dtype != loco::DataType::Unknown);
+
+ return true;
+}
+
+} // namespace locoex
diff --git a/compiler/exo-tflite/src/Dialect/Service/TFLTypeInferenceRule.h b/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.h
index 31765dcba..31765dcba 100644
--- a/compiler/exo-tflite/src/Dialect/Service/TFLTypeInferenceRule.h
+++ b/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.h
diff --git a/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.test.cpp b/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.test.cpp
new file mode 100644
index 000000000..9326e5e58
--- /dev/null
+++ b/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.test.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dialect/IR/TFLNodes.h"
+#include "Dialect/IR/TFLDialect.h"
+#include "Dialect/Service/TFLTypeInferenceRule.h"
+
+#include "TestGraph.h"
+
+#include <loco.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/Service/TypeInference.h>
+
+#include <stdex/Memory.h>
+
+#include <gtest/gtest.h>
+
+TEST(TFLTypeInferenceRuleTest, minimal_with_TFLRelu)
+{
+ // Create a simple network
+ exo::test::TestGraph graph;
+ auto tfl_node = graph.append<locoex::TFLRelu>(graph.pull);
+ graph.complete(tfl_node);
+
+ graph.pull->dtype(loco::DataType::S32);
+
+ // pre-check
+ ASSERT_FALSE(loco::dtype_known(tfl_node));
+
+ // type inference
+ locoex::TFLTypeInferenceRule tfl_rule;
+ loco::CanonicalTypeInferenceRule canon_rule;
+ loco::MultiDialectTypeInferenceRule rules;
+
+ rules.bind(loco::CanonicalDialect::get(), &canon_rule);
+ rules.bind(locoex::TFLDialect::get(), &tfl_rule);
+
+ loco::apply(&rules).to(graph.g.get());
+
+ // Verify
+ ASSERT_TRUE(loco::dtype_known(tfl_node));
+ auto type = loco::dtype_get(tfl_node);
+ ASSERT_EQ(loco::DataType::S32, type);
+}
diff --git a/compiler/exo/src/ExoFormattedGraph.cpp b/compiler/exo/src/ExoFormattedGraph.cpp
new file mode 100644
index 000000000..5d3b18be1
--- /dev/null
+++ b/compiler/exo/src/ExoFormattedGraph.cpp
@@ -0,0 +1,525 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ExoFormattedGraph.h"
+
+#include "Dialect/IR/TFLDialect.h"
+#include "Dialect/IR/TFLNodes.h"
+
+#include "Dialect/IR/CircleDialect.h"
+#include "Dialect/IR/CircleNodes.h"
+
+#include <locoex/Service/COpFormattedGraph.h>
+#include <pepper/str.h>
+
+#include <sstream>
+#include <cassert>
+
+// For TF lite
+namespace
+{
+
+const char *to_str(locoex::FusedActFunc fused)
+{
+ switch (fused)
+ {
+ case locoex::FusedActFunc::NONE:
+ return "NONE";
+ case locoex::FusedActFunc::RELU:
+ return "RELU";
+ case locoex::FusedActFunc::RELU6:
+ return "RELU6";
+ default:
+ return "Error";
+ }
+}
+
+const char *to_str(locoex::Padding padding)
+{
+ switch (padding)
+ {
+ case locoex::Padding::SAME:
+ return "SAME";
+ case locoex::Padding::VALID:
+ return "VALID";
+ default:
+ return "Error";
+ }
+}
+
+std::string to_str(const locoex::Stride *stride)
+{
+ return pepper::str(stride->h(), ",", stride->w());
+}
+
+std::string to_str(const locoex::Filter *filter)
+{
+ return pepper::str(filter->h(), ",", filter->w());
+}
+
+std::string tfl_opname(uint32_t opnum)
+{
+ static std::string prefix{"tfl."};
+
+ switch (static_cast<locoex::TFLOpcode>(opnum))
+ {
+#define TFL_NODE(OPCODE, CLASS) \
+ case locoex::TFLOpcode::OPCODE: \
+ return prefix + #OPCODE;
+#include "Dialect/IR/TFLNodes.lst"
+#undef TFL_NODE
+ default:
+ break;
+ };
+
+ return prefix + "Invalid";
+}
+
+// TFLNodeSummaryBuilder with default implementation
+class TFLNodeSummaryBuilderBase : public locop::NodeSummaryBuilder
+{
+public:
+ TFLNodeSummaryBuilderBase(const locop::SymbolTable *tbl) : _tbl{tbl}
+ {
+ // DO NOTHING
+ }
+
+public:
+ bool build(const loco::Node *, locop::NodeSummary &s) const final;
+
+protected:
+#define TFL_NODE(OPCODE, CLASS) \
+ virtual bool summary(const CLASS *, locop::NodeSummary &s) const \
+ { \
+ s.comments().append("Emitted by Default TFLNodeSummaryBuilder"); \
+ s.state(locop::NodeSummary::State::PartiallyKnown); \
+ return true; \
+ }
+#include "Dialect/IR/TFLNodes.lst"
+#undef TFL_NODE
+
+protected:
+ const locop::SymbolTable *tbl(void) const { return _tbl; }
+
+ // Please do not use _tbl directly and use tbl().
+ // This will be changed to private in near future.
+protected:
+ const locop::SymbolTable *_tbl;
+};
+
+class TFLNodeSummaryBuilder final : public TFLNodeSummaryBuilderBase
+{
+public:
+ TFLNodeSummaryBuilder(const locop::SymbolTable *tbl) : TFLNodeSummaryBuilderBase(tbl)
+ {
+ // DO NOTHING
+ }
+
+private:
+#define IMPLEMENT(CLASS) bool summary(const CLASS *, locop::NodeSummary &) const final;
+ IMPLEMENT(locoex::TFLAdd)
+ IMPLEMENT(locoex::TFLAveragePool2D)
+ IMPLEMENT(locoex::TFLConcatenation)
+ IMPLEMENT(locoex::TFLConst)
+ IMPLEMENT(locoex::TFLConv2D)
+ IMPLEMENT(locoex::TFLDepthwiseConv2D)
+ IMPLEMENT(locoex::TFLDiv)
+ IMPLEMENT(locoex::TFLMaximum)
+ IMPLEMENT(locoex::TFLMaxPool2D)
+ IMPLEMENT(locoex::TFLMean)
+ IMPLEMENT(locoex::TFLMul)
+ IMPLEMENT(locoex::TFLRelu)
+ IMPLEMENT(locoex::TFLRelu6)
+ IMPLEMENT(locoex::TFLReshape)
+ IMPLEMENT(locoex::TFLRsqrt)
+ IMPLEMENT(locoex::TFLSqrt)
+ IMPLEMENT(locoex::TFLSquaredDifference)
+ IMPLEMENT(locoex::TFLSub)
+ IMPLEMENT(locoex::TFLTranspose)
+ IMPLEMENT(locoex::TFLTransposeConv)
+#undef IMPLEMENT
+};
+
+bool TFLNodeSummaryBuilderBase::build(const loco::Node *node, locop::NodeSummary &s) const
+{
+ if (node->dialect() != locoex::TFLDialect::get())
+ return false;
+
+#define TFL_NODE(OPCODE, CLASS) \
+ if (dynamic_cast<const CLASS *>(node)) \
+ { \
+ s.opname(tfl_opname(node->opnum())); \
+ return summary(dynamic_cast<const CLASS *>(node), s); \
+ }
+#include "Dialect/IR/TFLNodes.lst"
+#undef TFL_NODE
+
+ return false;
+}
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLAdd *node, locop::NodeSummary &s) const
+{
+ assert(node->fusedActivationFunction() != locoex::FusedActFunc::UNDEFINED);
+
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.args().append("y", tbl()->lookup(node->y()));
+ s.args().append("fused_activation_function", to_str(node->fusedActivationFunction()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLAveragePool2D *node,
+ locop::NodeSummary &s) const
+{
+ assert(node->fusedActivationFunction() != locoex::FusedActFunc::UNDEFINED);
+
+ s.args().append("value", tbl()->lookup(node->value()));
+ s.args().append("filter(h,w)", to_str(node->filter()));
+ s.args().append("stride(h,w)", to_str(node->stride()));
+ s.args().append("padding", to_str(node->padding()));
+ s.args().append("fused", to_str(node->fusedActivationFunction()));
+
+ s.state(locop::NodeSummary::State::Complete);
+
+ return true;
+}
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLConcatenation *node,
+ locop::NodeSummary &s) const
+{
+ assert(node->fusedActivationFunction() != locoex::FusedActFunc::UNDEFINED);
+
+ for (uint32_t i = 0; i < node->numValues(); ++i)
+ s.args().append("values", tbl()->lookup(node->values(i)));
+ s.args().append("axis", pepper::str(node->axis()));
+ s.args().append("fused", to_str(node->fusedActivationFunction()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLConst *, locop::NodeSummary &s) const
+{
+ s.state(locop::NodeSummary::State::PartiallyKnown);
+ return true;
+}
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLConv2D *node, locop::NodeSummary &s) const
+{
+ assert(node->fusedActivationFunction() != locoex::FusedActFunc::UNDEFINED);
+ assert(node->padding() != locoex::Padding::UNDEFINED);
+
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.args().append("filter", tbl()->lookup(node->filter()));
+ s.args().append("bias", tbl()->lookup(node->bias()));
+
+ s.args().append("stride(h,w)", to_str(node->stride()));
+ s.args().append("padding", to_str(node->padding()));
+ s.args().append("fused", to_str(node->fusedActivationFunction()));
+
+ s.state(locop::NodeSummary::State::Complete);
+
+ return true;
+}
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLDepthwiseConv2D *node,
+ locop::NodeSummary &s) const
+{
+ assert(node->fusedActivationFunction() != locoex::FusedActFunc::UNDEFINED);
+ assert(node->padding() != locoex::Padding::UNDEFINED);
+
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.args().append("filter", tbl()->lookup(node->filter()));
+ s.args().append("bias", tbl()->lookup(node->bias()));
+
+ s.args().append("stride(h,w)", to_str(node->stride()));
+ s.args().append("padding", to_str(node->padding()));
+ s.args().append("depthMultiplier", std::to_string(node->depthMultiplier()));
+ s.args().append("fused", to_str(node->fusedActivationFunction()));
+
+ s.state(locop::NodeSummary::State::Complete);
+
+ return true;
+}
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLDiv *node, locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.args().append("y", tbl()->lookup(node->y()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLMaximum *node, locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.args().append("y", tbl()->lookup(node->y()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLMaxPool2D *node, locop::NodeSummary &s) const
+{
+ assert(node->fusedActivationFunction() != locoex::FusedActFunc::UNDEFINED);
+
+ s.args().append("value", tbl()->lookup(node->value()));
+ s.args().append("filter(h,w)", to_str(node->filter()));
+ s.args().append("stride(h,w)", to_str(node->stride()));
+ s.args().append("padding", to_str(node->padding()));
+ s.args().append("fused", to_str(node->fusedActivationFunction()));
+
+ s.state(locop::NodeSummary::State::Complete);
+
+ return true;
+}
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLMean *node, locop::NodeSummary &s) const
+{
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.args().append("reduction_indices", tbl()->lookup(node->reduction_indices()));
+ s.args().append("keep_dims", node->keep_dims() ? "true" : "false");
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLMul *node, locop::NodeSummary &s) const
+{
+ assert(node->fusedActivationFunction() != locoex::FusedActFunc::UNDEFINED);
+
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.args().append("y", tbl()->lookup(node->y()));
+ s.args().append("fused_activation_function", to_str(node->fusedActivationFunction()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLRelu *node, locop::NodeSummary &s) const
+{
+ s.args().append("features", tbl()->lookup(node->features()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLRelu6 *node, locop::NodeSummary &s) const
+{
+ s.args().append("features", tbl()->lookup(node->features()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLReshape *node, locop::NodeSummary &s) const
+{
+ s.args().append("tensor", tbl()->lookup(node->tensor()));
+ s.args().append("shape", tbl()->lookup(node->shape()));
+ // TODO Show newShape info
+ s.state(locop::NodeSummary::State::PartiallyKnown);
+ return true;
+}
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLRsqrt *node, locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+// TODO TFLSoftmax
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLSqrt *node, locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLSquaredDifference *node,
+ locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.args().append("y", tbl()->lookup(node->y()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLSub *node, locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.args().append("y", tbl()->lookup(node->y()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+// TODO TFLTanh
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLTranspose *node, locop::NodeSummary &s) const
+{
+ s.args().append("a", tbl()->lookup(node->a()));
+ s.args().append("perm", tbl()->lookup(node->perm()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFLNodeSummaryBuilder::summary(const locoex::TFLTransposeConv *node,
+ locop::NodeSummary &s) const
+{
+ assert(node->padding() != locoex::Padding::UNDEFINED);
+
+ s.args().append("inputSizes", tbl()->lookup(node->inputSizes()));
+ s.args().append("filter", tbl()->lookup(node->filter()));
+ s.args().append("outBackprop", tbl()->lookup(node->outBackprop()));
+
+ s.args().append("stride(h,w)", to_str(node->stride()));
+ s.args().append("padding", to_str(node->padding()));
+
+ s.state(locop::NodeSummary::State::Complete);
+
+ return true;
+}
+
+} // namespace
+
+// For Circle
+namespace
+{
+
+std::string circle_opname(uint32_t opnum)
+{
+ static std::string prefix{"circle."};
+
+ switch (static_cast<locoex::CircleOpcode>(opnum))
+ {
+#define CIRCLE_NODE(OPCODE, CLASS) \
+ case locoex::CircleOpcode::OPCODE: \
+ return prefix + #OPCODE;
+#include "Dialect/IR/CircleNodes.lst"
+#undef CIRCLE_NODE
+ default:
+ break;
+ };
+
+ return prefix + "Invalid";
+}
+
+// CircleNodeSummaryBuilder with default implementation
+class CircleNodeSummaryBuilderBase : public locop::NodeSummaryBuilder
+{
+public:
+ CircleNodeSummaryBuilderBase(const locop::SymbolTable *tbl) : _tbl{tbl}
+ {
+ // DO NOTHING
+ }
+
+public:
+ bool build(const loco::Node *, locop::NodeSummary &s) const final;
+
+protected:
+#define CIRCLE_NODE(OPCODE, CLASS) \
+ virtual bool summary(const CLASS *, locop::NodeSummary &s) const \
+ { \
+ s.comments().append("Emitted by Default CircleNodeSummaryBuilder"); \
+ s.state(locop::NodeSummary::State::PartiallyKnown); \
+ return true; \
+ }
+#include "Dialect/IR/CircleNodes.lst"
+#undef CIRCLE_NODE
+
+protected:
+ const locop::SymbolTable *tbl(void) const { return _tbl; }
+
+ // Please do not use _tbl directly and use tbl().
+ // This will be changed to private in near future.
+protected:
+ const locop::SymbolTable *_tbl;
+};
+
+class CircleNodeSummaryBuilder final : public CircleNodeSummaryBuilderBase
+{
+public:
+ CircleNodeSummaryBuilder(const locop::SymbolTable *tbl) : CircleNodeSummaryBuilderBase(tbl)
+ {
+ // DO NOTHING
+ }
+
+private:
+#define IMPLEMENT(CLASS) bool summary(const CLASS *, locop::NodeSummary &) const final;
+ IMPLEMENT(locoex::CircleInstanceNorm)
+#undef IMPLEMENT
+};
+
+bool CircleNodeSummaryBuilderBase::build(const loco::Node *node, locop::NodeSummary &s) const
+{
+ if (node->dialect() != locoex::CircleDialect::get())
+ return false;
+
+#define CIRCLE_NODE(OPCODE, CLASS) \
+ if (dynamic_cast<const CLASS *>(node)) \
+ { \
+ s.opname(circle_opname(node->opnum())); \
+ return summary(dynamic_cast<const CLASS *>(node), s); \
+ }
+#include "Dialect/IR/CircleNodes.lst"
+#undef CIRCLE_NODE
+
+ return false;
+}
+
+bool CircleNodeSummaryBuilder::summary(const locoex::CircleInstanceNorm *node,
+ locop::NodeSummary &s) const
+{
+ auto fused = node->fusedActivationFunction();
+ assert(fused != locoex::FusedActFunc::UNDEFINED);
+
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.args().append("gamma", tbl()->lookup(node->gamma()));
+ s.args().append("beta", tbl()->lookup(node->beta()));
+ s.args().append("epsilon", pepper::str(node->epsilon()));
+ s.args().append("fused_activation_function", to_str(fused));
+
+ s.state(locop::NodeSummary::State::Complete);
+
+ return true;
+}
+
+} // namespace
+
+namespace exo
+{
+
+bool NodeSummaryBuilder::build(const loco::Node *node, locop::NodeSummary &s) const
+{
+ if (locop::CanonicalNodeSummaryBuilder(_tbl).build(node, s))
+ {
+ return true;
+ }
+
+ if (TFLNodeSummaryBuilder(_tbl).build(node, s))
+ {
+ return true;
+ }
+
+ if (CircleNodeSummaryBuilder(_tbl).build(node, s))
+ {
+ return true;
+ }
+
+ if (locoex::COpNodeSummaryBuilder(_tbl).build(node, s))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/ExoFormattedGraph.h b/compiler/exo/src/ExoFormattedGraph.h
new file mode 100644
index 000000000..714e483b5
--- /dev/null
+++ b/compiler/exo/src/ExoFormattedGraph.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EXO_FORMATTED_GRAPH_H__
+#define __EXO_FORMATTED_GRAPH_H__
+
+#include <locop/FormattedGraph.h>
+
+#include <stdex/Memory.h>
+
+namespace exo
+{
+
+class NodeSummaryBuilder final : public locop::NodeSummaryBuilder
+{
+public:
+ NodeSummaryBuilder(const locop::SymbolTable *tbl) : _tbl{tbl}
+ {
+ // DO NOTHING
+ }
+
+public:
+ bool build(const loco::Node *node, locop::NodeSummary &s) const final;
+
+private:
+ const locop::SymbolTable *_tbl;
+};
+
+class NodeSummaryBuilderFactory final : public locop::NodeSummaryBuilderFactory
+{
+public:
+ NodeSummaryBuilderFactory() = default;
+
+public:
+ std::unique_ptr<locop::NodeSummaryBuilder> create(const locop::SymbolTable *tlb) const final
+ {
+ return stdex::make_unique<NodeSummaryBuilder>(tlb);
+ }
+};
+
+} // namespace exo
+
+#endif // __EXO_FORMATTED_GRAPH_H__
diff --git a/compiler/exo/src/ExoOptimize.cpp b/compiler/exo/src/ExoOptimize.cpp
new file mode 100644
index 000000000..d7278e900
--- /dev/null
+++ b/compiler/exo/src/ExoOptimize.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ExoOptimize.h"
+
+#include "Knob.h"
+#include "Passes.h"
+#include "ProgressReporter.h"
+
+#include <logo/Phase.h>
+
+#include <stdex/Memory.h>
+
+namespace exo
+{
+
+void optimize(loco::Graph *g)
+{
+ logo::Phase phase;
+ {
+ // prepare type and shape before optimization
+ phase.emplace_back(stdex::make_unique<TypeInferencePass>());
+ phase.emplace_back(stdex::make_unique<ShapeInferencePass>());
+
+ phase.emplace_back(stdex::make_unique<FoldReshapeOfConstPass>());
+ phase.emplace_back(stdex::make_unique<FoldTransposeOfConstPass>());
+
+ if (get<Knob::UseFuseBiasAddPass>())
+ {
+ phase.emplace_back(stdex::make_unique<FuseBiasAddPass>());
+ }
+
+ if (get<Knob::UseFuseInstanceNormPass>())
+ {
+ phase.emplace_back(stdex::make_unique<FuseInstanceNormPass>());
+ }
+
+ if (get<Knob::UseFuseReluPass>())
+ {
+ phase.emplace_back(stdex::make_unique<FuseReluPass>());
+ }
+ phase.emplace_back(stdex::make_unique<FuseRsqrtPass>());
+
+ if (get<Knob::UseFuseSquaredDifferencePass>())
+ {
+ phase.emplace_back(stdex::make_unique<FuseSquaredDifferencePass>());
+ }
+
+ phase.emplace_back(stdex::make_unique<MergeConcatNodesPass>());
+
+ phase.emplace_back(stdex::make_unique<logo::RemoveDeadNodePass>());
+ }
+
+ logo::PhaseRunner<logo::PhaseStrategy::Restart> phase_runner{g};
+
+ ProgressReporter prog(g, logo::PhaseStrategy::Restart);
+ phase_runner.attach(&prog);
+ phase_runner.run(phase);
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/ExoOptimize.h b/compiler/exo/src/ExoOptimize.h
new file mode 100644
index 000000000..4769c1193
--- /dev/null
+++ b/compiler/exo/src/ExoOptimize.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OPTIMIZE_H__
+#define __OPTIMIZE_H__
+
+#include <loco.h>
+
+namespace exo
+{
+
+/**
+ * @brief Run passes for a graph after completion of converting canonical nodes into TFL nodes.
+ *
+ * TODO Separate optimize pass dedicated to TFL and Circle dialect when necessary
+ */
+void optimize(loco::Graph *);
+
+} // namespace exo
+
+#endif // __OPTIMIZE_H__
diff --git a/compiler/exo/src/ExporterUtils.cpp b/compiler/exo/src/ExporterUtils.cpp
new file mode 100644
index 000000000..41ccdcd71
--- /dev/null
+++ b/compiler/exo/src/ExporterUtils.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ExporterUtils.h"
+
+#include <oops/InternalExn.h>
+
+#include <cassert>
+
+namespace exo
+{
+
+ShapeDescription to_shape_description(const loco::TensorShape &shape)
+{
+ ShapeDescription res;
+
+ res._rank_known = true;
+
+ res._dims.resize(shape.rank());
+ for (uint32_t axis = 0; axis < shape.rank(); ++axis)
+ {
+ // All the dimensions SHOULD be known
+ assert(shape.dim(axis).known());
+ res._dims.at(axis) = shape.dim(axis).value();
+ }
+
+ return res;
+}
+
+ShapeDescription to_shape_description(const loco::FeatureShape &shape)
+{
+ ShapeDescription res;
+
+ res._rank_known = true;
+
+ // T/F Lite encodes a feature map as a NHWC tensor
+ res._dims.resize(4);
+ res._dims.at(0) = shape.count().value();
+ res._dims.at(1) = shape.height().value();
+ res._dims.at(2) = shape.width().value();
+ res._dims.at(3) = shape.depth().value();
+
+ return res;
+}
+
+ShapeDescription to_shape_description(const loco::FilterShape &shape)
+{
+ ShapeDescription res;
+
+ res._rank_known = true;
+
+ // T/F Lite encodes a convolution filter as a NHWC tensor
+ res._dims.resize(4);
+ res._dims.at(0) = shape.count().value();
+ res._dims.at(1) = shape.height().value();
+ res._dims.at(2) = shape.width().value();
+ res._dims.at(3) = shape.depth().value();
+
+ return res;
+}
+
+ShapeDescription to_shape_description(const loco::DepthwiseFilterShape &shape)
+{
+ ShapeDescription res;
+
+ res._rank_known = true;
+
+ // T/F Lite encodes a depthwise convolution filter as a [1, H, W, C*M] tensor
+ res._dims.resize(4);
+ res._dims.at(0) = 1;
+ res._dims.at(1) = shape.height().value();
+ res._dims.at(2) = shape.width().value();
+ res._dims.at(3) = shape.depth().value() * shape.multiplier().value();
+
+ return res;
+}
+
+ShapeDescription to_shape_description(const loco::BiasShape &shape)
+{
+ ShapeDescription res;
+
+ res._rank_known = true;
+
+ res._dims.resize(1);
+ res._dims.at(0) = shape.length().value();
+
+ return res;
+}
+
+ShapeDescription to_shape_description(const loco::MatrixShape &shape)
+{
+ ShapeDescription res;
+
+ res._rank_known = true;
+
+ res._dims.resize(2);
+ res._dims.at(0) = shape.height().value();
+ res._dims.at(1) = shape.width().value();
+
+ return res;
+}
+
+ShapeDescription to_shape_description(const loco::NodeShape &shape)
+{
+ switch (shape.domain())
+ {
+ case loco::Domain::Tensor:
+ return to_shape_description(shape.as<loco::TensorShape>());
+ case loco::Domain::Feature:
+ return to_shape_description(shape.as<loco::FeatureShape>());
+ case loco::Domain::Filter:
+ return to_shape_description(shape.as<loco::FilterShape>());
+ case loco::Domain::DepthwiseFilter:
+ return to_shape_description(shape.as<loco::DepthwiseFilterShape>());
+ case loco::Domain::Bias:
+ return to_shape_description(shape.as<loco::BiasShape>());
+ case loco::Domain::Matrix:
+ return to_shape_description(shape.as<loco::MatrixShape>());
+ default:
+ break;
+ }
+
+ INTERNAL_EXN_V("Unsupported loco domain", oops::to_uint32(shape.domain()));
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/ExporterUtils.h b/compiler/exo/src/ExporterUtils.h
new file mode 100644
index 000000000..e1f1f66a8
--- /dev/null
+++ b/compiler/exo/src/ExporterUtils.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EXPORTER_UTILS_H__
+#define __EXPORTER_UTILS_H__
+
+#include "loco.h"
+
+#include "loco/IR/PermutingCodec.h"
+#include "loco/IR/NodeShape.h"
+
+namespace exo
+{
+
+struct ShapeDescription
+{
+ std::vector<int32_t> _dims;
+ bool _rank_known;
+};
+
+ShapeDescription to_shape_description(const loco::TensorShape &shape);
+ShapeDescription to_shape_description(const loco::FeatureShape &shape);
+ShapeDescription to_shape_description(const loco::FilterShape &shape);
+ShapeDescription to_shape_description(const loco::BiasShape &shape);
+ShapeDescription to_shape_description(const loco::MatrixShape &shape);
+ShapeDescription to_shape_description(const loco::NodeShape &shape);
+
+template <typename Permutation> inline bool isNHWC(Permutation *perm);
+
+template <> inline bool isNHWC(loco::Permutation<loco::Domain::Feature> *perm)
+{
+ return perm->axis(loco::FeatureAxis::Count) == 0 && perm->axis(loco::FeatureAxis::Height) == 1 &&
+ perm->axis(loco::FeatureAxis::Width) == 2 && perm->axis(loco::FeatureAxis::Depth) == 3;
+}
+
+template <> inline bool isNHWC(loco::Permutation<loco::Domain::Filter> *perm)
+{
+ return perm->axis(loco::FilterAxis::Count) == 0 && perm->axis(loco::FilterAxis::Height) == 1 &&
+ perm->axis(loco::FilterAxis::Width) == 2 && perm->axis(loco::FilterAxis::Depth) == 3;
+}
+
+} // namespace exo
+
+#endif // __EXPORTER_UTILS_H__
diff --git a/compiler/exo/src/GraphBlock.cpp b/compiler/exo/src/GraphBlock.cpp
new file mode 100644
index 000000000..0a45ce8ad
--- /dev/null
+++ b/compiler/exo/src/GraphBlock.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GraphBlock.h"
+
+#include "Check.h"
+
+#include <loco.h>
+#include <stdex/Memory.h>
+
+namespace
+{
+
+template <exo::FeatureLayout T> loco::Permutation<loco::Domain::Feature> perm();
+
+template <> loco::Permutation<loco::Domain::Feature> perm<exo::FeatureLayout::NHWC>()
+{
+ // Make NHWC permutation for encoder and decoder
+ loco::Permutation<loco::Domain::Feature> NHWC;
+
+ NHWC.axis(loco::FeatureAxis::Count) = 0;
+ NHWC.axis(loco::FeatureAxis::Height) = 1;
+ NHWC.axis(loco::FeatureAxis::Width) = 2;
+ NHWC.axis(loco::FeatureAxis::Depth) = 3;
+
+ return NHWC;
+}
+
+template <exo::FilterLayout T> loco::Permutation<loco::Domain::Filter> perm();
+
+template <> loco::Permutation<loco::Domain::Filter> perm<exo::FilterLayout::HWIO>()
+{
+ loco::Permutation<loco::Domain::Filter> HWIO; // a.k.a., HWCN
+
+ HWIO.axis(loco::FilterAxis::Height) = 0;
+ HWIO.axis(loco::FilterAxis::Width) = 1;
+ HWIO.axis(loco::FilterAxis::Depth) = 2;
+ HWIO.axis(loco::FilterAxis::Count) = 3;
+
+ return HWIO;
+}
+
+template <> loco::Permutation<loco::Domain::Filter> perm<exo::FilterLayout::OHWI>()
+{
+
+ // Make NHWC permutation for encoder and decoder
+ loco::Permutation<loco::Domain::Filter> OHWI; // a.k.a., NHWC
+
+ OHWI.axis(loco::FilterAxis::Count) = 0;
+ OHWI.axis(loco::FilterAxis::Height) = 1;
+ OHWI.axis(loco::FilterAxis::Width) = 2;
+ OHWI.axis(loco::FilterAxis::Depth) = 3;
+
+ return OHWI;
+}
+
+template <exo::DepthwiseFilterLayout T> loco::Permutation<loco::Domain::DepthwiseFilter> perm();
+
+template <>
+loco::Permutation<loco::Domain::DepthwiseFilter> perm<exo::DepthwiseFilterLayout::HWCM>()
+{
+ loco::Permutation<loco::Domain::DepthwiseFilter> HWCM;
+
+ HWCM.axis(loco::DepthwiseFilterAxis::Height) = 0;
+ HWCM.axis(loco::DepthwiseFilterAxis::Width) = 1;
+ HWCM.axis(loco::DepthwiseFilterAxis::Depth) = 2;
+ HWCM.axis(loco::DepthwiseFilterAxis::Multiplier) = 3;
+
+ return HWCM;
+}
+
+template <exo::MatrixLayout T> loco::Permutation<loco::Domain::Matrix> perm();
+
+template <> loco::Permutation<loco::Domain::Matrix> perm<exo::MatrixLayout::HW>()
+{
+ loco::Permutation<loco::Domain::Matrix> HW;
+
+ HW.axis(loco::MatrixAxis::Height) = 0;
+ HW.axis(loco::MatrixAxis::Width) = 1;
+
+ return HW;
+}
+
+template <> loco::Permutation<loco::Domain::Matrix> perm<exo::MatrixLayout::WH>()
+{
+ loco::Permutation<loco::Domain::Matrix> WH;
+
+ WH.axis(loco::MatrixAxis::Height) = 1;
+ WH.axis(loco::MatrixAxis::Width) = 0;
+
+ return WH;
+}
+
+} // namespace
+
+namespace exo
+{
+
+template <FeatureLayout T> loco::FeatureEncode *make_feature_encode(loco::Node *input_for_encode)
+{
+ EXO_ASSERT(input_for_encode != nullptr, "input should not be nullptr");
+ loco::Graph *g = input_for_encode->graph();
+
+ auto encoder = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>();
+
+ encoder->perm(perm<T>());
+
+ auto enc = g->nodes()->create<loco::FeatureEncode>();
+ enc->input(input_for_encode);
+ enc->encoder(std::move(encoder));
+
+ return enc;
+}
+
+template <FeatureLayout T> loco::FeatureDecode *make_feature_decode(loco::Node *input_for_decode)
+{
+ EXO_ASSERT(input_for_decode != nullptr, "input should not be nullptr");
+ loco::Graph *g = input_for_decode->graph();
+
+ auto decoder = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>();
+
+ decoder->perm(perm<T>());
+
+ auto dec = g->nodes()->create<loco::FeatureDecode>();
+ dec->input(input_for_decode);
+ dec->decoder(std::move(decoder));
+
+ return dec;
+}
+
+template <FilterLayout T> loco::FilterEncode *make_filter_encode(loco::Node *input_for_encode)
+{
+ EXO_ASSERT(input_for_encode != nullptr, "filter should not be nullptr");
+ loco::Graph *g = input_for_encode->graph();
+
+ auto encoder = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>();
+
+ encoder->perm(perm<T>());
+
+ auto enc = g->nodes()->create<loco::FilterEncode>();
+ enc->input(input_for_encode);
+ enc->encoder(std::move(encoder));
+
+ return enc;
+}
+
+template <FilterLayout T> loco::FilterDecode *make_filter_decode(loco::Node *input_for_decode)
+{
+ EXO_ASSERT(input_for_decode != nullptr, "filter should not be nullptr");
+ loco::Graph *g = input_for_decode->graph();
+
+ auto decoder = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Filter>>();
+
+ decoder->perm(perm<T>());
+
+ auto dec = g->nodes()->create<loco::FilterDecode>();
+ dec->input(input_for_decode);
+ dec->decoder(std::move(decoder));
+
+ return dec;
+}
+
+template <DepthwiseFilterLayout T>
+loco::DepthwiseFilterDecode *make_dw_filter_decode(loco::Node *input_for_decode)
+{
+ EXO_ASSERT(input_for_decode != nullptr, "filter should not be nullptr");
+ loco::Graph *g = input_for_decode->graph();
+
+ auto decoder = stdex::make_unique<loco::PermutingDecoder<loco::Domain::DepthwiseFilter>>();
+
+ decoder->perm(perm<T>());
+
+ auto dec = g->nodes()->create<loco::DepthwiseFilterDecode>();
+ dec->input(input_for_decode);
+ dec->decoder(std::move(decoder));
+
+ return dec;
+}
+
+template <MatrixLayout T> loco::MatrixEncode *make_matrix_encode(loco::Node *input_for_encode)
+{
+ EXO_ASSERT(input_for_encode != nullptr, "input should not be nullptr");
+ loco::Graph *g = input_for_encode->graph();
+
+ auto encoder = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Matrix>>();
+
+ encoder->perm(perm<T>());
+
+ auto enc = g->nodes()->create<loco::MatrixEncode>();
+ enc->input(input_for_encode);
+ enc->encoder(std::move(encoder));
+
+ return enc;
+}
+
+template <MatrixLayout T> loco::MatrixDecode *make_matrix_decode(loco::Node *input_for_decode)
+{
+ EXO_ASSERT(input_for_decode != nullptr, "input should not be nullptr");
+ loco::Graph *g = input_for_decode->graph();
+
+ auto decoder = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Matrix>>();
+
+ decoder->perm(perm<T>());
+
+ auto dec = g->nodes()->create<loco::MatrixDecode>();
+ dec->input(input_for_decode);
+ dec->decoder(std::move(decoder));
+
+ return dec;
+}
+
+// template instantiation
+template loco::FeatureEncode *
+make_feature_encode<FeatureLayout::NHWC>(loco::Node *input_for_encode);
+
+template loco::FeatureDecode *
+make_feature_decode<FeatureLayout::NHWC>(loco::Node *input_for_encode);
+
+template loco::FilterEncode *make_filter_encode<FilterLayout::HWIO>(loco::Node *input_for_encode);
+template loco::FilterDecode *make_filter_decode<FilterLayout::OHWI>(loco::Node *input_for_decode);
+
+template loco::DepthwiseFilterDecode *
+make_dw_filter_decode<DepthwiseFilterLayout::HWCM>(loco::Node *input_for_decode);
+
+template loco::MatrixEncode *make_matrix_encode<MatrixLayout::HW>(loco::Node *input_for_encode);
+template loco::MatrixEncode *make_matrix_encode<MatrixLayout::WH>(loco::Node *input_for_encode);
+template loco::MatrixDecode *make_matrix_decode<MatrixLayout::HW>(loco::Node *input_for_decode);
+template loco::MatrixDecode *make_matrix_decode<MatrixLayout::WH>(loco::Node *input_for_decode);
+
+} // namespace exo
diff --git a/compiler/exo/src/GraphBlock.h b/compiler/exo/src/GraphBlock.h
new file mode 100644
index 000000000..b771c821b
--- /dev/null
+++ b/compiler/exo/src/GraphBlock.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __GRAPH_BLOCK_H__
+#define __GRAPH_BLOCK_H__
+
+#include <loco.h>
+#include <loco/Service/ShapeInference.h>
+
+#include <oops/InternalExn.h>
+
+#include <functional>
+
+namespace exo
+{
+
+/// @brief feature layout of TFLITE file
+enum class FeatureLayout
+{
+ NHWC,
+};
+
+/// @brief Creates a loco::FeatureEncode with T layout (NHWC for tflite) and add it to graph.
+template <FeatureLayout T> loco::FeatureEncode *make_feature_encode(loco::Node *input_for_encode);
+
+/// @brief Creates a loco::FeatureDecode with T layout (NHWC for tflite) and add it to graph.
+template <FeatureLayout T> loco::FeatureDecode *make_feature_decode(loco::Node *input_for_decode);
+
+enum class FilterLayout
+{
+ OHWI, // a.k.a., NHWC, Tensorflow Lite uses this layout for filter
+ HWIO, // a.k.a., HWCN, Tensorflow uses this layout for filter
+};
+
+/// @brief Create a loco::FilterEncode of given layout
+template <FilterLayout T> loco::FilterEncode *make_filter_encode(loco::Node *input_for_encode);
+
+/// @brief Create a loco::FilterDecode of given layout
+template <FilterLayout T> loco::FilterDecode *make_filter_decode(loco::Node *input_for_decode);
+
+enum class DepthwiseFilterLayout
+{
+ HWCM,
+};
+
+/// @brief Create a loco::DepthwiseFilterDecode of given layout
+template <DepthwiseFilterLayout T>
+loco::DepthwiseFilterDecode *make_dw_filter_decode(loco::Node *input_for_decode);
+
+enum class MatrixLayout
+{
+ HW,
+ WH
+};
+
+/// @brief Create a loco::MatrixEncode of given layout
+template <MatrixLayout T> loco::MatrixEncode *make_matrix_encode(loco::Node *input_for_encode);
+
+/// @brief Create a loco::MatrixDecode of given layout
+template <MatrixLayout T> loco::MatrixDecode *make_matrix_decode(loco::Node *input_for_decode);
+
+} // exo
+
+//
+// DomainConverter
+//
+
+/**
+ * Some canonical nodes can have input of various loco::Domain, e.g., loco::Domain::Tensor,
+ * loco::Domain::Feature, etc. However, TFL node accepts only loco::Domain::Tensor.
+ * So, When converting such canonical node to TFL node and input(s) of a canonical node are not
+ * loco::Domain::Tensor, additional nodes need to be inserted.
+ *
+ * The following two classes helps this insertion.
+ *
+ * For example, in case of loco::Relu conversion,
+ *
+ * Before:
+ *
+ * A (output: feature) -- loco::ReLU --- B (input:feature)
+ *
+ * After:
+ *
+ * A -- loco::FeatureDecode -- locoex::TFLRelu -- loco::FeatureEncode --- B
+ *
+ * loco::ReLU (dead node)
+ */
+
+namespace exo
+{
+
+/**
+ * @brief Handles input(s) while converting a canonical node to TFL node(s).
+ * This class informs DomainConverter how to handle inputs of a specific canonical node.
+ */
+template <class CanonicalT, class TFLT> class InputHandler
+{
+public:
+ /**
+ * @brief Assign origin's inputs to replacer's inputs.
+ * (This is called when origin belongs in Tensor domain.)
+ */
+ virtual void handover(CanonicalT *origin, TFLT *replacer) = 0;
+
+ /**
+ * @brief Returns the list of inputs that needs to have FeatureDecode as its input.
+ * (This is called when origin belongs in Feature domain.)
+ */
+ virtual std::vector<loco::Node *> getInputsToConvert(CanonicalT *origin) = 0;
+
+ /// @brief Set the inputs of replacer to new_inputs
+ virtual void set(TFLT *replacer, std::vector<loco::Node *> &new_inputs) = 0;
+
+ /// @brief Set the inputs to nullptr
+ virtual void nullify(CanonicalT *origin) = 0;
+};
+
+/**
+ * @brief Class to handle domain conversion while converting a canonical node to TFL node(s)
+ */
+template <class CanonicalT, class TFLT> class DomainConverter
+{
+public:
+ template <FeatureLayout FeatureLayoutT>
+ TFLT *convert(CanonicalT *origin, InputHandler<CanonicalT, TFLT> &input_handler);
+};
+
+/**
+ * @brief Performs domain conversion
+ *
+ * 1. if origin belong to loco::Domain::Tensor, and replace origin to a TFL node.
+ * 2. if origin belong to loco::Domain::Feature, insert loco::FeatureDecode for input(s) and
+ * insert loco::FeatureEncode for output. Then replace origin to a TFL node.
+ *
+ * @return new TFL node; nullptr if shape of origin cannot be known
+ */
+template <class CanonicalT, class TFLT>
+template <FeatureLayout FeatureLayoutT>
+TFLT *DomainConverter<CanonicalT, TFLT>::convert(CanonicalT *origin,
+ InputHandler<CanonicalT, TFLT> &input_handler)
+{
+ static_assert(FeatureLayoutT == FeatureLayout::NHWC, "Feature layout should be NHWC");
+
+ if (!loco::shape_known(origin))
+ {
+ return nullptr;
+ }
+
+ auto tfl_node = origin->graph()->nodes()->template create<TFLT>();
+
+ // when the input is Tensor, just replace canonical node to TFL node.
+ if (loco::shape_get(origin).domain() == loco::Domain::Tensor)
+ {
+ input_handler.handover(origin, tfl_node);
+
+ loco::replace(origin).with(tfl_node);
+ input_handler.nullify(origin);
+
+ return tfl_node;
+ }
+ else if (loco::shape_get(origin).domain() == loco::Domain::Feature)
+ {
+ std::vector<loco::Node *> feature_decodes;
+
+ for (auto input : input_handler.getInputsToConvert(origin))
+ {
+ auto dec = make_feature_decode<FeatureLayoutT>(input);
+ feature_decodes.emplace_back(dec);
+ }
+
+ input_handler.set(tfl_node, feature_decodes);
+
+ auto enc = make_feature_encode<FeatureLayoutT>(tfl_node);
+
+ loco::replace(origin).with(enc);
+ input_handler.nullify(origin);
+
+ return tfl_node;
+ }
+ else
+ INTERNAL_EXN_V("Unsupported loco::Domain", oops::to_uint32(loco::shape_get(origin).domain()));
+}
+
+} // namespace exo
+
+#endif //__GRAPH_BLOCK_H__
diff --git a/compiler/exo/src/Knob.cpp b/compiler/exo/src/Knob.cpp
new file mode 100644
index 000000000..50d78f4b7
--- /dev/null
+++ b/compiler/exo/src/Knob.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Knob.h"
+
+#include <pepper/strcast.h>
+
+#include <iostream>
+#include <string>
+#include <map>
+
+// Basic Infrastructure to declare and access Knob values
+namespace
+{
+
+using KnobName = std::string;
+
+/**
+ * @brief Load configuration (from somewhere)
+ */
+struct KnobLoader
+{
+ virtual ~KnobLoader() = default;
+
+ virtual bool load(const KnobName &name, bool default_value) const = 0;
+};
+
+/**
+ * @brief Load configuration from environment variables
+ *
+ * Given a prefix P, EnvKnobLoader reads a configuration K from concat(P, K).
+ *
+ * For example, let us assume that P is "MY_" and K is "CONFIG".
+ *
+ * Then, EnvKnobLoader reads configuration CONFIG from environment variable MY_CONFIG.
+ */
+class EnvKnobLoader final : public KnobLoader
+{
+public:
+ EnvKnobLoader() = default;
+
+public:
+ bool load(const KnobName &knob_name, bool default_value) const override
+ {
+ auto envvar = _prefix + knob_name;
+ auto s = std::getenv(envvar.c_str());
+
+ return pepper::safe_strcast<int>(s, default_value ? 1 : 0) != 0;
+ }
+ void knob_set(const KnobName &knob_name, bool value) { _knob[knob_name] = value; }
+ void dialect_set(const exo::Dialect &dialect_name) { _prefix = _label[dialect_name]; }
+ bool knob_get(const KnobName &knob_name) { return load(knob_name, _knob[knob_name]); }
+
+private:
+ /// @brief Environment variable prefix
+ std::string _prefix;
+ std::map<KnobName, bool> _knob;
+ std::map<exo::Dialect, KnobName> _label = {{exo::Dialect::TFLITE, "TFL_"},
+ {exo::Dialect::CIRCLE, "CIRCLE_"}};
+};
+
+} // namespace
+
+namespace
+{
+
+EnvKnobLoader &knob_loader(void)
+{
+ // TODO separate "EXOTFLITE_" and "EXOCIRCLE_" when necessary
+ static EnvKnobLoader loader;
+ return loader;
+}
+
+} // namespace
+
+namespace exo
+{
+
+#define KNOB_BOOL(NAME, TFL_DEFAULT, CIRCLE_DEFAULT, DESC) \
+ template <> typename KnobTrait<Knob::NAME>::ValueType get<Knob::NAME>(void) \
+ { \
+ return ::knob_loader().knob_get(#NAME); \
+ }
+#include "Knob.lst"
+#undef KNOB_BOOL
+
+void set(Dialect d)
+{
+ ::knob_loader().dialect_set(d);
+ switch (d)
+ {
+ case Dialect::TFLITE:
+#define KNOB_BOOL(NAME, TFL_DEFAULT, CIRCLE_DEFAULT, DESC) \
+ ::knob_loader().knob_set(#NAME, TFL_DEFAULT);
+#include "Knob.lst"
+#undef KNOB_BOOL
+ break;
+ case Dialect::CIRCLE:
+#define KNOB_BOOL(NAME, TFL_DEFAULT, CIRCLE_DEFAULT, DESC) \
+ ::knob_loader().knob_set(#NAME, CIRCLE_DEFAULT);
+#include "Knob.lst"
+#undef KNOB_BOOL
+ break;
+ default:
+ std::runtime_error("UnKnown dialect");
+ }
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Knob.h b/compiler/exo/src/Knob.h
new file mode 100644
index 000000000..98613120c
--- /dev/null
+++ b/compiler/exo/src/Knob.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __KNOB_H__
+#define __KNOB_H__
+
+namespace exo
+{
+
+enum class Dialect
+{
+ TFLITE,
+ CIRCLE
+};
+
+enum class Knob
+{
+#define KNOB_BOOL(NAME, TFL_DEFAULT, CIRCLE_DEFAULT, DESC) NAME,
+#include "Knob.lst"
+#undef KNOB_BOOL
+};
+
+template <Knob K> struct KnobTrait;
+
+#define KNOB_BOOL(NAME, TFL_DEFAULT, CIRCLE_DEFAULT, DESC) \
+ template <> struct KnobTrait<Knob::NAME> \
+ { \
+ using ValueType = bool; \
+ };
+#include "Knob.lst"
+#undef KNOB_BOOL
+
+template <Knob K> typename KnobTrait<K>::ValueType get(void);
+void set(Dialect);
+
+} // namespace exo
+
+#endif // __KNOB_H__
diff --git a/compiler/exo/src/Knob.lst b/compiler/exo/src/Knob.lst
new file mode 100644
index 000000000..7f59c93f3
--- /dev/null
+++ b/compiler/exo/src/Knob.lst
@@ -0,0 +1,11 @@
+#ifndef KNOB_BOOL
+#error "KNOB_BOOL is not defined"
+#endif // KNOB_BOOL
+
+// KNOB_BOOL(KNOB_NAME, TFL_DEFAULT, CIRCLE_DEFAULT, DESCRIPTION)
+
+// Optimization pass
+KNOB_BOOL(UseFuseBiasAddPass, true, true, Fuse TFLAdd or TFLSub into TFLConv2D)
+KNOB_BOOL(UseFuseInstanceNormPass, false, true, Fuse InstanceNorm pattern)
+KNOB_BOOL(UseFuseReluPass, true, true, Fuse TFLAdd or TFLSub into TFLConv2D or so)
+KNOB_BOOL(UseFuseSquaredDifferencePass, false, true, Fuse SquaredDifference pattern)
diff --git a/compiler/exo/src/Log.cpp b/compiler/exo/src/Log.cpp
new file mode 100644
index 000000000..aa762968b
--- /dev/null
+++ b/compiler/exo/src/Log.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Log.h"
+
+#include <hermes/ConsoleReporter.h>
+#include <stdex/Memory.h>
+
+#include <cstdlib>
+#include <iostream>
+
+// TODO Extract these lexical conversion routines as a library
+namespace
+{
+
+/**
+ * @brief Convert C-string as a value of type T
+ *
+ * safecast(s, v) returns v if s is nullptr.
+ */
+template <typename T> T safecast(const char *, const T &);
+
+template <> bool safecast<bool>(const char *s, const bool &value)
+{
+ return (s == nullptr) ? value : (std::stoi(s) != 0);
+}
+
+} // namespace
+
+namespace exo
+{
+
+//
+// Logger
+//
+Logger::Logger(hermes::Context *ctx) { activate(ctx->sources(), ctx->bus()); }
+Logger::~Logger() { deactivate(); }
+
+//
+// LoggerConfig
+//
+LoggerConfig::LoggerConfig()
+{
+ // Turn on logging if EXO_LOG is set as non-zero value
+ _enabled = safecast<bool>(std::getenv("EXO_LOG"), false);
+}
+
+void LoggerConfig::configure(const hermes::Source *source, hermes::Source::Setting &setting) const
+{
+ // Let's ignore hermes::Sources if that is not a exo logger
+ if (auto logger = dynamic_cast<const Logger *>(source))
+ {
+ configure(logger, setting);
+ }
+}
+
+void LoggerConfig::configure(const Logger *, hermes::Source::Setting &setting) const
+{
+ if (_enabled)
+ {
+ // Enable all catagories
+ setting.accept_all();
+ }
+ else
+ {
+ // Disable all catagories
+ setting.reject_all();
+ }
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Log.h b/compiler/exo/src/Log.h
new file mode 100644
index 000000000..8ca38c3ec
--- /dev/null
+++ b/compiler/exo/src/Log.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOG_H__
+#define __LOG_H__
+
+#include "exo/LoggingContext.h"
+
+#include <hermes.h>
+
+namespace exo
+{
+
+/**
+ * @brief Logger Implementation
+ */
+class Logger final : public hermes::Source
+{
+public:
+ Logger(hermes::Context *ctx);
+ ~Logger();
+};
+
+/**
+ * @brief Logger Configuration
+ *
+ * Users are able to turn logging on/off via EXO_LOG environment variable.
+ */
+class LoggerConfig final : public hermes::Config
+{
+public:
+ LoggerConfig();
+
+public:
+ void configure(const hermes::Source *, hermes::Source::Setting &) const final;
+ void configure(const Logger *, hermes::Source::Setting &) const;
+
+private:
+ bool _enabled;
+};
+
+} // namespace exo
+
+/**
+ * HOW TO USE:
+ *
+ * LOGGER(l);
+ *
+ * INFO(l) << "Hello, World" << std::endl;
+ *
+ */
+#define LOGGER(name) ::exo::Logger name{::exo::LoggingContext::get()};
+
+// TODO Support FATAL, ERROR, WARN, and VERBOSE
+#define INFO(name) HERMES_INFO(name)
+
+// WARNING!
+//
+// THE CURRENT IMPLEMENTATION IS NOT THREAD SAFE.
+//
+
+#endif // __LOG_H__
diff --git a/compiler/exo/src/LogHelper.cpp b/compiler/exo/src/LogHelper.cpp
new file mode 100644
index 000000000..7520b7ec8
--- /dev/null
+++ b/compiler/exo/src/LogHelper.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LogHelper.h"
+
+namespace loco
+{
+
+std::ostream &operator<<(std::ostream &os, const loco::FeatureShape &feature_shape)
+{
+ os << "[" << feature_shape.count().value() << "," << feature_shape.height().value() << ","
+ << feature_shape.width().value() << "," << feature_shape.depth().value() << "]";
+ return os;
+}
+
+std::ostream &operator<<(std::ostream &os, const loco::FilterShape &filter_shape)
+{
+ os << "[" << filter_shape.height().value() << "," << filter_shape.width().value() << ","
+ << filter_shape.depth().value() << "," << filter_shape.count().value() << "]";
+ return os;
+}
+
+std::ostream &operator<<(std::ostream &os, const loco::TensorShape &tensor_shape)
+{
+ os << "[";
+ for (uint32_t r = 0; r < tensor_shape.rank(); ++r)
+ {
+ if (r)
+ os << ",";
+ os << tensor_shape.dim(r).value();
+ }
+ os << "]";
+ return os;
+}
+
+std::ostream &operator<<(std::ostream &os, const loco::Padding2D &pad)
+{
+ os << "[TLBR " << pad.top() << "," << pad.left() << "," << pad.bottom() << "," << pad.right()
+ << "]";
+
+ return os;
+}
+
+} // namespace loco
+
+std::ostream &operator<<(std::ostream &os, const std::vector<int64_t> &vi64)
+{
+ for (auto vi : vi64)
+ {
+ os << vi << " ";
+ }
+ return os;
+}
+
+#include "ExoFormattedGraph.h"
+
+namespace exo
+{
+
+FormattedGraph fmt(loco::Graph *g)
+{
+ auto node_summary_builder = stdex::make_unique<NodeSummaryBuilderFactory>();
+ return std::move(locop::fmt<locop::LinearV1>(g).with(std::move(node_summary_builder)));
+}
+
+} // namespace exo
diff --git a/compiler/exo-tflite/src/LogHelper.h b/compiler/exo/src/LogHelper.h
index 69d81af9e..69d81af9e 100644
--- a/compiler/exo-tflite/src/LogHelper.h
+++ b/compiler/exo/src/LogHelper.h
diff --git a/compiler/exo/src/LoggingContext.cpp b/compiler/exo/src/LoggingContext.cpp
new file mode 100644
index 000000000..1c14d97b9
--- /dev/null
+++ b/compiler/exo/src/LoggingContext.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "exo/LoggingContext.h"
+#include "Log.h" // To use LoggerConfig
+
+#include <hermes/ConsoleReporter.h>
+#include <stdex/Memory.h>
+
+namespace exo
+{
+
+hermes::Context *LoggingContext::get(void)
+{
+ static hermes::Context *ctx = nullptr;
+
+ if (ctx == nullptr)
+ {
+ ctx = new hermes::Context;
+ ctx->sinks()->append(stdex::make_unique<hermes::ConsoleReporter>());
+ ctx->config(stdex::make_unique<LoggerConfig>());
+ }
+
+ return ctx;
+}
+
+} // namespac exo
diff --git a/compiler/exo/src/Pass/FoldReshapeOfConstPass.cpp b/compiler/exo/src/Pass/FoldReshapeOfConstPass.cpp
new file mode 100644
index 000000000..0fdcea939
--- /dev/null
+++ b/compiler/exo/src/Pass/FoldReshapeOfConstPass.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FoldReshapeOfConstPass.h"
+
+#include "Check.h"
+
+#include "Dialect/IR/TFLNodes.h"
+#include "Dialect/IR/TFLNodeVisitor.h"
+
+#include <loco/Service/ShapeInference.h>
+
+#include <oops/InternalExn.h>
+
+namespace
+{
+
+/**
+ * @brief Check if node is TFLReshape and its input is TFLConst
+ * @return Casted TFLReshape for foldable candidate, nullptr otherwise
+ */
+locoex::TFLReshape *as_candidate(loco::Node *node)
+{
+ auto reshape = dynamic_cast<locoex::TFLReshape *>(node);
+ if (not reshape)
+ return nullptr;
+
+ // Only accept Constant input of Reshape
+ if (not dynamic_cast<locoex::TFLConst *>(reshape->tensor()))
+ return nullptr;
+
+ return reshape;
+}
+
+uint32_t volume(loco::Node *tensor_node)
+{
+ auto shape = loco::shape_get(tensor_node).as<loco::TensorShape>();
+
+ uint32_t vol = 1;
+ for (uint32_t axis = 0; axis < shape.rank(); ++axis)
+ vol *= shape.dim(axis).value();
+
+ return vol;
+}
+
+void fold_reshape_of_const(locoex::TFLReshape *reshape)
+{
+ const loco::DataType FLOAT32 = loco::DataType::FLOAT32;
+
+ auto const_orig = dynamic_cast<locoex::TFLConst *>(reshape->tensor());
+
+ // Exceptions
+ {
+ EXO_ASSERT(const_orig, "Only support for Reshape-Const pair");
+ // TODO support other data types
+ if (const_orig->dtype() != FLOAT32)
+ INTERNAL_EXN_V("NYI for this data type", oops::to_uint32(const_orig->dtype()));
+
+ if (volume(const_orig) != volume(reshape))
+ INTERNAL_EXN("New shape of Reshape is not matched");
+ }
+
+ auto new_shape = loco::shape_get(reshape).as<loco::TensorShape>();
+
+ // TFLConst to replace
+ auto const_new = reshape->graph()->nodes()->create<locoex::TFLConst>();
+
+ const_new->dtype(FLOAT32);
+ const_new->rank(new_shape.rank());
+ const_new->size<FLOAT32>(const_orig->size<FLOAT32>());
+ for (uint32_t axis = 0; axis < new_shape.rank(); ++axis)
+ const_new->dim(axis) = new_shape.dim(axis);
+
+ for (uint32_t i = 0; i < const_new->size<FLOAT32>(); ++i)
+ {
+ const_new->at<FLOAT32>(i) = const_orig->at<FLOAT32>(i);
+ }
+
+ // replace
+ loco::replace(reshape).with(const_new);
+}
+
+} // namespace
+
+namespace exo
+{
+
+bool FoldReshapeOfConstPass::run(loco::Graph *g)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ if (auto reshape = as_candidate(node))
+ {
+ fold_reshape_of_const(reshape);
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Pass/FoldReshapeOfConstPass.h b/compiler/exo/src/Pass/FoldReshapeOfConstPass.h
new file mode 100644
index 000000000..10f8004bf
--- /dev/null
+++ b/compiler/exo/src/Pass/FoldReshapeOfConstPass.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __PASS_FOLD_RESHAPE_OF_CONST_PASS_H__
+#define __PASS_FOLD_RESHAPE_OF_CONST_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace exo
+{
+
+/**
+ * @brief Class to fuse TFLReshape + TFLConst into one equivalent TFLConst
+ *
+ * <before>
+ * TFLConst --- TFLReshape --- Out
+ *
+ * <after>
+ * TFLConst --- TFLReshape ---
+ * TFLConst (new) ------------ Out
+ *
+ * TODO This pass is for temporary. Deprecate this pass.
+ */
+struct FoldReshapeOfConstPass final : public logo::Pass
+{
+ const char *name(void) const final { return "exo::FoldReshapeOfConstPass"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace exo
+
+#endif // __PASS_FOLD_RESHAPE_OF_CONST_PASS_H__
diff --git a/compiler/exo/src/Pass/FoldTransposeOfConstPass.cpp b/compiler/exo/src/Pass/FoldTransposeOfConstPass.cpp
new file mode 100644
index 000000000..005c42944
--- /dev/null
+++ b/compiler/exo/src/Pass/FoldTransposeOfConstPass.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FoldTransposeOfConstPass.h"
+
+#include "Check.h"
+
+#include "Dialect/IR/TFLNodes.h"
+#include "Dialect/IR/TFLNodeVisitor.h"
+
+// TODO remove dependency to angkor
+#include <nncc/core/ADT/tensor/IndexEnumerator.h>
+#include <nncc/core/ADT/tensor/LexicalLayout.h>
+
+#include <oops/InternalExn.h>
+
+namespace
+{
+
+/**
+ * @brief Check if node is TFLTranspose and its input is TFLConst
+ * @return Casted TFLTranspose for foldable candidate, nullptr otherwise
+ */
+locoex::TFLTranspose *as_candidate(loco::Node *node)
+{
+ auto transpose = dynamic_cast<locoex::TFLTranspose *>(node);
+ if (not transpose)
+ return nullptr;
+
+ // Only accept Constant input of Transpose
+ if (not dynamic_cast<locoex::TFLConst *>(transpose->a()))
+ return nullptr;
+
+ // Only accept Constant permutation of Transpose
+ if (not dynamic_cast<locoex::TFLConst *>(transpose->perm()))
+ return nullptr;
+
+ return transpose;
+}
+
+nncc::core::ADT::tensor::Shape angkor_shape(locoex::TFLConst *node)
+{
+ nncc::core::ADT::tensor::Shape ret;
+
+ ret.resize(node->rank());
+ for (uint32_t axis = 0; axis < node->rank(); ++axis)
+ {
+ ret.dim(axis) = node->dim(axis).value();
+ }
+
+ return ret;
+}
+
+void fold_transpose_of_const(locoex::TFLTranspose *transpose)
+{
+ const loco::DataType FLOAT32 = loco::DataType::FLOAT32;
+ const loco::DataType S32 = loco::DataType::S32;
+
+ auto const_orig = dynamic_cast<locoex::TFLConst *>(transpose->a());
+ auto perm = dynamic_cast<locoex::TFLConst *>(transpose->perm());
+
+ // Exceptions
+ {
+ EXO_ASSERT(const_orig, "Only support for Transpose-Const pair");
+ // TODO support other data types
+ if (const_orig->dtype() != FLOAT32)
+ INTERNAL_EXN_V("NYI for this data type", oops::to_uint32(const_orig->dtype()));
+
+ EXO_ASSERT(perm, "Only support for constant permutation for Transpose");
+ // TODO support other data types
+ if (perm->dtype() != S32)
+ INTERNAL_EXN_V("NYI for this data type", oops::to_uint32(perm->dtype()));
+
+ auto okay = [&]() {
+ if (perm->rank() != 1)
+ return false;
+ if (perm->dim(0).value() != const_orig->rank())
+ return false;
+ return true;
+ };
+ if (not okay())
+ INTERNAL_EXN("Input and permutation for Transpose is not congruent");
+ }
+
+ uint32_t rank = const_orig->rank();
+
+ // TFLConst to replace
+ auto const_new = transpose->graph()->nodes()->create<locoex::TFLConst>();
+
+ const_new->dtype(FLOAT32);
+ const_new->rank(rank);
+ const_new->size<FLOAT32>(const_orig->size<FLOAT32>());
+ for (uint32_t axis = 0; axis < rank; ++axis)
+ const_new->dim(axis) = const_orig->dim(perm->at<S32>(axis)).value();
+
+ // TODO remove dependency to angkor
+ auto shape_orig = angkor_shape(const_orig);
+ auto shape_new = angkor_shape(const_new);
+
+ nncc::core::ADT::tensor::LexicalLayout l;
+ nncc::core::ADT::tensor::IndexEnumerator e{shape_new};
+
+ for (; e.valid(); e.advance())
+ {
+ loco::TensorIndex index_new = e.current();
+ loco::TensorIndex index_orig;
+
+ // Set original index from matching new index
+ index_orig.resize(rank);
+ for (uint32_t axis = 0; axis < rank; ++axis)
+ index_orig.at(perm->at<S32>(axis)) = index_new.at(axis);
+
+ const_new->at<FLOAT32>(l.offset(shape_new, index_new)) =
+ const_orig->at<FLOAT32>(l.offset(shape_orig, index_orig));
+ }
+
+ // replace
+ loco::replace(transpose).with(const_new);
+}
+
+} // namespace
+
+namespace exo
+{
+
+bool FoldTransposeOfConstPass::run(loco::Graph *g)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ if (auto transpose = as_candidate(node))
+ {
+ fold_transpose_of_const(transpose);
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Pass/FoldTransposeOfConstPass.h b/compiler/exo/src/Pass/FoldTransposeOfConstPass.h
new file mode 100644
index 000000000..26656a118
--- /dev/null
+++ b/compiler/exo/src/Pass/FoldTransposeOfConstPass.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __PASS_FOLD_TRANSPOSE_OF_CONST_PASS_H__
+#define __PASS_FOLD_TRANSPOSE_OF_CONST_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace exo
+{
+
+/**
+ * @brief Class to fuse TFLTranspose + TFLConst into one equivalent TFLConst
+ *
+ * <before>
+ * TFLConst --- TFLTranspose --- Out
+ *
+ * <after>
+ * TFLConst --- TFLTranspose ---
+ * TFLConst (new) -------------- Out
+ *
+ * TODO This pass is for temporary. Deprecate this pass.
+ */
+struct FoldTransposeOfConstPass final : public logo::Pass
+{
+ const char *name(void) const final { return "exo::FoldTransposeOfConstPass"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace exo
+
+#endif // __PASS_FOLD_TRANSPOSE_OF_CONST_PASS_H__
diff --git a/compiler/exo/src/Pass/FuseBiasAddPass.cpp b/compiler/exo/src/Pass/FuseBiasAddPass.cpp
new file mode 100644
index 000000000..6338dff5d
--- /dev/null
+++ b/compiler/exo/src/Pass/FuseBiasAddPass.cpp
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FuseBiasAddPass.h"
+
+#include "Dialect/IR/TFLNodes.h"
+#include "Dialect/IR/TFLDialect.h"
+#include "Dialect/IR/TFLNodeVisitor.h"
+
+#include <loco/Service/TypeInference.h>
+#include <loco/Service/ShapeInference.h>
+
+#include <oops/InternalExn.h>
+
+#include <set>
+
+/*
+ Note: Terms for variables in this implementation is as follows:
+
+ ex) subgraph handled: TFLConv2D -------- TFLAdd
+ (or TFLDepthwiseConv2D) (or TFLSub)
+ | |
+ \|/ \|/
+ variable name : former latter
+ Type : FormerT LatterT
+ (shortened name from Mixin) (template type)
+*/
+namespace
+{
+
+using FormerT = locoex::TFLNodeMixin<locoex::TFLNodeTrait::Bias>;
+
+loco::Node *as_loco_node(FormerT *former)
+{
+ auto loco_node = dynamic_cast<loco::Node *>(former);
+ assert(loco_node != nullptr);
+
+ return loco_node;
+}
+
+locoex::TFLConst *get_const(loco::Node *x, loco::Node *y)
+{
+ if (auto const_node = dynamic_cast<locoex::TFLConst *>(x))
+ return const_node;
+ else if (auto const_node = dynamic_cast<locoex::TFLConst *>(y))
+ return const_node;
+
+ return nullptr;
+}
+
+FormerT *get_former(loco::Node *x, loco::Node *y)
+{
+ if (auto node = dynamic_cast<FormerT *>(x))
+ return node;
+ else if (auto node = dynamic_cast<FormerT *>(y))
+ return node;
+
+ return nullptr;
+}
+
+/// @brief Finds input that is TFLConst and set it to new_input
+void set_const_input(locoex::TFLNode *node, locoex::TFLConst *new_input)
+{
+ if (auto add = dynamic_cast<locoex::TFLAdd *>(node))
+ {
+ if (dynamic_cast<locoex::TFLConst *>(add->x()))
+ add->x(new_input);
+ else if (dynamic_cast<locoex::TFLConst *>(add->y()))
+ add->y(new_input);
+ else
+ assert(false and "One node should be TFLConst");
+
+ return;
+ }
+
+ if (auto sub = dynamic_cast<locoex::TFLSub *>(node))
+ {
+ if (dynamic_cast<locoex::TFLConst *>(sub->x()))
+ sub->x(new_input);
+ else if (dynamic_cast<locoex::TFLConst *>(sub->y()))
+ sub->y(new_input);
+ else
+ assert(false and "One node should be TFLConst");
+
+ return;
+ }
+
+ assert(false and "Param should be TFLAdd or TFLSub");
+}
+
+/**
+ * @brief Creates a TFLConst whose shape is [to] and values are all const_node->at(0),
+ * where const_node has only one element(a scalar or a tensor of shape [1])
+ */
+locoex::TFLConst *create_widened(locoex::TFLConst *const_node, uint32_t to)
+{
+ auto const_shape = loco::shape_get(const_node).as<loco::TensorShape>();
+
+ assert(const_shape.rank() == 0 or (const_shape.rank() == 1 and const_shape.dim(0) == 1));
+
+ auto g = const_node->graph();
+
+ auto widened_const = g->nodes()->create<locoex::TFLConst>();
+ {
+ widened_const->dtype(loco::DataType::FLOAT32);
+ widened_const->rank(1);
+ widened_const->dim(0) = to;
+ widened_const->size<loco::DataType::FLOAT32>(to);
+ for (uint32_t x = 0; x < to; x++)
+ widened_const->at<loco::DataType::FLOAT32>(x) = const_node->at<loco::DataType::FLOAT32>(0);
+ }
+ return widened_const;
+}
+
+template <typename TFLType> float calc(float, float);
+
+template <> float calc<locoex::TFLAdd>(float x, float y) { return x + y; }
+template <> float calc<locoex::TFLSub>(float x, float y) { return x - y; }
+
+template <class LatterT> class Fuser
+{
+public:
+ Fuser(LatterT *latter)
+ {
+ static_assert(std::is_same<LatterT, locoex::TFLAdd>::value ||
+ std::is_same<LatterT, locoex::TFLSub>::value,
+ "wrong template type");
+
+ _latter = latter;
+ _graph = _latter->graph();
+ _const_node = get_const(_latter->x(), _latter->y());
+ _former = get_former(_latter->x(), _latter->y());
+
+ assert(_const_node && _former);
+ }
+
+ void fuse(void);
+
+private:
+ loco::Graph *_graph;
+ LatterT *_latter;
+ locoex::TFLConst *_const_node;
+ FormerT *_former;
+
+ locoex::TFLConst *create_fused_bias_const();
+};
+
+// instantiation
+template class Fuser<locoex::TFLAdd>;
+template class Fuser<locoex::TFLSub>;
+
+template <class LatterT> locoex::TFLConst *Fuser<LatterT>::create_fused_bias_const()
+{
+ // we have to create a new bias const by adding/substracting bias and const node (of TFLAdd or
+ // TFLSub)
+ auto bias = loco::must_cast<locoex::TFLConst *>(_former->bias());
+ assert(bias->dtype() == loco::DataType::FLOAT32 &&
+ _const_node->dtype() == loco::DataType::FLOAT32);
+
+ assert(bias->rank() == 1 && _const_node->rank() == 1);
+ assert(bias->dim(0) == _const_node->dim(0));
+
+ // build a new bias const
+ auto new_bias = _graph->nodes()->create<locoex::TFLConst>();
+ {
+ new_bias->dtype(loco::DataType::FLOAT32);
+
+ new_bias->rank(1);
+ new_bias->dim(0) = bias->dim(0);
+
+ new_bias->size<loco::DataType::FLOAT32>(bias->dim(0).value());
+
+ for (uint32_t x = 0; x < bias->dim(0).value(); x++)
+ new_bias->at<loco::DataType::FLOAT32>(x) = calc<LatterT>(
+ bias->at<loco::DataType::FLOAT32>(x), _const_node->at<loco::DataType::FLOAT32>(x));
+ }
+
+ return new_bias;
+}
+
+// FuseBiasAddPass works when former->fusedActivationFunction() == NONE
+bool check_act_func(FormerT *former)
+{
+ using FusedActFuncMixin = locoex::TFLNodeMixin<locoex::TFLNodeTrait::FusedActFunc>;
+
+ if (auto node = dynamic_cast<FusedActFuncMixin *>(former))
+ return node->fusedActivationFunction() == locoex::FusedActFunc::NONE;
+ else
+ return true;
+}
+
+template <class LatterT> void set_act_func(FormerT *former, LatterT *latter)
+{
+ using FusedActFuncMixin = locoex::TFLNodeMixin<locoex::TFLNodeTrait::FusedActFunc>;
+
+ if (auto node = dynamic_cast<FusedActFuncMixin *>(former))
+ node->fusedActivationFunction(latter->fusedActivationFunction());
+}
+
+// instantiation
+template void set_act_func(FormerT *, locoex::TFLAdd *);
+template void set_act_func(FormerT *, locoex::TFLSub *);
+
+/**
+ * @brief Fuse TFLAdd or TFLSub (latter) into TFLConv2d or TFLDepthwiseConv2D (former).
+ * All conditions should be checked before calling this.
+ *
+ * @note TFLAdd can have fused activation function (let's call this FAF for simplicity).
+ *
+ * Conv2D's FAF | TFLAdd's FAF => FAF after fusing TFLAdd into TFLConv2D
+ * ----------------|--------------- --------------------------------------
+ * NONE | NONE, RELU or RELU6 => TFLAdd's FAF
+ * other than NONE | anything => cannot be fused
+ */
+template <class LatterT> void Fuser<LatterT>::fuse(void)
+{
+ // check fused activation function
+ {
+ assert(check_act_func(_former));
+
+ set_act_func<LatterT>(_former, _latter);
+ }
+
+ auto new_bias = create_fused_bias_const();
+
+ // replace node with new_bias
+ // note that loco::replace() is not used because bias could be input of other op just in case
+ _former->bias(new_bias);
+
+ // remove TFLAdd or TFLSub node
+ loco::replace(_latter).with(as_loco_node(_former));
+ _latter->x(nullptr);
+ _latter->y(nullptr);
+}
+
+struct Collector final : public locoex::TFLNodeMutableVisitor<void>
+{
+ template <class LatterT>
+ void setCandidate(FormerT *former, LatterT *latter, locoex::TFLConst *const_node)
+ {
+ static_assert(std::is_same<LatterT, locoex::TFLAdd>::value ||
+ std::is_same<LatterT, locoex::TFLSub>::value,
+ "wrong template type");
+
+ if (!check_act_func(former))
+ return;
+
+ auto depth =
+ loco::shape_get(as_loco_node(former)).template as<loco::TensorShape>().dim(3).value();
+ auto const_shape = loco::shape_get(const_node).template as<loco::TensorShape>();
+
+ if (const_shape.rank() == 1 and const_shape.dim(0) == depth)
+ {
+ candidates.insert(latter);
+ }
+ // when Const has only one value, create a new const with shape [depth]
+ else if (const_shape.rank() == 0 or (const_shape.rank() == 1 and const_shape.dim(0) == 1))
+ {
+ if (!(loco::dtype_get(as_loco_node(former)) == loco::DataType::FLOAT32))
+ INTERNAL_EXN_V("Unsupported data type",
+ oops::to_uint32(loco::dtype_get(as_loco_node(former))));
+ if (!(const_node->dtype() == loco::DataType::FLOAT32))
+ INTERNAL_EXN_V("Unsupported data type", oops::to_uint32(const_node->dtype()));
+
+ auto new_bias_node = create_widened(const_node, depth);
+
+ // Replacing TFLConst input of TFLAdd or TFLSub.
+ // Note that calling loco::replace(const_node).with(new_bias_node) could be dangerous
+ // because const_node could be the input of many nodes
+ set_const_input(latter, new_bias_node);
+
+ candidates.insert(latter);
+ }
+ }
+
+ void visit(locoex::TFLAdd *latter) final
+ {
+ auto former = get_former(latter->x(), latter->y());
+ auto const_node = get_const(latter->x(), latter->y());
+
+ if (former && const_node)
+ setCandidate<locoex::TFLAdd>(former, latter, const_node);
+ }
+
+ void visit(locoex::TFLSub *latter) final
+ {
+ // TFLSub, of which x() = TFLConv2D or TFLDepthwiseConv2D, y() = TFLConst, is fusing target
+ auto former = dynamic_cast<FormerT *>(latter->x());
+ auto const_node = dynamic_cast<locoex::TFLConst *>(latter->y());
+
+ if (former && const_node)
+ setCandidate<locoex::TFLSub>(former, latter, const_node);
+ }
+
+ void visit(locoex::TFLNode *) final { return; }
+
+ std::set<locoex::TFLNode *> candidates;
+};
+
+struct Performer final : public locoex::TFLNodeMutableVisitor<void>
+{
+ void visit(locoex::TFLAdd *latter) final
+ {
+ assert(get_former(latter->x(), latter->y()));
+
+ Fuser<locoex::TFLAdd> fuser(latter);
+ fuser.fuse();
+ }
+
+ void visit(locoex::TFLSub *latter) final
+ {
+ assert(get_former(latter->x(), latter->y()));
+
+ Fuser<locoex::TFLSub> fuser(latter);
+ fuser.fuse();
+ }
+
+ void visit(locoex::TFLNode *) final { assert(false && "should not be called"); }
+};
+
+} // namespace
+
+namespace exo
+{
+
+bool FuseBiasAddPass::run(loco::Graph *g)
+{
+ Collector collector;
+
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ if (node->dialect() == locoex::TFLDialect::get())
+ {
+ auto tfl_node = loco::must_cast<locoex::TFLNode *>(node);
+ tfl_node->accept(&collector);
+ }
+ }
+
+ Performer performer;
+
+ for (auto node : collector.candidates)
+ {
+ node->accept(&performer);
+ }
+
+ return collector.candidates.size() > 0;
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Pass/FuseBiasAddPass.h b/compiler/exo/src/Pass/FuseBiasAddPass.h
new file mode 100644
index 000000000..68e624c6b
--- /dev/null
+++ b/compiler/exo/src/Pass/FuseBiasAddPass.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __PASS_FUSE_BIASADD_PASS_H__
+#define __PASS_FUSE_BIASADD_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace exo
+{
+
+/**
+ * @brief Class to fuse TFLAdd or TFLSub into Bias input of the following ops:
+ * - TFLConv2D, TFLDepthwiseConv2D
+ * - TODO Consider to add FullyConnected, etc.
+ *
+ * Case 1. Conv2D and TFLAdd
+ *
+ * BEFORE:
+ *
+ * TFLConst A (a scalar or a tensor of shape [1] or [depth of TFLConv2D])
+ * |
+ * Foo -- TFLConv2D -- TFLAdd (or TFLSub) -- Bar
+ * |
+ * TFLConst B --+ (bias)
+ *
+ * AFTER:
+ * Foo ----- TFLConv2D ----- Bar
+ * |
+ * TFLConst A' --+ (bias)
+ *
+ * TFLConst B (dead node)
+ *
+ * TFLAdd (or TFLSub) (dead node)
+ *
+ * @note TFLSub, of which x() == TFLConv2D and y() == TFLConst, will be fused.
+ * If x() == TFLConst and y() == TFLConv2D, it won't be fused.
+ */
+struct FuseBiasAddPass final : public logo::Pass
+{
+ const char *name(void) const final { return "exo::FuseBiasAddPass"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace exo
+
+#endif // __PASS_FUSE_BIASADD_PASS_H__
diff --git a/compiler/exo/src/Pass/FuseBiasAddPass.test.cpp b/compiler/exo/src/Pass/FuseBiasAddPass.test.cpp
new file mode 100644
index 000000000..c464d765f
--- /dev/null
+++ b/compiler/exo/src/Pass/FuseBiasAddPass.test.cpp
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FuseBiasAddPass.h"
+
+#include "Dialect/IR/TFLNodes.h"
+#include "TestGraph.h"
+#include "TestHelper.h"
+
+#include <loco.h>
+
+#include <gtest/gtest.h>
+
+namespace
+{
+
+void init(loco::Pull *pull)
+{
+ pull->dtype(loco::DataType::FLOAT32);
+ pull->shape({2, 3, 3, 2});
+}
+
+/// @brief Initializes TFLConv2D and related filter and bias
+void init(locoex::TFLConv2D *conv2d, locoex::TFLConst *filter, locoex::TFLConst *bias)
+{
+ // set conv2d
+ {
+ conv2d->fusedActivationFunction(locoex::FusedActFunc::NONE);
+ conv2d->padding(locoex::Padding::VALID);
+ }
+
+ // set filter
+ {
+ filter->dtype(loco::DataType::FLOAT32);
+ filter->shape({2, 3, 3, 2});
+ filter->size<loco::DataType::FLOAT32>(2 * 3 * 3 * 2);
+
+ for (uint32_t x = 0; x < 2 * 3 * 3 * 2; x++)
+ filter->at<loco::DataType::FLOAT32>(x) = 0.0;
+ }
+
+ // set bias
+ {
+ bias->dtype(loco::DataType::FLOAT32);
+ bias->shape({2});
+ bias->size<loco::DataType::FLOAT32>(2);
+
+ for (uint32_t x = 0; x < 2; x++)
+ bias->at<loco::DataType::FLOAT32>(x) = 0.0;
+ }
+}
+
+template <class T> void init(T *node, locoex::FusedActFunc f)
+{
+ static_assert(std::is_same<T, locoex::TFLAdd>::value || std::is_same<T, locoex::TFLSub>::value,
+ "wrong template type");
+
+ node->fusedActivationFunction(f);
+}
+
+/// @brief Initializes one param of TFLAdd or TFLSub
+void init(locoex::TFLConst *addsub_param)
+{
+ // set addsub_param : y() value of TFLAdd or TFLSub
+ addsub_param->dtype(loco::DataType::FLOAT32);
+ addsub_param->shape({2});
+ addsub_param->size<loco::DataType::FLOAT32>(2);
+
+ for (uint32_t x = 0; x < 2; x++)
+ addsub_param->at<loco::DataType::FLOAT32>(x) = (x + 1) * 1.5; // 1.5, 3
+}
+
+} // namespace
+
+// A case when
+// - TFLConv2D has bias (0, 0)
+// - TFLAdd, of which x() or y() == TFLConv2D
+// - Another param of TFLAdd is TFLConst, (1.5, 3)
+//
+// After fusion, bias shold be (1.5, 3)
+TEST(FuseBiasAddPassTest, Conv2D_Add_01_basic)
+{
+ exo::test::TestGraph g;
+ auto filter = g.append<locoex::TFLConst>();
+ auto bias = g.append<locoex::TFLConst>();
+ auto conv2d = g.append<locoex::TFLConv2D>(g.pull, filter, bias);
+
+ auto add_y = g.append<locoex::TFLConst>();
+ auto add = g.append<locoex::TFLAdd>(conv2d, add_y);
+
+ g.complete(add);
+
+ init(g.pull);
+ init(conv2d, filter, bias);
+ init(add, locoex::FusedActFunc::NONE);
+ init(add_y);
+
+ // let's run fusion
+ {
+ exo::test::TypeShapeReadyPhase test_phase;
+
+ test_phase.add_pass<exo::FuseBiasAddPass>();
+ test_phase.run(g.graph());
+ }
+
+ auto a_conv2d = exo::test::find_first_node_bytype<locoex::TFLConv2D>(g.graph());
+ ASSERT_TRUE(a_conv2d != nullptr);
+
+ auto a_bias = dynamic_cast<locoex::TFLConst *>(a_conv2d->bias());
+ ASSERT_TRUE(a_bias != nullptr);
+
+ ASSERT_TRUE(a_bias->dim(0) == 2);
+ ASSERT_FLOAT_EQ(a_bias->at<loco::DataType::FLOAT32>(0),
+ bias->at<loco::DataType::FLOAT32>(0) + add_y->at<loco::DataType::FLOAT32>(0));
+ ASSERT_FLOAT_EQ(a_bias->at<loco::DataType::FLOAT32>(1),
+ bias->at<loco::DataType::FLOAT32>(1) + add_y->at<loco::DataType::FLOAT32>(1));
+}
+
+// A case when
+// - TFLConv2D has bias (0, 0)
+// - TFLAdd, of which x() or y() == TFLConv2D
+// - Another param of TFLAdd is TFLConst, (1.5) <-- scalar
+//
+// After fusion, bias shold be (1.5, 1.5)
+TEST(FuseBiasAddPassTest, Conv2D_Add_02_TFLAdd_y_is_scalar)
+{
+ exo::test::TestGraph g;
+ auto filter = g.append<locoex::TFLConst>();
+ auto bias = g.append<locoex::TFLConst>();
+ auto conv2d = g.append<locoex::TFLConv2D>(g.pull, filter, bias);
+
+ auto add_y = g.append<locoex::TFLConst>();
+ auto add = g.append<locoex::TFLAdd>(conv2d, add_y);
+
+ g.complete(add);
+
+ init(g.pull);
+ init(conv2d, filter, bias); // channel of conv2d is 2
+
+ {
+ // Size of this TFLConst is 1.
+ // Note that this should be widened later to the shape of [channel of Conv2D], which is [2]
+ add_y->dtype(loco::DataType::FLOAT32);
+ add_y->shape({1});
+ add_y->size<loco::DataType::FLOAT32>(1);
+ add_y->at<loco::DataType::FLOAT32>(0) = 1.5;
+ }
+ init(add, locoex::FusedActFunc::NONE);
+
+ // let's run fusion
+ {
+ exo::test::TypeShapeReadyPhase test_phase;
+
+ test_phase.add_pass<exo::FuseBiasAddPass>();
+ test_phase.run(g.graph());
+ }
+
+ auto a_conv2d = exo::test::find_first_node_bytype<locoex::TFLConv2D>(g.graph());
+ ASSERT_TRUE(a_conv2d != nullptr);
+
+ auto a_bias = dynamic_cast<locoex::TFLConst *>(a_conv2d->bias());
+ ASSERT_TRUE(a_bias != nullptr);
+
+ ASSERT_TRUE(a_bias->dim(0) == 2);
+ ASSERT_FLOAT_EQ(a_bias->at<loco::DataType::FLOAT32>(0),
+ bias->at<loco::DataType::FLOAT32>(0) + 1.5);
+ ASSERT_FLOAT_EQ(a_bias->at<loco::DataType::FLOAT32>(1),
+ bias->at<loco::DataType::FLOAT32>(1) + 1.5);
+}
+
+// A case when
+// - TFLConv2D has bias (0, 0)
+// - TFLSub.x() == TFLConv2D
+// - TFLSub.y() == TFLConst, (1.5, 3)
+//
+// After fusion, bias shold be (-1.5, -3)
+TEST(FuseBiasAddPassTest, Conv2D_Sub_01_basic)
+{
+ exo::test::TestGraph g;
+ auto filter = g.append<locoex::TFLConst>();
+ auto bias = g.append<locoex::TFLConst>();
+ auto conv2d = g.append<locoex::TFLConv2D>(g.pull, filter, bias);
+
+ auto sub_y = g.append<locoex::TFLConst>();
+ auto sub = g.append<locoex::TFLSub>(conv2d, sub_y);
+
+ g.complete(sub);
+
+ init(g.pull);
+ init(conv2d, filter, bias);
+ init(sub, locoex::FusedActFunc::NONE);
+ init(sub_y);
+
+ // let's run fusion
+ {
+ exo::test::TypeShapeReadyPhase test_phase;
+
+ test_phase.add_pass<exo::FuseBiasAddPass>();
+ test_phase.run(g.graph());
+ }
+
+ auto a_conv2d = exo::test::find_first_node_bytype<locoex::TFLConv2D>(g.graph());
+ ASSERT_TRUE(a_conv2d != nullptr);
+
+ auto a_bias = dynamic_cast<locoex::TFLConst *>(a_conv2d->bias());
+ ASSERT_TRUE(a_bias != nullptr);
+
+ ASSERT_TRUE(a_bias->dim(0) == 2);
+ ASSERT_FLOAT_EQ(a_bias->at<loco::DataType::FLOAT32>(0),
+ bias->at<loco::DataType::FLOAT32>(0) - sub_y->at<loco::DataType::FLOAT32>(0));
+ ASSERT_FLOAT_EQ(a_bias->at<loco::DataType::FLOAT32>(1),
+ bias->at<loco::DataType::FLOAT32>(1) - sub_y->at<loco::DataType::FLOAT32>(1));
+}
+
+// A case when TFLConv2D is input of TFLSub but fusion cannot be performed.
+// - TFLSub.x() == TFLConst
+// - TFLSub.y() == TFLConv2D
+//
+// Here, TFLSub cannot be fused into TFLConst. To be fused, TFLSub.x() should be TFLConv2D and
+// TFLSub.y() should be TFLConst. So fusion will NOT happen.
+TEST(FuseBiasAddPassTest, Conv2D_Sub_02_fusing_will_not_performed)
+{
+ exo::test::TestGraph g;
+ auto filter = g.append<locoex::TFLConst>();
+ auto bias = g.append<locoex::TFLConst>();
+ auto conv2d = g.append<locoex::TFLConv2D>(g.pull, filter, bias);
+
+ auto sub_y = g.append<locoex::TFLConst>();
+ auto sub = g.append<locoex::TFLSub>(sub_y, conv2d); // This WON'T be fused
+
+ g.complete(sub);
+
+ init(g.pull);
+ init(conv2d, filter, bias);
+ init(sub, locoex::FusedActFunc::NONE);
+ init(sub_y);
+
+ // let's run fusion
+ {
+ exo::test::TypeShapeReadyPhase test_phase;
+
+ test_phase.add_pass<exo::FuseBiasAddPass>();
+ test_phase.run(g.graph());
+ }
+
+ auto a_conv2d = exo::test::find_first_node_bytype<locoex::TFLConv2D>(g.graph());
+ ASSERT_TRUE(a_conv2d != nullptr);
+
+ auto a_bias = dynamic_cast<locoex::TFLConst *>(a_conv2d->bias());
+ ASSERT_TRUE(a_bias != nullptr);
+
+ ASSERT_TRUE(a_bias->dim(0) == 2);
+ ASSERT_FLOAT_EQ(0, a_bias->at<loco::DataType::FLOAT32>(0));
+ ASSERT_FLOAT_EQ(0, a_bias->at<loco::DataType::FLOAT32>(1));
+
+ auto a_sub = exo::test::find_first_node_bytype<locoex::TFLSub>(g.graph());
+ ASSERT_TRUE(a_sub != nullptr);
+ ASSERT_TRUE(a_sub->y() == a_conv2d); // Checking 'not-fused' state
+}
+
+// A case when
+// - TFLConv2D has an activation function with Relu
+// - TFLAdd, has no activation function
+//
+// No fusion should happen
+TEST(FuseBiasAddPassTest, Regression_Conv2D_Add_fused_action_00)
+{
+ exo::test::TestGraph g;
+ auto filter = g.append<locoex::TFLConst>();
+ auto bias = g.append<locoex::TFLConst>();
+ auto conv2d = g.append<locoex::TFLConv2D>(g.pull, filter, bias);
+
+ auto add_y = g.append<locoex::TFLConst>();
+ auto add = g.append<locoex::TFLAdd>(conv2d, add_y);
+
+ g.complete(add);
+
+ init(g.pull);
+ init(conv2d, filter, bias);
+ init(add, locoex::FusedActFunc::NONE);
+ init(add_y);
+
+ // Updating Fused Activation for this test
+ conv2d->fusedActivationFunction(locoex::FusedActFunc::RELU);
+
+ // let's run fusion
+ {
+ exo::test::TypeShapeReadyPhase test_phase;
+
+ test_phase.add_pass<exo::FuseBiasAddPass>();
+ test_phase.run(g.graph());
+ }
+
+ auto a_conv2d = exo::test::find_first_node_bytype<locoex::TFLConv2D>(g.graph());
+ ASSERT_TRUE(a_conv2d != nullptr);
+ ASSERT_TRUE(a_conv2d->fusedActivationFunction() == locoex::FusedActFunc::RELU);
+
+ auto an_add = exo::test::find_first_node_bytype<locoex::TFLAdd>(g.graph());
+ ASSERT_TRUE(an_add != nullptr);
+ ASSERT_TRUE(an_add->fusedActivationFunction() == locoex::FusedActFunc::NONE);
+
+ ASSERT_TRUE(an_add->x() == a_conv2d or an_add->y() == a_conv2d);
+}
+
+// A case when
+// - TFLConv2D has NONE activation function
+// - TFLAdd has Relu activation function
+//
+// TFLConv2D should have Relu activation function, TFLAdd is fused into bias input
+TEST(FuseBiasAddPassTest, Regression_Conv2D_Add_fused_action_01)
+{
+ exo::test::TestGraph g;
+ auto filter = g.append<locoex::TFLConst>();
+ auto bias = g.append<locoex::TFLConst>();
+ auto conv2d = g.append<locoex::TFLConv2D>(g.pull, filter, bias);
+
+ auto add_y = g.append<locoex::TFLConst>();
+ auto add = g.append<locoex::TFLAdd>(conv2d, add_y);
+
+ g.complete(add);
+
+ init(g.pull);
+ init(conv2d, filter, bias);
+ init(add, locoex::FusedActFunc::RELU);
+ init(add_y);
+
+ // let's run fusion
+ {
+ exo::test::TypeShapeReadyPhase test_phase;
+
+ test_phase.add_pass<exo::FuseBiasAddPass>();
+ test_phase.run(g.graph());
+ }
+
+ auto a_conv2d = exo::test::find_first_node_bytype<locoex::TFLConv2D>(g.graph());
+ ASSERT_TRUE(a_conv2d != nullptr);
+
+ auto a_bias = dynamic_cast<locoex::TFLConst *>(a_conv2d->bias());
+ ASSERT_TRUE(a_bias != nullptr);
+
+ ASSERT_TRUE(a_bias->dim(0) == 2);
+ ASSERT_FLOAT_EQ(a_bias->at<loco::DataType::FLOAT32>(0),
+ bias->at<loco::DataType::FLOAT32>(0) + add_y->at<loco::DataType::FLOAT32>(0));
+ ASSERT_FLOAT_EQ(a_bias->at<loco::DataType::FLOAT32>(1),
+ bias->at<loco::DataType::FLOAT32>(1) + add_y->at<loco::DataType::FLOAT32>(1));
+
+ ASSERT_TRUE(a_conv2d->fusedActivationFunction() == locoex::FusedActFunc::RELU);
+}
diff --git a/compiler/exo/src/Pass/FuseInstanceNormPass.cpp b/compiler/exo/src/Pass/FuseInstanceNormPass.cpp
new file mode 100644
index 000000000..04d4a62cd
--- /dev/null
+++ b/compiler/exo/src/Pass/FuseInstanceNormPass.cpp
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FuseInstanceNormPass.h"
+
+#include "Dialect/IR/TFLNodes.h"
+#include "Dialect/IR/CircleNodes.h"
+
+#include <loco/Service/ShapeInference.h>
+
+#include <cassert>
+#include <set>
+
+// Helper to find commutative node's arguments
+namespace
+{
+
+/**
+ * INTRODUCTION
+ * Binary operation f(x,y) is 'commutative' when
+ * f(x,y) == f(y,x) holds for all x, y.
+ * For examples, ADD, MUL and SQUARED_DIFFERENCE are commutative.
+ * These helpers make it easy to find commutative arguemnts of commtative node.
+ *
+ * HOW TO USE
+ * COMM_NODE *node;
+ * ARG_TYPE_1 *arg1;
+ * ARG_TYPE_2 *arg2;
+ *
+ * bool ok = fill(&arg1, &arg2).with_commutative_args_of(node);
+ *
+ * Result
+ * If 'node's commutative argument types are actually {ARG_TYPE_1, ARG_TYPE_2}
+ * (as a set), 'arg1' and 'arg2' set as actual 'node's arguemnts with matching
+ * type, and return value 'ok' is true.
+ * Otherwise, 'arg1' and 'arg2' not changed, 'ok' is false.
+ */
+
+template <class ARG_TYPE_1, class ARG_TYPE_2> class NodeFiller final
+{
+public:
+ NodeFiller(ARG_TYPE_1 **arg_1, ARG_TYPE_2 **arg_2) : _arg_1(arg_1), _arg_2(arg_2)
+ {
+ // DO NOTHING
+ }
+
+ /**
+ * @return true When 'node's argument types are 'ARG_TYPE_1' and 'ARG_TYPE_2'
+ * In such case, it assign '_arg_1' and '_arg_2' to actual arguments
+ *
+ * @return false When 'node's argument types are NOT matched with 'ARG_TYPE_*'
+ * In such case, it does not amend '_arg_1' and '_arg_2'
+ *
+ * @require COMM_NODE has member x() and y()
+ */
+ template <class COMM_NODE> bool with_commutative_args_of(const COMM_NODE *node);
+
+private:
+ ARG_TYPE_1 **_arg_1;
+ ARG_TYPE_2 **_arg_2;
+};
+
+template <class ARG_TYPE_1, class ARG_TYPE_2>
+inline NodeFiller<ARG_TYPE_1, ARG_TYPE_2> fill(ARG_TYPE_1 **arg_1, ARG_TYPE_2 **arg_2)
+{
+ return NodeFiller<ARG_TYPE_1, ARG_TYPE_2>{arg_1, arg_2};
+}
+
+template <class ARG_TYPE_1, class ARG_TYPE_2>
+template <class COMM_NODE>
+bool NodeFiller<ARG_TYPE_1, ARG_TYPE_2>::with_commutative_args_of(const COMM_NODE *node)
+{
+ // Case 1) X == ARG_TYPE_1 / Y == ARG_TYPE_2
+ {
+ auto x = dynamic_cast<ARG_TYPE_1 *>(node->x());
+ auto y = dynamic_cast<ARG_TYPE_2 *>(node->y());
+
+ if (x && y)
+ {
+ *_arg_1 = x;
+ *_arg_2 = y;
+ return true;
+ }
+ }
+
+ // Case 2) X == ARG_TYPE_2 / Y == ARG_TYPE_1
+ {
+ auto x = dynamic_cast<ARG_TYPE_2 *>(node->x());
+ auto y = dynamic_cast<ARG_TYPE_1 *>(node->y());
+
+ if (x && y)
+ {
+ *_arg_1 = y;
+ *_arg_2 = x;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace
+
+// Helper to check detail
+namespace
+{
+
+/// @return true When node has shape of '1 x .. x 1 x depth'
+bool is_1D_with_dummy_dim(locoex::TFLConst *node, uint32_t depth)
+{
+ auto rank = node->rank();
+ uint32_t axis;
+ for (axis = 0; axis < rank - 1; ++axis)
+ {
+ if (node->dim(axis).value() != 1)
+ return false;
+ }
+ return node->dim(axis).value() == depth;
+}
+
+bool is_instance_mean(locoex::TFLMean *mean)
+{
+ //
+ // CHECK 1) input is rank 4
+ //
+ auto input = mean->input();
+ if (not loco::shape_known(input))
+ return false;
+ auto input_shape = loco::shape_get(input).as<loco::TensorShape>();
+ if (input_shape.rank() != 4)
+ return false;
+
+ //
+ // CHECK 2) 'reduction indices' is TFLConst of value [1,2], that is HW of NHWC
+ //
+ // TODO Support equivalent case, like [-3,-2]
+ // TODO Support non-Const case?
+ // TODO What if input is NCHW format in Circle?
+ auto red_indices = dynamic_cast<locoex::TFLConst *>(mean->reduction_indices());
+ if (not red_indices)
+ return false;
+ if (red_indices->rank() != 1)
+ return false;
+ std::set<int32_t> red_indices_set;
+ {
+ // TODO Currently only support S32, support other types
+ assert(red_indices->dtype() == loco::DataType::S32);
+ for (uint32_t i = 0; i < red_indices->dim(0).value(); ++i)
+ red_indices_set.insert(red_indices->at<loco::DataType::S32>(i));
+ }
+ if (red_indices_set.size() != 2)
+ return false;
+ if (red_indices_set.find(1) == red_indices_set.end())
+ return false;
+ if (red_indices_set.find(2) == red_indices_set.end())
+ return false;
+
+ //
+ // CHECK 3) keep_dims == true (?)
+ //
+ // We only have case of 'keep_dims == true' so far, but it might be okay with 'keep_dims == false'
+ // TODO Check this fact, and if true, return true regardless of keep_dims
+ return mean->keep_dims();
+}
+
+} // namespace
+
+// Helper to fuse Instance Norm
+namespace
+{
+
+/**
+ * SUBGRAPH PATTERN
+ *
+ * - Below diagram shows Instance Norm pattern to fuse.
+ * - Execution dependency order is top to the bottom.
+ * - Node name is matched with variable name of InstanceNormPattern class.
+ * - Usually, first word of node name (variable name) is node type. For e.g.
+ * variable 'mean_as_variance' is pointer to TFLMean.
+ * - (Item in parenthesis) means actually exist, but not having a name and
+ * not a variable of InstanceNormPattern class.
+ *
+ * TODO support other semantically same patterns for instance norm
+ *
+ * [In]
+ * |
+ * V
+ * +----------- ifm -----+ (reduction indicies)
+ * | | | |
+ * | | V V
+ * | | mean_of_ifm ----------------+
+ * | V | |
+ * | sqdiff <--+ (reduction indicies) |
+ * | | | |
+ * | V | |
+ * | mean_as_variance <---+ const_as_epsilon |
+ * | | | |
+ * | V | |
+ * | add_as_variance <--------+ |
+ * | | |
+ * | V |
+ * | rsqrt const_as_gamma |
+ * | | | |
+ * | V | |
+ * | mul_gamma <--+ |
+ * | | | |
+ * V V V |
+ * mul_as_scaled_ifm mul_as_scaled_mean <-------------+
+ * | |
+ * | const_as_beta |
+ * | | V
+ * | +------> sub
+ * V |
+ * add_as_terminal <----------+
+ * |
+ * V
+ * [Out]
+ */
+class InstanceNormPattern final
+{
+public:
+ InstanceNormPattern(locoex::TFLAdd *candidate)
+ {
+ assert(candidate);
+ add_as_terminal = candidate;
+ }
+
+public:
+ bool matched();
+ bool matched() const { return _matched; }
+
+public:
+ // Context
+ loco::Node *ifm = nullptr;
+ locoex::TFLMean *mean_of_ifm = nullptr;
+ locoex::TFLSquaredDifference *sqdiff = nullptr;
+ locoex::TFLMean *mean_as_variance = nullptr;
+ locoex::TFLConst *const_as_epsilon = nullptr;
+ locoex::TFLAdd *add_as_variance = nullptr;
+ locoex::TFLRsqrt *rsqrt = nullptr;
+ locoex::TFLConst *const_as_gamma = nullptr;
+ locoex::TFLMul *mul_gamma = nullptr;
+ locoex::TFLMul *mul_as_scaled_ifm = nullptr;
+ locoex::TFLMul *mul_as_scaled_mean = nullptr;
+ locoex::TFLConst *const_as_beta = nullptr;
+ locoex::TFLSub *sub = nullptr;
+ locoex::TFLAdd *add_as_terminal = nullptr;
+
+private:
+ bool _matched = false;
+};
+
+bool InstanceNormPattern::matched()
+{
+ if (_matched)
+ return true;
+
+#define CHECK_OR_FALSE(condition) \
+ if (not(condition)) \
+ return false;
+
+ // Check order is DFS
+
+ CHECK_OR_FALSE(fill(&mul_as_scaled_ifm, &sub).with_commutative_args_of(add_as_terminal));
+ CHECK_OR_FALSE(fill(&ifm, &mul_gamma).with_commutative_args_of(mul_as_scaled_ifm));
+
+ CHECK_OR_FALSE(loco::shape_known(ifm));
+ auto ifm_shape = loco::shape_get(ifm);
+ CHECK_OR_FALSE(ifm_shape.domain() == loco::Domain::Tensor);
+ auto ifm_tensor_shape = ifm_shape.as<loco::TensorShape>();
+ CHECK_OR_FALSE(ifm_tensor_shape.rank() == 4);
+ uint32_t ifm_channel_depth = ifm_tensor_shape.dim(3).value();
+
+ CHECK_OR_FALSE(fill(&rsqrt, &const_as_gamma).with_commutative_args_of(mul_gamma));
+ CHECK_OR_FALSE(is_1D_with_dummy_dim(const_as_gamma, ifm_channel_depth));
+
+ add_as_variance = dynamic_cast<locoex::TFLAdd *>(rsqrt->x());
+ CHECK_OR_FALSE(add_as_variance);
+
+ CHECK_OR_FALSE(
+ fill(&mean_as_variance, &const_as_epsilon).with_commutative_args_of(add_as_variance));
+
+ CHECK_OR_FALSE(const_as_epsilon->dtype() == loco::DataType::FLOAT32);
+ // TODO Support regarding broadcast
+ CHECK_OR_FALSE(const_as_epsilon->size<loco::DataType::FLOAT32>() == 1);
+
+ CHECK_OR_FALSE(is_instance_mean(mean_as_variance));
+ sqdiff = dynamic_cast<locoex::TFLSquaredDifference *>(mean_as_variance->input());
+ CHECK_OR_FALSE(sqdiff);
+
+ loco::Node *ifm_should_be = nullptr;
+ CHECK_OR_FALSE(fill(&ifm_should_be, &mean_of_ifm).with_commutative_args_of(sqdiff));
+ CHECK_OR_FALSE(ifm == ifm_should_be);
+ CHECK_OR_FALSE(is_instance_mean(mean_of_ifm));
+ CHECK_OR_FALSE(ifm == mean_of_ifm->input());
+
+ const_as_beta = dynamic_cast<locoex::TFLConst *>(sub->x());
+ CHECK_OR_FALSE(const_as_beta);
+ CHECK_OR_FALSE(is_1D_with_dummy_dim(const_as_beta, ifm_channel_depth));
+
+ mul_as_scaled_mean = dynamic_cast<locoex::TFLMul *>(sub->y());
+ CHECK_OR_FALSE(mul_as_scaled_mean);
+
+ locoex::TFLMul *mul_gamma_should_be = nullptr;
+ locoex::TFLMean *mean_of_ifm_should_be = nullptr;
+ CHECK_OR_FALSE(fill(&mul_gamma_should_be, &mean_of_ifm_should_be)
+ .with_commutative_args_of(mul_as_scaled_mean));
+ CHECK_OR_FALSE(mul_gamma == mul_gamma_should_be);
+ CHECK_OR_FALSE(mean_of_ifm == mean_of_ifm_should_be);
+#undef CHECK_OR_FALSE
+ _matched = true;
+ return true;
+}
+
+/**
+ * Instance norm pattern would be fused like following diagram:
+ *
+ * [In] --------------------------- CircleInstanceNorm --- [Out]
+ * / /
+ * const_as_gamma --- TFLReshape --- /
+ * /
+ * const_as_beta ---- TFLReshape ---
+ *
+ * Note
+ * - 'const_as_gamma' and 'const_as_beta' are from original graph
+ * - Value of 'const_as_epsilon' would be copied to CircleInstanceNorm's attribute
+ * - TFLReshape is added as CircleInstanceNorm only accept 1D tensor
+ * - 'TFLConst --- TFLReshape' is expected to be fused in constant folding for Reshape
+ */
+void fuse_instance_norm(const InstanceNormPattern &p)
+{
+ assert(p.matched());
+
+ auto graph = p.add_as_terminal->graph();
+
+ // Make reshape for gamma & beta
+ auto reshape_gamma = graph->nodes()->create<locoex::TFLReshape>();
+ auto reshape_beta = graph->nodes()->create<locoex::TFLReshape>();
+ {
+ auto ifm_shape = loco::shape_get(p.ifm).as<loco::TensorShape>();
+ uint32_t ifm_channel_depth = ifm_shape.dim(3).value();
+
+ int32_t new_shape[1] = {static_cast<int32_t>(ifm_channel_depth)};
+
+ reshape_gamma->tensor(p.const_as_gamma);
+ reshape_beta->tensor(p.const_as_beta);
+
+ locoex::set_new_shape(reshape_gamma, new_shape, 1);
+ locoex::set_new_shape(reshape_beta, new_shape, 1);
+ }
+
+ // Make Instance Norm to replace
+ auto instance_norm = graph->nodes()->create<locoex::CircleInstanceNorm>();
+ instance_norm->input(p.ifm);
+ instance_norm->gamma(reshape_gamma);
+ instance_norm->beta(reshape_beta);
+ float epsilon = p.const_as_epsilon->at<loco::DataType::FLOAT32>(0);
+ instance_norm->epsilon(epsilon);
+ instance_norm->fusedActivationFunction(p.add_as_terminal->fusedActivationFunction());
+
+ replace(p.add_as_terminal).with(instance_norm);
+}
+
+} // namespace
+
+namespace exo
+{
+
+bool FuseInstanceNormPass::run(loco::Graph *g)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ auto add = dynamic_cast<locoex::TFLAdd *>(node);
+ if (not add)
+ continue;
+
+ InstanceNormPattern pattern(add);
+ if (not pattern.matched())
+ continue;
+
+ fuse_instance_norm(pattern);
+ changed = true;
+ }
+
+ return changed;
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Pass/FuseInstanceNormPass.h b/compiler/exo/src/Pass/FuseInstanceNormPass.h
new file mode 100644
index 000000000..e6361021c
--- /dev/null
+++ b/compiler/exo/src/Pass/FuseInstanceNormPass.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __FUSE_INSTANCE_NORM_PASS_H__
+#define __FUSE_INSTANCE_NORM_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace exo
+{
+
+/**
+ * @brief Class to fuse certain pattern of subgraph into CircleInstanceNorm
+ * with auxiliary nodes
+ *
+ * For detailed subgraph pattern to be fused, please check its implementation.
+ */
+struct FuseInstanceNormPass final : public logo::Pass
+{
+ const char *name(void) const final { return "exo::FuseInstanceNormPass"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace exo
+
+#endif // __FUSE_INSTANCE_NORM_PASS_H__
diff --git a/compiler/exo/src/Pass/FuseReluPass.cpp b/compiler/exo/src/Pass/FuseReluPass.cpp
new file mode 100644
index 000000000..ae430bc46
--- /dev/null
+++ b/compiler/exo/src/Pass/FuseReluPass.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FuseReluPass.h"
+
+#include "Dialect/IR/TFLNodes.h"
+#include "Dialect/IR/TFLDialect.h"
+#include "Dialect/IR/TFLNodeVisitor.h"
+
+#include <set>
+
+namespace
+{
+
+bool is_pred_fusable(loco::Node *node)
+{
+ using namespace locoex;
+
+ auto fusable_node = dynamic_cast<TFLNodeMixin<TFLNodeTrait::FusedActFunc> *>(node);
+
+ return (fusable_node and fusable_node->fusedActivationFunction() == FusedActFunc::NONE);
+};
+
+struct Collector final : public locoex::TFLNodeMutableVisitor<void>
+{
+ void visit(locoex::TFLRelu *node) final
+ {
+ if (is_pred_fusable(node->features()))
+ candidates.insert(node);
+ }
+
+ void visit(locoex::TFLRelu6 *node) final
+ {
+ if (is_pred_fusable(node->features()))
+ candidates.insert(node);
+ }
+
+ void visit(locoex::TFLNode *) final { return; }
+
+ std::set<locoex::TFLNode *> candidates;
+};
+
+void set_activation_fusion(loco::Node *node, locoex::FusedActFunc f)
+{
+ using namespace locoex;
+
+ if (auto fusable_node = dynamic_cast<TFLNodeMixin<TFLNodeTrait::FusedActFunc> *>(node))
+ fusable_node->fusedActivationFunction(f);
+ else
+ assert(false);
+}
+
+struct Performer final : public locoex::TFLNodeMutableVisitor<void>
+{
+ void visit(locoex::TFLRelu *the_relu) final
+ {
+ set_activation_fusion(the_relu->features(), locoex::FusedActFunc::RELU);
+
+ loco::replace(the_relu).with(the_relu->features());
+ the_relu->features(nullptr);
+ }
+
+ void visit(locoex::TFLRelu6 *the_relu6) final
+ {
+ set_activation_fusion(the_relu6->features(), locoex::FusedActFunc::RELU6);
+
+ loco::replace(the_relu6).with(the_relu6->features());
+ the_relu6->features(nullptr);
+ }
+
+ void visit(locoex::TFLNode *) final { assert(false && "should not be called"); }
+};
+
+} // namespace
+
+namespace exo
+{
+
+bool FuseReluPass::run(loco::Graph *g)
+{
+ Collector collector;
+
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ if (node->dialect() == locoex::TFLDialect::get())
+ {
+ auto tfl_node = loco::must_cast<locoex::TFLNode *>(node);
+ tfl_node->accept(&collector);
+ }
+ }
+
+ Performer performer;
+
+ for (auto node : collector.candidates)
+ {
+ node->accept(&performer);
+ }
+
+ return collector.candidates.size() > 0;
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Pass/FuseReluPass.h b/compiler/exo/src/Pass/FuseReluPass.h
new file mode 100644
index 000000000..1cd276b29
--- /dev/null
+++ b/compiler/exo/src/Pass/FuseReluPass.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __PASS_FUSE_RELU_PASS_H__
+#define __PASS_FUSE_RELU_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace exo
+{
+
+/**
+ * @brief Class to fuse TFLRelu or TFLRelu6 into the TensorFlow Lite ops below:
+ *
+ * ADD, AVERAGE_POOL_2D, CONCATENATION, CONV_2D, DEPTHWISE_CONV_2D,
+ * FULLY_CONNECTED, L2_NORMALIZATION, L2_POOL_2D, MAX_POOL_2D, MUL
+ */
+struct FuseReluPass final : public logo::Pass
+{
+ const char *name(void) const final { return "exo::FuseReluPass"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace exo
+
+#endif // __PASS_FUSE_RELU_PASS_H__
diff --git a/compiler/exo/src/Pass/FuseReluPass.test.cpp b/compiler/exo/src/Pass/FuseReluPass.test.cpp
new file mode 100644
index 000000000..6f83d4dd0
--- /dev/null
+++ b/compiler/exo/src/Pass/FuseReluPass.test.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FuseReluPass.h"
+
+#include "Dialect/IR/TFLNodes.h"
+#include "TestGraph.h"
+
+#include <loco.h>
+#include <logo/RemoveDeadNodePass.h>
+
+#include <gtest/gtest.h>
+
+#include <type_traits> // for std::is_same
+
+namespace
+{
+
+void init(loco::Pull *pull)
+{
+ pull->dtype(loco::DataType::FLOAT32);
+ pull->shape({2, 3, 3, 2});
+}
+
+/// @brief Initializes TFLConv2D and related filter and bias
+void init(locoex::TFLConv2D *conv2d, locoex::TFLConst *filter, locoex::TFLConst *bias)
+{
+ // set conv2d
+ {
+ conv2d->fusedActivationFunction(locoex::FusedActFunc::NONE);
+ conv2d->padding(locoex::Padding::VALID);
+ }
+
+ // set filter
+ {
+ filter->dtype(loco::DataType::FLOAT32);
+ filter->shape({2, 3, 3, 2});
+ filter->size<loco::DataType::FLOAT32>(2 * 3 * 3 * 2);
+
+ for (uint32_t x = 0; x < 2 * 3 * 3 * 2; x++)
+ filter->at<loco::DataType::FLOAT32>(x) = 0.0;
+ }
+
+ // set bias
+ {
+ bias->dtype(loco::DataType::FLOAT32);
+ bias->shape({2});
+ bias->size<loco::DataType::FLOAT32>(2);
+
+ for (uint32_t x = 0; x < 2; x++)
+ bias->at<loco::DataType::FLOAT32>(x) = 0.0;
+ }
+}
+
+} // namespace
+
+/// Test code called by TEST(..)
+/// This tests whether Conv2D - FusedTFLType is fused.
+template <class FusedTFLType, locoex::FusedActFunc FusedActFunc> void test()
+{
+ static_assert((std::is_same<FusedTFLType, locoex::TFLRelu>::value &&
+ FusedActFunc == locoex::FusedActFunc::RELU) ||
+ (std::is_same<FusedTFLType, locoex::TFLRelu6>::value &&
+ FusedActFunc == locoex::FusedActFunc::RELU6),
+ "wrong template type");
+
+ exo::test::TestGraph g;
+ {
+ auto filter = g.append<locoex::TFLConst>();
+ auto bias = g.append<locoex::TFLConst>();
+ auto conv2d = g.append<locoex::TFLConv2D>(g.pull, filter, bias);
+
+ auto fusable_node = g.append<FusedTFLType>(conv2d);
+
+ g.complete(fusable_node);
+
+ init(g.pull);
+ init(conv2d, filter, bias);
+ }
+
+ // let's run fusion
+ {
+ exo::test::TypeShapeReadyPhase test_phase;
+
+ test_phase.add_pass<exo::FuseReluPass>();
+ test_phase.add_pass<logo::RemoveDeadNodePass>(); // to remove TFLRelu
+ test_phase.run(g.graph());
+ }
+
+ auto a_conv2d = exo::test::find_first_node_bytype<locoex::TFLConv2D>(g.graph());
+ ASSERT_TRUE(a_conv2d != nullptr);
+ ASSERT_TRUE(a_conv2d->fusedActivationFunction() == FusedActFunc);
+
+ auto removed_fusable_node = exo::test::find_first_node_bytype<FusedTFLType>(g.graph());
+ ASSERT_TRUE(removed_fusable_node == nullptr);
+}
+
+// A case with Conv2D-Relu
+TEST(FuseReluTest, Conv2D_Relu_basic) { test<locoex::TFLRelu, locoex::FusedActFunc::RELU>(); }
+
+// A case with Conv2D-Relu6
+TEST(FuseReluTest, Conv2D_Relu6_basic) { test<locoex::TFLRelu6, locoex::FusedActFunc::RELU6>(); }
diff --git a/compiler/exo/src/Pass/FuseRsqrtPass.cpp b/compiler/exo/src/Pass/FuseRsqrtPass.cpp
new file mode 100644
index 000000000..08d704139
--- /dev/null
+++ b/compiler/exo/src/Pass/FuseRsqrtPass.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FuseRsqrtPass.h"
+
+#include "Check.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+namespace
+{
+
+/**
+ * @return Casted TFLDiv for fusable candidate, nullptr otherwise
+ *
+ * This helper checkes fusability with following conditions:
+ * - TFLDiv has no activation
+ * - TFLDiv's first argument is TFLConst with all value 1
+ * - TFLDiv's second argument is TFLSqrt
+ */
+locoex::TFLDiv *as_candidate(loco::Node *node)
+{
+ auto div = dynamic_cast<locoex::TFLDiv *>(node);
+ if (not div)
+ return nullptr;
+
+ // Cannot fuse Div with activation function
+ if (div->fusedActivationFunction() != locoex::FusedActFunc::NONE)
+ return nullptr;
+
+ auto const_one = dynamic_cast<locoex::TFLConst *>(div->x());
+ if (not const_one)
+ return nullptr;
+
+ const loco::DataType FLOAT32 = loco::DataType::FLOAT32;
+ // TODO Support other dtype
+ EXO_ASSERT(const_one->dtype() == FLOAT32, "Only support FLOAT32 now");
+ for (uint32_t i = 0; i < const_one->size<FLOAT32>(); ++i)
+ if (const_one->at<FLOAT32>(i) != 1.0f)
+ return nullptr;
+
+ auto sqrt = dynamic_cast<locoex::TFLSqrt *>(div->y());
+ if (not sqrt)
+ return nullptr;
+
+ return div;
+}
+
+void fuse_rsqrt(locoex::TFLDiv *div)
+{
+ auto sqrt = dynamic_cast<locoex::TFLSqrt *>(div->y());
+ EXO_ASSERT(sqrt, "sqrt should be valid at this point");
+
+ // TFLRsqrt to replace
+ auto rsqrt = div->graph()->nodes()->create<locoex::TFLRsqrt>();
+ rsqrt->x(sqrt->x());
+
+ // replace
+ loco::replace(div).with(rsqrt);
+}
+
+} // namespace
+
+namespace exo
+{
+
+bool FuseRsqrtPass::run(loco::Graph *g)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ if (auto div = as_candidate(node))
+ {
+ fuse_rsqrt(div);
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Pass/FuseRsqrtPass.h b/compiler/exo/src/Pass/FuseRsqrtPass.h
new file mode 100644
index 000000000..1e60e4a49
--- /dev/null
+++ b/compiler/exo/src/Pass/FuseRsqrtPass.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __FUSE_RSQRT_PASS_H__
+#define __FUSE_RSQRT_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace exo
+{
+
+/**
+ * @brief Class to fuse TFLSqrt that is divided(TFLDiv) by 1, into TFLRsqrt
+ *
+ * <BEFORE>
+ *
+ * TFLConst(1) ------
+ * \
+ * A --- TFLSqrt --- TFLDiv --- B
+ *
+ * <AFTER>
+ *
+ * A --- TFLRsqrt --- B
+ */
+struct FuseRsqrtPass final : public logo::Pass
+{
+ const char *name(void) const final { return "exo::FuseRsqrtPass"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace exo
+
+#endif // __FUSE_RSQRT_PASS_H__
diff --git a/compiler/exo/src/Pass/FuseSquaredDifferencePass.cpp b/compiler/exo/src/Pass/FuseSquaredDifferencePass.cpp
new file mode 100644
index 000000000..3f985a505
--- /dev/null
+++ b/compiler/exo/src/Pass/FuseSquaredDifferencePass.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FuseSquaredDifferencePass.h"
+
+#include "Check.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+namespace
+{
+
+/**
+ * @return Casted TFLMul for fusable candidate, nullptr otherwise
+ *
+ * This helper checkes fusability with following conditions:
+ * - TFLMul has no activation
+ * - TFLMul's first and second arguments are equal and TFLSub
+ */
+locoex::TFLMul *as_candidate(loco::Node *node)
+{
+ auto mul = dynamic_cast<locoex::TFLMul *>(node);
+ if (not mul)
+ return nullptr;
+
+ // Cannot fuse mul with activation function
+ if (mul->fusedActivationFunction() != locoex::FusedActFunc::NONE)
+ return nullptr;
+
+ if (mul->x() != mul->y())
+ return nullptr;
+
+ if (not dynamic_cast<locoex::TFLSub *>(mul->x()))
+ return nullptr;
+
+ return mul;
+}
+
+void fuse_squared_difference(locoex::TFLMul *mul)
+{
+ auto sub = dynamic_cast<locoex::TFLSub *>(mul->x());
+ EXO_ASSERT(sub, "sub should be valid at this point");
+
+ // TFLSquaredDifference to replace
+ auto sq_diff = mul->graph()->nodes()->create<locoex::TFLSquaredDifference>();
+ sq_diff->x(sub->x());
+ sq_diff->y(sub->y());
+
+ // replace
+ loco::replace(mul).with(sq_diff);
+}
+
+} // namespace
+
+namespace exo
+{
+
+bool FuseSquaredDifferencePass::run(loco::Graph *g)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ if (auto mul = as_candidate(node))
+ {
+ fuse_squared_difference(mul);
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Pass/FuseSquaredDifferencePass.h b/compiler/exo/src/Pass/FuseSquaredDifferencePass.h
new file mode 100644
index 000000000..dbc15149f
--- /dev/null
+++ b/compiler/exo/src/Pass/FuseSquaredDifferencePass.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __FUSE_SQUARED_DIFFERENCE_PASS_H__
+#define __FUSE_SQUARED_DIFFERENCE_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace exo
+{
+
+/**
+ * @brief Class to fuse SquaredDifference pattern
+ *
+ * <BEFORE>
+ *
+ * A --- TFLSub --- TFLMul --- C
+ * / \ /
+ * B ---- -----
+ *
+ * <AFTER>
+ *
+ * A --- TFLSquaredDifference --- C
+ * /
+ * B ----
+ */
+struct FuseSquaredDifferencePass final : public logo::Pass
+{
+ const char *name(void) const final { return "exo::FuseSquaredDifferencePass"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace exo
+
+#endif // __FUSE_SQUARED_DIFFERENCE_PASS_H__
diff --git a/compiler/exo/src/Pass/MergeConcatNodesPass.cpp b/compiler/exo/src/Pass/MergeConcatNodesPass.cpp
new file mode 100644
index 000000000..8945fcfce
--- /dev/null
+++ b/compiler/exo/src/Pass/MergeConcatNodesPass.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MergeConcatNodesPass.h"
+#include "Dialect/IR/TFLNodes.h"
+
+#include <oops/InternalExn.h>
+
+#include <vector>
+
+namespace
+{
+
+bool canMerge(locoex::TFLConcatenation *node1, locoex::TFLConcatenation *node2)
+{
+ if (node1->fusedActivationFunction() != node2->fusedActivationFunction())
+ return false;
+
+ if (node1->axis() != node2->axis())
+ return false;
+
+ switch (node1->fusedActivationFunction())
+ {
+ case locoex::FusedActFunc::NONE:
+ case locoex::FusedActFunc::RELU:
+ case locoex::FusedActFunc::RELU6:
+ return true;
+
+ // case locoex::FusedActFunc::TANH:
+ // return false;
+
+ default:
+ INTERNAL_EXN_V("Unknown FusedActFunc", oops::to_uint32(node1->fusedActivationFunction()));
+ }
+}
+
+/**
+ * @brief Collect all the inputs of newly created TFLConcatenation nodes
+ *
+ * in:0 -------------------------------\
+ * in:1 ---- TFLConcatenation:0 -------- TFLConcatenation:3 --- C
+ * (axis = 0, NONE) (axis = 0, NONE)
+ * in:2 ---/ /
+ * in:3 ---- TFLConcatenation:1 ------/
+ * (axis = 1, NONE) /
+ * in:4 ---/ /
+ * in:5 ---- TFLConcatenation:2 ---/
+ * (axis = 0, RELU)
+ * in:6 ---/
+ *
+ * For exmaple, if graph is like above, dfs(TFLConcatenation:3) will
+ * return [in:0, in:1, in:2, TFLConcatenation:1, TFLConcatenation:2]
+ *
+ * TFLConcatenation:0 can be merged to TFLConcatenation:3,
+ * because axis and fusedActivationFunction are same.
+ * It means that [in:1, in:2] will be linked as inputs of new TFLConcatenation.
+ *
+ * However, TFLConcatenation:1 and TFLConcatenation:2 cannot be merged to
+ * TFLConcatenation:3 because axis and fusedActivationFunction of each are different.
+ * So [in:3, in:4, in:5, in:6] will not be linked as inputs of new TFLConcatenation
+ * and [TFLConcatenation:1, TFLConcatenation:2] will be linked instead.
+ *
+ * Therefore, inputs of newly created TFLConcatenation node for merging
+ * TFLConcatenation:3 will be [in:0, in:1, in:2, TFLConcatenation:1, TFLConcatenation:2]
+ * and dfs(TFLConcatenation:3) will return it.
+ *
+ *
+ * @note The input nodes should be traversed by LRV,
+ * which is from left to right (input:0 --> input:N)
+ */
+std::vector<loco::Node *> dfs(locoex::TFLConcatenation *root)
+{
+ std::vector<loco::Node *> res;
+
+ for (uint32_t i = 0; i < root->numValues(); ++i)
+ {
+ auto input = dynamic_cast<locoex::TFLConcatenation *>(root->values(i));
+ if (input != nullptr && canMerge(input, root))
+ {
+ auto children = dfs(input);
+ for (auto child : children)
+ res.push_back(child);
+ }
+ else
+ {
+ res.push_back(root->values(i));
+ }
+ }
+
+ return res;
+}
+
+} // namespace
+
+namespace exo
+{
+
+/**
+ * @brief Merge TFLConcatenate nodes whose axis and fusedActivationFunction are same
+ *
+ * [Before]
+ * in:0 -------------------------------\
+ * in:1 ---- TFLConcatenation:0 -------- TFLConcatenation:3 --- C
+ * (axis = 0, NONE) (axis = 0, NONE)
+ * in:2 ---/ /
+ * in:3 ---- TFLConcatenation:1 ------/
+ * (axis = 1, NONE) /
+ * in:4 ---/ /
+ * in:5 ---- TFLConcatenation:2 ---/
+ * (axis = 0, RELU)
+ * in:6 ---/
+ *
+ * [After]
+ * in:0 -------------------------------\
+ * in:1 -------------------------------- TFLConcatenation:4 --- C
+ * (axis = 0, NONE)
+ * in:2 -------------------------------/
+ * in:3 ---- TFLConcatenation:1 ------/
+ * (axis = 1, NONE) /
+ * in:4 ---/ /
+ * in:5 ---- TFLConcatenation:2 ---/
+ * (axis = 0, RELU)
+ * in:6 ---/
+ *
+ *
+ * in:1 ---- TFLConcatenation:0 ----
+ * (axis = 0, NONE)
+ * in:2 ---/
+ *
+ *
+ * ---- TFLConcatenation:3 ----
+ * (axis = 0, NONE)
+ */
+bool MergeConcatNodesPass::run(loco::Graph *graph)
+{
+ // Let's enumerate nodes required to compute output nodes
+ auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
+
+ // Find TFLConcatenation nodes which have another TFLConcatenation nodes
+ // as inputs, with same axis and same fusedActivationFunction
+ std::vector<locoex::TFLConcatenation *> candidates;
+ for (auto node : active_nodes)
+ {
+ if (auto concat = dynamic_cast<locoex::TFLConcatenation *>(node))
+ {
+ for (uint32_t i = 0; i < concat->numValues(); ++i)
+ {
+ auto input = dynamic_cast<locoex::TFLConcatenation *>(concat->values(i));
+ if (input != nullptr && canMerge(input, concat))
+ {
+ candidates.push_back(concat);
+ break;
+ }
+ }
+ }
+ }
+
+ // Merge multiple TFLConcatenation nodes as one TFLConcatenation node
+ for (auto node : candidates)
+ {
+ auto inputs = dfs(node);
+
+ auto new_concat = graph->nodes()->create<locoex::TFLConcatenation>(inputs.size());
+ new_concat->axis(node->axis());
+ new_concat->fusedActivationFunction(node->fusedActivationFunction());
+
+ for (uint32_t i = 0; i < inputs.size(); ++i)
+ new_concat->values(i, inputs.at(i));
+
+ loco::replace(node).with(new_concat);
+ for (uint32_t i = 0; i < node->numValues(); ++i)
+ node->values(i, nullptr);
+ }
+
+ return candidates.size() > 0;
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Pass/MergeConcatNodesPass.h b/compiler/exo/src/Pass/MergeConcatNodesPass.h
new file mode 100644
index 000000000..823214f43
--- /dev/null
+++ b/compiler/exo/src/Pass/MergeConcatNodesPass.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __PASS_MERGE_CONCAT_NODES_H__
+#define __PASS_MERGE_CONCAT_NODES_H__
+
+#include <loco.h>
+#include <logo/Pass.h>
+
+namespace exo
+{
+
+/**
+ * @brief Merge concat nodes whose axis and fusedActivationFunction are same
+ *
+ */
+class MergeConcatNodesPass : public logo::Pass
+{
+public:
+ virtual const char *name(void) const { return "exo::MergeConcatNodesPass"; }
+
+public:
+ bool run(loco::Graph *graph);
+};
+
+} // namespace exo
+
+#endif // __PASS_MERGE_CONCAT_NODES_H__
diff --git a/compiler/exo/src/Pass/ShapeInferencePass.cpp b/compiler/exo/src/Pass/ShapeInferencePass.cpp
new file mode 100644
index 000000000..bc60f91c4
--- /dev/null
+++ b/compiler/exo/src/Pass/ShapeInferencePass.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ShapeInferencePass.h"
+
+#include "Dialect/IR/TFLDialect.h"
+#include "Dialect/Service/TFLShapeInferenceRule.h"
+
+#include "Dialect/IR/CircleDialect.h"
+#include "Dialect/Service/CircleShapeInferenceRule.h"
+
+#include <loco.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/Service/CanonicalShapeInferenceRule.h>
+#include <loco/Service/ShapeInference.h>
+#include <loco/Service/MultiDialectShapeInferenceRule.h>
+
+#include <locoex/COpDialect.h>
+#include <locoex/Service/COpShapeInferenceRule.h>
+
+namespace exo
+{
+
+/**
+ * @note Currently, TFL and Circle backend share this inference. However, TFL
+ * backend does not require rule for Circle dialect.
+ * TODO Make dedicated inference pass for Circle Dialect.
+ */
+bool ShapeInferencePass::run(loco::Graph *g)
+{
+ loco::CanonicalShapeInferenceRule canonical_rule;
+ locoex::TFLShapeInferenceRule tfl_rule;
+ locoex::CircleShapeInferenceRule circle_rule;
+ locoex::COpShapeInferenceRule cop_rule;
+
+ loco::MultiDialectShapeInferenceRule rules;
+
+ rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
+ .bind(locoex::TFLDialect::get(), &tfl_rule)
+ .bind(locoex::CircleDialect::get(), &circle_rule)
+ .bind(locoex::COpDialect::get(), &cop_rule);
+
+ return loco::apply(&rules).to(g);
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Pass/ShapeInferencePass.h b/compiler/exo/src/Pass/ShapeInferencePass.h
new file mode 100644
index 000000000..518c87403
--- /dev/null
+++ b/compiler/exo/src/Pass/ShapeInferencePass.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __PASS_SHAPE_INFERENCE_PASS_H__
+#define __PASS_SHAPE_INFERENCE_PASS_H__
+
+#include <loco.h>
+#include <logo/Pass.h>
+
+namespace exo
+{
+
+/**
+ * @brief Pass to infer shape of nodes
+ */
+class ShapeInferencePass : public logo::Pass
+{
+public:
+ virtual const char *name(void) const { return "exo::ShapeInferencePass"; }
+
+public:
+ bool run(loco::Graph *graph);
+};
+
+} // namespace exo
+
+#endif //__PASS_SHAPE_INFERENCE_PASS_H__
diff --git a/compiler/exo/src/Pass/TypeInferencePass.cpp b/compiler/exo/src/Pass/TypeInferencePass.cpp
new file mode 100644
index 000000000..31d4f13b6
--- /dev/null
+++ b/compiler/exo/src/Pass/TypeInferencePass.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TypeInferencePass.h"
+
+#include "Dialect/IR/TFLDialect.h"
+#include "Dialect/Service/TFLTypeInferenceRule.h"
+
+#include "Dialect/IR/CircleDialect.h"
+#include "Dialect/Service/CircleTypeInferenceRule.h"
+
+#include <loco.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/Service/TypeInference.h>
+
+#include <locoex/COpDialect.h>
+#include <locoex/Service/COpTypeInference.h>
+
+namespace exo
+{
+
+/**
+ * @note Currently, TFL and Circle backend share this inference. However, TFL
+ * backend does not require rule for Circle dialect.
+ * TODO Make dedicated inference pass for Circle Dialect.
+ */
+bool TypeInferencePass::run(loco::Graph *g)
+{
+ loco::CanonicalTypeInferenceRule canonical_rule;
+ locoex::TFLTypeInferenceRule tfl_rule;
+ locoex::CircleTypeInferenceRule circle_rule;
+ locoex::COpTypeInferenceRule cop_rule;
+
+ loco::MultiDialectTypeInferenceRule rules;
+
+ rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
+ .bind(locoex::TFLDialect::get(), &tfl_rule)
+ .bind(locoex::CircleDialect::get(), &circle_rule)
+ .bind(locoex::COpDialect::get(), &cop_rule);
+
+ return loco::apply(&rules).to(g);
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/Pass/TypeInferencePass.h b/compiler/exo/src/Pass/TypeInferencePass.h
new file mode 100644
index 000000000..3ede587a0
--- /dev/null
+++ b/compiler/exo/src/Pass/TypeInferencePass.h
@@ -0,0 +1,42 @@
+
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __PASS_TYPE_INFERENCE_PASS_H__
+#define __PASS_TYPE_INFERENCE_PASS_H__
+
+#include <loco.h>
+
+#include <logo/Pass.h>
+
+namespace exo
+{
+
+/**
+ * @brief Pass to infer type of nodes
+ */
+class TypeInferencePass : public logo::Pass
+{
+public:
+ virtual const char *name(void) const { return "exo::TypeInferencePass"; }
+
+public:
+ bool run(loco::Graph *graph);
+};
+
+} // namespace exo
+
+#endif //__PASS_TYPE_INFERENCE_PASS_H__
diff --git a/compiler/exo/src/Passes.cpp b/compiler/exo/src/Passes.cpp
new file mode 100644
index 000000000..99d229c9c
--- /dev/null
+++ b/compiler/exo/src/Passes.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Passes.h"
+
+// This file is to make sure that Passes.h be compiled
diff --git a/compiler/exo/src/Passes.h b/compiler/exo/src/Passes.h
new file mode 100644
index 000000000..2a702d01d
--- /dev/null
+++ b/compiler/exo/src/Passes.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __PASSES_H__
+#define __PASSES_H__
+
+// Please add in alphabetical order
+// Please append 'Pass' suffix to Pass class and file names
+
+#include "Pass/FoldReshapeOfConstPass.h"
+#include "Pass/FoldTransposeOfConstPass.h"
+#include "Pass/FuseBiasAddPass.h"
+#include "Pass/FuseInstanceNormPass.h"
+#include "Pass/FuseReluPass.h"
+#include "Pass/FuseRsqrtPass.h"
+#include "Pass/FuseSquaredDifferencePass.h"
+#include "Pass/MergeConcatNodesPass.h"
+#include "Pass/ShapeInferencePass.h"
+#include "Pass/TypeInferencePass.h"
+
+#include <logo/RemoveDeadNodePass.h>
+#include <logo/RemoveForwardNodePass.h>
+#include <logo/SimplifyDomainConversionPass.h>
+
+#endif // __PASSES_H__
diff --git a/compiler/exo-tflite/src/ProgressReporter.cpp b/compiler/exo/src/ProgressReporter.cpp
index ff919dae8..ff919dae8 100644
--- a/compiler/exo-tflite/src/ProgressReporter.cpp
+++ b/compiler/exo/src/ProgressReporter.cpp
diff --git a/compiler/exo-tflite/src/ProgressReporter.h b/compiler/exo/src/ProgressReporter.h
index b0f420df9..b0f420df9 100644
--- a/compiler/exo-tflite/src/ProgressReporter.h
+++ b/compiler/exo/src/ProgressReporter.h
diff --git a/compiler/exo/src/ShapeInference.cpp b/compiler/exo/src/ShapeInference.cpp
new file mode 100644
index 000000000..bceb1495f
--- /dev/null
+++ b/compiler/exo/src/ShapeInference.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ShapeInference.h"
+#include "Dialect/IR/TFLDialect.h"
+#include "Dialect/Service/TFLShapeInferenceRule.h"
+
+#include <loco/IR/CanonicalNode.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/IR/CanonicalNodeVisitor.h>
+#include <loco/Service/ShapeInference.h>
+#include <loco/Service/CanonicalShapeInferenceRule.h>
+#include <loco/Service/MultiDialectShapeInferenceRule.h>
+
+#include <locoex/COpCall.h>
+#include <locoex/COpDialect.h>
+#include <locoex/Service/COpShapeInferenceRule.h>
+
+namespace exo
+{
+
+ShapeDescription ShapeInference::get(loco::Node *node)
+{
+ // TODO Adjust indentation level
+ {
+ assert(loco::shape_known(node));
+ return to_shape_description(loco::shape_get(node));
+ }
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/ShapeInference.h b/compiler/exo/src/ShapeInference.h
new file mode 100644
index 000000000..ec141ccfc
--- /dev/null
+++ b/compiler/exo/src/ShapeInference.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SHAPE_INFERENCE_H__
+#define __SHAPE_INFERENCE_H__
+
+#include "ExporterUtils.h"
+
+#include <loco/IR/Nodes.h>
+
+namespace exo
+{
+
+/**
+ * @brief Get the shape of each node as a node annotation
+ *
+ * HOW TO USE
+ *
+ * ShapeInference::get(g->nodes()->at(..));
+ */
+struct ShapeInference
+{
+ static ShapeDescription get(loco::Node *node);
+};
+
+} // namespace exo
+
+#endif // __SHAPE_INFERENCE_H__
diff --git a/compiler/exo/src/TFLite/TFLExporter.cpp b/compiler/exo/src/TFLite/TFLExporter.cpp
new file mode 100644
index 000000000..cf002b3e1
--- /dev/null
+++ b/compiler/exo/src/TFLite/TFLExporter.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "exo/TFLExporter.h"
+
+#include "TFLExporterImpl.h"
+
+#include <stdex/Memory.h>
+
+#include <oops/InternalExn.h>
+
+#include <fstream>
+
+namespace exo
+{
+
+TFLExporter::TFLExporter(loco::Graph *graph) : _impl(stdex::make_unique<Impl>(graph))
+{
+ // NOTHING TO DO
+}
+
+TFLExporter::~TFLExporter() = default;
+
+void TFLExporter::dumpToFile(const char *path) const
+{
+ const char *ptr = _impl->getBufferPointer();
+ const size_t size = _impl->getBufferSize();
+
+ if (!ptr)
+ INTERNAL_EXN("Graph was not serialized by FlatBuffer for some reason");
+
+ std::ofstream file(path, std::ofstream::binary);
+ file.write(ptr, size);
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/TFLite/TFLExporterImpl.cpp b/compiler/exo/src/TFLite/TFLExporterImpl.cpp
new file mode 100644
index 000000000..07adbfb9d
--- /dev/null
+++ b/compiler/exo/src/TFLite/TFLExporterImpl.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TFLExporterImpl.h"
+
+#include "Convert.h"
+#include "ExoOptimize.h"
+
+#include "TFLTensorExporter.h"
+#include "TFLOperationExporter.h"
+#include "TFLExporterUtils.h"
+
+#include "Log.h"
+#include "Knob.h"
+
+#include <oops/InternalExn.h>
+
+#include <cassert>
+#include <unordered_map>
+#include <string>
+#include <stdexcept>
+
+namespace
+{
+
+using namespace exo;
+using namespace exo::tflite_detail;
+
+void registerGraphInputTensors(loco::Graph *graph, SubGraphContext &ctx)
+{
+ for (uint32_t n = 0; n < graph->inputs()->size(); ++n)
+ {
+ auto node = loco::pull_node(graph, n);
+ assert(node != nullptr);
+ ctx._inputs.push_back(get_tensor_index(node));
+ }
+}
+
+void registerGraphOutputTensors(loco::Graph *graph, SubGraphContext &ctx)
+{
+ for (uint32_t n = 0; n < graph->outputs()->size(); ++n)
+ {
+ auto push = loco::push_node(graph, n);
+ assert(push != nullptr);
+ auto node = push->from();
+ assert(node != nullptr);
+ ctx._outputs.push_back(get_tensor_index(node));
+ }
+}
+
+} // namespace
+
+namespace
+{
+using namespace tflite;
+using namespace flatbuffers;
+
+Offset<Vector<Offset<OperatorCode>>>
+encodeOperatorCodes(FlatBufferBuilder &builder, std::unordered_map<OpCode, uint32_t> &opcodes,
+ std::unordered_map<OpCode, std::string> &custom_opcodes)
+{
+ std::vector<Offset<OperatorCode>> operator_codes_vec(opcodes.size());
+ for (auto it : opcodes)
+ {
+ uint32_t idx = it.second;
+ if (it.first.opcode != BuiltinOperator_CUSTOM)
+ {
+ operator_codes_vec[idx] = CreateOperatorCode(builder, it.first.opcode);
+ }
+ else // custom op
+ {
+ auto opCode = it.first;
+ auto custom_code = custom_opcodes.find(opCode);
+ if (custom_code == custom_opcodes.end())
+ INTERNAL_EXN("Cannot find code for custom op");
+
+ operator_codes_vec[idx] =
+ CreateOperatorCode(builder, it.first.opcode, builder.CreateString(custom_code->second));
+ }
+ }
+ return builder.CreateVector(operator_codes_vec);
+}
+
+} // namespace
+
+namespace exo
+{
+
+using namespace exo::tflite_detail;
+using namespace tflite;
+using namespace flatbuffers;
+
+TFLExporter::Impl::Impl(loco::Graph *graph) { exportGraph(graph); }
+
+::flatbuffers::Offset<::tflite::SubGraph> TFLExporter::Impl::exportSubgraph(SerializedModelData &gd)
+{
+ auto tensors = _builder.CreateVector(gd._tensors);
+ auto inputs = _builder.CreateVector(gd._inputs);
+ auto outputs = _builder.CreateVector(gd._outputs);
+ auto operators = _builder.CreateVector(gd._operators);
+ auto subgraph = CreateSubGraph(_builder, tensors, inputs, outputs, operators);
+ return subgraph;
+}
+
+void TFLExporter::Impl::exportGraph(loco::Graph *graph)
+{
+ LOGGER(l);
+
+ // IR-level conversion and optimization
+ {
+ convert_to_TFLNodes(graph);
+ set(Dialect::TFLITE);
+ optimize(graph);
+ }
+
+ _builder.Clear();
+
+ SerializedModelData gd;
+
+ // This version is taken from comment in fbs
+ constexpr uint32_t version = 3;
+
+ registerGraphIOName(graph, gd);
+
+ // parse graph into SerializedModelData structure
+ exportOpDefinedTensors(graph, _builder, gd);
+
+ // NOTE Invoke these register functions only after each node is annotated with its tensor_index
+ registerGraphInputTensors(graph, gd);
+ registerGraphOutputTensors(graph, gd);
+
+ exportNodes(graph, _builder, gd);
+
+ // encode operator codes
+ auto operator_codes =
+ encodeOperatorCodes(_builder, gd._operator_codes, gd._custom_operator_codes);
+
+ // Subgraphs
+ Offset<SubGraph> subgraph = exportSubgraph(gd);
+ auto subgraphs = _builder.CreateVector(std::vector<Offset<SubGraph>>{subgraph});
+
+ // Description
+ std::string description_str = "nnpackage";
+ auto description = _builder.CreateString(description_str);
+
+ // create array of buffers
+ auto buffers = _builder.CreateVector(gd._buffers);
+
+ // empty metadata
+ std::vector<int> metadata_buffer_vec;
+ auto metadata_buffer = _builder.CreateVector(metadata_buffer_vec);
+
+ // Model
+ auto model_offset = CreateModel(_builder, version, operator_codes, subgraphs, description,
+ buffers, metadata_buffer);
+ FinishModelBuffer(_builder, model_offset);
+}
+
+const char *TFLExporter::Impl::getBufferPointer() const
+{
+ return reinterpret_cast<const char *>(_builder.GetBufferPointer());
+}
+
+size_t TFLExporter::Impl::getBufferSize() const { return _builder.GetSize(); }
+
+} // namespace exo
diff --git a/compiler/exo/src/TFLite/TFLExporterImpl.h b/compiler/exo/src/TFLite/TFLExporterImpl.h
new file mode 100644
index 000000000..01c549a43
--- /dev/null
+++ b/compiler/exo/src/TFLite/TFLExporterImpl.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFL_EXPORTER_IMPL_H__
+#define __TFL_EXPORTER_IMPL_H__
+
+#include "exo/TFLExporter.h"
+#include "schema_generated.h"
+
+#include <loco.h>
+
+namespace exo
+{
+
+namespace tflite_detail
+{
+
+struct SerializedModelData;
+
+} // namespace tflite_detail
+
+using namespace tflite_detail;
+
+/**
+ * internal implementation of interface exporter class
+ */
+class TFLExporter::Impl
+{
+public:
+ Impl() = delete;
+ ~Impl() = default;
+
+ explicit Impl(loco::Graph *graph);
+
+ /**
+ * @return pointer to buffer with serialized graph
+ */
+ const char *getBufferPointer() const;
+
+ /**
+ * @return size of buffer with serialized graph
+ */
+ size_t getBufferSize() const;
+
+private:
+ /**
+ * @brief create Subgraph using data stored in SerializedModelData
+ * @param gd information about serializer parts of model
+ * @return offset in buffer corresponding to serialized subgraph
+ */
+ flatbuffers::Offset<tflite::SubGraph> exportSubgraph(SerializedModelData &gd);
+
+ /**
+ * @brief root function that writes graph into internal buffer
+ * @param graph
+ */
+ void exportGraph(loco::Graph *graph);
+
+private:
+ flatbuffers::FlatBufferBuilder _builder;
+};
+
+} // namespace exo
+
+#endif // __TFL_EXPORTER_IMPL_H__
diff --git a/compiler/exo/src/TFLite/TFLExporterImpl.test.cpp b/compiler/exo/src/TFLite/TFLExporterImpl.test.cpp
new file mode 100644
index 000000000..866ede6a2
--- /dev/null
+++ b/compiler/exo/src/TFLite/TFLExporterImpl.test.cpp
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TFLExporterImpl.h"
+
+#include "schema_generated.h"
+
+#include "TestGraph.h"
+#include "GraphBlock.h"
+#include "Knob.h"
+
+#include <loco/IR/PermutingCodec.h>
+#include <stdex/Memory.h>
+
+#include <gtest/gtest.h>
+
+namespace
+{
+
+class TFLExporterImplTests : public ::testing::Test
+{
+public:
+ TFLExporterImplTests() { _graph = loco::make_graph(); }
+
+public:
+ virtual ~TFLExporterImplTests() = default;
+
+protected:
+ loco::Graph *graph(void) { return _graph.get(); }
+
+ template <typename NodeT> NodeT *make_node(void);
+
+private:
+ std::unique_ptr<loco::Graph> _graph;
+};
+
+template <typename NodeT> NodeT *TFLExporterImplTests::make_node(void)
+{
+ return graph()->nodes()->create<NodeT>();
+}
+
+template <> loco::FeatureEncode *TFLExporterImplTests::make_node(void)
+{
+ loco::FeatureEncode *encode_layer = graph()->nodes()->create<loco::FeatureEncode>();
+
+ auto encoder = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>();
+ (*encoder->perm())[loco::FeatureAxis::Count] = 0;
+ (*encoder->perm())[loco::FeatureAxis::Depth] = 1;
+ (*encoder->perm())[loco::FeatureAxis::Height] = 2;
+ (*encoder->perm())[loco::FeatureAxis::Width] = 3;
+ encode_layer->encoder(std::move(encoder));
+
+ return encode_layer;
+}
+
+template <> loco::FeatureDecode *TFLExporterImplTests::make_node(void)
+{
+ loco::FeatureDecode *decode_layer = graph()->nodes()->create<loco::FeatureDecode>();
+
+ auto decoder = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>();
+ (*decoder->perm())[loco::FeatureAxis::Count] = 0;
+ (*decoder->perm())[loco::FeatureAxis::Depth] = 1;
+ (*decoder->perm())[loco::FeatureAxis::Height] = 2;
+ (*decoder->perm())[loco::FeatureAxis::Width] = 3;
+ decode_layer->decoder(std::move(decoder));
+
+ return decode_layer;
+}
+
+} // namespace
+
+// TODO TFLAdd
+
+// TODO TFLAveragePool2D
+
+TEST_F(TFLExporterImplTests, Concatenate)
+{
+ auto pull1 = make_node<loco::Pull>();
+ {
+ pull1->dtype(loco::DataType::FLOAT32);
+ pull1->shape({1, 2, 3, 4});
+ }
+ auto pull2 = make_node<loco::Pull>();
+ {
+ pull2->dtype(loco::DataType::FLOAT32);
+ pull2->shape({1, 2, 3, 4});
+ }
+ auto concat = make_node<loco::TensorConcat>();
+ {
+ concat->lhs(pull1);
+ concat->rhs(pull2);
+ }
+ auto push = make_node<loco::Push>();
+ {
+ push->from(concat);
+ }
+
+ auto input1 = graph()->inputs()->create();
+ {
+ input1->name("input1");
+ loco::link(input1, pull1);
+ }
+ auto input2 = graph()->inputs()->create();
+ {
+ input2->name("input2");
+ loco::link(input2, pull2);
+ }
+ auto output = graph()->outputs()->create();
+ {
+ output->name("output");
+ loco::link(output, push);
+ }
+
+ exo::TFLExporter::Impl exporter{graph()};
+
+ // TODO Add more checks
+ SUCCEED();
+}
+
+// TODO TFLConv2D
+
+// TODO TFLDepthwiseConv2D
+
+// TODO TFLDiv
+
+// TODO TFLMaxPool2D
+
+// TODO TFLMul
+
+TEST_F(TFLExporterImplTests, Relu6)
+{
+ auto pull = make_node<loco::Pull>();
+ {
+ pull->dtype(loco::DataType::FLOAT32);
+ pull->shape({1, 8, 8, 3});
+ }
+ auto relu6 = make_node<loco::ReLU6>();
+ {
+ relu6->input(pull);
+ }
+ auto push = make_node<loco::Push>();
+ {
+ push->from(relu6);
+ }
+
+ auto input = graph()->inputs()->create();
+ {
+ input->name("input");
+ loco::link(input, pull);
+ }
+ auto output = graph()->outputs()->create();
+ {
+ output->name("output");
+ loco::link(output, push);
+ }
+
+ exo::TFLExporter::Impl exporter{graph()};
+
+ // TODO Add more checks
+ SUCCEED();
+}
+
+// TODO TFLRelu6
+
+// TODO TFLReshape
+
+// TODO TFLSoftmax
+
+// TODO TFLSqrt
+
+// TODO TFLSub
+
+// TODO TFLTanh
+
+TEST(TFLExporterImplTest, Transpose_simple)
+{
+ exo::test::ExampleGraph<exo::test::ExampleGraphType::Transpose> g;
+
+ // pull attribute
+ {
+ g.pull->dtype(loco::DataType::FLOAT32);
+ g.pull->shape({1, 2, 2, 3});
+ }
+
+ // transpose attribute
+ {
+ g.transpose->perm()->size(4);
+ g.transpose->perm()->axis(0) = 1;
+ g.transpose->perm()->axis(1) = 2;
+ g.transpose->perm()->axis(2) = 3;
+ g.transpose->perm()->axis(3) = 0;
+ }
+
+ exo::TFLExporter::Impl exporter{g.graph()};
+ {
+ auto model = tflite::GetModel(exporter.getBufferPointer());
+ auto operators = model->subgraphs()->Get(0)->operators();
+
+ assert(operators->Length() == 1);
+
+ int n = 0; // op index of Transpose in tflite file
+
+ auto opcode_index = operators->Get(n)->opcode_index();
+
+ ASSERT_EQ(model->operator_codes()->Get(opcode_index)->builtin_code(),
+ tflite::BuiltinOperator_TRANSPOSE);
+
+ auto perm = operators->Get(n)->inputs()->Get(1);
+
+ auto perm_tensor = model->subgraphs()->Get(0)->tensors()->Get(perm);
+ ASSERT_EQ(tflite::TensorType::TensorType_INT32, perm_tensor->type());
+ ASSERT_EQ(1, perm_tensor->shape()->size());
+ ASSERT_EQ(4, perm_tensor->shape()->Get(0));
+
+ auto bufs = (model->buffers());
+ auto *perm_buf =
+ reinterpret_cast<const int32_t *>(bufs->Get(perm_tensor->buffer())->data()->data());
+
+ ASSERT_EQ(1, perm_buf[0]);
+ ASSERT_EQ(2, perm_buf[1]);
+ ASSERT_EQ(3, perm_buf[2]);
+ ASSERT_EQ(0, perm_buf[3]);
+ }
+}
+
+/*
+ test case:
+ Pull ----- FeatureEncode ---- FeatureDecode --- Push
+ 0 -----------> H ---------+ O 0
+ 1 W +----> H -----------> 1
+ 2 I(depth) W 2
+ 3 O(coutn) I 3
+
+ axis 0 ----------> H --------------> H -----------> 1
+ axis 1 ----------> W --------------> W -----------> 2
+ axis 2 ----------> I --------------> I -----------> 3
+ axis 3 ----------> O --------------> O -----------> 0
+
+ So, perm vector of Tranpose = [3, 0, 1, 2].
+ Please refer to loco::TensorTranspose about the definition of perm vector.
+*/
+TEST(TFLExporterImplTest, Transpose_from_FilterEncode_FilterDecode)
+{
+ exo::test::ExampleGraph<exo::test::ExampleGraphType::FilterEncode_FilterDecode> g;
+
+ // pull attribute
+ {
+ g.pull->dtype(loco::DataType::FLOAT32);
+ g.pull->shape({1, 2, 3, 4}); // whatever value of rank 4
+ }
+
+ exo::TFLExporter::Impl exporter{g.graph()};
+ {
+ auto model = tflite::GetModel(exporter.getBufferPointer());
+ auto operators = model->subgraphs()->Get(0)->operators();
+
+ assert(operators->Length() == 1);
+
+ int n = 0; // op index of Transpose in tflite file
+
+ auto opcode_index = operators->Get(n)->opcode_index();
+
+ ASSERT_EQ(model->operator_codes()->Get(opcode_index)->builtin_code(),
+ tflite::BuiltinOperator_TRANSPOSE);
+
+ auto perm = operators->Get(n)->inputs()->Get(1);
+
+ auto perm_tensor = model->subgraphs()->Get(0)->tensors()->Get(perm);
+ ASSERT_EQ(tflite::TensorType::TensorType_INT32, perm_tensor->type());
+ ASSERT_EQ(1, perm_tensor->shape()->size());
+ ASSERT_EQ(4, perm_tensor->shape()->Get(0));
+
+ auto bufs = (model->buffers());
+ auto *perm_buf =
+ reinterpret_cast<const int32_t *>(bufs->Get(perm_tensor->buffer())->data()->data());
+ ASSERT_EQ(3, perm_buf[0]);
+ ASSERT_EQ(0, perm_buf[1]);
+ ASSERT_EQ(1, perm_buf[2]);
+ ASSERT_EQ(2, perm_buf[3]);
+ }
+}
+
+/**
+ * What happens when there is a mismatch between generation and execution order!?
+ */
+TEST_F(TFLExporterImplTests, Regression_0000)
+{
+ // This test was written without considering fusion.
+ // For this reason, this check is needed.
+ // TODO Rewrite this test
+ if (exo::get<exo::Knob::UseFuseReluPass>())
+ return;
+
+ // Execution Order: MaxPool2D -> ReLU
+ // Generation Order: ReLU -> MaxPool2D
+ auto pull = make_node<loco::Pull>();
+ {
+ pull->dtype(loco::DataType::FLOAT32);
+ pull->shape({1, 8, 8, 3});
+ }
+ auto relu = make_node<loco::ReLU>();
+ auto encode = exo::make_feature_encode<exo::FeatureLayout::NHWC>(pull);
+ auto maxpool = make_node<loco::MaxPool2D>();
+ auto decode = exo::make_feature_decode<exo::FeatureLayout::NHWC>(relu);
+ auto push = make_node<loco::Push>();
+
+ ASSERT_EQ(1, maxpool->window()->vertical());
+ ASSERT_EQ(1, maxpool->window()->horizontal());
+
+ maxpool->ifm(encode);
+ relu->input(maxpool);
+ push->from(decode);
+
+ auto input = graph()->inputs()->create();
+ {
+ input->name("input");
+ loco::link(input, pull);
+ }
+ auto output = graph()->outputs()->create();
+ {
+ output->name("output");
+ loco::link(output, push);
+ }
+
+ exo::TFLExporter::Impl exporter{graph()};
+ {
+ int64_t maxpool_execution_index = -1;
+ int64_t relu_exeuction_index = -1;
+
+ auto model = tflite::GetModel(exporter.getBufferPointer());
+ auto operators = model->subgraphs()->Get(0)->operators();
+
+ for (uint32_t n = 0; n < operators->Length(); ++n)
+ {
+ auto opcode_index = operators->Get(n)->opcode_index();
+
+ switch (model->operator_codes()->Get(opcode_index)->builtin_code())
+ {
+ case tflite::BuiltinOperator_RELU:
+ ASSERT_EQ(-1, relu_exeuction_index);
+ relu_exeuction_index = static_cast<int64_t>(n);
+ break;
+ case tflite::BuiltinOperator_MAX_POOL_2D:
+ ASSERT_EQ(-1, maxpool_execution_index);
+ maxpool_execution_index = static_cast<int64_t>(n);
+ break;
+ default:
+ break;
+ }
+ }
+
+ ASSERT_NE(maxpool_execution_index, -1);
+ ASSERT_NE(relu_exeuction_index, -1);
+ // maxpool SHOULD precede ReLU
+ ASSERT_LT(maxpool_execution_index, relu_exeuction_index);
+ }
+}
+
+/**
+ * @brief Test exporter buffer generation
+ */
+TEST_F(TFLExporterImplTests, Regression_0001)
+{
+ auto cgen = make_node<loco::ConstGen>();
+ cgen->rank(1);
+ cgen->dim(0) = 2;
+ cgen->dtype(loco::DataType::FLOAT32);
+ cgen->size<loco::DataType::FLOAT32>(2);
+ cgen->at<loco::DataType::FLOAT32>(0) = 3.3f;
+ cgen->at<loco::DataType::FLOAT32>(1) = 1.1f;
+
+ auto push = make_node<loco::Push>();
+ push->from(cgen);
+
+ auto output = graph()->outputs()->create();
+ {
+ output->name("output");
+ loco::link(output, push);
+ }
+
+ exo::TFLExporter::Impl exporter{graph()};
+ {
+ auto model = tflite::GetModel(exporter.getBufferPointer());
+ auto buffers = model->buffers();
+
+ // 0'th empty buffer + ConstGen data + ConstGen node output
+ ASSERT_EQ(3, buffers->Length());
+
+ // 0'th should be empty buffer
+ auto buffer_0 = (*buffers)[0];
+ auto array_0 = buffer_0->data();
+ ASSERT_EQ(nullptr, array_0);
+
+ // 1'st should be ConstGen data which is two float
+ auto buffer_1 = (*buffers)[1];
+ auto array_1 = buffer_1->data();
+ size_t size_1 = array_1->size();
+ ASSERT_EQ(2 * sizeof(float), size_1);
+ }
+}
diff --git a/compiler/exo/src/TFLite/TFLExporterUtils.cpp b/compiler/exo/src/TFLite/TFLExporterUtils.cpp
new file mode 100644
index 000000000..d35afc9aa
--- /dev/null
+++ b/compiler/exo/src/TFLite/TFLExporterUtils.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TFLExporterUtils.h"
+
+#include <oops/InternalExn.h>
+
+namespace exo
+{
+
+tflite::ActivationFunctionType to_tflite_actfunc(locoex::FusedActFunc func)
+{
+ switch (func)
+ {
+ case locoex::FusedActFunc::NONE:
+ return tflite::ActivationFunctionType_NONE;
+ case locoex::FusedActFunc::RELU:
+ return tflite::ActivationFunctionType_RELU;
+ case locoex::FusedActFunc::RELU6:
+ return tflite::ActivationFunctionType_RELU6;
+ default:
+ INTERNAL_EXN_V("Unsupported locoex FusedActFunc Type", oops::to_uint32(func));
+ }
+}
+
+} // namespace exo
+
+namespace exo
+{
+namespace tflite_detail
+{
+
+uint32_t SerializedModelData::registerBuiltinOpcode(tflite::BuiltinOperator builtin_code)
+{
+ auto it = _operator_codes.find(OpCode{builtin_code});
+ if (it != _operator_codes.end())
+ {
+ return it->second;
+ }
+ auto idx = static_cast<uint32_t>(_operator_codes.size());
+ _operator_codes.emplace(OpCode{builtin_code}, idx);
+ return idx;
+}
+
+uint32_t SerializedModelData::registerCustomOpcode(const std::string &custom_op)
+{
+ tflite::BuiltinOperator custom_code = tflite::BuiltinOperator_CUSTOM;
+ auto idx = registerBuiltinOpcode(custom_code);
+ _custom_operator_codes.emplace(OpCode{custom_code}, custom_op);
+ return idx;
+}
+
+tflite::Padding getOpPadding(const loco::Padding2D *pad, const loco::Stride<2> *stride,
+ const ShapeDescription &ifm, const ShapeDescription &ofm)
+{
+ // VALID padding
+ if (pad->top() == 0 && pad->bottom() == 0 && pad->left() == 0 && pad->right() == 0)
+ return tflite::Padding_VALID;
+
+ // SAME padding
+ //
+ // For same padding, by definition, following equation should hold:
+ // O = floor((I - 1) / S) + 1
+ // where input size I, output size O, stride S
+ //
+ // NOTE input and output 'feature' map are shape of NHWC
+ bool same_padding_criterion_1 =
+ (static_cast<uint32_t>(ofm._dims[1]) == (ifm._dims[1] - 1) / stride->vertical() + 1) &&
+ (static_cast<uint32_t>(ofm._dims[2]) == (ifm._dims[2] - 1) / stride->horizontal() + 1);
+
+ // For same padding, rear padding is same or bigger than front padding by at most 1
+ bool same_padding_criterion_2 =
+ (pad->top() <= pad->bottom()) && (pad->bottom() <= pad->top() + 1) &&
+ (pad->left() <= pad->right()) && (pad->right() <= pad->left() + 1);
+
+ if (same_padding_criterion_1 && same_padding_criterion_2)
+ return tflite::Padding_SAME;
+
+ INTERNAL_EXN("NYI for custom PAD");
+}
+
+tflite::Padding getOpPadding(const locoex::Padding pad)
+{
+ if (pad == locoex::Padding::VALID)
+ return tflite::Padding_VALID;
+ if (pad == locoex::Padding::SAME)
+ return tflite::Padding_SAME;
+
+ INTERNAL_EXN_V("Unknown padding", oops::to_uint32(pad));
+}
+
+void registerGraphIOName(loco::Graph *graph, SerializedModelData &gd)
+{
+ for (uint32_t in = 0; in < graph->inputs()->size(); ++in)
+ {
+ auto pull = loco::pull_node(graph, in);
+ auto name = graph->inputs()->at(in)->name();
+
+ gd._pull_to_name[pull] = name;
+ }
+ for (uint32_t out = 0; out < graph->outputs()->size(); ++out)
+ {
+ auto push = loco::push_node(graph, out);
+ auto name = graph->outputs()->at(out)->name();
+
+ gd._push_to_name[push] = name;
+ }
+}
+
+#include <stdex/Memory.h>
+
+#include <cassert>
+
+namespace
+{
+
+class TFLTensorIndexAnnotation final : public loco::NodeAnnotation
+{
+public:
+ TFLTensorIndexAnnotation(const TFLTensorIndex &index) : _index{index}
+ {
+ // DO NOTHING
+ }
+
+public:
+ const TFLTensorIndex &index(void) const { return _index; }
+
+private:
+ TFLTensorIndex _index;
+};
+
+} // namespace
+
+void set_tensor_index(loco::Node *node, const TFLTensorIndex &tensor_id)
+{
+ assert(node->annot<TFLTensorIndexAnnotation>() == nullptr);
+ node->annot(stdex::make_unique<TFLTensorIndexAnnotation>(tensor_id));
+}
+
+TFLTensorIndex get_tensor_index(loco::Node *node)
+{
+ assert(node->annot<TFLTensorIndexAnnotation>() != nullptr);
+ return node->annot<TFLTensorIndexAnnotation>()->index();
+}
+
+} // namespace tflite_detail
+} // namespace exo
diff --git a/compiler/exo/src/TFLite/TFLExporterUtils.h b/compiler/exo/src/TFLite/TFLExporterUtils.h
new file mode 100644
index 000000000..dbd7a52fb
--- /dev/null
+++ b/compiler/exo/src/TFLite/TFLExporterUtils.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFL_EXPORTER_UTILS_H__
+#define __TFL_EXPORTER_UTILS_H__
+
+#include "ExporterUtils.h"
+
+#include "schema_generated.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+#include <loco.h>
+
+#include <unordered_map>
+
+namespace exo
+{
+namespace tflite_detail
+{
+
+struct OpCode
+{
+ tflite::BuiltinOperator opcode;
+
+ bool operator==(const OpCode &rhs) const { return opcode == rhs.opcode; }
+};
+
+} // namespace tflite_detail
+} // namespace exo
+
+namespace exo
+{
+
+tflite::ActivationFunctionType to_tflite_actfunc(locoex::FusedActFunc func);
+
+} // namespace exo
+
+namespace std
+{
+
+template <> struct hash<exo::tflite_detail::OpCode>
+{
+ size_t operator()(const exo::tflite_detail::OpCode &x) const { return hash<int>()(x.opcode); }
+};
+
+} // namespace std
+
+namespace exo
+{
+namespace tflite_detail
+{
+
+/**
+ * @breif Record the information of T/F Lite SubGraph and its mapping to loco
+ */
+struct SubGraphContext
+{
+ /// @brief SubGraph input tensor id
+ std::vector<int32_t> _inputs;
+ /// @brief SubGraph output tensor id
+ std::vector<int32_t> _outputs;
+};
+
+// Prerequisites for tflite::Model object creation
+struct SerializedModelData final : public SubGraphContext
+{
+ SerializedModelData() = default;
+ SerializedModelData(const SerializedModelData &) = delete;
+
+ std::unordered_map<OpCode, uint32_t> _operator_codes;
+ std::unordered_map<OpCode, std::string> _custom_operator_codes;
+ std::vector<flatbuffers::Offset<tflite::Operator>> _operators;
+ std::vector<flatbuffers::Offset<tflite::Tensor>> _tensors;
+ std::vector<flatbuffers::Offset<tflite::Buffer>> _buffers;
+
+ // Graph input and output names
+ std::unordered_map<loco::Pull *, std::string> _pull_to_name;
+ std::unordered_map<loco::Push *, std::string> _push_to_name;
+
+ /**
+ * @brief if opcode is not registered in table of opcodes add it
+ * @param builtin_code
+ * @return idx of opcode in table of opcodes (see schema)
+ */
+ uint32_t registerBuiltinOpcode(tflite::BuiltinOperator builtin_code);
+ uint32_t registerCustomOpcode(const std::string &custom_op);
+};
+
+tflite::Padding getOpPadding(const loco::Padding2D *pad, const loco::Stride<2> *stride,
+ const ShapeDescription &ifm, const ShapeDescription &ofm);
+tflite::Padding getOpPadding(const locoex::Padding pad);
+
+/// @brief Register graph input and output names to SerializedModelData
+void registerGraphIOName(loco::Graph *graph, SerializedModelData &gd);
+
+using TFLTensorIndex = int32_t;
+
+void set_tensor_index(loco::Node *node, const TFLTensorIndex &tensor_id);
+TFLTensorIndex get_tensor_index(loco::Node *node);
+
+} // namespace tflite_detail
+} // namespace exo
+
+#endif // __TFL_EXPORTER_UTILS_H__
diff --git a/compiler/exo/src/TFLite/TFLExporterUtils.test.cpp b/compiler/exo/src/TFLite/TFLExporterUtils.test.cpp
new file mode 100644
index 000000000..ec9714d6d
--- /dev/null
+++ b/compiler/exo/src/TFLite/TFLExporterUtils.test.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TFLExporterUtils.h"
+
+#include <gtest/gtest.h>
+
+using namespace exo::tflite_detail;
+
+TEST(ExporterUtilsTests, getOpPadding)
+{
+ loco::Padding2D pad;
+ loco::Stride<2> stride;
+ exo::ShapeDescription ifm;
+ exo::ShapeDescription ofm;
+
+ ifm._dims.resize(4);
+ ofm._dims.resize(4);
+
+ // VALID padding
+ {
+ pad.top(0);
+ pad.bottom(0);
+ pad.left(0);
+ pad.right(0);
+
+ stride.vertical(2);
+ stride.horizontal(2);
+
+ ifm._dims[1] = 5;
+ ifm._dims[2] = 5;
+
+ ofm._dims[1] = 2;
+ ofm._dims[2] = 2;
+
+ ASSERT_EQ(tflite::Padding_VALID, getOpPadding(&pad, &stride, ifm, ofm));
+ }
+
+ // SAME padding
+ {
+ pad.top(1);
+ pad.bottom(1);
+ pad.left(1);
+ pad.right(1);
+
+ stride.vertical(2);
+ stride.horizontal(2);
+
+ ifm._dims[1] = 5;
+ ifm._dims[2] = 5;
+
+ ofm._dims[1] = 3;
+ ofm._dims[2] = 3;
+
+ ASSERT_EQ(tflite::Padding_SAME, getOpPadding(&pad, &stride, ifm, ofm));
+ }
+
+ // Custom padding 1 - Not supported by tflite
+ {
+ pad.top(2);
+ pad.bottom(0);
+ pad.left(1);
+ pad.right(1);
+
+ stride.vertical(2);
+ stride.horizontal(2);
+
+ ifm._dims[1] = 5;
+ ifm._dims[2] = 5;
+
+ ofm._dims[1] = 3;
+ ofm._dims[2] = 3;
+
+ ASSERT_ANY_THROW(getOpPadding(&pad, &stride, ifm, ofm));
+ }
+
+ // Custom padding 2 - Not supported by tflite
+ {
+ pad.top(2);
+ pad.bottom(2);
+ pad.left(2);
+ pad.right(2);
+
+ stride.vertical(2);
+ stride.horizontal(2);
+
+ ifm._dims[1] = 5;
+ ifm._dims[2] = 5;
+
+ ofm._dims[1] = 4;
+ ofm._dims[2] = 4;
+
+ ASSERT_ANY_THROW(getOpPadding(&pad, &stride, ifm, ofm));
+ }
+}
diff --git a/compiler/exo/src/TFLite/TFLOperationExporter.cpp b/compiler/exo/src/TFLite/TFLOperationExporter.cpp
new file mode 100644
index 000000000..79b5b6287
--- /dev/null
+++ b/compiler/exo/src/TFLite/TFLOperationExporter.cpp
@@ -0,0 +1,1199 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TFLOperationExporter.h"
+#include "TFLExporterUtils.h"
+#include "ShapeInference.h"
+
+#include "Dialect/IR/TFLNode.h"
+#include "Dialect/IR/TFLNodes.h"
+#include "Dialect/IR/TFLNodeVisitor.h"
+
+#include "Check.h"
+
+#include <loco/IR/CanonicalNode.h>
+#include <loco/IR/CanonicalNodeVisitor.h>
+#include <loco/Service/ShapeInference.h>
+#include <locoex/COpCall.h>
+
+#include <oops/InternalExn.h>
+
+#include <flatbuffers/flexbuffers.h>
+
+using namespace flatbuffers;
+using namespace tflite;
+
+namespace
+{
+
+using namespace exo;
+using namespace exo::tflite_detail;
+
+class OperationExporter final : public locoex::TFLNodeMutableVisitor<void>,
+ public loco::CanonicalNodeMutableVisitor<void>
+{
+public:
+ OperationExporter(FlatBufferBuilder &fbb, SerializedModelData &ctx) : builder{fbb}, gd{ctx}
+ {
+ // DO NOTHING
+ }
+
+public:
+ // FOR TFLNodes
+ void visit(locoex::TFLAdd *) final;
+ void visit(locoex::TFLAveragePool2D *) final;
+ void visit(locoex::TFLConcatenation *) final;
+ void visit(locoex::TFLConst *) final{/* skip, everything is done in exportOpDefinedTensors */};
+ void visit(locoex::TFLConv2D *) final;
+ void visit(locoex::TFLDepthwiseConv2D *) final;
+ void visit(locoex::TFLDiv *) final;
+ void visit(locoex::TFLFullyConnected *) final;
+ void visit(locoex::TFLMaximum *) final;
+ void visit(locoex::TFLMaxPool2D *) final;
+ void visit(locoex::TFLMean *) final;
+ void visit(locoex::TFLMul *) final;
+ void visit(locoex::TFLRelu *) final;
+ void visit(locoex::TFLRelu6 *) final;
+ // TODO TFLReshape
+ void visit(locoex::TFLRsqrt *) final;
+ // TODO TFLSoftmax
+ void visit(locoex::TFLSqrt *) final;
+ void visit(locoex::TFLSquaredDifference *) final;
+ void visit(locoex::TFLSub *) final;
+ // TODO TFLTanh
+ void visit(locoex::TFLTranspose *) final;
+ void visit(locoex::TFLTransposeConv *) final;
+
+ // FOR canonical nodes. These will be removed later
+ void visit(loco::ReLU *) final;
+ void visit(loco::ReLU6 *) final;
+ void visit(loco::Tanh *) final;
+ void visit(loco::Push *) final { /* DO NOTHING */}
+ void visit(loco::Pull *) final { /* DO NOTHING */}
+ void visit(loco::FeatureEncode *) final;
+ void visit(loco::FeatureDecode *) final;
+ void visit(loco::FilterEncode *) final;
+ void visit(loco::DepthwiseFilterEncode *) final;
+ void visit(loco::ConstGen *) final { /* skip, everything is done in exportOpDefinedTensors */}
+ void visit(loco::MaxPool2D *) final;
+ void visit(loco::AvgPool2D *) final;
+ void visit(loco::Conv2D *) final;
+ void visit(loco::TransposedConv2D *) final;
+ void visit(loco::DepthwiseConv2D *) final;
+ void visit(loco::TensorConcat *) final;
+ void visit(loco::TensorReduce *) final;
+ void visit(loco::TensorSoftmax *) final;
+ void visit(loco::BiasEncode *) final;
+ void visit(loco::TensorBiasAdd *) final;
+ void visit(loco::FeatureBiasAdd *) final;
+ void visit(loco::EltwiseAdd *) final;
+ void visit(loco::EltwiseMax *) final;
+ void visit(loco::EltwiseMul *) final;
+ void visit(loco::EltwiseSub *) final;
+ void visit(loco::EltwiseDiv *) final;
+ void visit(loco::EltwiseSqrt *) final;
+ void visit(loco::FixedReshape *) final;
+ void visit(loco::TensorBroadcast *) final;
+ void visit(loco::TensorConstantPad *) final;
+
+ void visit(locoex::COpCall *);
+
+private:
+ /**
+ * @brief Exports TFLMaxPool2D or TFLAveragePool2D
+ *
+ * @note TFLPool2D should be one of TFLMaxPool2D or TFLAveragePool2D
+ */
+ template <class TFLPool2D>
+ void export_pool_2d(TFLPool2D *node, tflite::BuiltinOperator builtin_op);
+
+private:
+ FlatBufferBuilder &builder;
+ SerializedModelData &gd;
+};
+
+void OperationExporter::visit(locoex::TFLAdd *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_ADD);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateAddOptions(builder, to_tflite_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_AddOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLAveragePool2D *node)
+{
+ export_pool_2d<locoex::TFLAveragePool2D>(node, tflite::BuiltinOperator_AVERAGE_POOL_2D);
+}
+
+void OperationExporter::visit(locoex::TFLConcatenation *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_CONCATENATION);
+ std::vector<int32_t> inputs_vec;
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+
+ for (uint32_t i = 0; i < node->numValues(); ++i)
+ inputs_vec.push_back(get_tensor_index(node->values(i)));
+
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateConcatenationOptions(builder, node->axis(),
+ to_tflite_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_ConcatenationOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLConv2D *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_CONV_2D);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->filter()),
+ get_tensor_index(node->bias())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ tflite::Padding padding = getOpPadding(node->padding());
+ auto options = CreateConv2DOptions(builder, padding, node->stride()->w(), node->stride()->h(),
+ to_tflite_actfunc(node->fusedActivationFunction()));
+
+ // Make CONV_2D operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_Conv2DOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLDepthwiseConv2D *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_DEPTHWISE_CONV_2D);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->filter()),
+ get_tensor_index(node->bias())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ tflite::Padding padding = getOpPadding(node->padding());
+ auto options = CreateDepthwiseConv2DOptions(builder, padding, node->stride()->w(),
+ node->stride()->h(), node->depthMultiplier(),
+ to_tflite_actfunc(node->fusedActivationFunction()));
+
+ // Make DEPTHWISE_CONV_2D operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_DepthwiseConv2DOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLDiv *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_DIV);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateDivOptions(builder, to_tflite_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_DivOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLFullyConnected *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_FULLY_CONNECTED);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()),
+ get_tensor_index(node->weights()),
+ get_tensor_index(node->bias())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options =
+ CreateFullyConnectedOptions(builder, to_tflite_actfunc(node->fusedActivationFunction()));
+
+ // Make FULLY_CONNECTED operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_FullyConnectedOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLMaximum *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_MAXIMUM);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateMaximumMinimumOptions(builder);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_MaximumMinimumOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLMaxPool2D *node)
+{
+ export_pool_2d<locoex::TFLMaxPool2D>(node, tflite::BuiltinOperator_MAX_POOL_2D);
+}
+
+void OperationExporter::visit(locoex::TFLMean *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_MEAN);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()),
+ get_tensor_index(node->reduction_indices())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateReducerOptions(builder, node->keep_dims());
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_ReducerOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLMul *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_MUL);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateMulOptions(builder, to_tflite_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_MulOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLRelu *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_RELU);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->features())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLRelu6 *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_RELU6);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->features())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+// TODO TFLReshape
+
+void OperationExporter::visit(locoex::TFLRsqrt *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_RSQRT);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+// TODO TFLSoftmax
+
+void OperationExporter::visit(locoex::TFLSqrt *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_SQRT);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLSquaredDifference *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_SQUARED_DIFFERENCE);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateSquaredDifferenceOptions(builder);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_SquaredDifferenceOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLSub *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_SUB);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateSubOptions(builder, to_tflite_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_SubOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+// TODO TFLTanh
+
+void OperationExporter::visit(locoex::TFLTranspose *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_TRANSPOSE);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->arg(0)), get_tensor_index(node->arg(1))};
+ std::vector<int32_t> outputs_vec{get_tensor_index(node)};
+
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateTransposeOptions(builder);
+
+ auto op_offset =
+ CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions::BuiltinOptions_TransposeOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(locoex::TFLTransposeConv *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_TRANSPOSE_CONV);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->inputSizes()),
+ get_tensor_index(node->filter()),
+ get_tensor_index(node->outBackprop())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ tflite::Padding padding = getOpPadding(node->padding());
+ auto options =
+ CreateTransposeConvOptions(builder, padding, node->stride()->w(), node->stride()->h());
+
+ // Make TRANSPOSE_CONV operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_TransposeConvOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+template <class TFLPool2D>
+void OperationExporter::export_pool_2d(TFLPool2D *node, tflite::BuiltinOperator builtin_op)
+{
+ EXO_ASSERT(builtin_op == tflite::BuiltinOperator_MAX_POOL_2D ||
+ builtin_op == tflite::BuiltinOperator_AVERAGE_POOL_2D,
+ "should be maxpool or avgpool");
+ EXO_ASSERT(node->padding() != locoex::Padding::UNDEFINED, "Padding is not set");
+
+ uint32_t op_idx = gd.registerBuiltinOpcode(builtin_op);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->value())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+
+ tflite::Padding padding = getOpPadding(node->padding());
+
+ auto options = CreatePool2DOptions(builder, padding, node->stride()->w(), node->stride()->h(),
+ node->filter()->w(), node->filter()->h(),
+ to_tflite_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_Pool2DOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::ReLU *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_RELU);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::ReLU6 *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_RELU6);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::Tanh *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_TANH);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::MaxPool2D *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_MAX_POOL_2D);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->ifm())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ tflite::Padding padding = getOpPadding(
+ node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node));
+ auto options = CreatePool2DOptions(builder, padding, node->stride()->horizontal(),
+ node->stride()->vertical(), node->window()->horizontal(),
+ node->window()->vertical());
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_Pool2DOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::AvgPool2D *node)
+{
+ // TFlite only support Valid convention of average pooling
+ assert(node->convention() == loco::AvgPool2D::Convention::Valid);
+
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_AVERAGE_POOL_2D);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->ifm())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ tflite::Padding padding = getOpPadding(
+ node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node));
+ auto options = CreatePool2DOptions(builder, padding, node->stride()->horizontal(),
+ node->stride()->vertical(), node->window()->horizontal(),
+ node->window()->vertical());
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_Pool2DOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::Conv2D *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_CONV_2D);
+
+ // Third input of CONV_2D of tflite should be bias. We will make (and register to gd) dummy zero
+ // bias. Bias would be rank 1, have size of output kernel count, and have all zero values, i.e.
+ // zero bias.
+ auto *ker = dynamic_cast<loco::FilterEncode *>(node->ker());
+ assert(ker);
+ int32_t bias_vec_size = ShapeInference::get(ker)._dims[0]; // output kernel count
+
+ auto bias_vec_shape_offset = builder.CreateVector(std::vector<int32_t>{bias_vec_size});
+ size_t raw_bias_vec_size = bias_vec_size * sizeof(int32_t);
+
+ std::vector<float> bias_vec_data(bias_vec_size); // initialized as zero vector
+
+ auto bias_vec_offset =
+ builder.CreateVector(reinterpret_cast<uint8_t *>(bias_vec_data.data()), raw_bias_vec_size);
+
+ auto bias_buffer_offset = CreateBuffer(builder, bias_vec_offset);
+
+ const auto bias_buffer_id = static_cast<uint32_t>(gd._buffers.size());
+
+ gd._buffers.push_back(bias_buffer_offset);
+
+ auto bias_tensor_id = static_cast<int32_t>(gd._tensors.size());
+ auto name_offset = builder.CreateString("t_" + std::to_string(bias_tensor_id));
+
+ auto bias_tensor_offset =
+ CreateTensor(builder, bias_vec_shape_offset, TensorType_FLOAT32, bias_buffer_id, name_offset);
+ gd._tensors.push_back(bias_tensor_offset);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->ifm()), get_tensor_index(node->ker()),
+ bias_tensor_id};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ tflite::Padding padding = getOpPadding(
+ node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node));
+ auto options = CreateConv2DOptions(builder, padding, node->stride()->horizontal(),
+ node->stride()->vertical());
+
+ // Make CONV_2D operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_Conv2DOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::TransposedConv2D *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_TRANSPOSE_CONV);
+
+ // TRANSPOSE_CONV's first input is output shape array.
+ const int32_t outshape_vec_size = 4;
+ auto outshape_vec_shape_offset = builder.CreateVector(std::vector<int32_t>{outshape_vec_size});
+ size_t raw_outshape_vec_size = outshape_vec_size * sizeof(int32_t);
+
+ std::vector<int32_t> outshape_vec_data(outshape_vec_size);
+ {
+ // Copy inferred output shape of node
+ auto out_feature_shape = loco::shape_get(node).as<loco::FeatureShape>();
+
+ // Feature tensor in TFlite is NHWC
+ outshape_vec_data.at(0) = out_feature_shape.count().value();
+ outshape_vec_data.at(1) = out_feature_shape.height().value();
+ outshape_vec_data.at(2) = out_feature_shape.width().value();
+ outshape_vec_data.at(3) = out_feature_shape.depth().value();
+ }
+
+ auto outshape_vec_offset = builder.CreateVector(
+ reinterpret_cast<uint8_t *>(outshape_vec_data.data()), raw_outshape_vec_size);
+
+ auto outshape_buffer_offset = CreateBuffer(builder, outshape_vec_offset);
+
+ const auto outshape_buffer_id = static_cast<uint32_t>(gd._buffers.size());
+
+ gd._buffers.push_back(outshape_buffer_offset);
+
+ auto outshape_tensor_id = static_cast<int32_t>(gd._tensors.size());
+ auto name_offset = builder.CreateString("t_" + std::to_string(outshape_tensor_id));
+
+ auto outshape_tensor_offset = CreateTensor(builder, outshape_vec_shape_offset, TensorType_INT32,
+ outshape_buffer_id, name_offset);
+ gd._tensors.push_back(outshape_tensor_offset);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{outshape_tensor_id, get_tensor_index(node->ker()),
+ get_tensor_index(node->ifm())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ // NOTE input and output is inversed to use this function
+ tflite::Padding padding = getOpPadding(node->pad(), node->stride(), ShapeInference::get(node),
+ ShapeInference::get(node->ifm()));
+ auto options = CreateTransposeConvOptions(builder, padding, node->stride()->horizontal(),
+ node->stride()->vertical());
+
+ // Make TRANSPOSE_CONV operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_TransposeConvOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::DepthwiseConv2D *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_DEPTHWISE_CONV_2D);
+
+ // Third input of DEPTHWISE_CONV2D of tflite should be bias. We will make (and register to gd)
+ // dummy zero bias. Bias would be rank 1, have size of output kernel count, and have all zero
+ // values, i.e. zero bias.
+ auto *ker = dynamic_cast<loco::DepthwiseFilterEncode *>(node->ker());
+ assert(ker);
+
+ int32_t bias_vec_size = ShapeInference::get(ker)._dims[3]; // output_size(C*M)
+ auto bias_vec_shape_offset = builder.CreateVector(std::vector<int32_t>{bias_vec_size});
+
+ size_t raw_bias_vec_size = bias_vec_size * sizeof(int32_t);
+ std::vector<float> bias_vec_data(bias_vec_size);
+ auto bias_vec_offset =
+ builder.CreateVector(reinterpret_cast<uint8_t *>(bias_vec_data.data()), raw_bias_vec_size);
+
+ auto bias_buffer_offset = CreateBuffer(builder, bias_vec_offset);
+
+ const auto bias_buffer_id = static_cast<uint32_t>(gd._buffers.size());
+
+ gd._buffers.push_back(bias_buffer_offset);
+
+ auto bias_tensor_id = static_cast<int32_t>(gd._tensors.size());
+ auto name_offset = builder.CreateString("t_" + std::to_string(bias_tensor_id));
+
+ auto bias_tensor_offset =
+ CreateTensor(builder, bias_vec_shape_offset, TensorType_FLOAT32, bias_buffer_id, name_offset);
+ gd._tensors.push_back(bias_tensor_offset);
+
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->ifm()), get_tensor_index(node->ker()),
+ bias_tensor_id};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ tflite::Padding padding = getOpPadding(
+ node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node));
+
+ int32_t ifm_channel_size = ShapeInference::get(node->ifm())._dims[3];
+ // multiplier = bias_vec_size(output_size)/ifm_channel_size
+ auto options =
+ CreateDepthwiseConv2DOptions(builder, padding, node->stride()->horizontal(),
+ node->stride()->vertical(), bias_vec_size / ifm_channel_size);
+
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_DepthwiseConv2DOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::TensorReduce *node)
+{
+ uint32_t op_idx;
+
+ switch (node->func())
+ {
+ case loco::ReduceFunc::Mean:
+ op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_MEAN);
+ break;
+
+ // TODO Support more reduce type operation
+ default:
+ INTERNAL_EXN_V("Not supported reduce type", oops::to_uint32(node->func()));
+ }
+
+ // Create a vector for axes data
+ std::vector<int32_t> axes_vec;
+ auto rank = ShapeInference::get(node->input())._dims.size();
+ for (uint32_t i = 0; i < rank; ++i)
+ if (node->axes()->defined(i))
+ axes_vec.push_back(i);
+
+ int32_t axes_vec_size = axes_vec.size();
+ auto axes_vec_shape_offset = builder.CreateVector(std::vector<int32_t>{axes_vec_size});
+
+ size_t raw_axes_vec_size = axes_vec_size * sizeof(int32_t);
+ auto axes_vec_offset =
+ builder.CreateVector(reinterpret_cast<uint8_t *>(axes_vec.data()), raw_axes_vec_size);
+
+ auto axes_buffer_offset = CreateBuffer(builder, axes_vec_offset);
+
+ const auto axes_buffer_id = static_cast<uint32_t>(gd._buffers.size());
+
+ gd._buffers.push_back(axes_buffer_offset);
+
+ auto axes_tensor_id = static_cast<int32_t>(gd._tensors.size());
+ auto name_offset = builder.CreateString("t_" + std::to_string(axes_tensor_id));
+
+ auto axes_tensor_offset =
+ CreateTensor(builder, axes_vec_shape_offset, TensorType_INT32, axes_buffer_id, name_offset);
+ gd._tensors.push_back(axes_tensor_offset);
+
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), axes_tensor_id};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateReducerOptions(builder, true); // true is for keep_dims option
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_ReducerOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::TensorSoftmax *node)
+{
+ // TODO Support when the input rank of TensorSoftmax is not 2
+ assert(ShapeInference::get(node->input())._dims.size() == 2);
+
+ // NOTE TFLite only accepts axis when the value is last dimension
+ assert(node->axis() == ShapeInference::get(node->input())._dims.size() - 1);
+
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_SOFTMAX);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateSoftmaxOptions(builder, 1.0f);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_SoftmaxOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+/// @brief Export given node into identity, i.e. CONCATENATION with one input
+template <typename NodeT>
+void exportIdentity(NodeT *node, FlatBufferBuilder &builder, SerializedModelData &gd)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_CONCATENATION);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->arg(0))};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateConcatenationOptions(builder); // use dummy 0 axis and NONE activation
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_ConcatenationOptions, options.Union());
+
+ gd._operators.push_back(op_offset);
+}
+
+/// @brief Export loco nodes as TRANSPOSE
+void exportAsTranspose(loco::Node *node, FlatBufferBuilder &builder,
+ std::vector<int32_t> &perm_vec_data, SerializedModelData &gd)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_TRANSPOSE);
+
+ auto options = CreateTransposeOptions(builder);
+
+ // Create constant tensor with perm vector
+ constexpr int perm_vec_size = 4;
+ assert(perm_vec_data.size() == perm_vec_size);
+ auto perm_vec_shape_offset = builder.CreateVector(std::vector<int32_t>{perm_vec_size});
+ constexpr size_t raw_perm_vec_size = perm_vec_size * sizeof(int32_t);
+
+ auto perm_vec_offset =
+ builder.CreateVector(reinterpret_cast<uint8_t *>(perm_vec_data.data()), raw_perm_vec_size);
+
+ auto perm_buffer_offset = CreateBuffer(builder, perm_vec_offset);
+
+ const auto perm_buffer_id = static_cast<uint32_t>(gd._buffers.size());
+
+ gd._buffers.push_back(perm_buffer_offset);
+
+ auto perm_tensor_id = static_cast<int32_t>(gd._tensors.size());
+ auto name_offset = builder.CreateString("t_" + std::to_string(perm_tensor_id));
+
+ auto perm_tensor_offset =
+ CreateTensor(builder, perm_vec_shape_offset, TensorType_INT32, perm_buffer_id, name_offset);
+ gd._tensors.push_back(perm_tensor_offset);
+
+ // Create permutation node
+
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->arg(0)), perm_tensor_id};
+ std::vector<int32_t> outputs_vec{get_tensor_index(node)};
+
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+
+ constexpr auto options_type = tflite::BuiltinOptions::BuiltinOptions_TransposeOptions;
+
+ auto transpose_offset =
+ CreateOperator(builder, op_idx, inputs, outputs, options_type, options.Union());
+ gd._operators.push_back(transpose_offset);
+}
+
+void OperationExporter::visit(loco::FeatureEncode *node)
+{
+ auto encoder = dynamic_cast<loco::PermutingEncoder<loco::Domain::Feature> *>(node->encoder());
+ auto perm = encoder->perm();
+
+ if (isNHWC(perm))
+ {
+ // Note that tflite represents feature as NHWC
+ exportIdentity(node, builder, gd);
+ }
+ else
+ {
+ std::vector<int32_t> perm_vec_data(4);
+ perm_vec_data[0] = perm->axis(loco::FeatureAxis::Count);
+ perm_vec_data[1] = perm->axis(loco::FeatureAxis::Height);
+ perm_vec_data[2] = perm->axis(loco::FeatureAxis::Width);
+ perm_vec_data[3] = perm->axis(loco::FeatureAxis::Depth);
+
+ exportAsTranspose(node, builder, perm_vec_data, gd);
+ }
+}
+
+void OperationExporter::visit(loco::FeatureDecode *node)
+{
+ auto decoder = dynamic_cast<loco::PermutingDecoder<loco::Domain::Feature> *>(node->decoder());
+ auto perm = decoder->perm();
+
+ if (isNHWC(perm))
+ {
+ // Note that tflite represents feature as NHWC
+ exportIdentity(node, builder, gd);
+ }
+ else
+ {
+ std::vector<int32_t> perm_vec_data(4);
+ perm_vec_data[perm->axis(loco::FeatureAxis::Count)] = 0;
+ perm_vec_data[perm->axis(loco::FeatureAxis::Height)] = 1;
+ perm_vec_data[perm->axis(loco::FeatureAxis::Width)] = 2;
+ perm_vec_data[perm->axis(loco::FeatureAxis::Depth)] = 3;
+
+ exportAsTranspose(node, builder, perm_vec_data, gd);
+ }
+}
+
+void OperationExporter::visit(loco::FilterEncode *node)
+{
+ auto encoder = dynamic_cast<loco::PermutingEncoder<loco::Domain::Filter> *>(node->encoder());
+ auto perm = encoder->perm();
+
+ if (isNHWC(perm))
+ {
+ // Note that tflite represents filter as NHWC
+ exportIdentity(node, builder, gd);
+ }
+ else
+ {
+ std::vector<int32_t> perm_vec_data(4);
+ // NOTE In tflite, all tensors means NHWC, so 0 = N, 1 = H, 2 = W, 3 = C
+ perm_vec_data[0] = perm->axis(loco::FilterAxis::Count);
+ perm_vec_data[1] = perm->axis(loco::FilterAxis::Height);
+ perm_vec_data[2] = perm->axis(loco::FilterAxis::Width);
+ perm_vec_data[3] = perm->axis(loco::FilterAxis::Depth);
+
+ exportAsTranspose(node, builder, perm_vec_data, gd);
+ }
+}
+
+void exportAsReshape(loco::Node *node, FlatBufferBuilder &builder,
+ std::vector<int32_t> &new_shape_vec, SerializedModelData &gd)
+{
+ // NOTE TFLite has two ways to get new shape paramter,
+ // one is by attribute 'new_shape' and the other is by input 'shape'.
+ // Therefore TFLite interpreter calculates Reshape operation correctly
+ // if one of them is valid.
+ // However, since NN runtime usually get new shape parameter by input 'shape',
+ // passing new shape only by attribute can cause some problems.
+ // Of course, the opposite situation can be occurred in the future.
+ // To prevent those problems, we pass new shape parameter not only by attribute
+ // but also by input.
+
+ auto input_shape_shape_vec_offset =
+ builder.CreateVector(std::vector<int32_t>{(int32_t)new_shape_vec.size()});
+
+ size_t input_shape_vec_size = new_shape_vec.size() * sizeof(int32_t);
+ auto input_shape_input_vec_offset =
+ builder.CreateVector(reinterpret_cast<uint8_t *>(new_shape_vec.data()), input_shape_vec_size);
+ auto input_shape_buffer_offset = CreateBuffer(builder, input_shape_input_vec_offset);
+
+ const auto input_shape_buffer_id = static_cast<uint32_t>(gd._buffers.size());
+ gd._buffers.push_back(input_shape_buffer_offset);
+
+ auto input_shape_tensor_id = static_cast<int32_t>(gd._tensors.size());
+ auto name_offset = builder.CreateString("t_" + std::to_string(input_shape_tensor_id));
+ auto input_shape_tensor_offset = CreateTensor(
+ builder, input_shape_shape_vec_offset, TensorType_INT32, input_shape_buffer_id, name_offset);
+ gd._tensors.push_back(input_shape_tensor_offset);
+
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_RESHAPE);
+
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->arg(0)), input_shape_tensor_id};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+
+ auto new_shape_vec_offset = builder.CreateVector(new_shape_vec);
+ auto options = CreateReshapeOptions(builder, new_shape_vec_offset);
+
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_ReshapeOptions, options.Union());
+
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::DepthwiseFilterEncode *node)
+{
+ auto ker = node->input(); // [H, W, C, M]
+
+ // tflite represents filter as [1, H, W, C*M] where M is multiplier.
+ std::vector<int32_t> new_shape_vec(4);
+ new_shape_vec[0] = 1;
+ new_shape_vec[1] = ShapeInference::get(ker)._dims[0];
+ new_shape_vec[2] = ShapeInference::get(ker)._dims[1];
+ new_shape_vec[3] = ShapeInference::get(ker)._dims[2] * ShapeInference::get(ker)._dims[3];
+
+ exportAsReshape(node, builder, new_shape_vec, gd);
+}
+
+void OperationExporter::visit(loco::BiasAdd<loco::Domain::Tensor> *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_ADD);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->value()), get_tensor_index(node->bias())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateAddOptions(builder); // dummy option
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_AddOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::FeatureBiasAdd *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_ADD);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->value()), get_tensor_index(node->bias())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateAddOptions(builder); // dummy option
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_AddOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+/// @brief Export CONCATENATION of **TWO** tensors only
+void OperationExporter::visit(loco::TensorConcat *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_CONCATENATION);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateConcatenationOptions(builder, node->axis());
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_ConcatenationOptions, options.Union());
+
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::BiasEncode *encode) { exportIdentity(encode, builder, gd); }
+
+void OperationExporter::visit(loco::EltwiseAdd *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_ADD);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateAddOptions(builder); // dummy option
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_AddOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::EltwiseMax *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_MAXIMUM);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateMaximumMinimumOptions(builder); // dummy option
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_MaximumMinimumOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::EltwiseMul *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_MUL);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateMulOptions(builder); // dummy option
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_MulOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::EltwiseSub *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_SUB);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateSubOptions(builder); // dummy option
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_SubOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::EltwiseDiv *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_DIV);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->lhs()), get_tensor_index(node->rhs())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateDivOptions(builder); // dummy option
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_DivOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::EltwiseSqrt *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_SQRT);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(loco::FixedReshape *node)
+{
+ std::vector<int32_t> new_shape_vec;
+ for (uint32_t axis = 0; axis < node->rank(); ++axis)
+ {
+ assert(node->dim(axis).known());
+ new_shape_vec.push_back(node->dim(axis).value());
+ }
+
+ exportAsReshape(node, builder, new_shape_vec, gd);
+}
+
+void OperationExporter::visit(loco::TensorBroadcast *)
+{
+ INTERNAL_EXN("TensorBroadcast should not exist in the graph");
+}
+
+void OperationExporter::visit(loco::TensorConstantPad *node)
+{
+ uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_PAD);
+
+ // make padding attribute an input
+ auto padding = node->padding();
+ // get padding vector size
+ int32_t padding_vec_size = padding->rank();
+ // get byte size of vector
+ size_t padding_vec_byte_size = padding_vec_size * sizeof(int32_t) * 2; // [rank, 2]
+ // create vector for data
+ std::vector<int32_t> padding_vec_data(padding_vec_size * 2);
+ // set data
+ for (int32_t i = 0; i < padding_vec_size; i++)
+ {
+ padding_vec_data.at(i * 2) = padding->front(i);
+ padding_vec_data.at(i * 2 + 1) = padding->back(i);
+ }
+ // create FlatBuffer vector
+ auto padding_vec_ptr = builder.CreateVector(reinterpret_cast<uint8_t *>(padding_vec_data.data()),
+ padding_vec_byte_size);
+
+ // create buffer
+ auto padding_buffer_ptr = CreateBuffer(builder, padding_vec_ptr);
+ // get buffer id
+ const auto padding_buffer_id = static_cast<uint32_t>(gd._buffers.size());
+
+ gd._buffers.push_back(padding_buffer_ptr);
+
+ // create padding shape vector
+ auto padding_shape_vec_ptr = builder.CreateVector(std::vector<int32_t>{padding_vec_size, 2});
+ // create tensor
+ auto padding_tensor_ptr =
+ CreateTensor(builder, padding_shape_vec_ptr, TensorType_INT32, padding_buffer_id);
+ // get tensor id
+ const auto padding_tensor_id = static_cast<int32_t>(gd._tensors.size());
+
+ gd._tensors.push_back(padding_tensor_ptr);
+
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), padding_tensor_id};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+inline flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
+CreateCOpCallOptions(flatbuffers::FlatBufferBuilder &fbb, locoex::COpCall *copCall)
+{
+ // read attrs in FlexBuffer format and pass them to FlatBuffer builder
+ flexbuffers::Builder flexbuf;
+ {
+ size_t map_start = flexbuf.StartMap();
+
+ // Note: among attrs of COpCall, 'op' and 'name' won't be included into tflite file
+ auto names = copCall->attr_names();
+ for (auto name : names)
+ {
+ if (auto int_val = copCall->attr<locoex::COpAttrType::Int>(name))
+ flexbuf.Int(name.c_str(), int_val->val());
+ else if (auto float_val = copCall->attr<locoex::COpAttrType::Float>(name))
+ flexbuf.Float(name.c_str(), float_val->val());
+ else
+ // TODO Support more attribute types
+ INTERNAL_EXN("Not supported type while writing flexbuffer");
+ }
+
+ flexbuf.EndMap(map_start);
+ flexbuf.Finish();
+ }
+
+ auto offset = fbb.CreateVector(flexbuf.GetBuffer());
+
+ return offset;
+}
+
+void OperationExporter::visit(locoex::COpCall *call)
+{
+ // Registering this custom op name into tflite Operator Codes table
+ uint32_t op_idx = gd.registerCustomOpcode(call->op());
+
+ std::vector<int32_t> inputs_vec;
+ {
+ inputs_vec.resize(call->arity());
+ for (uint32_t i = 0; i < call->arity(); i++)
+ inputs_vec[i] = get_tensor_index(call->arg(i));
+ }
+
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(call))};
+
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+
+ auto custom_options = CreateCOpCallOptions(builder, call);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ tflite::BuiltinOptions_NONE, // builtin_options_type
+ 0, // built-in option
+ custom_options, // custom options
+ tflite::CustomOptionsFormat_FLEXBUFFERS);
+
+ gd._operators.push_back(op_offset);
+}
+
+void exportNode(loco::Node *node, flatbuffers::FlatBufferBuilder &builder,
+ SerializedModelData &data)
+{
+ // TODO Use explicit tagging to prevent possible mistake
+ auto isNoOp = [](loco::Node *node) {
+ if (node->arity() == 1)
+ {
+ assert(node->arg(0) != nullptr);
+ return get_tensor_index(node) == get_tensor_index(node->arg(0));
+ }
+ return false;
+ };
+
+ if (isNoOp(node))
+ {
+ // Skip if a given node is marked as NoOp (op with no effect) before
+ return;
+ }
+
+ if (auto canonical_node = dynamic_cast<loco::CanonicalNode *>(node))
+ { // TODO Consider removing this later
+ OperationExporter exporter{builder, data};
+ canonical_node->accept(&exporter);
+ }
+ else if (auto tfl_node = dynamic_cast<locoex::TFLNode *>(node))
+ {
+ OperationExporter exporter{builder, data};
+ tfl_node->accept(&exporter);
+ }
+ else if (dynamic_cast<locoex::COpNode *>(node))
+ {
+ OperationExporter exporter{builder, data};
+ exporter.visit(dynamic_cast<locoex::COpCall *>(node));
+ }
+ else
+ {
+ assert(false && "unsupported node found");
+ }
+}
+
+} // namespace
+
+namespace exo
+{
+namespace tflite_detail
+{
+
+void exportNodes(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &gd)
+{
+ for (auto node : loco::postorder_traversal(loco::output_nodes(g)))
+ {
+ exportNode(node, builder, gd);
+ }
+}
+
+} // namespace tflite_detail
+} // namespace exo
diff --git a/compiler/exo/src/TFLite/TFLOperationExporter.h b/compiler/exo/src/TFLite/TFLOperationExporter.h
new file mode 100644
index 000000000..60f2b5eb2
--- /dev/null
+++ b/compiler/exo/src/TFLite/TFLOperationExporter.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFL_OPERATION_EXPORTER_H__
+#define __TFL_OPERATION_EXPORTER_H__
+
+#include "TFLExporterUtils.h"
+
+#include <loco/IR/Graph.h>
+
+namespace exo
+{
+namespace tflite_detail
+{
+
+/**
+ * @brief create Operators corresponding to model nodes
+ * @param nodes container with nodes
+ * @param gd information about serializer parts of model
+ */
+void exportNodes(loco::Graph *g, flatbuffers::FlatBufferBuilder &builder, SerializedModelData &gd);
+
+} // namespace tflite_detail
+} // namespace exo
+
+#endif // __TFL_OPERATION_EXPORTER_H__
diff --git a/compiler/exo/src/TFLite/TFLTensorExporter.cpp b/compiler/exo/src/TFLite/TFLTensorExporter.cpp
new file mode 100644
index 000000000..23c810ed5
--- /dev/null
+++ b/compiler/exo/src/TFLite/TFLTensorExporter.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TFLTensorExporter.h"
+#include "TFLTypeInference.h"
+#include "ShapeInference.h"
+
+// TODO Fix include style
+#include "loco/IR/Algorithm.h"
+#include "loco/IR/CanonicalNode.h"
+#include "loco/IR/CanonicalNodeVisitor.h"
+#include "loco/IR/DataTypeTraits.h"
+
+#include "Dialect/IR/TFLNodes.h"
+
+#include <oops/InternalExn.h>
+
+using namespace tflite;
+using namespace flatbuffers;
+
+namespace
+{
+
+using namespace exo;
+using namespace exo::tflite_detail;
+
+class TFLTensorInfo
+{
+public:
+ TFLTensorInfo() = default;
+
+public:
+ void name(const std::string &name) { _name = name; }
+ const std::string &name(void) const { return _name; }
+
+public:
+ const tflite::TensorType &dtype(void) const { return _dtype; }
+ void dtype(const tflite::TensorType &dtype) { _dtype = dtype; }
+
+ const ShapeDescription &shape(void) const { return _shape; }
+ void shape(const ShapeDescription &shape) { _shape = shape; }
+
+public:
+ locoex::TFLConst *tfl_content(void) const { return _tfl_content; }
+ void tfl_content(locoex::TFLConst *c) { _tfl_content = c; }
+
+private:
+ std::string _name;
+
+ tflite::TensorType _dtype{TensorType_FLOAT32};
+ ShapeDescription _shape{};
+
+ // TODO Find a better design
+ loco::ConstGen *_content = nullptr; // TODO deprecate
+ locoex::TFLConst *_tfl_content = nullptr;
+};
+
+using TFLTensorContext = std::vector<TFLTensorInfo>;
+
+struct NoOpDetector final : public loco::CanonicalNodeMutableVisitor<bool>
+{
+ bool visit(loco::BiasEncode *) final
+ {
+ // BiasEncode is always noop
+ return true;
+ }
+
+ bool visit(loco::FilterEncode *node) final
+ {
+ auto encoder = loco::must_cast<loco::PermutingEncoder<loco::Domain::Filter> *>(node->encoder());
+ auto perm = encoder->perm();
+
+ return isNHWC(perm);
+ }
+
+ bool visit(loco::FeatureEncode *node) final
+ {
+ auto encoder =
+ loco::must_cast<loco::PermutingEncoder<loco::Domain::Feature> *>(node->encoder());
+ auto perm = encoder->perm();
+ return isNHWC(perm);
+ }
+
+ bool visit(loco::FeatureDecode *node) final
+ {
+ auto decoder =
+ loco::must_cast<loco::PermutingDecoder<loco::Domain::Feature> *>(node->decoder());
+ auto perm = decoder->perm();
+ return isNHWC(perm);
+ }
+
+ // Return false by default
+ bool visit(loco::Node *) final { return false; }
+};
+
+bool isNoOp(loco::Node *node)
+{
+ if (auto canonical_node = dynamic_cast<loco::CanonicalNode *>(node))
+ {
+ NoOpDetector d;
+ return canonical_node->accept(&d);
+ }
+ return false;
+}
+
+void allocateTFLiteTensor(loco::Node *node, TFLTensorContext &ctx)
+{
+ if (isNoOp(node))
+ {
+ assert(node->arity() == 1 && node->arg(0) != nullptr);
+ set_tensor_index(node, get_tensor_index(node->arg(0)));
+ return;
+ }
+
+ auto tensor_index = static_cast<TFLTensorIndex>(ctx.size());
+ // TODO Use Graph-level metadata for Input & Output
+ auto tensor_name = "t_" + std::to_string(tensor_index);
+
+ TFLTensorInfo tensor_info;
+
+ tensor_info.name(tensor_name);
+ tensor_info.dtype(TypeInference::get(node));
+ tensor_info.shape(ShapeInference::get(node));
+
+ if (auto const_node = dynamic_cast<locoex::TFLConst *>(node))
+ tensor_info.tfl_content(const_node);
+
+ set_tensor_index(node, tensor_index);
+
+ ctx.emplace_back(tensor_info);
+}
+
+} // namespace
+
+namespace
+{
+
+flatbuffers::Offset<Vector<int32_t>> encodeShape(FlatBufferBuilder &builder,
+ const ShapeDescription &shape)
+{
+ assert(shape._rank_known && "unknown number of dimensions is not supported");
+ return builder.CreateVector(shape._dims);
+}
+
+flatbuffers::Offset<tflite::Buffer> encodeOpBuffer(FlatBufferBuilder &builder)
+{
+ return CreateBuffer(builder);
+}
+
+template <typename NodeT>
+flatbuffers::Offset<tflite::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, NodeT *)
+{
+ return CreateBuffer(builder);
+}
+
+template <loco::DataType DT>
+flatbuffers::Offset<tflite::Buffer> encodeOpBufferByDType(FlatBufferBuilder &builder,
+ locoex::TFLConst *c)
+{
+ using NativeType = typename loco::DataTypeImpl<DT>::Type;
+
+ std::vector<NativeType> raw_data;
+ const uint32_t size = c->size<DT>();
+ raw_data.reserve(size);
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ raw_data.push_back(c->at<DT>(i));
+ }
+ const size_t raw_size = size * sizeof(NativeType);
+ auto array_offset = builder.CreateVector(reinterpret_cast<uint8_t *>(raw_data.data()), raw_size);
+ return CreateBuffer(builder, array_offset);
+}
+
+template <>
+flatbuffers::Offset<tflite::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, locoex::TFLConst *c)
+{
+ if (c->dtype() == loco::DataType::FLOAT32)
+ {
+ return encodeOpBufferByDType<loco::DataType::FLOAT32>(builder, c);
+ }
+ else if (c->dtype() == loco::DataType::S32)
+ {
+ return encodeOpBufferByDType<loco::DataType::S32>(builder, c);
+ }
+
+ INTERNAL_EXN_V("Unsupported datatype", oops::to_uint32(c->dtype()));
+}
+
+} // namespace
+
+namespace exo
+{
+namespace tflite_detail
+{
+
+void exportOpDefinedTensor(const TFLTensorInfo &info, FlatBufferBuilder &builder,
+ SerializedModelData &gd)
+{
+ // Create and register output tensor shape
+ auto shape_offset = encodeShape(builder, info.shape());
+
+ // encode and register output tensor buffer
+ auto buffer = info.tfl_content() == nullptr ? encodeOpBuffer(builder)
+ : encodeOpBuffer(builder, info.tfl_content());
+
+ auto buffer_id = static_cast<uint32_t>(gd._buffers.size());
+ gd._buffers.push_back(buffer);
+
+ auto name_offset = builder.CreateString(info.name());
+ auto tensor_offset = CreateTensor(builder, shape_offset, info.dtype(), buffer_id, name_offset,
+ /*quantization*/ 0, /*is_variable*/ false);
+ gd._tensors.push_back(tensor_offset);
+}
+
+void exportOpDefinedTensors(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &gd)
+{
+ TFLTensorContext tensor_ctx;
+
+ for (auto node : loco::postorder_traversal(loco::output_nodes(g)))
+ {
+ allocateTFLiteTensor(node, tensor_ctx);
+ }
+
+ // add one empty buffer
+ // note: there's a comment in tflite fbs file
+ // - Note the 0th entry of this array must be an empty buffer (sentinel).
+ // - This is a convention so that tensors without a buffer can provide 0 as
+ // - their buffer.
+ auto buffer = encodeOpBuffer(builder);
+ gd._buffers.push_back(buffer);
+
+ for (const auto &tensor_info : tensor_ctx)
+ {
+ exportOpDefinedTensor(tensor_info, builder, gd);
+ }
+}
+
+} // namespace tflite_detail
+} // namespace exo
diff --git a/compiler/exo/src/TFLite/TFLTensorExporter.h b/compiler/exo/src/TFLite/TFLTensorExporter.h
new file mode 100644
index 000000000..97e702665
--- /dev/null
+++ b/compiler/exo/src/TFLite/TFLTensorExporter.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFL_TENSOR_EXPORTER_H__
+#define __TFL_TENSOR_EXPORTER_H__
+
+#include "TFLExporterUtils.h"
+
+#include <loco/IR/Graph.h>
+
+#include <flatbuffers/flatbuffers.h>
+
+namespace exo
+{
+namespace tflite_detail
+{
+
+/**
+ * @brief create Tensors corresponding to results of all nodes in graph
+ * @param computational graph
+ * @param gd information about serialized parts of model
+ */
+void exportOpDefinedTensors(loco::Graph *g, flatbuffers::FlatBufferBuilder &builder,
+ SerializedModelData &gd);
+
+} // namespace tflite_detail
+} // namespace exo
+
+#endif // __TFL_TENSOR_EXPORTER_H__
diff --git a/compiler/exo/src/TFLite/TFLTypeInference.cpp b/compiler/exo/src/TFLite/TFLTypeInference.cpp
new file mode 100644
index 000000000..8d6bb8d8c
--- /dev/null
+++ b/compiler/exo/src/TFLite/TFLTypeInference.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TFLTypeInference.h"
+
+#include "schema_generated.h"
+
+#include "Dialect/Service/TFLTypeInferenceRule.h"
+#include "Dialect/IR/TFLDialect.h"
+
+#include <loco/IR/CanonicalNode.h>
+#include <loco/IR/CanonicalNodeVisitor.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/Service/TypeInference.h>
+
+#include <locoex/COpDialect.h>
+#include <locoex/Service/COpTypeInference.h>
+
+#include <oops/InternalExn.h>
+
+#include <stdex/Memory.h>
+
+#include <stdexcept>
+#include <type_traits>
+
+namespace
+{
+
+tflite::TensorType translateLocoTypeToTFLite(loco::DataType dtype)
+{
+ switch (dtype)
+ {
+ case loco::DataType::U8:
+ return tflite::TensorType_UINT8;
+ // case loco::DataType::U16: unsupported
+ // case loco::DataType::U32: unsupported
+ // case loco::DataType::U64: unsupported
+ case loco::DataType::S8:
+ return tflite::TensorType_INT8;
+ case loco::DataType::S16:
+ return tflite::TensorType_INT16;
+ case loco::DataType::S32:
+ return tflite::TensorType_INT32;
+ case loco::DataType::S64:
+ return tflite::TensorType_INT64;
+ case loco::DataType::FLOAT16:
+ return tflite::TensorType_FLOAT16;
+ case loco::DataType::FLOAT32:
+ return tflite::TensorType_FLOAT32;
+ // case loco::DataType::FLOAT64: unsupported
+ default:
+ break;
+ }
+
+ INTERNAL_EXN_V("Trying to converte unsupported loco dtype", oops::to_uint32(dtype));
+}
+
+} // namespace
+
+namespace exo
+{
+
+tflite::TensorType TypeInference::get(loco::Node *node)
+{
+ assert(loco::dtype_known(node));
+ return translateLocoTypeToTFLite(loco::dtype_get(node));
+}
+
+} // namespace exo
diff --git a/compiler/exo/src/TFLite/TFLTypeInference.h b/compiler/exo/src/TFLite/TFLTypeInference.h
new file mode 100644
index 000000000..3d3a2e480
--- /dev/null
+++ b/compiler/exo/src/TFLite/TFLTypeInference.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFL_TYPE_INFERENCE_H__
+#define __TFL_TYPE_INFERENCE_H__
+
+#include "TFLExporterUtils.h"
+
+#include <loco/IR/Nodes.h>
+
+namespace exo
+{
+
+/**
+ * @brief Get the type of each node as NodeAnnotation
+ *
+ * HOW TO USE
+ *
+ * TypeInference::get(g->nodes()->at(0));
+ * TypeInference::get(g->nodes()->at(...));
+ */
+struct TypeInference
+{
+ static tflite::TensorType get(loco::Node *node);
+};
+
+} // namespace exo
+
+#endif // __TFL_TYPE_INFERENCE_H__
diff --git a/compiler/exo/src/TFLite/TFLTypeInference.test.cpp b/compiler/exo/src/TFLite/TFLTypeInference.test.cpp
new file mode 100644
index 000000000..8a3a08da9
--- /dev/null
+++ b/compiler/exo/src/TFLite/TFLTypeInference.test.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TFLTypeInference.h"
+#include "Pass/TypeInferencePass.h"
+
+#include <loco/IR/PermutingCodec.h>
+#include <stdex/Memory.h>
+
+#include <gtest/gtest.h>
+
+using stdex::make_unique;
+
+namespace
+{
+
+class Sequential
+{
+public:
+ loco::Pull *addPullLayer(const loco::DataType &dtype = loco::DataType::FLOAT32)
+ {
+ loco::Pull *pull = _graph.nodes()->create<loco::Pull>();
+
+ auto graph_input = _graph.inputs()->create();
+ graph_input->name("graph_input");
+ loco::link(graph_input, pull);
+
+ pull->dtype(dtype);
+ setSampleShape(pull);
+
+ return last(pull);
+ }
+
+ loco::ReLU *addReLULayer(void)
+ {
+ loco::ReLU *relu = _graph.nodes()->create<loco::ReLU>();
+
+ relu->input(_last);
+
+ return last(relu);
+ }
+
+ loco::Push *addPushLayer(void)
+ {
+ loco::Push *push = _graph.nodes()->create<loco::Push>();
+
+ auto graph_output = _graph.outputs()->create();
+ graph_output->name("graph_output");
+ loco::link(graph_output, push);
+
+ push->from(_last);
+
+ return last(push);
+ }
+
+ loco::Graph *graph() { return &_graph; }
+
+private:
+ template <typename T> uint32_t setSampleShape(T *op)
+ {
+ const uint32_t n = 1;
+ const uint32_t h = 100;
+ const uint32_t w = 100;
+ const uint32_t c = 3;
+ op->rank(4);
+ op->dim(0).set(n);
+ op->dim(1).set(c);
+ op->dim(2).set(h);
+ op->dim(3).set(w);
+ return n * h * w * c;
+ }
+
+ template <typename T> T *last(T *node)
+ {
+ _last = node;
+ return node;
+ }
+
+private:
+ loco::Graph _graph;
+ loco::Node *_last{nullptr};
+};
+
+struct TypeInferenceTest : public Sequential, public ::testing::Test
+{
+ virtual ~TypeInferenceTest() = default;
+};
+
+} // namespace
+
+// TypeInference SHOULD PROPAGATE type information properly
+TEST_F(TypeInferenceTest, Regression_0000)
+{
+ auto pull = addPullLayer(loco::DataType::S8);
+ auto relu = addReLULayer();
+ auto push = addPushLayer();
+
+ using namespace exo;
+
+ TypeInferencePass type_inf_pass;
+ type_inf_pass.run(graph());
+
+ ASSERT_EQ(tflite::TensorType_INT8, TypeInference::get(relu));
+ ASSERT_EQ(tflite::TensorType_INT8, TypeInference::get(push));
+}
diff --git a/compiler/exo/src/TestGraph.h b/compiler/exo/src/TestGraph.h
new file mode 100644
index 000000000..f919cc9ae
--- /dev/null
+++ b/compiler/exo/src/TestGraph.h
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TEST_GRAPH_H__
+#define __TEST_GRAPH_H__
+
+#include "Dialect/IR/TFLNodes.h"
+#include "GraphBlock.h"
+#include "TestHelper.h"
+
+#include <loco.h>
+
+#include <stdex/Memory.h>
+
+#include <cassert>
+
+namespace exo
+{
+namespace test
+{
+
+class TestGraph
+{
+public:
+ std::unique_ptr<loco::Graph> g;
+ loco::Pull *pull;
+ loco::Push *push;
+
+ TestGraph() // creates Pull and Push
+ {
+ g = loco::make_graph();
+
+ pull = g->nodes()->create<loco::Pull>();
+
+ push = g->nodes()->create<loco::Push>();
+
+ auto input = g->inputs()->create();
+ {
+ input->name("input");
+ loco::link(input, pull);
+ }
+ auto output = g->outputs()->create();
+ {
+ output->name("output");
+ loco::link(output, push);
+ }
+
+ _next_input = pull;
+ }
+
+ loco::Graph *graph() { return g.get(); }
+
+ /// @brief Creates node with NO arg and appends it to graph
+ template <class T> T *append()
+ {
+ auto node = g->nodes()->create<T>();
+ _next_input = node;
+
+ return node;
+ }
+
+ /// @brief Creates op T (arity=1) with arg1 as an input and appends it to graph
+ template <class T> T *append(loco::Node *arg1)
+ {
+ auto node = g->nodes()->create<T>();
+ setInput(node, arg1);
+ _next_input = node;
+
+ return node;
+ }
+
+ /// @brief Creates op T (arity=2) with arg1, arg2 as inputs and appends it to graph
+ template <class T> T *append(loco::Node *arg1, loco::Node *arg2)
+ {
+ auto node = g->nodes()->create<T>();
+ setInput(node, arg1, arg2);
+ _next_input = node;
+
+ return node;
+ }
+
+ /// @brief Creates op T (arity=3) with arg1, arg2, arg3 as inputs and appends it to graph
+ template <class T> T *append(loco::Node *arg1, loco::Node *arg2, loco::Node *arg3)
+ {
+ auto node = g->nodes()->create<T>();
+ setInput(node, arg1, arg2, arg3);
+ _next_input = node;
+
+ return node;
+ }
+
+ // push will get the last appended node
+ void complete() { push->from(_next_input); }
+
+ void complete(loco::Node *last_node) { push->from(last_node); }
+
+private:
+ // arity 1
+ void setInput(loco::Node *node, loco::Node *) { assert(false && "NYI"); };
+
+ void setInput(loco::AvgPool2D *node, loco::Node *input) { node->ifm(input); }
+ void setInput(loco::BiasDecode *node, loco::Node *input) { node->input(input); };
+ void setInput(loco::BiasEncode *node, loco::Node *input) { node->input(input); };
+ void setInput(loco::FeatureDecode *node, loco::Node *input) { node->input(input); };
+ void setInput(loco::FeatureEncode *node, loco::Node *input) { node->input(input); };
+ void setInput(loco::MaxPool2D *node, loco::Node *input) { node->ifm(input); }
+ void setInput(loco::Push *node, loco::Node *input) { node->from(input); };
+ void setInput(loco::ReLU *node, loco::Node *input) { node->input(input); };
+ void setInput(loco::ReLU6 *node, loco::Node *input) { node->input(input); };
+ void setInput(loco::Tanh *node, loco::Node *input) { node->input(input); };
+ void setInput(loco::TensorTranspose *node, loco::Node *input) { node->input(input); };
+
+ void setInput(locoex::TFLAveragePool2D *node, loco::Node *input) { node->value(input); };
+ void setInput(locoex::TFLMaxPool2D *node, loco::Node *input) { node->value(input); };
+ void setInput(locoex::TFLRelu *node, loco::Node *input) { node->features(input); };
+ void setInput(locoex::TFLRelu6 *node, loco::Node *input) { node->features(input); };
+
+ // arity 2
+ void setInput(loco::Node *node, loco::Node *, loco::Node *) { assert(false && "NYI"); };
+
+ void setInput(loco::Conv2D *node, loco::Node *input, loco::Node *filter)
+ {
+ node->ifm(input);
+ node->ker(filter);
+ }
+
+ void setInput(loco::EltwiseAdd *node, loco::Node *arg1, loco::Node *arg2)
+ {
+ node->lhs(arg1);
+ node->rhs(arg2);
+ };
+
+ void setInput(loco::FeatureBiasAdd *node, loco::Node *arg1, loco::Node *arg2)
+ {
+ node->value(arg1);
+ node->bias(arg2);
+ };
+
+ void setInput(locoex::TFLAdd *node, loco::Node *arg1, loco::Node *arg2)
+ {
+ node->x(arg1);
+ node->y(arg2);
+ };
+
+ void setInput(locoex::TFLMul *node, loco::Node *arg1, loco::Node *arg2)
+ {
+ node->x(arg1);
+ node->y(arg2);
+ };
+
+ void setInput(locoex::TFLSub *node, loco::Node *arg1, loco::Node *arg2)
+ {
+ node->x(arg1);
+ node->y(arg2);
+ };
+
+ void setInput(locoex::TFLTranspose *node, loco::Node *arg1, loco::Node *arg2)
+ {
+ node->a(arg1);
+ node->perm(arg2);
+ };
+
+ // arity 3
+ void setInput(loco::Node *node, loco::Node *, loco::Node *, loco::Node *)
+ {
+ assert(false && "NYI");
+ };
+
+ void setInput(locoex::TFLConv2D *node, loco::Node *input, loco::Node *filter, loco::Node *bias)
+ {
+ node->input(input);
+ node->filter(filter);
+ node->bias(bias);
+ }
+
+private:
+ loco::Node *_next_input;
+};
+
+enum class ExampleGraphType
+{
+ FeatureBiasAdd,
+ ConstGen_ReLU,
+ FilterEncode_FilterDecode,
+ Transpose,
+
+ TFLTranspose,
+};
+
+template <ExampleGraphType T> class ExampleGraph;
+
+/**
+ * @brief Class to create the following:
+ *
+ * Pull - FeatureEncoder - FeatureBiasAdd - FeatureDecode - Push
+ * |
+ * ConstGen - BiasEncode --+
+ */
+template <> class ExampleGraph<ExampleGraphType::FeatureBiasAdd> : public TestGraph
+{
+public:
+ loco::FeatureEncode *fea_enc = nullptr;
+ loco::ConstGen *constgen = nullptr;
+ loco::BiasEncode *bias_enc = nullptr;
+ loco::FeatureBiasAdd *fea_bias_add = nullptr;
+ loco::FeatureDecode *fea_dec = nullptr;
+
+public:
+ ExampleGraph()
+ {
+ fea_enc = exo::make_feature_encode<exo::FeatureLayout::NHWC>(pull);
+ constgen = append<loco::ConstGen>();
+ bias_enc = append<loco::BiasEncode>(constgen);
+ fea_bias_add = append<loco::FeatureBiasAdd>(fea_enc, bias_enc);
+ fea_dec = exo::make_feature_decode<exo::FeatureLayout::NHWC>(fea_bias_add);
+ complete(fea_dec);
+ }
+};
+
+/**
+ * @brief Class to creates the following:
+ *
+ * ConstGen -- ReLU -- Push
+ */
+template <> class ExampleGraph<ExampleGraphType::ConstGen_ReLU> : public TestGraph
+{
+public:
+ loco::ConstGen *constgen = nullptr;
+ loco::ReLU *relu = nullptr;
+
+public:
+ ExampleGraph()
+ {
+ constgen = append<loco::ConstGen>();
+ relu = append<loco::ReLU>(constgen);
+ complete(relu);
+ }
+};
+
+/**
+ * @brief Class to creates the following:
+ *
+ * Pull -- Transpose -- Push
+ */
+template <> class ExampleGraph<ExampleGraphType::Transpose> : public TestGraph
+{
+public:
+ loco::TensorTranspose *transpose = nullptr;
+
+public:
+ ExampleGraph()
+ {
+ transpose = append<loco::TensorTranspose>(pull);
+ complete(transpose);
+ }
+};
+
+/**
+ * @brief Class to creates the following:
+ *
+ * Pull -- FilterEncode -- FilterDecode -- Push
+ */
+template <> class ExampleGraph<ExampleGraphType::FilterEncode_FilterDecode> : public TestGraph
+{
+public:
+ loco::FilterEncode *filterEncode = nullptr;
+ loco::FilterDecode *filterDecode = nullptr;
+
+public:
+ ExampleGraph()
+ {
+ filterEncode = exo::make_filter_encode<exo::FilterLayout::HWIO>(pull); // from Tensorflow
+ filterDecode =
+ exo::make_filter_decode<exo::FilterLayout::OHWI>(filterEncode); // to Tensorflow Lite
+ complete(filterDecode);
+ }
+};
+
+/**
+ * @brief Class to create the following:
+ *
+ * Pull -- TFLTranspose -- Push
+ */
+template <> class ExampleGraph<ExampleGraphType::TFLTranspose> : public TestGraph
+{
+public:
+ loco::ConstGen *const_perm = nullptr;
+ locoex::TFLTranspose *tfl_transpose = nullptr;
+
+public:
+ ExampleGraph()
+ {
+ const_perm = append<loco::ConstGen>();
+ tfl_transpose = append<locoex::TFLTranspose>(pull, const_perm);
+ complete(tfl_transpose);
+ }
+};
+
+} // namespace test
+} // namespace exo
+
+#endif // __TEST_GRAPH_H__
diff --git a/compiler/exo/src/TestHelper.h b/compiler/exo/src/TestHelper.h
new file mode 100644
index 000000000..1a3de50f5
--- /dev/null
+++ b/compiler/exo/src/TestHelper.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TEST_HELPER_H__
+#define __TEST_HELPER_H__
+
+#include "Check.h"
+#include "ProgressReporter.h"
+#include "Passes.h"
+
+#include <logo/Pass.h>
+#include <logo/Phase.h>
+
+#include <loco.h>
+
+#include <stdex/Memory.h>
+
+#include <gtest/gtest.h>
+
+/**
+ * @brief Check the number of nodes in a graph starting from OUTPUTS
+ */
+#define EXO_TEST_ASSERT_NODE_COUNT(OUTPUTS, COUNT) \
+ { \
+ auto v = loco::postorder_traversal(OUTPUTS); \
+ ASSERT_EQ(v.size(), (COUNT)); \
+ }
+
+namespace exo
+{
+namespace test
+{
+
+/**
+ * @brief Phase for test, that is used to test pass. This phase initially adds TypeInferencePass
+ * and ShapeInferencePass
+ */
+class TypeShapeReadyPhase
+{
+public:
+ TypeShapeReadyPhase()
+ {
+ // Type and Shape inference is prerequisite for run other test
+ _phase.emplace_back(stdex::make_unique<::exo::TypeInferencePass>());
+ _phase.emplace_back(stdex::make_unique<::exo::ShapeInferencePass>());
+ }
+
+ template <typename PassT> void add_pass() { _phase.emplace_back(stdex::make_unique<PassT>()); }
+
+ void run(loco::Graph *g)
+ {
+ const auto restart = logo::PhaseStrategy::Restart;
+ logo::PhaseRunner<restart> phase_runner{g};
+
+ ::exo::ProgressReporter prog(g, restart);
+ phase_runner.attach(&prog);
+ phase_runner.run(_phase);
+ }
+
+private:
+ logo::Phase _phase;
+};
+
+/**
+ * @brief Get the only succ object of type LocoNodeT. (The name `only succ` comes from English word
+ * `only child`.)
+ * parent must have 1 succ only.
+ * When there is no succ of type LocoNodeT, nullptr will be returned.
+ */
+template <typename LocoNodeT> inline LocoNodeT *get_only_succ(loco::Node *parent)
+{
+ auto succs = loco::succs(parent);
+ EXO_ASSERT(succs.size() == 1, "parent has more than 1 succs.");
+
+ return dynamic_cast<LocoNodeT *>(*succs.begin());
+}
+
+template <typename T> inline T *find_first_node_bytype(loco::Graph *g)
+{
+ T *first_node = nullptr;
+ loco::Graph::NodeContext *nodes = g->nodes();
+ uint32_t count = nodes->size();
+
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ first_node = dynamic_cast<T *>(nodes->at(i));
+ if (first_node != nullptr)
+ break;
+ }
+
+ return first_node;
+}
+
+} // namespace test
+} // namespace exo
+
+#endif // __TEST_HELPER_H__
diff --git a/compiler/fipe/CMakeLists.txt b/compiler/fipe/CMakeLists.txt
index 1459c60ef..2cabf6279 100644
--- a/compiler/fipe/CMakeLists.txt
+++ b/compiler/fipe/CMakeLists.txt
@@ -5,7 +5,7 @@ if(NOT ENABLE_TEST)
return()
endif(NOT ENABLE_TEST)
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
GTest_AddTest(fipe_test fipe.test.cpp)
target_link_libraries(fipe_test fipe)
diff --git a/compiler/fipe/README.md b/compiler/fipe/README.md
new file mode 100644
index 000000000..7f3c74ae2
--- /dev/null
+++ b/compiler/fipe/README.md
@@ -0,0 +1 @@
+# fipe
diff --git a/compiler/foder/CMakeLists.txt b/compiler/foder/CMakeLists.txt
new file mode 100644
index 000000000..6a413c61e
--- /dev/null
+++ b/compiler/foder/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_library(foder INTERFACE)
+target_include_directories(foder INTERFACE include)
diff --git a/compiler/foder/README.md b/compiler/foder/README.md
new file mode 100644
index 000000000..2bb1635a1
--- /dev/null
+++ b/compiler/foder/README.md
@@ -0,0 +1,13 @@
+# foder
+
+_foder_ is a header only library that loads files.
+
+## Example
+
+```cpp
+foder::FileLoader fileloader{input_path};
+
+std::vector<char> data = fileloader.load();
+
+DO_SOMETHING_WITH(data);
+```
diff --git a/compiler/foder/include/foder/FileLoader.h b/compiler/foder/include/foder/FileLoader.h
new file mode 100644
index 000000000..e2143ecf6
--- /dev/null
+++ b/compiler/foder/include/foder/FileLoader.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fstream>
+#include <vector>
+
+namespace foder
+{
+
+class FileLoader
+{
+private:
+ using DataBuffer = std::vector<char>;
+
+public:
+ explicit FileLoader(const std::string &path) : _path(path) {}
+
+public:
+ FileLoader(const FileLoader &) = delete;
+ FileLoader(FileLoader &&) = delete;
+
+public:
+ DataBuffer load(void) const
+ {
+ std::ifstream file(_path, std::ios::binary | std::ios::in);
+ if (!file.good())
+ {
+ std::string errmsg = "ERROR: Failed to open file: " + _path;
+ throw std::runtime_error(errmsg.c_str());
+ }
+
+ file.unsetf(std::ios::skipws);
+
+ file.seekg(0, std::ios::end);
+ auto fileSize = file.tellg();
+ file.seekg(0, std::ios::beg);
+
+ // reserve capacity
+ DataBuffer data(fileSize);
+
+ // read the data
+ file.read(data.data(), fileSize);
+ if (file.fail())
+ {
+ std::string errmsg = "ERROR: Failed to read file: " + _path;
+ throw std::runtime_error(errmsg.c_str());
+ }
+
+ return data;
+ }
+
+private:
+ const std::string _path;
+};
+
+} // namespace foder
diff --git a/compiler/hermes-std/CMakeLists.txt b/compiler/hermes-std/CMakeLists.txt
index 85dc482c7..c7b02e14c 100644
--- a/compiler/hermes-std/CMakeLists.txt
+++ b/compiler/hermes-std/CMakeLists.txt
@@ -20,7 +20,7 @@ if(NOT ENABLE_TEST)
endif(NOT ENABLE_TEST)
# Google Test is mandatory for internal testing
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
GTest_AddTest(hermes_std_test ${TESTS})
target_link_libraries(hermes_std_test stdex)
diff --git a/compiler/hermes/CMakeLists.txt b/compiler/hermes/CMakeLists.txt
index 4876303e0..5debfbca0 100644
--- a/compiler/hermes/CMakeLists.txt
+++ b/compiler/hermes/CMakeLists.txt
@@ -18,7 +18,7 @@ if(NOT ENABLE_TEST)
endif(NOT ENABLE_TEST)
# Google Test is mandatory for internal testing
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
add_executable(hermes_test ${TESTS})
target_link_libraries(hermes_test gtest_main)
diff --git a/compiler/hermes/src/hermes.test.cpp b/compiler/hermes/src/hermes.test.cpp
index 2cbc0939d..ea7ef65d8 100644
--- a/compiler/hermes/src/hermes.test.cpp
+++ b/compiler/hermes/src/hermes.test.cpp
@@ -18,7 +18,28 @@
#include <gtest/gtest.h>
-TEST(HermesTest, simple_usecase)
+namespace
{
- // TO BE FILLED
+
+class Logger final : public hermes::Source
+{
+public:
+ Logger(hermes::Context *ctx);
+ ~Logger();
+};
+
+Logger::Logger(hermes::Context *ctx) { activate(ctx->sources(), ctx->bus()); }
+Logger::~Logger() { deactivate(); }
+
+} // namespace
+
+TEST(HermesTest, logger_constructor_NEG)
+{
+ hermes::Context context;
+ // we expect segmentfault from nullptr->sources()
+ ASSERT_DEATH(Logger logger(&context), "");
+
+ SUCCEED();
}
+
+// TODO add HermesTest simple_usecase
diff --git a/compiler/i5diff/CMakeLists.txt b/compiler/i5diff/CMakeLists.txt
index 321ae49a0..c310a668e 100644
--- a/compiler/i5diff/CMakeLists.txt
+++ b/compiler/i5diff/CMakeLists.txt
@@ -1,4 +1,4 @@
-find_package(HDF5 COMPONENTS CXX QUIET)
+nnas_find_package(HDF5 QUIET)
if(NOT HDF5_FOUND)
return()
diff --git a/compiler/kuma/CMakeLists.txt b/compiler/kuma/CMakeLists.txt
new file mode 100644
index 000000000..e705bfedb
--- /dev/null
+++ b/compiler/kuma/CMakeLists.txt
@@ -0,0 +1,19 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(kuma STATIC ${SOURCES})
+set_target_properties(kuma PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(kuma PUBLIC include)
+target_link_libraries(kuma PRIVATE nncc_common)
+target_link_libraries(kuma PUBLIC nncc_coverage)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+# Google Test is mandatory for test
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(kuma_test ${TESTS})
+target_link_libraries(kuma_test kuma)
diff --git a/compiler/kuma/README.md b/compiler/kuma/README.md
new file mode 100644
index 000000000..7e5123968
--- /dev/null
+++ b/compiler/kuma/README.md
@@ -0,0 +1,7 @@
+# kuma
+
+_kuma_ is a collection of offline memory allocators.
+
+## What does "kuma" mean?
+
+_kuma_ originates from _cooma_ which is an abbreviation of **C**ollection **O**f **O**ffline **M**emory **A**lloators.
diff --git a/compiler/kuma/include/kuma.h b/compiler/kuma/include/kuma.h
new file mode 100644
index 000000000..a3d9a2e91
--- /dev/null
+++ b/compiler/kuma/include/kuma.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __KUMA_H__
+#define __KUMA_H__
+
+#include <cstdint>
+#include <set>
+
+namespace kuma
+{
+
+// Supported algorithms
+enum Algorithm
+{
+ // No reuse
+ Greedy,
+ LinearScanFirstFit,
+};
+
+/**
+ * Each algorithm defines its own context. The context describes its in and out.
+ */
+template <Algorithm Alg> class Context;
+
+using ItemID = uint32_t;
+using ItemSize = uint32_t;
+
+using MemoryOffset = uint32_t;
+using MemorySize = uint32_t;
+
+//
+// Greedy Algorithm
+//
+template <> class Context<Algorithm::Greedy>
+{
+public:
+ virtual ~Context() = default;
+
+public: // Inputs
+ // count() returns the number of items to be allocated
+ virtual uint32_t item_count(void) const = 0;
+
+ // size(N) returns the size of the N-th item
+ virtual ItemSize item_size(const ItemID &) const = 0;
+
+public: // Outputs
+ virtual void mem_offset(const ItemID &, const MemoryOffset &) = 0;
+ virtual void mem_total(const MemorySize &) = 0;
+};
+
+void solve(Context<Greedy> *);
+
+//
+// Linear Scan First-Fit Algorithm
+//
+template <> class Context<Algorithm::LinearScanFirstFit>
+{
+public:
+ virtual ~Context() = default;
+
+public: // Inputs
+ // count() returns the number of items to be allocated
+ virtual uint32_t item_count(void) const = 0;
+
+ // size(N) returns the size of the N-th item
+ virtual ItemSize item_size(const ItemID &) const = 0;
+
+ // conflict_with(N) returns all the items that are in conflict with item N
+ // - An item N is said to be in conflict with item M if item M and N cannot have overlap
+ //
+ // NOTE
+ // - conflict_with(N) SHOULD NOT include N itself
+ virtual std::set<ItemID> conflict_with(const ItemID &) const = 0;
+
+public: // Outputs
+ virtual void mem_offset(const ItemID &, const MemoryOffset &) = 0;
+ virtual void mem_total(const MemorySize &) = 0;
+};
+
+void solve(Context<Algorithm::LinearScanFirstFit> *);
+
+} // namespace kuma
+
+#endif // __KUMA_H__
diff --git a/compiler/kuma/src/IntervalSet.cpp b/compiler/kuma/src/IntervalSet.cpp
new file mode 100644
index 000000000..f6790c654
--- /dev/null
+++ b/compiler/kuma/src/IntervalSet.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "IntervalSet.h"
+
+#include <cassert>
+#include <stdexcept>
+
+namespace kuma
+{
+namespace details
+{
+
+IntervalSet::IntervalSet(uint32_t len)
+{
+ // Update _content
+ _content[len] = len;
+}
+
+void IntervalSet::insert(const IntervalMask &m)
+{
+ auto s = m.s;
+ auto e = m.e;
+
+ assert(s <= e);
+
+ if (s == e)
+ {
+ // Empty region, nothing to do
+ return;
+ }
+
+ // lower_bound() returns an iterator to the first element not less than the given key
+ auto lb = _content.lower_bound(s);
+
+ // NOTE 1. "lower_bound" ensures "prev_s < s <= curr_e"
+ // NOTE 2. "e" points to somewhere after "s"
+ auto curr_s = lb->first - lb->second;
+ auto curr_e = lb->first;
+
+ if (curr_s < s)
+ {
+ // Split the current interval
+ _content[s] = s - curr_s;
+ // NOTE The invariant over "_content" is temporarily broken here.
+ }
+
+ if (e < curr_e)
+ {
+ // Adjust the current interval
+ _content[curr_e] = curr_e - e;
+ }
+ else
+ {
+ // Remove the current interval
+ _content.erase(curr_e);
+ // Check the next interval (e > curr_e)
+ //
+ // TODO Remove this recursive call (to prevent stack overflow issue)
+ insert(mask(curr_e, e));
+ }
+}
+
+uint32_t IntervalSet::firstfit(uint32_t len) const
+{
+ for (auto it = _content.begin(); it != _content.end(); ++it)
+ {
+ if (it->second >= len)
+ {
+ // Got it! This interval is larger than "len".
+ return it->first - it->second;
+ }
+ }
+
+ throw std::runtime_error{"infeasible"};
+}
+
+} // namespace details
+} // namespace kuma
diff --git a/compiler/kuma/src/IntervalSet.h b/compiler/kuma/src/IntervalSet.h
new file mode 100644
index 000000000..3b6c5f666
--- /dev/null
+++ b/compiler/kuma/src/IntervalSet.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __KUMA_DETAILS_LIVE_INTERVAL_SET_H__
+#define __KUMA_DETAILS_LIVE_INTERVAL_SET_H__
+
+#include <map>
+
+namespace kuma
+{
+namespace details
+{
+
+struct IntervalMask
+{
+ uint32_t s;
+ uint32_t e;
+};
+
+inline IntervalMask mask(uint32_t s, uint32_t e)
+{
+ IntervalMask mask;
+
+ mask.s = s;
+ mask.e = e;
+
+ return mask;
+}
+
+class IntervalSet
+{
+public:
+ // [0, len) is live at the beginning
+ IntervalSet(uint32_t len = 0xffffffff);
+
+public:
+ void insert(const IntervalMask &);
+
+ /**
+ * "firstfit(l)" returns the offset of an interval whose length is larger than "l".
+ *
+ * When multiple intervals meet this condition, "firstfit(l)" chooses the interval
+ * with the smallest offset as its name suggests.
+ *
+ * NOTE This method throws std::runtime_error if fails to find a proper region
+ */
+ uint32_t firstfit(uint32_t len) const;
+
+private:
+ using End = uint32_t;
+ using Len = uint32_t;
+
+ // If [e -> l] is in _content, it means that [e - l, e) is a valid interval.
+ //
+ // INVARIANT
+ //
+ // If key m and n (m <= n) are consecutive in _content, "m <= n - _content.at(n)" holds.
+ //
+ std::map<End, Len> _content;
+};
+
+} // namespace details
+} // namespace kuma
+
+#endif // __KUMA_DETAILS_LIVE_INTERVAL_SET_H__
diff --git a/compiler/kuma/src/IntervalSet.test.cpp b/compiler/kuma/src/IntervalSet.test.cpp
new file mode 100644
index 000000000..848ddee03
--- /dev/null
+++ b/compiler/kuma/src/IntervalSet.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "IntervalSet.h"
+
+#include <gtest/gtest.h>
+
+using namespace kuma::details;
+
+TEST(IntervalSetTests, mask_and_firstfit)
+{
+ IntervalSet intervals;
+
+ // Exclude [0, 16) from available region
+ intervals.insert(mask(0, 16));
+
+ ASSERT_EQ(intervals.firstfit(4), 16);
+}
diff --git a/compiler/kuma/src/kuma.cpp b/compiler/kuma/src/kuma.cpp
new file mode 100644
index 000000000..6fad96b26
--- /dev/null
+++ b/compiler/kuma/src/kuma.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kuma.h"
+
+//
+// Greedy Allocation Algorithm
+//
+namespace kuma
+{
+
+void solve(Context<Algorithm::Greedy> *ctx)
+{
+ uint32_t next = 0;
+
+ for (uint32_t n = 0; n < ctx->item_count(); ++n)
+ {
+ ctx->mem_offset(n, next);
+ next += ctx->item_size(n);
+ }
+
+ ctx->mem_total(next);
+};
+
+} // namespace kuma
+
+//
+// Linear Scan First Fit Algorithm
+//
+#include "IntervalSet.h"
+
+namespace kuma
+{
+
+void solve(Context<Algorithm::LinearScanFirstFit> *ctx)
+{
+ using namespace kuma::details;
+
+ uint32_t upper_bound = 0;
+ std::map<ItemID, std::pair<uint32_t /* BEGIN */, uint32_t /* END */>> committed_items;
+
+ // Allocate items in linear order (from item 0, item 1, ...)
+ //
+ // The implementor of Context is responsible for item ordering.
+ for (uint32_t n = 0; n < ctx->item_count(); ++n)
+ {
+ IntervalSet intervals;
+
+ for (auto item_in_conflict : ctx->conflict_with(n))
+ {
+ auto it = committed_items.find(item_in_conflict);
+
+ // Skip if item_in_conflict is not committed yet
+ if (it == committed_items.end())
+ {
+ continue;
+ }
+
+ auto const alloc_s = it->second.first;
+ auto const alloc_e = it->second.second;
+ intervals.insert(mask(alloc_s, alloc_e));
+ }
+
+ uint32_t const item_size = ctx->item_size(n);
+ uint32_t const item_alloc_s = intervals.firstfit(item_size);
+ uint32_t const item_alloc_e = item_alloc_s + item_size;
+
+ // Notify "mem_offset"
+ ctx->mem_offset(n, item_alloc_s);
+
+ // Update "upper bound" and commit allocation
+ upper_bound = std::max(upper_bound, item_alloc_e);
+ committed_items[n] = std::make_pair(item_alloc_s, item_alloc_e);
+ }
+
+ // Notify "mem_total"
+ ctx->mem_total(upper_bound);
+}
+
+} // namespace kuma
diff --git a/compiler/kuma/src/kuma.test.cpp b/compiler/kuma/src/kuma.test.cpp
new file mode 100644
index 000000000..5d947ea6b
--- /dev/null
+++ b/compiler/kuma/src/kuma.test.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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 "kuma.h"
+
+#include <gtest/gtest.h>
+
+using namespace kuma;
+
+TEST(GreedyAlgorithmTests, empty)
+{
+ struct ContextImpl : public Context<Algorithm::Greedy>
+ {
+ uint32_t item_count(void) const final { return 0; }
+ ItemSize item_size(const ItemID &) const final { throw std::runtime_error{"error"}; }
+
+ void mem_offset(const ItemID &, const MemoryOffset &) { throw std::runtime_error{"error"}; };
+ void mem_total(const MemorySize &total) final { _total = total; }
+
+ uint32_t _total = 0xffffffff;
+ };
+
+ ContextImpl ctx;
+
+ solve(&ctx);
+
+ ASSERT_EQ(ctx._total, 0);
+}
+
+TEST(LinearScanFirstFitTests, reuse)
+{
+ struct ContextImpl : public Context<Algorithm::LinearScanFirstFit>
+ {
+ uint32_t item_count(void) const final { return 3; }
+ ItemSize item_size(const ItemID &) const final { return 4; }
+
+ std::set<ItemID> conflict_with(const ItemID &id) const
+ {
+ // 0 <-> 1 <-> 2
+ switch (id)
+ {
+ case 0:
+ return std::set<ItemID>({1});
+ case 1:
+ return std::set<ItemID>({0, 2});
+ case 2:
+ return std::set<ItemID>({1});
+ default:
+ break;
+ };
+
+ throw std::runtime_error{"Invalid"};
+ }
+
+ void mem_offset(const ItemID &id, const MemoryOffset &offset) { _offsets[id] = offset; };
+ void mem_total(const MemorySize &total) final { _total = total; }
+
+ uint32_t _offsets[3];
+ uint32_t _total = 0xffffffff;
+ };
+
+ ContextImpl ctx;
+
+ solve(&ctx);
+
+ // EXPECTED MEMORY LAYOUT:
+ // ------------------ 0
+ // | ITEM 0, ITEM 2 |
+ // ------------------ 4
+ // | ITEM 1 |
+ // ------------------ 8
+ ASSERT_EQ(ctx._total, 8);
+ ASSERT_EQ(ctx._offsets[0], 0);
+ ASSERT_EQ(ctx._offsets[1], 4);
+ ASSERT_EQ(ctx._offsets[2], 0);
+}
diff --git a/compiler/loco/CMakeLists.txt b/compiler/loco/CMakeLists.txt
index 8d1a85770..f94052840 100644
--- a/compiler/loco/CMakeLists.txt
+++ b/compiler/loco/CMakeLists.txt
@@ -13,17 +13,16 @@ target_link_libraries(loco PRIVATE stdex)
# Please refer to the top-level CMakeLists.txt for details
target_link_libraries(loco PRIVATE nncc_common)
target_link_libraries(loco PUBLIC nncc_coverage)
+# Q. HOW TO MAKE DEV PACKAGE(?)
+install(TARGETS loco DESTINATION lib)
if(NOT ENABLE_TEST)
return()
endif(NOT ENABLE_TEST)
# Google Test is mandatory for internal testing
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
-add_executable(loco_test ${TESTS})
-target_link_libraries(loco_test gtest_main)
+GTest_AddTest(loco_test ${TESTS})
target_link_libraries(loco_test stdex)
target_link_libraries(loco_test loco)
-
-add_test(loco_test loco_test)
diff --git a/compiler/loco/include/loco/IR/CanonicalNodes.lst b/compiler/loco/include/loco/IR/CanonicalNodes.lst
index 32d0629c4..527856fbe 100644
--- a/compiler/loco/include/loco/IR/CanonicalNodes.lst
+++ b/compiler/loco/include/loco/IR/CanonicalNodes.lst
@@ -13,15 +13,18 @@ CANONICAL_NODE(BiasEncode, BiasEncode)
CANONICAL_NODE(ConstGen, ConstGen)
CANONICAL_NODE(Conv2D, Conv2D)
CANONICAL_NODE(DepthwiseConv2D, DepthwiseConv2D)
+CANONICAL_NODE(DepthwiseFilterDecode, DepthwiseFilterDecode)
CANONICAL_NODE(DepthwiseFilterEncode, DepthwiseFilterEncode)
CANONICAL_NODE(EltwiseAdd, EltwiseAdd)
CANONICAL_NODE(EltwiseDiv, EltwiseDiv)
+CANONICAL_NODE(EltwiseMax, EltwiseMax)
CANONICAL_NODE(EltwiseMul, EltwiseMul)
CANONICAL_NODE(EltwiseSqrt, EltwiseSqrt)
CANONICAL_NODE(EltwiseSub, EltwiseSub)
CANONICAL_NODE(FeatureBiasAdd, BiasAdd<Domain::Feature>)
CANONICAL_NODE(FeatureDecode, FeatureDecode)
CANONICAL_NODE(FeatureEncode, FeatureEncode)
+CANONICAL_NODE(FilterDecode, FilterDecode)
CANONICAL_NODE(FilterEncode, FilterEncode)
CANONICAL_NODE(FixedReshape, Reshape<ReshapeType::Fixed>)
CANONICAL_NODE(Forward, Forward)
@@ -34,7 +37,13 @@ CANONICAL_NODE(ReLU, ReLU)
CANONICAL_NODE(ReLU6, ReLU6)
CANONICAL_NODE(Tanh, Tanh)
CANONICAL_NODE(TensorConcat, TensorConcat)
+CANONICAL_NODE(TensorConstantPad, TensorConstantPad)
CANONICAL_NODE(TensorBiasAdd, BiasAdd<Domain::Tensor>)
CANONICAL_NODE(TensorBroadcast, TensorBroadcast)
+CANONICAL_NODE(TensorReduce, TensorReduce)
+CANONICAL_NODE(TensorTranspose, TensorTranspose)
CANONICAL_NODE(TensorSoftmax, Softmax<Domain::Tensor>)
CANONICAL_NODE(TransposedConv2D, TransposedConv2D)
+CANONICAL_NODE(MatrixEncode, MatrixEncode)
+CANONICAL_NODE(MatrixDecode, MatrixDecode)
+CANONICAL_NODE(MatMul, MatMul)
diff --git a/compiler/loco/include/loco/IR/CastHelpers.h b/compiler/loco/include/loco/IR/CastHelpers.h
new file mode 100644
index 000000000..0dcd92df6
--- /dev/null
+++ b/compiler/loco/include/loco/IR/CastHelpers.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOCO_IR_CAST_HELPERS_H__
+#define __LOCO_IR_CAST_HELPERS_H__
+
+#include <string>
+#include <stdexcept>
+#include <typeinfo>
+
+namespace loco
+{
+
+// TODO move to somewhere appropriate
+template <typename T, typename ARG> T _must_cast(ARG arg)
+{
+ auto cast_arg = dynamic_cast<T>(arg);
+ if (cast_arg == nullptr)
+ {
+ std::string msg = "loco::must_cast() failed to cast: ";
+ msg += typeid(T).name();
+ throw std::invalid_argument(msg.c_str());
+ }
+ return cast_arg;
+}
+
+} // namespace loco
+
+#endif // __LOCO_IR_CAST_HELPERS_H__
diff --git a/compiler/loco/include/loco/IR/DataType.h b/compiler/loco/include/loco/IR/DataType.h
index 1c3abd151..b07022bf5 100644
--- a/compiler/loco/include/loco/IR/DataType.h
+++ b/compiler/loco/include/loco/IR/DataType.h
@@ -40,6 +40,10 @@ enum class DataType
FLOAT16, // IEEE 16-bit floating-point
FLOAT32, // IEEE 32-bit floating-point
FLOAT64, // IEEE 64-bit floating-point
+
+ // WARNING the size of Bool may vary for NN frameworks
+ // TODO we need to find a way to resolve this issue
+ BOOL, // Boolean
};
} // namespace loco
diff --git a/compiler/loco/include/loco/IR/DataTypeTraits.h b/compiler/loco/include/loco/IR/DataTypeTraits.h
index ef731877e..c186300de 100644
--- a/compiler/loco/include/loco/IR/DataTypeTraits.h
+++ b/compiler/loco/include/loco/IR/DataTypeTraits.h
@@ -19,6 +19,7 @@
#include "loco/IR/DataType.h"
+#include <cassert>
#include <cstdint>
namespace loco
@@ -33,18 +34,86 @@ template <DataType DT> struct DataTypeImpl
};
// TODO Support other enum values
+template <> struct DataTypeImpl<DataType::S8>
+{
+ // Use C++ int8_t type for 8bit integer
+ using Type = int8_t;
+};
+
+template <> struct DataTypeImpl<DataType::U8>
+{
+ // Use C++ uint8_t type for unsigned 8bit integer
+ using Type = uint8_t;
+};
+
+template <> struct DataTypeImpl<DataType::S16>
+{
+ // Use C++ int16_t type for 16bit integer
+ using Type = int16_t;
+};
+
template <> struct DataTypeImpl<DataType::S32>
{
// Use C++ int32_t type for 32bit integer
using Type = int32_t;
};
+template <> struct DataTypeImpl<DataType::U32>
+{
+ // Use C++ uint32_t type for unsigned 32bit integer
+ using Type = uint32_t;
+};
+
+template <> struct DataTypeImpl<DataType::S64>
+{
+ // Use C++ int64_t type for 64bit integer
+ using Type = int64_t;
+};
+
template <> struct DataTypeImpl<DataType::FLOAT32>
{
// Use C++ float type for IEEE 32-bit floating-point numbers
using Type = float;
};
+// NOTE DataTypeImpl for BOOL is subject to change
+template <> struct DataTypeImpl<DataType::BOOL>
+{
+ // Use C++ uint8_t type for bool
+ using Type = uint8_t;
+};
+
+/**
+ * @brief Returns the size of the data type.
+ * @note If you need the size at compile time, use `sizeof(typename DataTypeImpl<DT>::Type)`.
+ */
+inline uint32_t size(DataType data_type)
+{
+ switch (data_type)
+ {
+ case DataType::S8:
+ return sizeof(DataTypeImpl<DataType::S8>::Type);
+ case DataType::U8:
+ return sizeof(DataTypeImpl<DataType::U8>::Type);
+ case DataType::S16:
+ return sizeof(DataTypeImpl<DataType::S16>::Type);
+ case DataType::S32:
+ return sizeof(DataTypeImpl<DataType::S32>::Type);
+ case DataType::U32:
+ return sizeof(DataTypeImpl<DataType::U32>::Type);
+ case DataType::S64:
+ return sizeof(DataTypeImpl<DataType::S64>::Type);
+ case DataType::FLOAT32:
+ return sizeof(DataTypeImpl<DataType::FLOAT32>::Type);
+ case DataType::BOOL:
+ return sizeof(DataTypeImpl<DataType::BOOL>::Type);
+ default:
+ // TODO Support remaining data types.
+ assert(false);
+ return UINT32_MAX; // Avoid compiler warning.
+ }
+}
+
} // namespace loco
#endif // __LOCO_IR_DATA_TYPE_TRAITS_H__
diff --git a/compiler/loco/include/loco/IR/DepthwiseFilterCodec.h b/compiler/loco/include/loco/IR/DepthwiseFilterCodec.h
index e7fba7346..0d9286b46 100644
--- a/compiler/loco/include/loco/IR/DepthwiseFilterCodec.h
+++ b/compiler/loco/include/loco/IR/DepthwiseFilterCodec.h
@@ -27,7 +27,7 @@ namespace loco
{
/**
- * @brief Decribe how to build a depthwise convolution filter from a tensor
+ * @brief Describe how to build a depthwise convolution filter from a tensor
*
* Let us assume that "enc" is a depthwise filter encoder.
*
@@ -45,7 +45,24 @@ struct DepthwiseFilterEncoder
virtual TensorIndex value(const DepthwiseFilterIndex &index) const = 0;
};
-// TODO Introduce DepthwiseFilterDecoder if required.
+/**
+ * @brief Describe how to build a tensor from a depthwise convolution filter
+ *
+ * Let us assume that "dec" is a depthwise filter decoder.
+ *
+ * Given a depthwise filter "inp" and its shape "inp.shape", "dec" builds a tensor
+ * "out" as follows:
+ *
+ * for each valid tensor_index for dec.shape(inp.shape)
+ * out.at(tensor_index) = inp.at(dec.value(tensor_index))
+ */
+struct DepthwiseFilterDecoder
+{
+ virtual ~DepthwiseFilterDecoder() = default;
+
+ virtual TensorShape shape(const DepthwiseFilterShape &shape) const = 0;
+ virtual DepthwiseFilterIndex value(const TensorIndex &index) const = 0;
+};
} // namespace loco
diff --git a/compiler/loco/include/loco/IR/Dimension.h b/compiler/loco/include/loco/IR/Dimension.h
index 7b5d5943f..a939f1a30 100644
--- a/compiler/loco/include/loco/IR/Dimension.h
+++ b/compiler/loco/include/loco/IR/Dimension.h
@@ -36,18 +36,18 @@ private:
};
public:
- // @brief Construct an "unknown" dimension
+ /// @brief Construct an "unknown" dimension
Dimension() = default;
- // @brief Construct a "known" dimension
+ /// @brief Construct a "known" dimension
Dimension(uint32_t value) { set(value); }
public:
- // @brief Return whether the value is known (or not)
+ /// @brief Return whether the value is known (or not)
bool known(void) const { return _kind == Kind::Known; }
- // @brief Return the value
- // @note This value is meaningful only for known dimension
+ /// @brief Return the value
+ /// @note This value is meaningful only for known dimension
uint32_t value(void) const { return _value; }
void set(uint32_t value)
@@ -77,7 +77,7 @@ bool operator==(const Dimension &, const Dimension &);
bool operator==(const Dimension &, uint32_t);
bool operator==(uint32_t, const Dimension &);
-// @brief Make an "unknown" dimension
+/// @brief Make an "unknown" dimension
Dimension make_dimension(void);
} // namespace loco
diff --git a/compiler/loco/include/loco/IR/FeatureCodec.h b/compiler/loco/include/loco/IR/FeatureCodec.h
index 93094e13a..c5c465625 100644
--- a/compiler/loco/include/loco/IR/FeatureCodec.h
+++ b/compiler/loco/include/loco/IR/FeatureCodec.h
@@ -23,6 +23,8 @@
#include "loco/IR/TensorShape.h"
#include "loco/IR/TensorIndex.h"
+#include "loco/IR/CastHelpers.h"
+
#include <memory>
namespace loco
@@ -72,6 +74,29 @@ struct FeatureDecoder
virtual std::unique_ptr<FeatureDecoder> clone(void) const = 0;
};
+/**
+ * @brief A helper dynamic_cast that throws when failed
+ */
+template <typename T> T must_cast(FeatureEncoder *node)
+{
+ return _must_cast<T, FeatureEncoder *>(node);
+}
+
+template <typename T> T must_cast(const FeatureEncoder *node)
+{
+ return _must_cast<T, const FeatureEncoder *>(node);
+}
+
+template <typename T> T must_cast(FeatureDecoder *node)
+{
+ return _must_cast<T, FeatureDecoder *>(node);
+}
+
+template <typename T> T must_cast(const FeatureDecoder *node)
+{
+ return _must_cast<T, const FeatureDecoder *>(node);
+}
+
} // namespace loco
#endif // __LOCO_IR_FEATURE_CODEC_H__
diff --git a/compiler/loco/include/loco/IR/FilterCodec.h b/compiler/loco/include/loco/IR/FilterCodec.h
index c7d34b65d..cf13deed3 100644
--- a/compiler/loco/include/loco/IR/FilterCodec.h
+++ b/compiler/loco/include/loco/IR/FilterCodec.h
@@ -23,6 +23,8 @@
#include "loco/IR/TensorShape.h"
#include "loco/IR/TensorIndex.h"
+#include "loco/IR/CastHelpers.h"
+
namespace loco
{
@@ -45,7 +47,31 @@ struct FilterEncoder
virtual TensorIndex value(const FilterIndex &index) const = 0;
};
-// TODO Introduce FilterDecoder if required.
+/**
+ * @brief Decribe how to build a a tensor from a filter
+ */
+struct FilterDecoder
+{
+ virtual ~FilterDecoder() = default;
+
+ virtual TensorShape shape(const FilterShape &shape) const = 0;
+ virtual FilterIndex value(const TensorIndex &index) const = 0;
+};
+
+/**
+ * @brief A helper dynamic_cast that throws when failed
+ */
+template <typename T> T must_cast(FilterEncoder *node)
+{
+ return _must_cast<T, FilterEncoder *>(node);
+}
+
+template <typename T> T must_cast(const FilterEncoder *node)
+{
+ return _must_cast<T, const FilterEncoder *>(node);
+}
+
+// TODO add must_cast for FilterDecoder
} // namespace loco
diff --git a/compiler/loco/include/loco/IR/Graph.h b/compiler/loco/include/loco/IR/Graph.h
index 86cb65cab..a820aba91 100644
--- a/compiler/loco/include/loco/IR/Graph.h
+++ b/compiler/loco/include/loco/IR/Graph.h
@@ -106,9 +106,6 @@ class GraphInput final : private NamedEntity,
public Mixin<Trait::TensorShaped>
{
public:
- friend void link(GraphInput *, Pull *);
-
-public:
LOCO_NAMED_ENTITY_EXPOSE;
// TODO Use GraphInputIndex (instead of uint32_t)
@@ -137,9 +134,6 @@ class GraphOutput final : private NamedEntity,
public Mixin<Trait::TensorShaped>
{
public:
- friend void link(GraphOutput *, Push *);
-
-public:
LOCO_NAMED_ENTITY_EXPOSE;
// TODO Use GraphOutputIndex (instead of uint32_t)
@@ -163,7 +157,7 @@ private:
/**
* @brief A neural network graph
*/
-class Graph final
+class Graph final : public NamedEntity
{
public:
/**
@@ -234,6 +228,25 @@ private:
OutputContext _output_ctx;
};
+struct GraphInputIndexQueryService : public DialectService
+{
+ virtual ~GraphInputIndexQueryService() = default;
+
+ /**
+ * @brief Check whether a given node is associated with any Graph-level input
+ */
+ virtual bool associated(const Node *node) const = 0;
+
+ /**
+ * Exceptions
+ * - index SHOULD throw std::invalid_argument exception if a given node is not associated with
+ * any input (i.e. assocaited above returns false).
+ */
+ virtual GraphInputIndex index(const Node *node) const = 0;
+};
+
+std::vector<Node *> input_nodes(const Graph *);
+
struct GraphOutputIndexQueryService : public DialectService
{
virtual ~GraphOutputIndexQueryService() = default;
@@ -244,7 +257,9 @@ struct GraphOutputIndexQueryService : public DialectService
virtual bool associated(const Node *node) const = 0;
/**
- * WARNING! CALLER SHOULD GUARANTEE that associated(node) is true before invoking this API.
+ * Exceptions
+ * - index SHOULD throw std::invalid_argument exception if a given node is not associated with
+ * any output (i.e. assocaited above returns false).
*/
virtual GraphOutputIndex index(const Node *node) const = 0;
};
diff --git a/compiler/loco/include/loco/IR/Node.h b/compiler/loco/include/loco/IR/Node.h
index ef0bf238d..28689b765 100644
--- a/compiler/loco/include/loco/IR/Node.h
+++ b/compiler/loco/include/loco/IR/Node.h
@@ -23,6 +23,7 @@
#include "loco/IR/Dialect.h"
#include "loco/IR/NodePool.forward.h"
#include "loco/IR/Graph.forward.h"
+#include "loco/IR/CastHelpers.h"
#include <array>
#include <memory>
@@ -142,6 +143,13 @@ private:
Subst<SubstQualifier::Default> replace(Node *node);
+/**
+ * @brief A helper dynamic_cast that throws when failed
+ */
+template <typename T> T must_cast(Node *node) { return _must_cast<T, Node *>(node); }
+
+template <typename T> T must_cast(const Node *node) { return _must_cast<T, const Node *>(node); }
+
} // namespace loco
#endif // __LOCO_IR_NODE_H__
diff --git a/compiler/loco/include/loco/IR/NodeMixins.h b/compiler/loco/include/loco/IR/NodeMixins.h
index 89e0920ec..f0e34b0ba 100644
--- a/compiler/loco/include/loco/IR/NodeMixins.h
+++ b/compiler/loco/include/loco/IR/NodeMixins.h
@@ -116,7 +116,7 @@ template <unsigned N> struct FixedArity
Use *at(unsigned n) const { return _args.at(n).get(); }
private:
- std::array<std::unique_ptr<Use>, N> _args;
+ std::array<std::unique_ptr<Use>, N> _args{};
};
};
diff --git a/compiler/loco/include/loco/IR/Nodes.h b/compiler/loco/include/loco/IR/Nodes.h
index 316b263b8..fecfad28d 100644
--- a/compiler/loco/include/loco/IR/Nodes.h
+++ b/compiler/loco/include/loco/IR/Nodes.h
@@ -26,10 +26,13 @@
#include "loco/IR/Window.h"
#include "loco/IR/Stride.h"
#include "loco/IR/Padding2D.h"
+#include "loco/IR/PaddingND.h"
#include "loco/IR/TensorAxis.h"
+#include "loco/IR/TensorAxisSet.h"
#include "loco/IR/FeatureCodec.h"
#include "loco/IR/FilterCodec.h"
#include "loco/IR/DepthwiseFilterCodec.h"
+#include "loco/IR/MatrixCodec.h"
#include "loco/IR/NodeMixins.h"
#include "loco/IR/CanonicalNodeDecl.h"
#include "loco/IR/GraphInputIndex.h"
@@ -426,7 +429,24 @@ private:
std::unique_ptr<FilterEncoder> _enc{nullptr};
};
-// TODO Introduce FilterDecode (if required)
+/**
+ * @brief Create a tensor from a filter
+ */
+class FilterDecode final
+ : public CanonicalNodeDef<CanonicalOpcode::FilterDecode, FixedArity<1>::Mixin>
+{
+public:
+ Node *input(void) const { return at(0)->node(); }
+ void input(Node *node) { at(0)->node(node); }
+
+public:
+ FilterDecoder *decoder(void) const { return _dec.get(); }
+ void decoder(std::unique_ptr<FilterDecoder> &&dec) { _dec = std::move(dec); }
+
+private:
+ /// @note "decoder" is mandatory
+ std::unique_ptr<FilterDecoder> _dec{nullptr};
+};
/**
* @brief Create a depthwise filter from a tensor
@@ -447,7 +467,24 @@ private:
std::unique_ptr<DepthwiseFilterEncoder> _enc{nullptr};
};
-// TODO Introduce DepthwiseFilterDecode (if required)
+/**
+ * @brief Create a tensor from a depthwise filter
+ */
+class DepthwiseFilterDecode final
+ : public CanonicalNodeDef<CanonicalOpcode::DepthwiseFilterDecode, FixedArity<1>::Mixin>
+{
+public:
+ Node *input(void) const { return at(0)->node(); }
+ void input(Node *node) { at(0)->node(node); }
+
+public:
+ DepthwiseFilterDecoder *decoder(void) const { return _dec.get(); }
+ void decoder(std::unique_ptr<DepthwiseFilterDecoder> &&dec) { _dec = std::move(dec); }
+
+private:
+ /// @note "decoder" is mandatory
+ std::unique_ptr<DepthwiseFilterDecoder> _dec{nullptr};
+};
enum class ReshapeType
{
@@ -566,6 +603,39 @@ private:
};
/**
+ * @brief Reduce type functions
+ */
+enum class ReduceFunc
+{
+ Mean, // ReduceMean
+ // TODO Support other reduce operations
+};
+
+/**
+ * @brief Computes ReduceFunc operations for Tensor domain
+ * @note All the reduce functions always keep dimensions
+ */
+class TensorReduce final
+ : public CanonicalNodeDef<CanonicalOpcode::TensorReduce, FixedArity<1>::Mixin>
+{
+public:
+ Node *input(void) const { return at(0)->node(); }
+ void input(Node *node) { at(0)->node(node); }
+
+public:
+ const TensorAxisSet *axes(void) const { return &_axes; }
+ TensorAxisSet *axes(void) { return &_axes; }
+
+public:
+ ReduceFunc func(void) const { return _func; }
+ void func(ReduceFunc func) { _func = func; }
+
+private:
+ TensorAxisSet _axes;
+ ReduceFunc _func{ReduceFunc::Mean};
+};
+
+/**
* @brief 2D Transposed Convolution
*
* @note TransposedConv2D have a few important conventions that IR users should
@@ -759,6 +829,43 @@ public:
using FeatureBiasAdd = BiasAdd<Domain::Feature>;
/**
+ * @brief Pads a tensor with constant value
+ *
+ * Pads a input tensor according to the padding with constant value.
+ *
+ * The dimension of each axis n of the output is
+ * output.dim(n) = padding.front(n) + input.dim(n) + padding.back(n)
+ *
+ * For example, input tensor of shape [1, 2] with
+ *
+ * padding.front(0) = 1;
+ * padding.back(0) = 2;
+ *
+ * padding.front(1) = 3;
+ * padding.back(1) = 4;
+ *
+ * will be a output tensor of shape
+ * [padding.front(0) + 1 + padding.back(0), padding.front(1) + 2 + padding.back(1)] = [4,9].
+ */
+class TensorConstantPad final
+ : public CanonicalNodeDef<CanonicalOpcode::TensorConstantPad, FixedArity<2>::Mixin>
+{
+public:
+ Node *input(void) const { return at(0)->node(); }
+ void input(Node *node) { at(0)->node(node); }
+
+ Node *constant(void) const { return at(1)->node(); }
+ void constant(Node *node) { at(1)->node(node); }
+
+public:
+ const PaddingND *padding(void) const { return &_padding; }
+ PaddingND *padding(void) { return &_padding; }
+
+private:
+ PaddingND _padding;
+};
+
+/**
* @brief Elementwise Add lhs and rhs
*/
class EltwiseAdd final : public CanonicalNodeDef<CanonicalOpcode::EltwiseAdd, FixedArity<2>::Mixin>
@@ -775,6 +882,24 @@ public:
};
/**
+ * @brief Elementwise Maximum of lhs and rhs
+ *
+ * o = (l > r) ? l : r (element-wise)
+ */
+class EltwiseMax final : public CanonicalNodeDef<CanonicalOpcode::EltwiseMax, FixedArity<2>::Mixin>
+{
+public:
+ EltwiseMax() = default;
+
+public:
+ Node *lhs(void) const { return at(0)->node(); }
+ void lhs(Node *node) { return at(0)->node(node); }
+
+ Node *rhs(void) const { return at(1)->node(); }
+ void rhs(Node *node) { return at(1)->node(node); }
+};
+
+/**
* @brief Elementwise Mul lhs and rhs
*/
class EltwiseMul final : public CanonicalNodeDef<CanonicalOpcode::EltwiseMul, FixedArity<2>::Mixin>
@@ -883,6 +1008,116 @@ private:
Mapping _mapping;
};
+/**
+ * @brief Create Matrix from Tensor
+ *
+ * MatrixEncode currently requires a rank-2 Tensor as its input.
+ */
+class MatrixEncode final
+ : public CanonicalNodeDef<CanonicalOpcode::MatrixEncode, FixedArity<1>::Mixin>
+{
+public:
+ MatrixEncode() = default;
+
+public:
+ Node *input(void) const { return at(0)->node(); }
+ void input(Node *node) { at(0)->node(node); }
+
+public:
+ MatrixEncoder *encoder(void) const { return _enc.get(); }
+ void encoder(std::unique_ptr<MatrixEncoder> &&enc) { _enc = std::move(enc); }
+
+private:
+ /// @note "encoder" is mandatory
+ std::unique_ptr<MatrixEncoder> _enc{nullptr};
+};
+
+/**
+ * @brief Create Tensor from Matrix
+ *
+ * MatrixDecode currently requires a Matrix as its input.
+ */
+class MatrixDecode final
+ : public CanonicalNodeDef<CanonicalOpcode::MatrixDecode, FixedArity<1>::Mixin>
+{
+public:
+ MatrixDecode() = default;
+
+public:
+ Node *input(void) const { return at(0)->node(); }
+ void input(Node *node) { at(0)->node(node); }
+
+public:
+ MatrixDecoder *decoder(void) const { return _dec.get(); }
+ void decoder(std::unique_ptr<MatrixDecoder> &&dec) { _dec = std::move(dec); }
+
+private:
+ /// @note "decoder" is mandatory
+ std::unique_ptr<MatrixDecoder> _dec{nullptr};
+};
+
+/**
+ * @brief Matrix Multiplication lhs and rhs
+ *
+ * LHS and RHS must be on Matrix domain
+ */
+class MatMul final : public CanonicalNodeDef<CanonicalOpcode::MatMul, FixedArity<2>::Mixin>
+{
+public:
+ MatMul() = default;
+
+public:
+ Node *lhs(void) const { return at(0)->node(); }
+ void lhs(Node *node) { return at(0)->node(node); }
+
+ Node *rhs(void) const { return at(1)->node(); }
+ void rhs(Node *node) { return at(1)->node(node); }
+};
+
+/**
+ * @brief Permute an input
+ *
+ * In the following case,
+ *
+ * output = loco::TensorTranspose(input)
+ *
+ * perm()->axis(output's axis) = input's axis
+ *
+ * Input and output belong to tensor domain.
+ */
+class TensorTranspose final
+ : public CanonicalNodeDef<CanonicalOpcode::TensorTranspose, FixedArity<1>::Mixin>
+{
+public:
+ TensorTranspose() = default;
+
+public:
+ Node *input(void) const { return at(0)->node(); }
+ void input(Node *node) { return at(0)->node(node); }
+
+ class Perm final
+ {
+ public:
+ Perm() = default;
+
+ public:
+ uint32_t size() const { return _vals.size(); }
+ void size(uint32_t size) { _vals.resize(size); }
+
+ const TensorAxis &axis(TensorAxis n) const { return _vals[n]; }
+ TensorAxis &axis(TensorAxis n) { return _vals[n]; }
+
+ private:
+ std::vector<TensorAxis> _vals;
+ };
+
+ Perm *perm(void) { return &_perm; }
+ const Perm *perm(void) const { return &_perm; }
+
+private:
+ Perm _perm;
+};
+
} // namespace loco
#endif // __LOCO_IR_NODES_H__
diff --git a/compiler/loco/include/loco/IR/PaddingND.h b/compiler/loco/include/loco/IR/PaddingND.h
new file mode 100644
index 000000000..59be73943
--- /dev/null
+++ b/compiler/loco/include/loco/IR/PaddingND.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOCO_IR_PADDINGND_H__
+#define __LOCO_IR_PADDINGND_H__
+
+#include <cstdint>
+#include <vector>
+
+namespace loco
+{
+
+/**
+ * This class indicates how many pads to add before(front) and after(back) the contents of
+ * tensor in that dimension.
+ */
+class PaddingND final
+{
+
+public:
+ const uint32_t &front(uint32_t dim) const { return _front.at(dim); }
+ uint32_t &front(uint32_t dim) { return _front.at(dim); }
+
+public:
+ const uint32_t &back(uint32_t dim) const { return _back.at(dim); }
+ uint32_t &back(uint32_t dim) { return _back.at(dim); }
+
+public:
+ uint32_t rank(void) const { return _front.size(); }
+ void rank(uint32_t s)
+ {
+ _front.resize(s);
+ _back.resize(s);
+ }
+
+private:
+ std::vector<uint32_t> _front;
+ std::vector<uint32_t> _back;
+};
+
+} // namespace loco
+
+#endif // __LOCO_IR_PADDINGND_H__
diff --git a/compiler/loco/include/loco/IR/PermutingCodec.h b/compiler/loco/include/loco/IR/PermutingCodec.h
index ea0ecaa9c..60b05dcbb 100644
--- a/compiler/loco/include/loco/IR/PermutingCodec.h
+++ b/compiler/loco/include/loco/IR/PermutingCodec.h
@@ -175,6 +175,12 @@ public:
PermutingEncoder() = default;
public:
+ explicit PermutingEncoder(const Permutation<Domain::Filter> &perm) : _perm{perm}
+ {
+ // DO NOTHING
+ }
+
+public:
bool valid(void) const;
public:
@@ -191,6 +197,36 @@ private:
};
/**
+ * @brief Permutation-based Filter-to-Tensor converter
+ */
+template <> class PermutingDecoder<Domain::Filter> final : public FilterDecoder
+{
+public:
+ PermutingDecoder() = default;
+
+public:
+ explicit PermutingDecoder(const Permutation<Domain::Filter> &perm) : _perm{perm}
+ {
+ // DO NOTHING
+ }
+
+public:
+ bool valid(void) const;
+
+public:
+ TensorShape shape(const FilterShape &tensor_shape) const override;
+ FilterIndex value(const TensorIndex &index) const override;
+
+public:
+ const Permutation<Domain::Filter> *perm(void) const { return &_perm; }
+ Permutation<Domain::Filter> *perm(void) { return &_perm; }
+ void perm(const Permutation<Domain::Filter> &p) { _perm = p; }
+
+private:
+ Permutation<Domain::Filter> _perm;
+};
+
+/**
* @brief Mapping between DepthwiseFilter/Tensor Axis
*/
template <> class Permutation<Domain::DepthwiseFilter>
@@ -256,6 +292,36 @@ private:
};
/**
+ * @brief Permutation-based DepthwiseFilter-to-Tensor converter
+ */
+template <> class PermutingDecoder<Domain::DepthwiseFilter> final : public DepthwiseFilterDecoder
+{
+public:
+ PermutingDecoder() = default;
+
+public:
+ PermutingDecoder(const Permutation<Domain::DepthwiseFilter> &perm) : _perm{perm}
+ {
+ // DO NOTHING
+ }
+
+public:
+ bool valid(void) const;
+
+public:
+ TensorShape shape(const DepthwiseFilterShape &shape) const override;
+ DepthwiseFilterIndex value(const TensorIndex &index) const override;
+
+public:
+ const Permutation<Domain::DepthwiseFilter> *perm(void) const { return &_perm; }
+ Permutation<Domain::DepthwiseFilter> *perm(void) { return &_perm; }
+ void perm(const Permutation<Domain::DepthwiseFilter> &p) { _perm = p; }
+
+private:
+ Permutation<Domain::DepthwiseFilter> _perm;
+};
+
+/**
* @brief Mapping between Matrix/Tensor Axis
*/
template <> class Permutation<Domain::Matrix>
diff --git a/compiler/loco/include/loco/IR/TensorShape.h b/compiler/loco/include/loco/IR/TensorShape.h
index a7d6a556b..44eca6798 100644
--- a/compiler/loco/include/loco/IR/TensorShape.h
+++ b/compiler/loco/include/loco/IR/TensorShape.h
@@ -19,6 +19,7 @@
#include "loco/IR/Dimension.h"
+#include <initializer_list>
#include <vector>
namespace loco
@@ -28,6 +29,7 @@ class TensorShape
{
public:
TensorShape() = default;
+ TensorShape(std::initializer_list<Dimension> dims) : _dims(dims.begin(), dims.end()) {}
public:
uint32_t rank(void) const { return _dims.size(); }
@@ -40,6 +42,26 @@ private:
std::vector<Dimension> _dims;
};
+/**
+ * @brief Return the number of elements in a tensor of given shape
+ *
+ * NOTE 1.
+ *
+ * "volume" returns 1 if the rank is 0.
+ *
+ * NOTE 2.
+ *
+ * "caller" SHOULD pass a valid shape that has no unknown dimension.
+ * - The behavior of "volume" on invalid is undefined.
+ *
+ */
+uint32_t element_count(const loco::TensorShape *tensor_shape);
+
+/**
+ * @brief '==' operator for TensorShape
+ */
+bool operator==(const TensorShape &lhs, const TensorShape &rhs);
+
} // namespace loco
#endif // __LOCO_IR_TENSOR_SHAPE_H__
diff --git a/compiler/loco/src/ADT/AnnotatedItem.test.cpp b/compiler/loco/src/ADT/AnnotatedItem.test.cpp
index 42113ff7b..45ca87d75 100644
--- a/compiler/loco/src/ADT/AnnotatedItem.test.cpp
+++ b/compiler/loco/src/ADT/AnnotatedItem.test.cpp
@@ -41,15 +41,15 @@ TEST(AnnotatedItemTest, annotation)
{
loco::AnnotatedItem<::Annotation> item;
- ASSERT_EQ(item.annot<DerivedAnnotation<0>>(), nullptr);
+ ASSERT_EQ(nullptr, item.annot<DerivedAnnotation<0>>());
item.annot(DerivedAnnotation<0>::make());
ASSERT_NE(item.annot<DerivedAnnotation<0>>(), nullptr);
- ASSERT_EQ(item.annot<DerivedAnnotation<1>>(), nullptr);
+ ASSERT_EQ(nullptr, item.annot<DerivedAnnotation<1>>());
item.annot<DerivedAnnotation<0>>(nullptr);
- ASSERT_EQ(item.annot<DerivedAnnotation<0>>(), nullptr);
+ ASSERT_EQ(nullptr, item.annot<DerivedAnnotation<0>>());
// Below check guarantees that "annot<T>(nullptr)" is allowed even when there is no annotation.
// This guarantee allows us to simplify code for some cases.
diff --git a/compiler/loco/src/IR/Algorithm.test.cpp b/compiler/loco/src/IR/Algorithm.test.cpp
index f0a3585c0..c60ae1434 100644
--- a/compiler/loco/src/IR/Algorithm.test.cpp
+++ b/compiler/loco/src/IR/Algorithm.test.cpp
@@ -50,9 +50,9 @@ TEST(AlgorithmTest, postorder_traversal)
auto seq = loco::postorder_traversal({push});
- ASSERT_EQ(seq.size(), 2);
- ASSERT_EQ(seq.at(0), pull_1);
- ASSERT_EQ(seq.at(1), push);
+ ASSERT_EQ(2, seq.size());
+ ASSERT_EQ(pull_1, seq.at(0));
+ ASSERT_EQ(push, seq.at(1));
}
TEST(AlgorithmTest, postorder_traversal_visit_once)
@@ -74,7 +74,7 @@ TEST(AlgorithmTest, postorder_traversal_visit_once)
auto seq = loco::postorder_traversal({push_1, push_2});
- ASSERT_EQ(seq.size(), 3);
+ ASSERT_EQ(3, seq.size());
ASSERT_TRUE(contains(seq, pull));
ASSERT_TRUE(contains(seq, push_1));
ASSERT_TRUE(contains(seq, push_2));
@@ -97,9 +97,9 @@ TEST(AlgorithmTest, postorder_traversal_incomplte_graph)
auto seq = loco::postorder_traversal({concat});
- ASSERT_EQ(seq.size(), 2);
- ASSERT_EQ(seq.at(0), pull);
- ASSERT_EQ(seq.at(1), concat);
+ ASSERT_EQ(2, seq.size());
+ ASSERT_EQ(pull, seq.at(0));
+ ASSERT_EQ(concat, seq.at(1));
}
TEST(AlgorithmTest, active_nodes)
@@ -116,7 +116,7 @@ TEST(AlgorithmTest, active_nodes)
auto s = loco::active_nodes({push});
- ASSERT_EQ(s.size(), 2);
+ ASSERT_EQ(2, s.size());
ASSERT_TRUE(contains(s, pull));
ASSERT_TRUE(contains(s, push));
}
diff --git a/compiler/loco/src/IR/CanonicalDialect.cpp b/compiler/loco/src/IR/CanonicalDialect.cpp
index f89ea447b..ea956b80e 100644
--- a/compiler/loco/src/IR/CanonicalDialect.cpp
+++ b/compiler/loco/src/IR/CanonicalDialect.cpp
@@ -21,6 +21,7 @@
#include <stdex/Memory.h>
#include <cassert>
+#include <stdexcept>
namespace
{
@@ -39,9 +40,11 @@ struct GraphOutputIndexQueryServiceImpl final : public loco::GraphOutputIndexQue
loco::GraphOutputIndex index(const loco::Node *node) const final
{
assert(associated(node));
- auto push = dynamic_cast<const loco::Push *>(node);
- assert(push != nullptr);
- return push->index();
+ if (auto push = dynamic_cast<const loco::Push *>(node))
+ {
+ return push->index();
+ }
+ throw std::invalid_argument("node");
}
};
diff --git a/compiler/loco/src/IR/CanonicalDialect.test.cpp b/compiler/loco/src/IR/CanonicalDialect.test.cpp
index 96b48218d..e479aeda2 100644
--- a/compiler/loco/src/IR/CanonicalDialect.test.cpp
+++ b/compiler/loco/src/IR/CanonicalDialect.test.cpp
@@ -25,5 +25,5 @@ TEST(CanonicalDialectTest, get)
// get() SHOULD return a valid(non-null) pointer
ASSERT_NE(d, nullptr);
// The return value SHOULD be stable across multiple invocations
- ASSERT_EQ(d, loco::CanonicalDialect::get());
+ ASSERT_EQ(loco::CanonicalDialect::get(), d);
}
diff --git a/compiler/loco/src/IR/CanonicalNode.test.cpp b/compiler/loco/src/IR/CanonicalNode.test.cpp
index cb61b5e83..dddb1588d 100644
--- a/compiler/loco/src/IR/CanonicalNode.test.cpp
+++ b/compiler/loco/src/IR/CanonicalNode.test.cpp
@@ -34,8 +34,8 @@ TEST(CanonicalNodeTest, visitor_with_user_default_impl)
MyVisitor v;
- ASSERT_EQ(forward.accept(&v), 128);
- ASSERT_EQ(constgen.accept(&v), 256);
+ ASSERT_EQ(128, forward.accept(&v));
+ ASSERT_EQ(256, constgen.accept(&v));
}
TEST(CanonicalNodeTest, visitor)
@@ -50,7 +50,7 @@ TEST(CanonicalNodeTest, visitor)
CountingVisitor v;
- ASSERT_EQ(node.accept(&v), 1);
+ ASSERT_EQ(1, node.accept(&v));
}
TEST(CanonicalNodeTest, mutable_visitor)
@@ -68,5 +68,5 @@ TEST(CanonicalNodeTest, mutable_visitor)
ResetForward v;
forward_node.accept(&v);
- ASSERT_EQ(forward_node.input(), nullptr);
+ ASSERT_EQ(nullptr, forward_node.input());
}
diff --git a/compiler/loco/src/IR/DataTypeTraits.test.cpp b/compiler/loco/src/IR/DataTypeTraits.test.cpp
index 76d2515a9..d33d99494 100644
--- a/compiler/loco/src/IR/DataTypeTraits.test.cpp
+++ b/compiler/loco/src/IR/DataTypeTraits.test.cpp
@@ -25,5 +25,5 @@ TEST(DataTypeTraitsTest, FLOAT32)
auto obtained = std::type_index(typeid(loco::DataTypeImpl<loco::DataType::FLOAT32>::Type));
auto expected = std::type_index(typeid(float));
- ASSERT_EQ(obtained, expected);
+ ASSERT_EQ(expected, obtained);
}
diff --git a/compiler/loco/src/IR/DepthwiseFilterIndex.test.cpp b/compiler/loco/src/IR/DepthwiseFilterIndex.test.cpp
index 202647cfc..f62c067fa 100644
--- a/compiler/loco/src/IR/DepthwiseFilterIndex.test.cpp
+++ b/compiler/loco/src/IR/DepthwiseFilterIndex.test.cpp
@@ -23,10 +23,10 @@ TEST(DepthwiseFilterIndexTest, default_constructor)
loco::DepthwiseFilterIndex index;
// All the values are 0 at the beginning
- ASSERT_EQ(index.channel(), 0);
- ASSERT_EQ(index.nth(), 0);
- ASSERT_EQ(index.row(), 0);
- ASSERT_EQ(index.column(), 0);
+ ASSERT_EQ(0, index.channel());
+ ASSERT_EQ(0, index.nth());
+ ASSERT_EQ(0, index.row());
+ ASSERT_EQ(0, index.column());
}
TEST(DepthwiseFilterIndexTest, settet_and_getter)
@@ -36,32 +36,32 @@ TEST(DepthwiseFilterIndexTest, settet_and_getter)
// Set depth
index.channel() = 2;
- ASSERT_EQ(index.channel(), 2);
- ASSERT_EQ(index.nth(), 0);
- ASSERT_EQ(index.row(), 0);
- ASSERT_EQ(index.column(), 0);
+ ASSERT_EQ(2, index.channel());
+ ASSERT_EQ(0, index.nth());
+ ASSERT_EQ(0, index.row());
+ ASSERT_EQ(0, index.column());
// Set multiplier
index.nth() = 3;
- ASSERT_EQ(index.channel(), 2);
- ASSERT_EQ(index.nth(), 3);
- ASSERT_EQ(index.row(), 0);
- ASSERT_EQ(index.column(), 0);
+ ASSERT_EQ(2, index.channel());
+ ASSERT_EQ(3, index.nth());
+ ASSERT_EQ(0, index.row());
+ ASSERT_EQ(0, index.column());
// Set height
index.row() = 4;
- ASSERT_EQ(index.channel(), 2);
- ASSERT_EQ(index.nth(), 3);
- ASSERT_EQ(index.row(), 4);
- ASSERT_EQ(index.column(), 0);
+ ASSERT_EQ(2, index.channel());
+ ASSERT_EQ(3, index.nth());
+ ASSERT_EQ(4, index.row());
+ ASSERT_EQ(0, index.column());
// Set width
index.column() = 5;
- ASSERT_EQ(index.channel(), 2);
- ASSERT_EQ(index.nth(), 3);
- ASSERT_EQ(index.row(), 4);
- ASSERT_EQ(index.column(), 5);
+ ASSERT_EQ(2, index.channel());
+ ASSERT_EQ(3, index.nth());
+ ASSERT_EQ(4, index.row());
+ ASSERT_EQ(5, index.column());
}
diff --git a/compiler/loco/src/IR/DepthwiseFilterShape.test.cpp b/compiler/loco/src/IR/DepthwiseFilterShape.test.cpp
index 2b9518c1f..56bad5e62 100644
--- a/compiler/loco/src/IR/DepthwiseFilterShape.test.cpp
+++ b/compiler/loco/src/IR/DepthwiseFilterShape.test.cpp
@@ -40,7 +40,7 @@ TEST(DepthwiseFilterShapeTest, settet_and_getter)
ASSERT_FALSE(shape.height().known());
ASSERT_FALSE(shape.width().known());
- ASSERT_EQ(shape.depth(), 2);
+ ASSERT_EQ(2, shape.depth());
// Set multiplier
shape.multiplier() = 3;
@@ -50,8 +50,8 @@ TEST(DepthwiseFilterShapeTest, settet_and_getter)
ASSERT_FALSE(shape.height().known());
ASSERT_FALSE(shape.width().known());
- ASSERT_EQ(shape.depth(), 2);
- ASSERT_EQ(shape.multiplier(), 3);
+ ASSERT_EQ(2, shape.depth());
+ ASSERT_EQ(3, shape.multiplier());
// Set height
shape.height() = 4;
@@ -61,9 +61,9 @@ TEST(DepthwiseFilterShapeTest, settet_and_getter)
ASSERT_TRUE(shape.height().known());
ASSERT_FALSE(shape.width().known());
- ASSERT_EQ(shape.depth(), 2);
- ASSERT_EQ(shape.multiplier(), 3);
- ASSERT_EQ(shape.height(), 4);
+ ASSERT_EQ(2, shape.depth());
+ ASSERT_EQ(3, shape.multiplier());
+ ASSERT_EQ(4, shape.height());
// Set width
shape.width() = 5;
@@ -73,8 +73,8 @@ TEST(DepthwiseFilterShapeTest, settet_and_getter)
ASSERT_TRUE(shape.height().known());
ASSERT_TRUE(shape.width().known());
- ASSERT_EQ(shape.depth(), 2);
- ASSERT_EQ(shape.multiplier(), 3);
- ASSERT_EQ(shape.height(), 4);
- ASSERT_EQ(shape.width(), 5);
+ ASSERT_EQ(2, shape.depth());
+ ASSERT_EQ(3, shape.multiplier());
+ ASSERT_EQ(4, shape.height());
+ ASSERT_EQ(5, shape.width());
}
diff --git a/compiler/loco/src/IR/Dialect.test.cpp b/compiler/loco/src/IR/Dialect.test.cpp
index 312bb52ef..3af303375 100644
--- a/compiler/loco/src/IR/Dialect.test.cpp
+++ b/compiler/loco/src/IR/Dialect.test.cpp
@@ -36,6 +36,6 @@ TEST(DialectTest, service)
MockDialect dialect;
- ASSERT_EQ(dialect.service<S0>(), nullptr);
+ ASSERT_EQ(nullptr, dialect.service<S0>());
ASSERT_NE(dialect.service<S1>(), nullptr);
}
diff --git a/compiler/loco/src/IR/Dimension.test.cpp b/compiler/loco/src/IR/Dimension.test.cpp
index 4faf78ac8..aa3a8d6aa 100644
--- a/compiler/loco/src/IR/Dimension.test.cpp
+++ b/compiler/loco/src/IR/Dimension.test.cpp
@@ -44,7 +44,7 @@ TEST_F(DimensionTest, value_constructor)
loco::Dimension dim{value()};
ASSERT_TRUE(dim.known());
- ASSERT_EQ(dim.value(), value());
+ ASSERT_EQ(value(), dim.value());
}
TEST_F(DimensionTest, set)
@@ -54,7 +54,7 @@ TEST_F(DimensionTest, set)
dim.set(value());
ASSERT_TRUE(dim.known());
- ASSERT_EQ(dim.value(), value());
+ ASSERT_EQ(value(), dim.value());
}
TEST_F(DimensionTest, unset)
diff --git a/compiler/loco/src/IR/FeatureIndex.test.cpp b/compiler/loco/src/IR/FeatureIndex.test.cpp
index 82b563986..0ed6b4f55 100644
--- a/compiler/loco/src/IR/FeatureIndex.test.cpp
+++ b/compiler/loco/src/IR/FeatureIndex.test.cpp
@@ -23,10 +23,10 @@ TEST(FeatureIndexTest, default_constructor)
loco::FeatureIndex index;
// All the values are 0 at the beginning
- ASSERT_EQ(index.batch(), 0);
- ASSERT_EQ(index.channel(), 0);
- ASSERT_EQ(index.row(), 0);
- ASSERT_EQ(index.column(), 0);
+ ASSERT_EQ(0, index.batch());
+ ASSERT_EQ(0, index.channel());
+ ASSERT_EQ(0, index.row());
+ ASSERT_EQ(0, index.column());
}
TEST(FeatureIndexTest, settet_and_getter)
@@ -36,32 +36,32 @@ TEST(FeatureIndexTest, settet_and_getter)
// Set count
index.batch() = 2;
- ASSERT_EQ(index.batch(), 2);
- ASSERT_EQ(index.channel(), 0);
- ASSERT_EQ(index.row(), 0);
- ASSERT_EQ(index.column(), 0);
+ ASSERT_EQ(2, index.batch());
+ ASSERT_EQ(0, index.channel());
+ ASSERT_EQ(0, index.row());
+ ASSERT_EQ(0, index.column());
// Set channel
index.channel() = 3;
- ASSERT_EQ(index.batch(), 2);
- ASSERT_EQ(index.channel(), 3);
- ASSERT_EQ(index.row(), 0);
- ASSERT_EQ(index.column(), 0);
+ ASSERT_EQ(2, index.batch());
+ ASSERT_EQ(3, index.channel());
+ ASSERT_EQ(0, index.row());
+ ASSERT_EQ(0, index.column());
// Set height
index.row() = 4;
- ASSERT_EQ(index.batch(), 2);
- ASSERT_EQ(index.channel(), 3);
- ASSERT_EQ(index.row(), 4);
- ASSERT_EQ(index.column(), 0);
+ ASSERT_EQ(2, index.batch());
+ ASSERT_EQ(3, index.channel());
+ ASSERT_EQ(4, index.row());
+ ASSERT_EQ(0, index.column());
// Set width
index.column() = 5;
- ASSERT_EQ(index.batch(), 2);
- ASSERT_EQ(index.channel(), 3);
- ASSERT_EQ(index.row(), 4);
- ASSERT_EQ(index.column(), 5);
+ ASSERT_EQ(2, index.batch());
+ ASSERT_EQ(3, index.channel());
+ ASSERT_EQ(4, index.row());
+ ASSERT_EQ(5, index.column());
}
diff --git a/compiler/loco/src/IR/FeatureShape.test.cpp b/compiler/loco/src/IR/FeatureShape.test.cpp
index 59e25ac23..b0414bec8 100644
--- a/compiler/loco/src/IR/FeatureShape.test.cpp
+++ b/compiler/loco/src/IR/FeatureShape.test.cpp
@@ -40,7 +40,7 @@ TEST(FeatureShapeTest, settet_and_getter)
ASSERT_FALSE(shape.height().known());
ASSERT_FALSE(shape.width().known());
- ASSERT_EQ(shape.count(), 2);
+ ASSERT_EQ(2, shape.count());
// Set depth
shape.depth() = 3;
@@ -50,8 +50,8 @@ TEST(FeatureShapeTest, settet_and_getter)
ASSERT_FALSE(shape.height().known());
ASSERT_FALSE(shape.width().known());
- ASSERT_EQ(shape.count(), 2);
- ASSERT_EQ(shape.depth(), 3);
+ ASSERT_EQ(2, shape.count());
+ ASSERT_EQ(3, shape.depth());
// Set height
shape.height() = 4;
@@ -61,9 +61,9 @@ TEST(FeatureShapeTest, settet_and_getter)
ASSERT_TRUE(shape.height().known());
ASSERT_FALSE(shape.width().known());
- ASSERT_EQ(shape.count(), 2);
- ASSERT_EQ(shape.depth(), 3);
- ASSERT_EQ(shape.height(), 4);
+ ASSERT_EQ(2, shape.count());
+ ASSERT_EQ(3, shape.depth());
+ ASSERT_EQ(4, shape.height());
// Set width
shape.width() = 5;
@@ -73,8 +73,8 @@ TEST(FeatureShapeTest, settet_and_getter)
ASSERT_TRUE(shape.height().known());
ASSERT_TRUE(shape.width().known());
- ASSERT_EQ(shape.count(), 2);
- ASSERT_EQ(shape.depth(), 3);
- ASSERT_EQ(shape.height(), 4);
- ASSERT_EQ(shape.width(), 5);
+ ASSERT_EQ(2, shape.count());
+ ASSERT_EQ(3, shape.depth());
+ ASSERT_EQ(4, shape.height());
+ ASSERT_EQ(5, shape.width());
}
diff --git a/compiler/loco/src/IR/FilterIndex.test.cpp b/compiler/loco/src/IR/FilterIndex.test.cpp
index 58f38718e..4edf62c85 100644
--- a/compiler/loco/src/IR/FilterIndex.test.cpp
+++ b/compiler/loco/src/IR/FilterIndex.test.cpp
@@ -23,10 +23,10 @@ TEST(FilterIndexTest, default_constructor)
loco::FilterIndex index;
// All the values are 0 at the beginning
- ASSERT_EQ(index.nth(), 0);
- ASSERT_EQ(index.channel(), 0);
- ASSERT_EQ(index.row(), 0);
- ASSERT_EQ(index.column(), 0);
+ ASSERT_EQ(0, index.nth());
+ ASSERT_EQ(0, index.channel());
+ ASSERT_EQ(0, index.row());
+ ASSERT_EQ(0, index.column());
}
TEST(FilterIndexTest, settet_and_getter)
@@ -36,32 +36,32 @@ TEST(FilterIndexTest, settet_and_getter)
// Set count
index.nth() = 2;
- ASSERT_EQ(index.nth(), 2);
- ASSERT_EQ(index.channel(), 0);
- ASSERT_EQ(index.row(), 0);
- ASSERT_EQ(index.column(), 0);
+ ASSERT_EQ(2, index.nth());
+ ASSERT_EQ(0, index.channel());
+ ASSERT_EQ(0, index.row());
+ ASSERT_EQ(0, index.column());
// Set channel
index.channel() = 3;
- ASSERT_EQ(index.nth(), 2);
- ASSERT_EQ(index.channel(), 3);
- ASSERT_EQ(index.row(), 0);
- ASSERT_EQ(index.column(), 0);
+ ASSERT_EQ(2, index.nth());
+ ASSERT_EQ(3, index.channel());
+ ASSERT_EQ(0, index.row());
+ ASSERT_EQ(0, index.column());
// Set height
index.row() = 4;
- ASSERT_EQ(index.nth(), 2);
- ASSERT_EQ(index.channel(), 3);
- ASSERT_EQ(index.row(), 4);
- ASSERT_EQ(index.column(), 0);
+ ASSERT_EQ(2, index.nth());
+ ASSERT_EQ(3, index.channel());
+ ASSERT_EQ(4, index.row());
+ ASSERT_EQ(0, index.column());
// Set width
index.column() = 5;
- ASSERT_EQ(index.nth(), 2);
- ASSERT_EQ(index.channel(), 3);
- ASSERT_EQ(index.row(), 4);
- ASSERT_EQ(index.column(), 5);
+ ASSERT_EQ(2, index.nth());
+ ASSERT_EQ(3, index.channel());
+ ASSERT_EQ(4, index.row());
+ ASSERT_EQ(5, index.column());
}
diff --git a/compiler/loco/src/IR/FilterShape.test.cpp b/compiler/loco/src/IR/FilterShape.test.cpp
index ccb60ed76..8033027c2 100644
--- a/compiler/loco/src/IR/FilterShape.test.cpp
+++ b/compiler/loco/src/IR/FilterShape.test.cpp
@@ -40,7 +40,7 @@ TEST(FilterShapeTest, settet_and_getter)
ASSERT_FALSE(shape.height().known());
ASSERT_FALSE(shape.width().known());
- ASSERT_EQ(shape.count(), 2);
+ ASSERT_EQ(2, shape.count());
// Set depth
shape.depth() = 3;
@@ -50,8 +50,8 @@ TEST(FilterShapeTest, settet_and_getter)
ASSERT_FALSE(shape.height().known());
ASSERT_FALSE(shape.width().known());
- ASSERT_EQ(shape.count(), 2);
- ASSERT_EQ(shape.depth(), 3);
+ ASSERT_EQ(2, shape.count());
+ ASSERT_EQ(3, shape.depth());
// Set height
shape.height() = 4;
@@ -61,9 +61,9 @@ TEST(FilterShapeTest, settet_and_getter)
ASSERT_TRUE(shape.height().known());
ASSERT_FALSE(shape.width().known());
- ASSERT_EQ(shape.count(), 2);
- ASSERT_EQ(shape.depth(), 3);
- ASSERT_EQ(shape.height(), 4);
+ ASSERT_EQ(2, shape.count());
+ ASSERT_EQ(3, shape.depth());
+ ASSERT_EQ(4, shape.height());
// Set width
shape.width() = 5;
@@ -73,8 +73,8 @@ TEST(FilterShapeTest, settet_and_getter)
ASSERT_TRUE(shape.height().known());
ASSERT_TRUE(shape.width().known());
- ASSERT_EQ(shape.count(), 2);
- ASSERT_EQ(shape.depth(), 3);
- ASSERT_EQ(shape.height(), 4);
- ASSERT_EQ(shape.width(), 5);
+ ASSERT_EQ(2, shape.count());
+ ASSERT_EQ(3, shape.depth());
+ ASSERT_EQ(4, shape.height());
+ ASSERT_EQ(5, shape.width());
}
diff --git a/compiler/loco/src/IR/Graph.cpp b/compiler/loco/src/IR/Graph.cpp
index 345525b9e..8073d4545 100644
--- a/compiler/loco/src/IR/Graph.cpp
+++ b/compiler/loco/src/IR/Graph.cpp
@@ -37,7 +37,7 @@ std::unique_ptr<loco::TensorShape> make_tensor_shape(std::initializer_list<loco:
assert(axis == dims.size());
}
- return std::move(tensor_shape);
+ return tensor_shape;
}
} // namespace
@@ -72,6 +72,36 @@ std::set<loco::Node *> all_nodes(loco::Graph *g)
return res;
}
+std::vector<Node *> input_nodes(const Graph *g)
+{
+ std::map<GraphInputIndex, loco::Node *> table;
+
+ for (uint32_t n = 0; n < g->nodes()->size(); ++n)
+ {
+ auto node = g->nodes()->at(n);
+
+ if (auto service = node->dialect()->service<GraphInputIndexQueryService>())
+ {
+ if (service->associated(node))
+ {
+ auto input_index = service->index(node);
+ assert(table.find(input_index) == table.end());
+ table[input_index] = node;
+ }
+ }
+ }
+
+ std::vector<loco::Node *> res;
+
+ for (uint32_t n = 0; n < g->inputs()->size(); ++n)
+ {
+ auto it = table.find(n);
+ res.emplace_back(it == table.end() ? nullptr : it->second);
+ }
+
+ return res;
+}
+
std::vector<loco::Node *> output_nodes(loco::Graph *g)
{
std::map<GraphOutputIndex, loco::Node *> table;
diff --git a/compiler/loco/src/IR/Graph.test.cpp b/compiler/loco/src/IR/Graph.test.cpp
index 0b77e34f8..ad6894f30 100644
--- a/compiler/loco/src/IR/Graph.test.cpp
+++ b/compiler/loco/src/IR/Graph.test.cpp
@@ -21,7 +21,7 @@
namespace
{
-// @brief Mockup class for loco::NamedEntity
+/// @brief Mockup class for loco::NamedEntity
struct NamedElement final : private loco::NamedEntity
{
LOCO_NAMED_ENTITY_EXPOSE;
@@ -33,7 +33,7 @@ TEST(NamedTest, constructor)
{
NamedElement elem;
- ASSERT_EQ(elem.name(), "");
+ ASSERT_EQ("", elem.name());
}
TEST(NamedTest, setter_and_getter)
@@ -41,14 +41,14 @@ TEST(NamedTest, setter_and_getter)
NamedElement elem;
elem.name("name");
- ASSERT_EQ(elem.name(), "name");
+ ASSERT_EQ("name", elem.name());
}
TEST(DataTypedMixinTest, constructor)
{
loco::Mixin<loco::Trait::DataTyped> mixin;
- ASSERT_EQ(mixin.dtype(), loco::DataType::Unknown);
+ ASSERT_EQ(loco::DataType::Unknown, mixin.dtype());
}
TEST(DataTypedMixinTest, setter_and_getter)
@@ -56,7 +56,7 @@ TEST(DataTypedMixinTest, setter_and_getter)
loco::Mixin<loco::Trait::DataTyped> mixin;
mixin.dtype(loco::DataType::FLOAT32);
- ASSERT_EQ(mixin.dtype(), loco::DataType::FLOAT32);
+ ASSERT_EQ(loco::DataType::FLOAT32, mixin.dtype());
}
TEST(TensorShapedMixinTest, setter_and_getter)
@@ -65,11 +65,11 @@ TEST(TensorShapedMixinTest, setter_and_getter)
mixin.shape({1, 2, 3, 4});
ASSERT_NE(mixin.shape(), nullptr);
- ASSERT_EQ(mixin.shape()->rank(), 4);
- ASSERT_EQ(mixin.shape()->dim(0), 1);
- ASSERT_EQ(mixin.shape()->dim(1), 2);
- ASSERT_EQ(mixin.shape()->dim(2), 3);
- ASSERT_EQ(mixin.shape()->dim(3), 4);
+ ASSERT_EQ(4, mixin.shape()->rank());
+ ASSERT_EQ(1, mixin.shape()->dim(0));
+ ASSERT_EQ(2, mixin.shape()->dim(1));
+ ASSERT_EQ(3, mixin.shape()->dim(2));
+ ASSERT_EQ(4, mixin.shape()->dim(3));
}
TEST(GraphTest, create_and_destroy_node)
@@ -89,8 +89,8 @@ TEST(GraphTest, create_input)
auto input = g->inputs()->create();
// TODO Add more checks
- ASSERT_EQ(input->shape(), nullptr);
- ASSERT_EQ(input->index(), 0);
+ ASSERT_EQ(nullptr, input->shape());
+ ASSERT_EQ(0, input->index());
}
TEST(GraphTest, create_output)
@@ -100,8 +100,8 @@ TEST(GraphTest, create_output)
auto output = g->outputs()->create();
// TODO Add more checks
- ASSERT_EQ(output->shape(), nullptr);
- ASSERT_EQ(output->index(), 0);
+ ASSERT_EQ(nullptr, output->shape());
+ ASSERT_EQ(0, output->index());
}
namespace
@@ -132,10 +132,10 @@ TEST(GraphTest, consturctor_with_param_node)
auto test_node = g->nodes()->create<ParamCtorNode>(22, 11.11);
- ASSERT_EQ(test_node->graph(), g.get());
- ASSERT_EQ(const_cast<const ParamCtorNode *>(test_node)->graph(), g.get());
+ ASSERT_EQ(g.get(), test_node->graph());
+ ASSERT_EQ(g.get(), const_cast<const ParamCtorNode *>(test_node)->graph());
- ASSERT_EQ(test_node->i(), 22);
+ ASSERT_EQ(22, test_node->i());
ASSERT_FLOAT_EQ(test_node->f(), 11.11);
ASSERT_NO_THROW(g->nodes()->destroy(test_node));
@@ -170,7 +170,7 @@ TEST(GraphTest, graph_node_enumeration)
// Returns true if "nodes" includes a given node
auto member = [&nodes](loco::Node *node) { return nodes.find(node) != nodes.end(); };
- ASSERT_EQ(nodes.size(), 2);
+ ASSERT_EQ(2, nodes.size());
ASSERT_TRUE(member(pull_1));
ASSERT_TRUE(member(push_1));
}
@@ -197,7 +197,22 @@ TEST(GraphTest, graph_inout_enumeration)
auto output_nodes = loco::output_nodes(g.get());
- ASSERT_EQ(output_nodes.size(), 2);
- ASSERT_EQ(output_nodes.at(0), push_1);
- ASSERT_EQ(output_nodes.at(1), push_3);
+ ASSERT_EQ(2, output_nodes.size());
+ ASSERT_EQ(push_1, output_nodes.at(0));
+ ASSERT_EQ(push_3, output_nodes.at(1));
+}
+
+TEST(GraphTest, graph_name)
+{
+ auto g = loco::make_graph();
+
+ g->name("HelloGraph");
+ ASSERT_TRUE(g->name() == "HelloGraph");
+}
+
+TEST(GraphTest, graph_name_nullptr_NEG)
+{
+ auto g = loco::make_graph();
+
+ EXPECT_ANY_THROW(g->name(nullptr));
}
diff --git a/compiler/loco/src/IR/MockupNode.h b/compiler/loco/src/IR/MockupNode.h
index ec56c90e2..16eaccf36 100644
--- a/compiler/loco/src/IR/MockupNode.h
+++ b/compiler/loco/src/IR/MockupNode.h
@@ -32,7 +32,7 @@ struct MockDialect final : public loco::Dialect
}
};
-// @brief Mockup node for internal testing
+/// @brief Mockup node for internal testing
class MockupNode final : public loco::Node
{
public:
@@ -53,6 +53,21 @@ private:
loco::Use _arg{this};
};
+/// @brief Mockup2Node node for internal testing
+class Mockup2Node final : public loco::Node
+{
+public:
+ Mockup2Node() = default;
+
+public:
+ const loco::Dialect *dialect(void) const final { return MockDialect::get(); }
+ uint32_t opnum(void) const final { return 1; }
+
+ uint32_t arity(void) const final { return 0; }
+ Node *arg(uint32_t) const final { return nullptr; }
+ void drop(void) final {}
+};
+
} // namespace
#endif // __LOCO_IR_MOCKUP_NODE_H__
diff --git a/compiler/loco/src/IR/Node.test.cpp b/compiler/loco/src/IR/Node.test.cpp
index 00e444465..7c3eb3fc9 100644
--- a/compiler/loco/src/IR/Node.test.cpp
+++ b/compiler/loco/src/IR/Node.test.cpp
@@ -29,7 +29,7 @@ TEST(NodeTest, preds)
auto preds = loco::preds(&node);
- ASSERT_EQ(preds.size(), 1);
+ ASSERT_EQ(1, preds.size());
ASSERT_NE(preds.find(&arg), preds.end());
}
@@ -44,7 +44,7 @@ TEST(NodeTest, succs)
auto succs = loco::succs(&node);
- ASSERT_EQ(succs.size(), 2);
+ ASSERT_EQ(2, succs.size());
ASSERT_NE(succs.find(&succ_1), succs.end());
ASSERT_NE(succs.find(&succ_2), succs.end());
}
@@ -63,8 +63,8 @@ TEST(NodeTest, replace_with)
// The following holds at this point
// - node_3 USE node_1
// - node_4 USE node_2
- ASSERT_EQ(node_3.in(), &node_1);
- ASSERT_EQ(node_4.in(), &node_2);
+ ASSERT_EQ(&node_1, node_3.in());
+ ASSERT_EQ(&node_2, node_4.in());
// Replace all the usage of node_1 with node_2
replace(&node_1).with(&node_2);
@@ -72,8 +72,8 @@ TEST(NodeTest, replace_with)
// The following holds at this point
// - node_3 USE node_2
// - node_4 USE node_2
- ASSERT_EQ(node_3.in(), &node_2);
- ASSERT_EQ(node_4.in(), &node_2);
+ ASSERT_EQ(&node_2, node_3.in());
+ ASSERT_EQ(&node_2, node_4.in());
}
TEST(NodeTest, constructor)
@@ -81,7 +81,7 @@ TEST(NodeTest, constructor)
MockupNode node;
// graph() SHOULD return nullptr if node is not constructed through "Graph"
- ASSERT_EQ(node.graph(), nullptr);
+ ASSERT_EQ(nullptr, node.graph());
}
// TODO Rewrite this as a FixedAritry mix-in test
@@ -96,7 +96,23 @@ TEST(FixedArityNodeTest, constructor)
DerivedNode node;
- ASSERT_EQ(node.arity(), 1);
- ASSERT_EQ(node.arg(0), nullptr);
+ ASSERT_EQ(1, node.arity());
+ ASSERT_EQ(nullptr, node.arg(0));
}
#endif
+
+TEST(NodeTest, cast_with_must_NEG)
+{
+ Mockup2Node mockupnode;
+ loco::Node *node = &mockupnode;
+
+ ASSERT_THROW(loco::must_cast<MockupNode *>(node), std::invalid_argument);
+}
+
+TEST(NodeTest, cast_with_const_must_NEG)
+{
+ Mockup2Node mockupnode;
+ const loco::Node *node = &mockupnode;
+
+ ASSERT_THROW(loco::must_cast<const MockupNode *>(node), std::invalid_argument);
+}
diff --git a/compiler/loco/src/IR/NodeShape.test.cpp b/compiler/loco/src/IR/NodeShape.test.cpp
index 4f092e024..8bf7900cc 100644
--- a/compiler/loco/src/IR/NodeShape.test.cpp
+++ b/compiler/loco/src/IR/NodeShape.test.cpp
@@ -22,7 +22,7 @@ TEST(NodeShapeTest, default_constructor)
{
loco::NodeShape node_shape;
- ASSERT_EQ(node_shape.domain(), loco::Domain::Unknown);
+ ASSERT_EQ(loco::Domain::Unknown, node_shape.domain());
}
TEST(NodeShapeTest, bias_shape_constructor)
@@ -33,8 +33,8 @@ TEST(NodeShapeTest, bias_shape_constructor)
loco::NodeShape node_shape{bias_shape};
- ASSERT_EQ(node_shape.domain(), loco::Domain::Bias);
- ASSERT_EQ(node_shape.as<loco::BiasShape>().length(), 4);
+ ASSERT_EQ(loco::Domain::Bias, node_shape.domain());
+ ASSERT_EQ(4, node_shape.as<loco::BiasShape>().length());
}
TEST(NodeShapeTest, dwfilter_shape_constructor)
@@ -48,11 +48,11 @@ TEST(NodeShapeTest, dwfilter_shape_constructor)
loco::NodeShape node_shape{dwfilter_shape};
- ASSERT_EQ(node_shape.domain(), loco::Domain::DepthwiseFilter);
- ASSERT_EQ(node_shape.as<loco::DepthwiseFilterShape>().depth(), 2);
- ASSERT_EQ(node_shape.as<loco::DepthwiseFilterShape>().multiplier(), 3);
- ASSERT_EQ(node_shape.as<loco::DepthwiseFilterShape>().height(), 4);
- ASSERT_EQ(node_shape.as<loco::DepthwiseFilterShape>().width(), 5);
+ ASSERT_EQ(loco::Domain::DepthwiseFilter, node_shape.domain());
+ ASSERT_EQ(2, node_shape.as<loco::DepthwiseFilterShape>().depth());
+ ASSERT_EQ(3, node_shape.as<loco::DepthwiseFilterShape>().multiplier());
+ ASSERT_EQ(4, node_shape.as<loco::DepthwiseFilterShape>().height());
+ ASSERT_EQ(5, node_shape.as<loco::DepthwiseFilterShape>().width());
}
TEST(NodeShapeTest, feature_shape_constructor)
@@ -66,11 +66,11 @@ TEST(NodeShapeTest, feature_shape_constructor)
loco::NodeShape node_shape{feature_shape};
- ASSERT_EQ(node_shape.domain(), loco::Domain::Feature);
- ASSERT_EQ(node_shape.as<loco::FeatureShape>().count(), 2);
- ASSERT_EQ(node_shape.as<loco::FeatureShape>().depth(), 3);
- ASSERT_EQ(node_shape.as<loco::FeatureShape>().height(), 4);
- ASSERT_EQ(node_shape.as<loco::FeatureShape>().width(), 5);
+ ASSERT_EQ(loco::Domain::Feature, node_shape.domain());
+ ASSERT_EQ(2, node_shape.as<loco::FeatureShape>().count());
+ ASSERT_EQ(3, node_shape.as<loco::FeatureShape>().depth());
+ ASSERT_EQ(4, node_shape.as<loco::FeatureShape>().height());
+ ASSERT_EQ(5, node_shape.as<loco::FeatureShape>().width());
}
TEST(NodeShapeTest, filter_shape_constructor)
@@ -84,11 +84,11 @@ TEST(NodeShapeTest, filter_shape_constructor)
loco::NodeShape node_shape{filter_shape};
- ASSERT_EQ(node_shape.domain(), loco::Domain::Filter);
- ASSERT_EQ(node_shape.as<loco::FilterShape>().count(), 2);
- ASSERT_EQ(node_shape.as<loco::FilterShape>().depth(), 3);
- ASSERT_EQ(node_shape.as<loco::FilterShape>().height(), 4);
- ASSERT_EQ(node_shape.as<loco::FilterShape>().width(), 5);
+ ASSERT_EQ(loco::Domain::Filter, node_shape.domain());
+ ASSERT_EQ(2, node_shape.as<loco::FilterShape>().count());
+ ASSERT_EQ(3, node_shape.as<loco::FilterShape>().depth());
+ ASSERT_EQ(4, node_shape.as<loco::FilterShape>().height());
+ ASSERT_EQ(5, node_shape.as<loco::FilterShape>().width());
}
TEST(NodeShapeTest, tensor_shape_constructor)
@@ -101,10 +101,10 @@ TEST(NodeShapeTest, tensor_shape_constructor)
loco::NodeShape node_shape{tensor_shape};
- ASSERT_EQ(node_shape.domain(), loco::Domain::Tensor);
- ASSERT_EQ(node_shape.as<loco::TensorShape>().rank(), 2);
- ASSERT_EQ(node_shape.as<loco::TensorShape>().dim(0), 4);
- ASSERT_EQ(node_shape.as<loco::TensorShape>().dim(1), 5);
+ ASSERT_EQ(loco::Domain::Tensor, node_shape.domain());
+ ASSERT_EQ(2, node_shape.as<loco::TensorShape>().rank());
+ ASSERT_EQ(4, node_shape.as<loco::TensorShape>().dim(0));
+ ASSERT_EQ(5, node_shape.as<loco::TensorShape>().dim(1));
}
TEST(NodeShapeTest, copy_constructible)
@@ -118,8 +118,8 @@ TEST(NodeShapeTest, copy_constructible)
loco::NodeShape orig{tensor_shape};
loco::NodeShape copy{orig}; // Call Copy Constructor
- ASSERT_EQ(copy.domain(), loco::Domain::Tensor);
- ASSERT_EQ(copy.as<loco::TensorShape>().rank(), 2);
- ASSERT_EQ(copy.as<loco::TensorShape>().dim(0), 4);
- ASSERT_EQ(copy.as<loco::TensorShape>().dim(1), 5);
+ ASSERT_EQ(loco::Domain::Tensor, copy.domain());
+ ASSERT_EQ(2, copy.as<loco::TensorShape>().rank());
+ ASSERT_EQ(4, copy.as<loco::TensorShape>().dim(0));
+ ASSERT_EQ(5, copy.as<loco::TensorShape>().dim(1));
}
diff --git a/compiler/loco/src/IR/Nodes.cpp b/compiler/loco/src/IR/Nodes.cpp
index 417fa6f3f..133b69430 100644
--- a/compiler/loco/src/IR/Nodes.cpp
+++ b/compiler/loco/src/IR/Nodes.cpp
@@ -24,8 +24,13 @@
namespace
{
+/**
+ * @note This function is currently only used in assert. Compiler will
+ * warn/error this function as unused in Release build.
+ * Making inline will make compiler happy.
+ */
// Is it possible to update lhs as rhs?
-bool dtype_assignable(loco::DataType lhs, loco::DataType rhs)
+inline bool dtype_assignable(loco::DataType lhs, loco::DataType rhs)
{
if (lhs == loco::DataType::Unknown)
{
diff --git a/compiler/loco/src/IR/Nodes.test.cpp b/compiler/loco/src/IR/Nodes.test.cpp
index 2f3c8cb78..bd1c74253 100644
--- a/compiler/loco/src/IR/Nodes.test.cpp
+++ b/compiler/loco/src/IR/Nodes.test.cpp
@@ -23,8 +23,8 @@ TEST(PushTest, constructor)
{
loco::Push push_node;
- ASSERT_EQ(push_node.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(push_node.opcode(), loco::CanonicalOpcode::Push);
+ ASSERT_EQ(loco::CanonicalDialect::get(), push_node.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::Push, push_node.opcode());
ASSERT_FALSE(push_node.indexed());
}
@@ -37,24 +37,24 @@ TEST(PushTest, shape)
push_node.shape({dims[0], dims[1], dims[2], dims[3]});
- ASSERT_EQ(push_node.rank(), dims.size());
- ASSERT_EQ(push_node.dim(0), dims[0]);
- ASSERT_EQ(push_node.dim(1), dims[1]);
- ASSERT_EQ(push_node.dim(2), dims[2]);
- ASSERT_EQ(push_node.dim(3), dims[3]);
+ ASSERT_EQ(dims.size(), push_node.rank());
+ ASSERT_EQ(dims[0], push_node.dim(0));
+ ASSERT_EQ(dims[1], push_node.dim(1));
+ ASSERT_EQ(dims[2], push_node.dim(2));
+ ASSERT_EQ(dims[3], push_node.dim(3));
}
TEST(PullTest, constructor)
{
loco::Pull pull_node;
- ASSERT_EQ(pull_node.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(pull_node.opcode(), loco::CanonicalOpcode::Pull);
+ ASSERT_EQ(loco::CanonicalDialect::get(), pull_node.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::Pull, pull_node.opcode());
ASSERT_FALSE(pull_node.indexed());
- ASSERT_EQ(pull_node.dtype(), loco::DataType::Unknown);
- ASSERT_EQ(pull_node.rank(), 0);
+ ASSERT_EQ(loco::DataType::Unknown, pull_node.dtype());
+ ASSERT_EQ(0, pull_node.rank());
}
TEST(PullTest, shape)
@@ -65,58 +65,58 @@ TEST(PullTest, shape)
pull_node.shape({dims[0], dims[1], dims[2], dims[3]});
- ASSERT_EQ(pull_node.rank(), dims.size());
- ASSERT_EQ(pull_node.dim(0), dims[0]);
- ASSERT_EQ(pull_node.dim(1), dims[1]);
- ASSERT_EQ(pull_node.dim(2), dims[2]);
- ASSERT_EQ(pull_node.dim(3), dims[3]);
+ ASSERT_EQ(dims.size(), pull_node.rank());
+ ASSERT_EQ(dims[0], pull_node.dim(0));
+ ASSERT_EQ(dims[1], pull_node.dim(1));
+ ASSERT_EQ(dims[2], pull_node.dim(2));
+ ASSERT_EQ(dims[3], pull_node.dim(3));
}
TEST(ForwardTest, constructor)
{
loco::Forward forward_node;
- ASSERT_EQ(forward_node.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(forward_node.opcode(), loco::CanonicalOpcode::Forward);
+ ASSERT_EQ(loco::CanonicalDialect::get(), forward_node.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::Forward, forward_node.opcode());
- ASSERT_EQ(forward_node.input(), nullptr);
+ ASSERT_EQ(nullptr, forward_node.input());
}
TEST(ReLUTest, constructor)
{
loco::ReLU relu_node;
- ASSERT_EQ(relu_node.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(relu_node.opcode(), loco::CanonicalOpcode::ReLU);
+ ASSERT_EQ(loco::CanonicalDialect::get(), relu_node.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::ReLU, relu_node.opcode());
- ASSERT_EQ(relu_node.input(), nullptr);
+ ASSERT_EQ(nullptr, relu_node.input());
}
TEST(ReLU6Test, constructor)
{
loco::ReLU6 relu6_node;
- ASSERT_EQ(relu6_node.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(relu6_node.opcode(), loco::CanonicalOpcode::ReLU6);
+ ASSERT_EQ(loco::CanonicalDialect::get(), relu6_node.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::ReLU6, relu6_node.opcode());
- ASSERT_EQ(relu6_node.input(), nullptr);
+ ASSERT_EQ(nullptr, relu6_node.input());
}
TEST(ConstGenTest, constructor)
{
loco::ConstGen constgen_node;
- ASSERT_EQ(constgen_node.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(constgen_node.opcode(), loco::CanonicalOpcode::ConstGen);
+ ASSERT_EQ(loco::CanonicalDialect::get(), constgen_node.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::ConstGen, constgen_node.opcode());
- ASSERT_EQ(constgen_node.dtype(), loco::DataType::Unknown);
- ASSERT_EQ(constgen_node.rank(), 0);
+ ASSERT_EQ(loco::DataType::Unknown, constgen_node.dtype());
+ ASSERT_EQ(0, constgen_node.rank());
constgen_node.dtype(loco::DataType::FLOAT32);
- ASSERT_EQ(constgen_node.dtype(), loco::DataType::FLOAT32);
+ ASSERT_EQ(loco::DataType::FLOAT32, constgen_node.dtype());
constgen_node.rank(2);
- ASSERT_EQ(constgen_node.rank(), 2);
+ ASSERT_EQ(2, constgen_node.rank());
constgen_node.dim(0) = 2;
constgen_node.dim(1) = 3;
@@ -124,12 +124,12 @@ TEST(ConstGenTest, constructor)
ASSERT_TRUE(constgen_node.dim(0).known());
ASSERT_TRUE(constgen_node.dim(1).known());
- ASSERT_EQ(constgen_node.dim(0), 2);
- ASSERT_EQ(constgen_node.dim(1), 3);
+ ASSERT_EQ(2, constgen_node.dim(0));
+ ASSERT_EQ(3, constgen_node.dim(1));
constgen_node.size<loco::DataType::FLOAT32>(6);
- ASSERT_EQ(constgen_node.size<loco::DataType::FLOAT32>(), 6);
+ ASSERT_EQ(6, constgen_node.size<loco::DataType::FLOAT32>());
constgen_node.at<loco::DataType::FLOAT32>(0) = 0.0f; // Set 0,0
constgen_node.at<loco::DataType::FLOAT32>(1) = 1.0f; // Set 0,1
@@ -138,26 +138,26 @@ TEST(ConstGenTest, constructor)
constgen_node.at<loco::DataType::FLOAT32>(4) = 4.0f; // Set 1,1
constgen_node.at<loco::DataType::FLOAT32>(5) = 5.0f; // Set 1,2
- ASSERT_EQ(constgen_node.at<loco::DataType::FLOAT32>(0), 0.0f);
- ASSERT_EQ(constgen_node.at<loco::DataType::FLOAT32>(1), 1.0f);
- ASSERT_EQ(constgen_node.at<loco::DataType::FLOAT32>(2), 2.0f);
- ASSERT_EQ(constgen_node.at<loco::DataType::FLOAT32>(3), 3.0f);
- ASSERT_EQ(constgen_node.at<loco::DataType::FLOAT32>(4), 4.0f);
- ASSERT_EQ(constgen_node.at<loco::DataType::FLOAT32>(5), 5.0f);
+ ASSERT_EQ(0.0f, constgen_node.at<loco::DataType::FLOAT32>(0));
+ ASSERT_EQ(1.0f, constgen_node.at<loco::DataType::FLOAT32>(1));
+ ASSERT_EQ(2.0f, constgen_node.at<loco::DataType::FLOAT32>(2));
+ ASSERT_EQ(3.0f, constgen_node.at<loco::DataType::FLOAT32>(3));
+ ASSERT_EQ(4.0f, constgen_node.at<loco::DataType::FLOAT32>(4));
+ ASSERT_EQ(5.0f, constgen_node.at<loco::DataType::FLOAT32>(5));
}
TEST(ConstGenTest, constructor_s32)
{
loco::ConstGen constgen_node;
- ASSERT_EQ(constgen_node.dtype(), loco::DataType::Unknown);
- ASSERT_EQ(constgen_node.rank(), 0);
+ ASSERT_EQ(loco::DataType::Unknown, constgen_node.dtype());
+ ASSERT_EQ(0, constgen_node.rank());
constgen_node.dtype(loco::DataType::S32);
- ASSERT_EQ(constgen_node.dtype(), loco::DataType::S32);
+ ASSERT_EQ(loco::DataType::S32, constgen_node.dtype());
constgen_node.rank(2);
- ASSERT_EQ(constgen_node.rank(), 2);
+ ASSERT_EQ(2, constgen_node.rank());
constgen_node.dim(0) = 2;
constgen_node.dim(1) = 3;
@@ -165,12 +165,12 @@ TEST(ConstGenTest, constructor_s32)
ASSERT_TRUE(constgen_node.dim(0).known());
ASSERT_TRUE(constgen_node.dim(1).known());
- ASSERT_EQ(constgen_node.dim(0), 2);
- ASSERT_EQ(constgen_node.dim(1), 3);
+ ASSERT_EQ(2, constgen_node.dim(0));
+ ASSERT_EQ(3, constgen_node.dim(1));
constgen_node.size<loco::DataType::S32>(6);
- ASSERT_EQ(constgen_node.size<loco::DataType::S32>(), 6);
+ ASSERT_EQ(6, constgen_node.size<loco::DataType::S32>());
constgen_node.at<loco::DataType::S32>(0) = 0; // Set 0,0
constgen_node.at<loco::DataType::S32>(1) = 1; // Set 0,1
@@ -179,33 +179,33 @@ TEST(ConstGenTest, constructor_s32)
constgen_node.at<loco::DataType::S32>(4) = -4; // Set 1,1
constgen_node.at<loco::DataType::S32>(5) = -5; // Set 1,2
- ASSERT_EQ(constgen_node.at<loco::DataType::S32>(0), 0);
- ASSERT_EQ(constgen_node.at<loco::DataType::S32>(1), 1);
- ASSERT_EQ(constgen_node.at<loco::DataType::S32>(2), 2);
- ASSERT_EQ(constgen_node.at<loco::DataType::S32>(3), -3);
- ASSERT_EQ(constgen_node.at<loco::DataType::S32>(4), -4);
- ASSERT_EQ(constgen_node.at<loco::DataType::S32>(5), -5);
+ ASSERT_EQ(0, constgen_node.at<loco::DataType::S32>(0));
+ ASSERT_EQ(1, constgen_node.at<loco::DataType::S32>(1));
+ ASSERT_EQ(2, constgen_node.at<loco::DataType::S32>(2));
+ ASSERT_EQ(-3, constgen_node.at<loco::DataType::S32>(3));
+ ASSERT_EQ(-4, constgen_node.at<loco::DataType::S32>(4));
+ ASSERT_EQ(-5, constgen_node.at<loco::DataType::S32>(5));
}
TEST(MaxPool2DTest, constructor)
{
loco::MaxPool2D maxpool_node;
- ASSERT_EQ(maxpool_node.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(maxpool_node.opcode(), loco::CanonicalOpcode::MaxPool2D);
+ ASSERT_EQ(loco::CanonicalDialect::get(), maxpool_node.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::MaxPool2D, maxpool_node.opcode());
- ASSERT_EQ(maxpool_node.ifm(), nullptr);
+ ASSERT_EQ(nullptr, maxpool_node.ifm());
- ASSERT_EQ(maxpool_node.pad()->top(), 0);
- ASSERT_EQ(maxpool_node.pad()->bottom(), 0);
- ASSERT_EQ(maxpool_node.pad()->left(), 0);
- ASSERT_EQ(maxpool_node.pad()->right(), 0);
+ ASSERT_EQ(0, maxpool_node.pad()->top());
+ ASSERT_EQ(0, maxpool_node.pad()->bottom());
+ ASSERT_EQ(0, maxpool_node.pad()->left());
+ ASSERT_EQ(0, maxpool_node.pad()->right());
- ASSERT_EQ(maxpool_node.window()->vertical(), 1);
- ASSERT_EQ(maxpool_node.window()->horizontal(), 1);
+ ASSERT_EQ(1, maxpool_node.window()->vertical());
+ ASSERT_EQ(1, maxpool_node.window()->horizontal());
- ASSERT_EQ(maxpool_node.stride()->vertical(), 1);
- ASSERT_EQ(maxpool_node.stride()->horizontal(), 1);
+ ASSERT_EQ(1, maxpool_node.stride()->vertical());
+ ASSERT_EQ(1, maxpool_node.stride()->horizontal());
}
TEST(MaxPool2DTest, pad)
@@ -218,71 +218,71 @@ TEST(MaxPool2DTest, pad)
loco::MaxPool2D maxpool_node;
maxpool_node.pad()->top(t);
- ASSERT_EQ(maxpool_node.pad()->top(), t);
+ ASSERT_EQ(t, maxpool_node.pad()->top());
maxpool_node.pad()->bottom(b);
- ASSERT_EQ(maxpool_node.pad()->bottom(), b);
+ ASSERT_EQ(b, maxpool_node.pad()->bottom());
maxpool_node.pad()->left(l);
- ASSERT_EQ(maxpool_node.pad()->left(), l);
+ ASSERT_EQ(l, maxpool_node.pad()->left());
maxpool_node.pad()->right(r);
- ASSERT_EQ(maxpool_node.pad()->right(), r);
+ ASSERT_EQ(r, maxpool_node.pad()->right());
}
TEST(AvgPool2DTest, constructor)
{
loco::AvgPool2D avgpool_node;
- ASSERT_EQ(avgpool_node.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(avgpool_node.opcode(), loco::CanonicalOpcode::AvgPool2D);
+ ASSERT_EQ(loco::CanonicalDialect::get(), avgpool_node.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::AvgPool2D, avgpool_node.opcode());
- ASSERT_EQ(avgpool_node.ifm(), nullptr);
+ ASSERT_EQ(nullptr, avgpool_node.ifm());
- ASSERT_EQ(avgpool_node.convention(), loco::AvgPool2D::Convention::Unknown);
+ ASSERT_EQ(loco::AvgPool2D::Convention::Unknown, avgpool_node.convention());
- ASSERT_EQ(avgpool_node.pad()->top(), 0);
- ASSERT_EQ(avgpool_node.pad()->bottom(), 0);
- ASSERT_EQ(avgpool_node.pad()->left(), 0);
- ASSERT_EQ(avgpool_node.pad()->right(), 0);
+ ASSERT_EQ(0, avgpool_node.pad()->top());
+ ASSERT_EQ(0, avgpool_node.pad()->bottom());
+ ASSERT_EQ(0, avgpool_node.pad()->left());
+ ASSERT_EQ(0, avgpool_node.pad()->right());
- ASSERT_EQ(avgpool_node.window()->vertical(), 1);
- ASSERT_EQ(avgpool_node.window()->horizontal(), 1);
+ ASSERT_EQ(1, avgpool_node.window()->vertical());
+ ASSERT_EQ(1, avgpool_node.window()->horizontal());
- ASSERT_EQ(avgpool_node.stride()->vertical(), 1);
- ASSERT_EQ(avgpool_node.stride()->horizontal(), 1);
+ ASSERT_EQ(1, avgpool_node.stride()->vertical());
+ ASSERT_EQ(1, avgpool_node.stride()->horizontal());
}
TEST(FeatureEncodeTest, constructor)
{
loco::FeatureEncode feature_encode;
- ASSERT_EQ(feature_encode.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(feature_encode.opcode(), loco::CanonicalOpcode::FeatureEncode);
+ ASSERT_EQ(loco::CanonicalDialect::get(), feature_encode.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::FeatureEncode, feature_encode.opcode());
- ASSERT_EQ(feature_encode.input(), nullptr);
- ASSERT_EQ(feature_encode.encoder(), nullptr);
+ ASSERT_EQ(nullptr, feature_encode.input());
+ ASSERT_EQ(nullptr, feature_encode.encoder());
}
TEST(FeatureDecodeTest, constructor)
{
loco::FeatureDecode feature_decode;
- ASSERT_EQ(feature_decode.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(feature_decode.opcode(), loco::CanonicalOpcode::FeatureDecode);
+ ASSERT_EQ(loco::CanonicalDialect::get(), feature_decode.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::FeatureDecode, feature_decode.opcode());
- ASSERT_EQ(feature_decode.input(), nullptr);
- ASSERT_EQ(feature_decode.decoder(), nullptr);
+ ASSERT_EQ(nullptr, feature_decode.input());
+ ASSERT_EQ(nullptr, feature_decode.decoder());
}
TEST(Reshape_Fixed_Test, constructor)
{
loco::Reshape<loco::ReshapeType::Fixed> reshape;
- ASSERT_EQ(reshape.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(reshape.opcode(), loco::CanonicalOpcode::FixedReshape);
+ ASSERT_EQ(loco::CanonicalDialect::get(), reshape.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::FixedReshape, reshape.opcode());
- ASSERT_EQ(reshape.rank(), 0);
+ ASSERT_EQ(0, reshape.rank());
}
TEST(Reshape_Fixed_Test, shape)
@@ -290,131 +290,153 @@ TEST(Reshape_Fixed_Test, shape)
loco::Reshape<loco::ReshapeType::Fixed> reshape;
reshape.shape({2, 3});
- ASSERT_EQ(reshape.rank(), 2);
- ASSERT_EQ(reshape.dim(0), 2);
- ASSERT_EQ(reshape.dim(1), 3);
+ ASSERT_EQ(2, reshape.rank());
+ ASSERT_EQ(2, reshape.dim(0));
+ ASSERT_EQ(3, reshape.dim(1));
}
TEST(FilterEncodeTest, constructor)
{
loco::FilterEncode filter_encode;
- ASSERT_EQ(filter_encode.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(filter_encode.opcode(), loco::CanonicalOpcode::FilterEncode);
+ ASSERT_EQ(loco::CanonicalDialect::get(), filter_encode.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::FilterEncode, filter_encode.opcode());
- ASSERT_EQ(filter_encode.input(), nullptr);
- ASSERT_EQ(filter_encode.encoder(), nullptr);
+ ASSERT_EQ(nullptr, filter_encode.input());
+ ASSERT_EQ(nullptr, filter_encode.encoder());
+}
+
+TEST(FilterDecodeTest, constructor)
+{
+ loco::FilterDecode filter_decode;
+
+ ASSERT_EQ(loco::CanonicalDialect::get(), filter_decode.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::FilterDecode, filter_decode.opcode());
+
+ ASSERT_EQ(nullptr, filter_decode.input());
+ ASSERT_EQ(nullptr, filter_decode.decoder());
}
TEST(DepthwiseFilterEncodeTest, constructor)
{
loco::DepthwiseFilterEncode dw_filter_encode;
- ASSERT_EQ(dw_filter_encode.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(dw_filter_encode.opcode(), loco::CanonicalOpcode::DepthwiseFilterEncode);
+ ASSERT_EQ(loco::CanonicalDialect::get(), dw_filter_encode.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::DepthwiseFilterEncode, dw_filter_encode.opcode());
+
+ ASSERT_EQ(nullptr, dw_filter_encode.input());
+ ASSERT_EQ(nullptr, dw_filter_encode.encoder());
+}
+
+TEST(DepthwiseFilterDecodeTest, constructor)
+{
+ loco::DepthwiseFilterDecode dw_filter_decode;
+
+ ASSERT_EQ(loco::CanonicalDialect::get(), dw_filter_decode.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::DepthwiseFilterDecode, dw_filter_decode.opcode());
- ASSERT_EQ(dw_filter_encode.input(), nullptr);
- ASSERT_EQ(dw_filter_encode.encoder(), nullptr);
+ ASSERT_EQ(nullptr, dw_filter_decode.input());
+ ASSERT_EQ(nullptr, dw_filter_decode.decoder());
}
TEST(TensorConcatTest, constructor)
{
loco::TensorConcat tensor_concat;
- ASSERT_EQ(tensor_concat.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(tensor_concat.opcode(), loco::CanonicalOpcode::TensorConcat);
+ ASSERT_EQ(loco::CanonicalDialect::get(), tensor_concat.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::TensorConcat, tensor_concat.opcode());
- ASSERT_EQ(tensor_concat.lhs(), nullptr);
- ASSERT_EQ(tensor_concat.rhs(), nullptr);
- ASSERT_EQ(tensor_concat.axis(), 0);
+ ASSERT_EQ(nullptr, tensor_concat.lhs());
+ ASSERT_EQ(nullptr, tensor_concat.rhs());
+ ASSERT_EQ(0, tensor_concat.axis());
tensor_concat.axis(3);
- ASSERT_EQ(tensor_concat.axis(), 3);
+ ASSERT_EQ(3, tensor_concat.axis());
}
TEST(Conv2DTest, constructor)
{
loco::Conv2D conv2d;
- ASSERT_EQ(conv2d.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(conv2d.opcode(), loco::CanonicalOpcode::Conv2D);
+ ASSERT_EQ(loco::CanonicalDialect::get(), conv2d.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::Conv2D, conv2d.opcode());
- ASSERT_EQ(conv2d.ifm(), nullptr);
- ASSERT_EQ(conv2d.ker(), nullptr);
+ ASSERT_EQ(nullptr, conv2d.ifm());
+ ASSERT_EQ(nullptr, conv2d.ker());
ASSERT_NE(conv2d.pad(), nullptr);
- ASSERT_EQ(conv2d.pad()->top(), 0);
- ASSERT_EQ(conv2d.pad()->bottom(), 0);
- ASSERT_EQ(conv2d.pad()->left(), 0);
- ASSERT_EQ(conv2d.pad()->right(), 0);
+ ASSERT_EQ(0, conv2d.pad()->top());
+ ASSERT_EQ(0, conv2d.pad()->bottom());
+ ASSERT_EQ(0, conv2d.pad()->left());
+ ASSERT_EQ(0, conv2d.pad()->right());
ASSERT_NE(conv2d.stride(), nullptr);
- ASSERT_EQ(conv2d.stride()->vertical(), 1);
- ASSERT_EQ(conv2d.stride()->horizontal(), 1);
+ ASSERT_EQ(1, conv2d.stride()->vertical());
+ ASSERT_EQ(1, conv2d.stride()->horizontal());
}
TEST(DepthwiseConv2DTest, constructor)
{
loco::DepthwiseConv2D dw_conv2d;
- ASSERT_EQ(dw_conv2d.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(dw_conv2d.opcode(), loco::CanonicalOpcode::DepthwiseConv2D);
+ ASSERT_EQ(loco::CanonicalDialect::get(), dw_conv2d.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::DepthwiseConv2D, dw_conv2d.opcode());
- ASSERT_EQ(dw_conv2d.ifm(), nullptr);
- ASSERT_EQ(dw_conv2d.ker(), nullptr);
+ ASSERT_EQ(nullptr, dw_conv2d.ifm());
+ ASSERT_EQ(nullptr, dw_conv2d.ker());
ASSERT_NE(dw_conv2d.pad(), nullptr);
- ASSERT_EQ(dw_conv2d.pad()->top(), 0);
- ASSERT_EQ(dw_conv2d.pad()->bottom(), 0);
- ASSERT_EQ(dw_conv2d.pad()->left(), 0);
- ASSERT_EQ(dw_conv2d.pad()->right(), 0);
+ ASSERT_EQ(0, dw_conv2d.pad()->top());
+ ASSERT_EQ(0, dw_conv2d.pad()->bottom());
+ ASSERT_EQ(0, dw_conv2d.pad()->left());
+ ASSERT_EQ(0, dw_conv2d.pad()->right());
ASSERT_NE(dw_conv2d.stride(), nullptr);
- ASSERT_EQ(dw_conv2d.stride()->vertical(), 1);
- ASSERT_EQ(dw_conv2d.stride()->horizontal(), 1);
+ ASSERT_EQ(1, dw_conv2d.stride()->vertical());
+ ASSERT_EQ(1, dw_conv2d.stride()->horizontal());
}
TEST(TransposedConv2DTest, constructor)
{
loco::TransposedConv2D tr_conv2d;
- ASSERT_EQ(tr_conv2d.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(tr_conv2d.opcode(), loco::CanonicalOpcode::TransposedConv2D);
+ ASSERT_EQ(loco::CanonicalDialect::get(), tr_conv2d.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::TransposedConv2D, tr_conv2d.opcode());
- ASSERT_EQ(tr_conv2d.ifm(), nullptr);
- ASSERT_EQ(tr_conv2d.ker(), nullptr);
+ ASSERT_EQ(nullptr, tr_conv2d.ifm());
+ ASSERT_EQ(nullptr, tr_conv2d.ker());
ASSERT_NE(tr_conv2d.pad(), nullptr);
- ASSERT_EQ(tr_conv2d.pad()->top(), 0);
- ASSERT_EQ(tr_conv2d.pad()->bottom(), 0);
- ASSERT_EQ(tr_conv2d.pad()->left(), 0);
- ASSERT_EQ(tr_conv2d.pad()->right(), 0);
+ ASSERT_EQ(0, tr_conv2d.pad()->top());
+ ASSERT_EQ(0, tr_conv2d.pad()->bottom());
+ ASSERT_EQ(0, tr_conv2d.pad()->left());
+ ASSERT_EQ(0, tr_conv2d.pad()->right());
ASSERT_NE(tr_conv2d.stride(), nullptr);
- ASSERT_EQ(tr_conv2d.stride()->vertical(), 1);
- ASSERT_EQ(tr_conv2d.stride()->horizontal(), 1);
+ ASSERT_EQ(1, tr_conv2d.stride()->vertical());
+ ASSERT_EQ(1, tr_conv2d.stride()->horizontal());
}
TEST(BiasEncodeTest, constructor)
{
loco::BiasEncode bias_encode;
- ASSERT_EQ(bias_encode.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(bias_encode.opcode(), loco::CanonicalOpcode::BiasEncode);
+ ASSERT_EQ(loco::CanonicalDialect::get(), bias_encode.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::BiasEncode, bias_encode.opcode());
- ASSERT_EQ(bias_encode.input(), nullptr);
+ ASSERT_EQ(nullptr, bias_encode.input());
}
TEST(TensorBiasAddTest, constructor)
{
loco::BiasAdd<loco::Domain::Tensor> bias_add;
- ASSERT_EQ(bias_add.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(bias_add.opcode(), loco::CanonicalOpcode::TensorBiasAdd);
+ ASSERT_EQ(loco::CanonicalDialect::get(), bias_add.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::TensorBiasAdd, bias_add.opcode());
- ASSERT_EQ(bias_add.value(), nullptr);
- ASSERT_EQ(bias_add.bias(), nullptr);
- ASSERT_EQ(bias_add.axis(), 0);
+ ASSERT_EQ(nullptr, bias_add.value());
+ ASSERT_EQ(nullptr, bias_add.bias());
+ ASSERT_EQ(0, bias_add.axis());
}
TEST(TensorBiasAddTest, alias)
@@ -428,11 +450,11 @@ TEST(FeatureBiasAddTest, constructor)
{
loco::BiasAdd<loco::Domain::Feature> bias_add;
- ASSERT_EQ(bias_add.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(bias_add.opcode(), loco::CanonicalOpcode::FeatureBiasAdd);
+ ASSERT_EQ(loco::CanonicalDialect::get(), bias_add.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::FeatureBiasAdd, bias_add.opcode());
- ASSERT_EQ(bias_add.value(), nullptr);
- ASSERT_EQ(bias_add.bias(), nullptr);
+ ASSERT_EQ(nullptr, bias_add.value());
+ ASSERT_EQ(nullptr, bias_add.bias());
}
TEST(FeatureBiasAddTest, alias)
@@ -449,6 +471,13 @@ TEST(EltwiseAddTest, constructor)
SUCCEED();
}
+TEST(EltwiseMaxTest, constructor)
+{
+ loco::EltwiseMax eltwise_max;
+
+ SUCCEED();
+}
+
TEST(EltwiseMulTest, constructor)
{
loco::EltwiseMul eltwise_mul;
@@ -474,30 +503,86 @@ TEST(EltwiseSqrtTest, constructor)
{
loco::EltwiseSqrt sqrt_node;
- ASSERT_EQ(sqrt_node.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(sqrt_node.opcode(), loco::CanonicalOpcode::EltwiseSqrt);
+ ASSERT_EQ(loco::CanonicalDialect::get(), sqrt_node.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::EltwiseSqrt, sqrt_node.opcode());
- ASSERT_EQ(sqrt_node.input(), nullptr);
+ ASSERT_EQ(nullptr, sqrt_node.input());
}
TEST(TensorBroadcastTest, constructor)
{
loco::TensorBroadcast tensor_broadcast_node;
- ASSERT_EQ(tensor_broadcast_node.dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(tensor_broadcast_node.opcode(), loco::CanonicalOpcode::TensorBroadcast);
+ ASSERT_EQ(loco::CanonicalDialect::get(), tensor_broadcast_node.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::TensorBroadcast, tensor_broadcast_node.opcode());
- ASSERT_EQ(tensor_broadcast_node.input(), nullptr);
+ ASSERT_EQ(nullptr, tensor_broadcast_node.input());
}
TEST(TensorBroadcastTest, mapping)
{
loco::TensorBroadcast tensor_broadcast_node;
- ASSERT_EQ(tensor_broadcast_node.mapping()->defined(0), false);
+ ASSERT_FALSE(tensor_broadcast_node.mapping()->defined(0));
tensor_broadcast_node.mapping()->dim(0) = 3;
- ASSERT_EQ(tensor_broadcast_node.mapping()->defined(0), true);
- ASSERT_EQ(tensor_broadcast_node.mapping()->dim(0), 3);
+ ASSERT_TRUE(tensor_broadcast_node.mapping()->defined(0));
+ ASSERT_EQ(3, tensor_broadcast_node.mapping()->dim(0));
+}
+
+TEST(MatrixEncodeTest, constructor)
+{
+ loco::MatrixEncode matrix_encode;
+
+ ASSERT_EQ(loco::CanonicalDialect::get(), matrix_encode.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::MatrixEncode, matrix_encode.opcode());
+
+ ASSERT_EQ(nullptr, matrix_encode.input());
+}
+
+TEST(MatrixDecodeTest, constructor)
+{
+ loco::MatrixDecode matrix_decode;
+
+ ASSERT_EQ(loco::CanonicalDialect::get(), matrix_decode.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::MatrixDecode, matrix_decode.opcode());
+
+ ASSERT_EQ(nullptr, matrix_decode.input());
+}
+
+TEST(MatMulTest, constructor)
+{
+ loco::MatMul mat_mul;
+
+ ASSERT_EQ(loco::CanonicalDialect::get(), mat_mul.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::MatMul, mat_mul.opcode());
+
+ ASSERT_EQ(nullptr, mat_mul.lhs());
+ ASSERT_EQ(nullptr, mat_mul.rhs());
+}
+
+TEST(TransposeTest, constructor)
+{
+ loco::TensorTranspose transpose;
+
+ ASSERT_EQ(loco::CanonicalDialect::get(), transpose.dialect());
+ ASSERT_EQ(loco::CanonicalOpcode::TensorTranspose, transpose.opcode());
+
+ ASSERT_EQ(nullptr, transpose.input());
+ ASSERT_EQ(0, transpose.perm()->size());
+}
+
+TEST(TransposeTest, perm)
+{
+ loco::TensorTranspose transpose;
+
+ transpose.perm()->size(3);
+ transpose.perm()->axis(0) = 1;
+ transpose.perm()->axis(1) = 2;
+ transpose.perm()->axis(2) = 0;
+
+ ASSERT_EQ(1, transpose.perm()->axis(0));
+ ASSERT_EQ(2, transpose.perm()->axis(1));
+ ASSERT_EQ(0, transpose.perm()->axis(2));
}
diff --git a/compiler/loco/src/IR/Pad.test.cpp b/compiler/loco/src/IR/Pad.test.cpp
deleted file mode 100644
index 2e3d4af87..000000000
--- a/compiler/loco/src/IR/Pad.test.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "loco/IR/Padding2D.h"
-
-#include <gtest/gtest.h>
-
-TEST(PadTest, default_constructor_2D)
-{
- loco::Padding2D pad;
-
- ASSERT_EQ(pad.top(), 0);
- ASSERT_EQ(pad.bottom(), 0);
- ASSERT_EQ(pad.left(), 0);
- ASSERT_EQ(pad.right(), 0);
-}
diff --git a/compiler/loco/src/IR/Padding2D.test.cpp b/compiler/loco/src/IR/Padding2D.test.cpp
new file mode 100644
index 000000000..b919f26a6
--- /dev/null
+++ b/compiler/loco/src/IR/Padding2D.test.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "loco/IR/Padding2D.h"
+
+#include <gtest/gtest.h>
+
+TEST(PadTest, default_constructor_2D)
+{
+ loco::Padding2D pad;
+
+ ASSERT_EQ(0, pad.top());
+ ASSERT_EQ(0, pad.bottom());
+ ASSERT_EQ(0, pad.left());
+ ASSERT_EQ(0, pad.right());
+}
diff --git a/compiler/loco/src/IR/PaddingND.test.cpp b/compiler/loco/src/IR/PaddingND.test.cpp
new file mode 100644
index 000000000..8c384c027
--- /dev/null
+++ b/compiler/loco/src/IR/PaddingND.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "loco/IR/PaddingND.h"
+
+#include <gtest/gtest.h>
+
+TEST(PaddingNDTest, default_constructor_ND)
+{
+ loco::PaddingND padding;
+
+ padding.rank(1);
+ padding.front(0) = 1;
+ padding.back(0) = 2;
+
+ ASSERT_EQ(1, padding.rank());
+ ASSERT_EQ(1, padding.front(0));
+ ASSERT_EQ(2, padding.back(0));
+}
diff --git a/compiler/loco/src/IR/PermutingCodec.cpp b/compiler/loco/src/IR/PermutingCodec.cpp
index ba76963fe..2857e5e28 100644
--- a/compiler/loco/src/IR/PermutingCodec.cpp
+++ b/compiler/loco/src/IR/PermutingCodec.cpp
@@ -303,6 +303,40 @@ TensorIndex PermutingEncoder<Domain::Filter>::value(const FilterIndex &in) const
bool PermutingEncoder<Domain::Filter>::valid(void) const { return ::valid(_perm); }
+//
+// Permuting Decoder
+//
+TensorShape PermutingDecoder<Domain::Filter>::shape(const FilterShape &in) const
+{
+ assert(valid() && "invalid permutation");
+
+ TensorShape out;
+
+ out.rank(4);
+ out.dim(_perm[FilterAxis::Count]) = in.count();
+ out.dim(_perm[FilterAxis::Depth]) = in.depth();
+ out.dim(_perm[FilterAxis::Height]) = in.height();
+ out.dim(_perm[FilterAxis::Width]) = in.width();
+
+ return out;
+}
+
+FilterIndex PermutingDecoder<Domain::Filter>::value(const TensorIndex &in) const
+{
+ assert(valid() && "invalid permutation");
+
+ FilterIndex out;
+
+ out.nth() = in.at(_perm[FilterAxis::Count]);
+ out.channel() = in.at(_perm[FilterAxis::Depth]);
+ out.row() = in.at(_perm[FilterAxis::Height]);
+ out.column() = in.at(_perm[FilterAxis::Width]);
+
+ return out;
+}
+
+bool PermutingDecoder<Domain::Filter>::valid(void) const { return ::valid(_perm); }
+
} // namespace loco
/**
@@ -422,6 +456,41 @@ TensorIndex PermutingEncoder<Domain::DepthwiseFilter>::value(const DepthwiseFilt
bool PermutingEncoder<Domain::DepthwiseFilter>::valid(void) const { return ::valid(_perm); }
+//
+// Permuting Decoder
+//
+TensorShape PermutingDecoder<Domain::DepthwiseFilter>::shape(const DepthwiseFilterShape &in) const
+{
+ assert(valid() && "invalid permutation");
+
+ TensorShape out;
+ out.rank(4);
+
+ out.dim(_perm[DepthwiseFilterAxis::Depth]) = in.depth();
+ out.dim(_perm[DepthwiseFilterAxis::Multiplier]) = in.multiplier();
+ out.dim(_perm[DepthwiseFilterAxis::Height]) = in.height();
+ out.dim(_perm[DepthwiseFilterAxis::Width]) = in.width();
+
+ return out;
+}
+
+DepthwiseFilterIndex PermutingDecoder<Domain::DepthwiseFilter>::value(const TensorIndex &in) const
+{
+ assert(valid() && "invalid permutation");
+ assert(in.rank() == 4);
+
+ DepthwiseFilterIndex out;
+
+ out.channel() = in.at(_perm[DepthwiseFilterAxis::Depth]);
+ out.nth() = in.at(_perm[DepthwiseFilterAxis::Multiplier]);
+ out.row() = in.at(_perm[DepthwiseFilterAxis::Height]);
+ out.column() = in.at(_perm[DepthwiseFilterAxis::Width]);
+
+ return out;
+}
+
+bool PermutingDecoder<Domain::DepthwiseFilter>::valid(void) const { return ::valid(_perm); }
+
} // namespace loco
/**
diff --git a/compiler/loco/src/IR/PermutingCodec.test.cpp b/compiler/loco/src/IR/PermutingCodec.test.cpp
index eb296d283..f8f754110 100644
--- a/compiler/loco/src/IR/PermutingCodec.test.cpp
+++ b/compiler/loco/src/IR/PermutingCodec.test.cpp
@@ -43,10 +43,10 @@ TEST(PemutationTest, feature)
ASSERT_TRUE(perm.mapped(FeatureAxis::Width));
// Check the value
- ASSERT_EQ(perm[FeatureAxis::Count], 5);
- ASSERT_EQ(perm[FeatureAxis::Depth], 6);
- ASSERT_EQ(perm[FeatureAxis::Height], 7);
- ASSERT_EQ(perm[FeatureAxis::Width], 8);
+ ASSERT_EQ(5, perm[FeatureAxis::Count]);
+ ASSERT_EQ(6, perm[FeatureAxis::Depth]);
+ ASSERT_EQ(7, perm[FeatureAxis::Height]);
+ ASSERT_EQ(8, perm[FeatureAxis::Width]);
}
TEST(PemutationTest, filter)
@@ -72,10 +72,10 @@ TEST(PemutationTest, filter)
ASSERT_TRUE(perm.mapped(FilterAxis::Width));
// Check the value
- ASSERT_EQ(perm[FilterAxis::Count], 5);
- ASSERT_EQ(perm[FilterAxis::Depth], 6);
- ASSERT_EQ(perm[FilterAxis::Height], 7);
- ASSERT_EQ(perm[FilterAxis::Width], 8);
+ ASSERT_EQ(5, perm[FilterAxis::Count]);
+ ASSERT_EQ(6, perm[FilterAxis::Depth]);
+ ASSERT_EQ(7, perm[FilterAxis::Height]);
+ ASSERT_EQ(8, perm[FilterAxis::Width]);
}
TEST(PemutationTest, depthwise_filter)
@@ -101,10 +101,10 @@ TEST(PemutationTest, depthwise_filter)
ASSERT_TRUE(perm.mapped(DepthwiseFilterAxis::Width));
// Check the value
- ASSERT_EQ(perm[DepthwiseFilterAxis::Depth], 5);
- ASSERT_EQ(perm[DepthwiseFilterAxis::Multiplier], 6);
- ASSERT_EQ(perm[DepthwiseFilterAxis::Height], 7);
- ASSERT_EQ(perm[DepthwiseFilterAxis::Width], 8);
+ ASSERT_EQ(5, perm[DepthwiseFilterAxis::Depth]);
+ ASSERT_EQ(6, perm[DepthwiseFilterAxis::Multiplier]);
+ ASSERT_EQ(7, perm[DepthwiseFilterAxis::Height]);
+ ASSERT_EQ(8, perm[DepthwiseFilterAxis::Width]);
}
TEST(PermutingEncoderTest, feature)
@@ -147,10 +147,10 @@ TEST(PermutingEncoderTest, feature)
// Get the feature shape corresponding to a given image
auto feature_shape = enc.shape(tensor_shape);
- ASSERT_EQ(feature_shape.count(), 1);
- ASSERT_EQ(feature_shape.depth(), 3);
- ASSERT_EQ(feature_shape.height(), 720);
- ASSERT_EQ(feature_shape.width(), 1280);
+ ASSERT_EQ(1, feature_shape.count());
+ ASSERT_EQ(3, feature_shape.depth());
+ ASSERT_EQ(720, feature_shape.height());
+ ASSERT_EQ(1280, feature_shape.width());
// Let's find a source tensor index!
FeatureIndex feature_index;
@@ -162,10 +162,10 @@ TEST(PermutingEncoderTest, feature)
auto tensor_index = enc.value(feature_index);
- ASSERT_EQ(tensor_index.at(0), 0); // BATCH(COUNT)
- ASSERT_EQ(tensor_index.at(1), 2); // ROW(HEIGHT)
- ASSERT_EQ(tensor_index.at(2), 3); // COLUMN(WIDTH)
- ASSERT_EQ(tensor_index.at(3), 1); // CHANNEL(DEPTH)
+ ASSERT_EQ(0, tensor_index.at(0)); // BATCH(COUNT)
+ ASSERT_EQ(2, tensor_index.at(1)); // ROW(HEIGHT)
+ ASSERT_EQ(3, tensor_index.at(2)); // COLUMN(WIDTH)
+ ASSERT_EQ(1, tensor_index.at(3)); // CHANNEL(DEPTH)
}
TEST(PermutingEncoderTest, feature_clone)
@@ -180,7 +180,7 @@ TEST(PermutingEncoderTest, feature_clone)
src_perm->axis(FeatureAxis::Width) = 2;
auto dst_enc = src_enc.clone();
- auto dst_perm = dynamic_cast<PermutingEncoder<Domain::Feature> *>(dst_enc.get())->perm();
+ auto dst_perm = loco::must_cast<PermutingEncoder<Domain::Feature> *>(dst_enc.get())->perm();
EXPECT_EQ(dst_perm->axis(FeatureAxis::Count), src_perm->axis(FeatureAxis::Count));
EXPECT_EQ(dst_perm->axis(FeatureAxis::Depth), src_perm->axis(FeatureAxis::Depth));
@@ -233,10 +233,10 @@ TEST(PermutingEncoderTest, filter)
// Get the corresponding filter shape
auto filter_shape = enc.shape(tensor_shape);
- ASSERT_EQ(filter_shape.count(), 8);
- ASSERT_EQ(filter_shape.depth(), 4);
- ASSERT_EQ(filter_shape.height(), 1);
- ASSERT_EQ(filter_shape.width(), 7);
+ ASSERT_EQ(8, filter_shape.count());
+ ASSERT_EQ(4, filter_shape.depth());
+ ASSERT_EQ(1, filter_shape.height());
+ ASSERT_EQ(7, filter_shape.width());
// Let's find a source tensor index!
FilterIndex filter_index;
@@ -248,10 +248,10 @@ TEST(PermutingEncoderTest, filter)
auto tensor_index = enc.value(filter_index);
- ASSERT_EQ(tensor_index.at(0), 1); // NTH(COUNT)
- ASSERT_EQ(tensor_index.at(1), 0); // ROW(HEIGHT)
- ASSERT_EQ(tensor_index.at(2), 3); // COLUMN(WIDTH)
- ASSERT_EQ(tensor_index.at(3), 2); // CHANNEL(DEPTH)
+ ASSERT_EQ(1, tensor_index.at(0)); // NTH(COUNT)
+ ASSERT_EQ(0, tensor_index.at(1)); // ROW(HEIGHT)
+ ASSERT_EQ(3, tensor_index.at(2)); // COLUMN(WIDTH)
+ ASSERT_EQ(2, tensor_index.at(3)); // CHANNEL(DEPTH)
}
TEST(PermutingEncoderTest, depthwise_filter)
@@ -293,10 +293,10 @@ TEST(PermutingEncoderTest, depthwise_filter)
// Get the corresponding depthwise filter shape
auto filter_shape = enc.shape(tensor_shape);
- ASSERT_EQ(filter_shape.depth(), 8);
- ASSERT_EQ(filter_shape.multiplier(), 4);
- ASSERT_EQ(filter_shape.height(), 1);
- ASSERT_EQ(filter_shape.width(), 7);
+ ASSERT_EQ(8, filter_shape.depth());
+ ASSERT_EQ(4, filter_shape.multiplier());
+ ASSERT_EQ(1, filter_shape.height());
+ ASSERT_EQ(7, filter_shape.width());
// Let's find a source tensor index!
DepthwiseFilterIndex filter_index;
@@ -308,13 +308,13 @@ TEST(PermutingEncoderTest, depthwise_filter)
auto tensor_index = enc.value(filter_index);
- ASSERT_EQ(tensor_index.at(0), 1); // CHANNEL(DEPTH)
- ASSERT_EQ(tensor_index.at(1), 0); // ROW(HEIGHT)
- ASSERT_EQ(tensor_index.at(2), 3); // COLUMN(WIDTH)
- ASSERT_EQ(tensor_index.at(3), 2); // NTH(MULTIPLIER)
+ ASSERT_EQ(1, tensor_index.at(0)); // CHANNEL(DEPTH)
+ ASSERT_EQ(0, tensor_index.at(1)); // ROW(HEIGHT)
+ ASSERT_EQ(3, tensor_index.at(2)); // COLUMN(WIDTH)
+ ASSERT_EQ(2, tensor_index.at(3)); // NTH(MULTIPLIER)
}
-TEST(PermutingEncoderTest, dpethwisefilter_init)
+TEST(PermutingEncoderTest, depthwisefilter_init)
{
Permutation<Domain::DepthwiseFilter> src_perm;
@@ -379,11 +379,11 @@ TEST(PermutingDecoderTest, feature)
// Get the tensor shape corresponding to a given image
auto tensor_shape = dec.shape(feature_shape);
- ASSERT_EQ(tensor_shape.rank(), 4);
- ASSERT_EQ(tensor_shape.dim(0), 1); // COUNT
- ASSERT_EQ(tensor_shape.dim(1), 720); // HEIGHT
- ASSERT_EQ(tensor_shape.dim(2), 1280); // WIDTH
- ASSERT_EQ(tensor_shape.dim(3), 3); // DEPTH
+ ASSERT_EQ(4, tensor_shape.rank());
+ ASSERT_EQ(1, tensor_shape.dim(0)); // COUNT
+ ASSERT_EQ(720, tensor_shape.dim(1)); // HEIGHT
+ ASSERT_EQ(1280, tensor_shape.dim(2)); // WIDTH
+ ASSERT_EQ(3, tensor_shape.dim(3)); // DEPTH
// Let's find a source feature index!
TensorIndex tensor_index;
@@ -397,10 +397,10 @@ TEST(PermutingDecoderTest, feature)
auto feature_index = dec.value(tensor_index);
- ASSERT_EQ(feature_index.batch(), 0);
- ASSERT_EQ(feature_index.channel(), 1);
- ASSERT_EQ(feature_index.row(), 2);
- ASSERT_EQ(feature_index.column(), 3);
+ ASSERT_EQ(0, feature_index.batch());
+ ASSERT_EQ(1, feature_index.channel());
+ ASSERT_EQ(2, feature_index.row());
+ ASSERT_EQ(3, feature_index.column());
}
TEST(PermutingDecoderTest, feature_clone)
@@ -415,7 +415,7 @@ TEST(PermutingDecoderTest, feature_clone)
src_perm->axis(FeatureAxis::Width) = 2;
auto dst_enc = src_enc.clone();
- auto dst_perm = dynamic_cast<PermutingDecoder<Domain::Feature> *>(dst_enc.get())->perm();
+ auto dst_perm = loco::must_cast<PermutingDecoder<Domain::Feature> *>(dst_enc.get())->perm();
EXPECT_EQ(dst_perm->axis(FeatureAxis::Count), src_perm->axis(FeatureAxis::Count));
EXPECT_EQ(dst_perm->axis(FeatureAxis::Depth), src_perm->axis(FeatureAxis::Depth));
@@ -428,3 +428,126 @@ TEST(PermutingDecoderTest, feature_clone)
EXPECT_EQ(src_perm->axis(FeatureAxis::Height), 1);
EXPECT_EQ(dst_perm->axis(FeatureAxis::Height), 2);
}
+
+TEST(PermutingDecoderTest, filter)
+{
+ PermutingDecoder<Domain::Filter> dec;
+
+ // Decoder is invalid at the beginning
+ ASSERT_FALSE(dec.valid());
+
+ // Set "invalid" mapping
+ dec.perm()->axis(FilterAxis::Count) = 0;
+ dec.perm()->axis(FilterAxis::Depth) = 6;
+ dec.perm()->axis(FilterAxis::Height) = 1;
+ dec.perm()->axis(FilterAxis::Width) = 2;
+
+ // Decoder is still invalid
+ ASSERT_FALSE(dec.valid());
+
+ // Set another "invalid" mapping
+ dec.perm()->axis(FilterAxis::Depth) = 1;
+
+ // Decoder is still invalid
+ ASSERT_FALSE(dec.valid());
+
+ // Set "valid" mapping
+ dec.perm()->axis(FilterAxis::Depth) = 3;
+
+ // Decoder is now valid
+ ASSERT_TRUE(dec.valid());
+
+ // Let's test with a small filter
+ FilterShape filter_shape;
+
+ filter_shape.count() = 10;
+ filter_shape.depth() = 3;
+ filter_shape.height() = 6;
+ filter_shape.width() = 8;
+
+ // Get the tensor shape corresponding to a given image
+ auto tensor_shape = dec.shape(filter_shape);
+
+ ASSERT_EQ(4, tensor_shape.rank());
+ ASSERT_EQ(10, tensor_shape.dim(0)); // COUNT
+ ASSERT_EQ(6, tensor_shape.dim(1)); // HEIGHT
+ ASSERT_EQ(8, tensor_shape.dim(2)); // WIDTH
+ ASSERT_EQ(3, tensor_shape.dim(3)); // DEPTH
+
+ // Let's find a source filter index!
+ TensorIndex tensor_index;
+
+ tensor_index.resize(4);
+
+ tensor_index.at(0) = 0; // BATCH(COUNT)
+ tensor_index.at(3) = 1; // CHANNEL(DEPTH)
+ tensor_index.at(1) = 2; // ROW(HEIGHT)
+ tensor_index.at(2) = 3; // COLUMN(WIDTH)
+
+ auto filter_index = dec.value(tensor_index);
+
+ ASSERT_EQ(0, filter_index.nth());
+ ASSERT_EQ(1, filter_index.channel());
+ ASSERT_EQ(2, filter_index.row());
+ ASSERT_EQ(3, filter_index.column());
+}
+
+TEST(PermutingDecoderTest, depthwise_filter)
+{
+ PermutingDecoder<Domain::DepthwiseFilter> dec;
+
+ // Decoder is invalid at the beginning
+ ASSERT_FALSE(dec.valid());
+
+ // Set "invalid" mapping
+ dec.perm()->axis(DepthwiseFilterAxis::Depth) = 0;
+ dec.perm()->axis(DepthwiseFilterAxis::Multiplier) = 6;
+ dec.perm()->axis(DepthwiseFilterAxis::Height) = 1;
+ dec.perm()->axis(DepthwiseFilterAxis::Width) = 2;
+
+ // Decoder is still invalid
+ ASSERT_FALSE(dec.valid());
+
+ // Set another "invalid" mapping
+ dec.perm()->axis(DepthwiseFilterAxis::Multiplier) = 1;
+
+ // Decoder is still invalid
+ ASSERT_FALSE(dec.valid());
+
+ // Set "valid" mapping
+ dec.perm()->axis(DepthwiseFilterAxis::Multiplier) = 3;
+
+ // Decoder is now valid
+ ASSERT_TRUE(dec.valid());
+
+ DepthwiseFilterShape dw_filter_shape;
+
+ dw_filter_shape.depth() = 8;
+ dw_filter_shape.multiplier() = 1;
+ dw_filter_shape.height() = 7;
+ dw_filter_shape.width() = 4;
+
+ // Get the corresponding depthwise filter shape
+ auto tensor_shape = dec.shape(dw_filter_shape);
+
+ ASSERT_EQ(8, tensor_shape.dim(0).value());
+ ASSERT_EQ(7, tensor_shape.dim(1).value());
+ ASSERT_EQ(4, tensor_shape.dim(2).value());
+ ASSERT_EQ(1, tensor_shape.dim(3).value());
+
+ // Let's find a source tensor index!
+ TensorIndex tensor_index;
+ tensor_index.resize(4);
+
+ tensor_index.at(0) = 4;
+ tensor_index.at(1) = 2;
+ tensor_index.at(2) = 1;
+ tensor_index.at(3) = 0;
+
+ auto dw_filter_index = dec.value(tensor_index);
+
+ ASSERT_EQ(4, dw_filter_index.channel());
+ ASSERT_EQ(0, dw_filter_index.nth());
+ ASSERT_EQ(2, dw_filter_index.row());
+ ASSERT_EQ(1, dw_filter_index.column());
+}
diff --git a/compiler/loco/src/IR/Stride.test.cpp b/compiler/loco/src/IR/Stride.test.cpp
index 60deb5c6f..9cc88f37b 100644
--- a/compiler/loco/src/IR/Stride.test.cpp
+++ b/compiler/loco/src/IR/Stride.test.cpp
@@ -22,8 +22,8 @@ TEST(StrideTest, default_constructor_2D)
{
loco::Stride<2> stride;
- ASSERT_EQ(stride.vertical(), 1);
- ASSERT_EQ(stride.horizontal(), 1);
+ ASSERT_EQ(1, stride.vertical());
+ ASSERT_EQ(1, stride.horizontal());
}
TEST(StrideTest, setter_and_getter_2D)
@@ -32,11 +32,11 @@ TEST(StrideTest, setter_and_getter_2D)
stride.vertical(2);
- ASSERT_EQ(stride.vertical(), 2);
- ASSERT_EQ(stride.horizontal(), 1);
+ ASSERT_EQ(2, stride.vertical());
+ ASSERT_EQ(1, stride.horizontal());
stride.horizontal(3);
- ASSERT_EQ(stride.vertical(), 2);
- ASSERT_EQ(stride.horizontal(), 3);
+ ASSERT_EQ(2, stride.vertical());
+ ASSERT_EQ(3, stride.horizontal());
}
diff --git a/compiler/loco/src/IR/TensorShape.cpp b/compiler/loco/src/IR/TensorShape.cpp
new file mode 100644
index 000000000..e9d4f7e14
--- /dev/null
+++ b/compiler/loco/src/IR/TensorShape.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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 "loco/IR/TensorShape.h"
+
+#include <cassert>
+
+namespace loco
+{
+
+uint32_t element_count(const loco::TensorShape *tensor_shape)
+{
+ uint32_t res = 1;
+
+ for (uint32_t axis = 0; axis < tensor_shape->rank(); ++axis)
+ {
+ // Let's use "assert" here as "caller" is responsible for this check.
+ // Please refer to the header for details.
+ assert(tensor_shape->dim(axis).known());
+ res *= tensor_shape->dim(axis).value();
+ }
+
+ return res;
+}
+
+} // namespace loco
+
+namespace loco
+{
+
+bool operator==(const TensorShape &lhs, const TensorShape &rhs)
+{
+ if (lhs.rank() != rhs.rank())
+ return false;
+ for (uint32_t axis = 0; axis < lhs.rank(); ++axis)
+ {
+ if (!(lhs.dim(axis) == rhs.dim(axis)))
+ return false;
+ }
+ return true;
+}
+
+} // namespace loco
diff --git a/compiler/loco/src/IR/TensorShape.test.cpp b/compiler/loco/src/IR/TensorShape.test.cpp
index 69260accf..ca7af721b 100644
--- a/compiler/loco/src/IR/TensorShape.test.cpp
+++ b/compiler/loco/src/IR/TensorShape.test.cpp
@@ -22,7 +22,20 @@ TEST(TensorShapeTest, default_constructor)
{
loco::TensorShape tensor_shape;
- ASSERT_EQ(tensor_shape.rank(), 0);
+ ASSERT_EQ(0, tensor_shape.rank());
+}
+
+TEST(TensorShapeTest, initializer_list_constructor)
+{
+ loco::TensorShape tensor_shape{3, 5};
+
+ ASSERT_EQ(2, tensor_shape.rank());
+
+ ASSERT_TRUE(tensor_shape.dim(0).known());
+ ASSERT_TRUE(tensor_shape.dim(1).known());
+
+ ASSERT_EQ(3, tensor_shape.dim(0).value());
+ ASSERT_EQ(5, tensor_shape.dim(1).value());
}
TEST(TensorShapeTest, rank)
@@ -31,7 +44,7 @@ TEST(TensorShapeTest, rank)
tensor_shape.rank(2);
- ASSERT_EQ(tensor_shape.rank(), 2);
+ ASSERT_EQ(2, tensor_shape.rank());
ASSERT_FALSE(tensor_shape.dim(0).known());
ASSERT_FALSE(tensor_shape.dim(1).known());
}
@@ -47,7 +60,7 @@ TEST(TensorShapeTest, dim)
ASSERT_TRUE(tensor_shape.dim(0).known());
ASSERT_FALSE(tensor_shape.dim(1).known());
- ASSERT_EQ(tensor_shape.dim(0), 3);
+ ASSERT_EQ(3, tensor_shape.dim(0));
}
TEST(TensorShapeTest, rank_update)
@@ -65,7 +78,7 @@ TEST(TensorShapeTest, rank_update)
ASSERT_FALSE(tensor_shape.dim(2).known());
ASSERT_FALSE(tensor_shape.dim(3).known());
- ASSERT_EQ(tensor_shape.dim(1), 3);
+ ASSERT_EQ(3, tensor_shape.dim(1));
}
TEST(TensorShapeTest, copy)
@@ -79,10 +92,49 @@ TEST(TensorShapeTest, copy)
dst = src;
- ASSERT_EQ(dst.rank(), 2);
+ ASSERT_EQ(2, dst.rank());
ASSERT_FALSE(dst.dim(0).known());
ASSERT_TRUE(dst.dim(1).known());
- ASSERT_EQ(dst.dim(1), 3);
+ ASSERT_EQ(3, dst.dim(1));
+}
+
+TEST(TensorShapeTest, element_count)
+{
+ // Check Rank-0 case
+ loco::TensorShape src;
+
+ ASSERT_EQ(1, loco::element_count(&src));
+}
+
+TEST(TensorShapeTest, equal_operator)
+{
+ loco::TensorShape lhs, rhs;
+
+ lhs.rank(2);
+ lhs.dim(0) = 1;
+ lhs.dim(1) = 3;
+
+ rhs.rank(1);
+ rhs.dim(0) = 1;
+
+ EXPECT_FALSE(lhs == rhs);
+
+ rhs.rank(2);
+ rhs.dim(0) = 1;
+ rhs.dim(1) = 3;
+
+ EXPECT_TRUE(lhs == rhs);
+
+ // for unknown
+ loco::TensorShape lhs_u, rhs_u;
+
+ lhs_u.rank(2);
+ lhs_u.dim(0) = 1;
+
+ rhs_u.rank(2);
+ rhs_u.dim(0) = 1;
+
+ EXPECT_FALSE(lhs == rhs_u);
}
diff --git a/compiler/loco/src/IR/Use.test.cpp b/compiler/loco/src/IR/Use.test.cpp
index 4a2f1cc25..251e3b416 100644
--- a/compiler/loco/src/IR/Use.test.cpp
+++ b/compiler/loco/src/IR/Use.test.cpp
@@ -25,8 +25,8 @@ TEST(UseTest, constructor)
MockupNode user;
loco::Use use{&user};
- ASSERT_EQ(use.user(), &user);
- ASSERT_EQ(use.node(), nullptr);
+ ASSERT_EQ(&user, use.user());
+ ASSERT_EQ(nullptr, use.node());
}
TEST(UseTest, link_node)
@@ -37,6 +37,6 @@ TEST(UseTest, link_node)
use.node(&def);
- ASSERT_EQ(use.user(), &user);
- ASSERT_EQ(use.node(), &def);
+ ASSERT_EQ(&user, use.user());
+ ASSERT_EQ(&def, use.node());
}
diff --git a/compiler/loco/src/IR/Verifier.test.cpp b/compiler/loco/src/IR/Verifier.test.cpp
index 247a59390..8c40a5058 100644
--- a/compiler/loco/src/IR/Verifier.test.cpp
+++ b/compiler/loco/src/IR/Verifier.test.cpp
@@ -58,7 +58,7 @@ TEST(VerifierTest, valid_error_reporter)
std::vector<ErrorDetail<ErrorCategory::MissingArgument>> errors;
ASSERT_FALSE(loco::valid(g.get(), make_unique<Collector>(&errors)));
- ASSERT_EQ(errors.size(), 1);
- ASSERT_EQ(errors.at(0).node(), push);
- ASSERT_EQ(errors.at(0).index(), 0);
+ ASSERT_EQ(1, errors.size());
+ ASSERT_EQ(push, errors.at(0).node());
+ ASSERT_EQ(0, errors.at(0).index());
}
diff --git a/compiler/loco/src/IR/Window.test.cpp b/compiler/loco/src/IR/Window.test.cpp
index c112e0f96..4cf7183d8 100644
--- a/compiler/loco/src/IR/Window.test.cpp
+++ b/compiler/loco/src/IR/Window.test.cpp
@@ -22,8 +22,8 @@ TEST(WindowTest, default_constructor_2D)
{
loco::Window<2> window;
- ASSERT_EQ(window.vertical(), 1);
- ASSERT_EQ(window.horizontal(), 1);
+ ASSERT_EQ(1, window.vertical());
+ ASSERT_EQ(1, window.horizontal());
}
TEST(WindowTest, setter_and_getter_2D)
@@ -32,11 +32,11 @@ TEST(WindowTest, setter_and_getter_2D)
window.vertical(2);
- ASSERT_EQ(window.vertical(), 2);
- ASSERT_EQ(window.horizontal(), 1);
+ ASSERT_EQ(2, window.vertical());
+ ASSERT_EQ(1, window.horizontal());
window.horizontal(3);
- ASSERT_EQ(window.vertical(), 2);
- ASSERT_EQ(window.horizontal(), 3);
+ ASSERT_EQ(2, window.vertical());
+ ASSERT_EQ(3, window.horizontal());
}
diff --git a/compiler/loco/src/Service/CanonicalShapeInferenceRule.cpp b/compiler/loco/src/Service/CanonicalShapeInferenceRule.cpp
index 59340076d..6d5adc525 100644
--- a/compiler/loco/src/Service/CanonicalShapeInferenceRule.cpp
+++ b/compiler/loco/src/Service/CanonicalShapeInferenceRule.cpp
@@ -363,6 +363,13 @@ public:
return loco::NodeShape{node->encoder()->shape(input_tensor_shape)};
}
+ // CASE: DepthwiseFilterDecode
+ loco::NodeShape visit(const loco::DepthwiseFilterDecode *node) final
+ {
+ auto input_dw_filter_shape = node_shape(node->input()).as<loco::DepthwiseFilterShape>();
+ return loco::NodeShape{node->decoder()->shape(input_dw_filter_shape)};
+ }
+
// CASE: EltwiseAdd
loco::NodeShape visit(const loco::EltwiseAdd *node) final
{
@@ -375,6 +382,12 @@ public:
return eltwise_binary_node_shape(node);
}
+ // CASE: EltwiseMax
+ loco::NodeShape visit(const loco::EltwiseMax *node) final
+ {
+ return eltwise_binary_node_shape(node);
+ }
+
// CASE: EltwiseMul
loco::NodeShape visit(const loco::EltwiseMul *node) final
{
@@ -418,6 +431,13 @@ public:
return loco::NodeShape{node->encoder()->shape(input_node_shape.as<loco::TensorShape>())};
}
+ // CASE: FilterDecode
+ loco::NodeShape visit(const loco::FilterDecode *node) final
+ {
+ auto input_filter_shape = node_shape(node->input()).as<loco::FilterShape>();
+ return loco::NodeShape{node->decoder()->shape(input_filter_shape)};
+ }
+
// CASE: FilterEncode
loco::NodeShape visit(const loco::FilterEncode *node) final
{
@@ -439,6 +459,39 @@ public:
return loco::NodeShape{tensor_shape};
}
+ // CASE: MatMul
+ loco::NodeShape visit(const loco::MatMul *node) final
+ {
+ assert(shape_known(node->lhs()));
+ assert(shape_known(node->rhs()));
+ auto const lhs_shape = node_shape(node->lhs()).as<loco::MatrixShape>();
+ auto const rhs_shape = node_shape(node->rhs()).as<loco::MatrixShape>();
+
+ loco::MatrixShape out_shape;
+
+ // Checking shape capability for multiplication
+ assert(lhs_shape.width() == rhs_shape.height());
+
+ out_shape.height() = lhs_shape.height();
+ out_shape.width() = rhs_shape.width();
+
+ return out_shape;
+ }
+
+ // CASE: MatrixDecode
+ loco::NodeShape visit(const loco::MatrixDecode *node) final
+ {
+ auto input_node_shape = node_shape(node->input());
+ return loco::NodeShape{node->decoder()->shape(input_node_shape.as<loco::MatrixShape>())};
+ }
+
+ // CASE: MatrixEncode
+ loco::NodeShape visit(const loco::MatrixEncode *node) final
+ {
+ auto input_node_shape = node_shape(node->input());
+ return loco::NodeShape{node->encoder()->shape(input_node_shape.as<loco::TensorShape>())};
+ }
+
// CASE: MaxPool2D
loco::NodeShape visit(const loco::MaxPool2D *node) final
{
@@ -547,8 +600,85 @@ public:
return loco::NodeShape{tensor_shape};
}
+ // CASE: TensorReduce
+ loco::NodeShape visit(const loco::TensorReduce *node) final
+ {
+ auto tensor_shape = node_shape(node->input()).as<loco::TensorShape>();
+ auto const tensor_rank = tensor_shape.rank();
+
+ for (uint32_t d = 0; d < tensor_rank; ++d)
+ if (node->axes()->defined(d))
+ tensor_shape.dim(d) = 1;
+
+ return loco::NodeShape{tensor_shape};
+ }
+
// CASE: TensorSoftmax
loco::NodeShape visit(const loco::TensorSoftmax *node) final { return node_shape(node->input()); }
+
+ // CASE: TensorTranspose
+ loco::NodeShape visit(const loco::TensorTranspose *node) final
+ {
+ loco::TensorShape output_shape;
+
+ auto input_shape = node_shape(node->input()).as<loco::TensorShape>();
+ assert(input_shape.rank() == node->perm()->size());
+
+ output_shape.rank(input_shape.rank());
+
+ for (uint32_t output_axis = 0; output_axis < output_shape.rank(); output_axis++)
+ {
+ auto new_dim = input_shape.dim(node->perm()->axis(output_axis));
+ output_shape.dim(output_axis) = new_dim;
+ }
+
+ return loco::NodeShape(output_shape);
+ }
+
+ // CASE: TransposedConv2D
+ loco::NodeShape visit(const loco::TransposedConv2D *node) final
+ {
+ auto filter_shape = node_shape(node->ker()).as<loco::FilterShape>();
+ auto filter_window = window_of(filter_shape);
+
+ PlaneInference<Direction::Backward> infer_plane_shape;
+
+ infer_plane_shape.pad(node->pad());
+ infer_plane_shape.window(&filter_window);
+ infer_plane_shape.stride(node->stride());
+
+ auto input_feature_shape = node_shape(node->ifm()).as<loco::FeatureShape>();
+ auto input_plane_shape = make_plane_shape(input_feature_shape);
+ auto output_plane_shape = infer_plane_shape(input_plane_shape);
+
+ loco::FeatureShape output_feature_shape;
+
+ // "COUNT" does not change
+ output_feature_shape.count() = input_feature_shape.count();
+ // Output "DEPTH" depends on count of filters
+ output_feature_shape.depth() = filter_shape.count();
+ // Update the height/width of output_feature_shape with that of output_plane_shape
+ update(output_feature_shape).with(output_plane_shape);
+
+ return loco::NodeShape{output_feature_shape};
+ }
+
+ // CASE: TensorConstantPad
+ loco::NodeShape visit(const loco::TensorConstantPad *node) final
+ {
+ auto const tensor_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ auto padding = node->padding();
+
+ loco::TensorShape out_shape;
+ out_shape.rank(tensor_shape.rank());
+ for (uint32_t axis = 0; axis < out_shape.rank(); ++axis)
+ {
+ out_shape.dim(axis) =
+ tensor_shape.dim(axis).value() + padding->front(axis) + padding->back(axis);
+ }
+
+ return loco::NodeShape{out_shape};
+ }
};
} // namespace
@@ -587,7 +717,7 @@ public:
void fail(void) final
{
- // Notify failrue
+ // Notify failure
_status = Fail;
}
@@ -636,7 +766,7 @@ void CanonicalShapeInferenceRule::infer(const Context *ctx, const Node *node, Si
assert(dynamic_cast<const loco::CanonicalNode *>(node) != nullptr);
ForwardShapeInferenceAlgorithm alg{ctx};
- auto shape = dynamic_cast<const loco::CanonicalNode *>(node)->accept(&alg);
+ auto shape = loco::must_cast<const loco::CanonicalNode *>(node)->accept(&alg);
sink->okay(shape);
}
diff --git a/compiler/loco/src/Service/CanonicalShapeInferenceRule.test.cpp b/compiler/loco/src/Service/CanonicalShapeInferenceRule.test.cpp
index 62fa6786a..e88872b5d 100644
--- a/compiler/loco/src/Service/CanonicalShapeInferenceRule.test.cpp
+++ b/compiler/loco/src/Service/CanonicalShapeInferenceRule.test.cpp
@@ -35,12 +35,12 @@ TEST(CanonicalShapeInferenceRuleTest, minimal)
// Verify!
ASSERT_TRUE(loco::shape_known(testcase.push_node));
- ASSERT_EQ(loco::shape_get(testcase.push_node).domain(), loco::Domain::Tensor);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().rank(), 4);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(0), 1);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(1), 2);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(2), 3);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(3), 4);
+ ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(testcase.push_node).domain());
+ ASSERT_EQ(4, loco::shape_get(testcase.push_node).as<loco::TensorShape>().rank());
+ ASSERT_EQ(1, loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(0));
+ ASSERT_EQ(2, loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(1));
+ ASSERT_EQ(3, loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(2));
+ ASSERT_EQ(4, loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(3));
}
TEST(CanonicalShapeInferenceRuleTest, const_gen)
@@ -58,10 +58,10 @@ TEST(CanonicalShapeInferenceRuleTest, const_gen)
// Verify!
ASSERT_TRUE(loco::shape_known(testcase.push_node));
- ASSERT_EQ(loco::shape_get(testcase.push_node).domain(), loco::Domain::Tensor);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().rank(), 2);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(0), 1);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(1), 2);
+ ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(testcase.push_node).domain());
+ ASSERT_EQ(2, loco::shape_get(testcase.push_node).as<loco::TensorShape>().rank());
+ ASSERT_EQ(1, loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(0));
+ ASSERT_EQ(2, loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(1));
}
TEST(CanonicalShapeInferenceRuleTest, relu)
@@ -78,12 +78,12 @@ TEST(CanonicalShapeInferenceRuleTest, relu)
// Verify!
ASSERT_TRUE(loco::shape_known(testcase.push_node));
- ASSERT_EQ(loco::shape_get(testcase.push_node).domain(), loco::Domain::Tensor);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().rank(), 4);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(0), 1);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(1), 2);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(2), 3);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(3), 4);
+ ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(testcase.push_node).domain());
+ ASSERT_EQ(4, loco::shape_get(testcase.push_node).as<loco::TensorShape>().rank());
+ ASSERT_EQ(1, loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(0));
+ ASSERT_EQ(2, loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(1));
+ ASSERT_EQ(3, loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(2));
+ ASSERT_EQ(4, loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(3));
}
TEST(CanonicalShapeInferenceRuleTest, feature_codec)
@@ -100,15 +100,15 @@ TEST(CanonicalShapeInferenceRuleTest, feature_codec)
// Verify!
ASSERT_TRUE(loco::shape_known(testcase.encode_node));
- ASSERT_EQ(loco::shape_get(testcase.encode_node).domain(), loco::Domain::Feature);
+ ASSERT_EQ(loco::Domain::Feature, loco::shape_get(testcase.encode_node).domain());
ASSERT_TRUE(loco::shape_known(testcase.decode_node));
- ASSERT_EQ(loco::shape_get(testcase.decode_node).domain(), loco::Domain::Tensor);
- ASSERT_EQ(loco::shape_get(testcase.decode_node).as<loco::TensorShape>().rank(), 4);
- ASSERT_EQ(loco::shape_get(testcase.decode_node).as<loco::TensorShape>().dim(0), 1);
- ASSERT_EQ(loco::shape_get(testcase.decode_node).as<loco::TensorShape>().dim(1), 2);
- ASSERT_EQ(loco::shape_get(testcase.decode_node).as<loco::TensorShape>().dim(2), 3);
- ASSERT_EQ(loco::shape_get(testcase.decode_node).as<loco::TensorShape>().dim(3), 4);
+ ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(testcase.decode_node).domain());
+ ASSERT_EQ(4, loco::shape_get(testcase.decode_node).as<loco::TensorShape>().rank());
+ ASSERT_EQ(1, loco::shape_get(testcase.decode_node).as<loco::TensorShape>().dim(0));
+ ASSERT_EQ(2, loco::shape_get(testcase.decode_node).as<loco::TensorShape>().dim(1));
+ ASSERT_EQ(3, loco::shape_get(testcase.decode_node).as<loco::TensorShape>().dim(2));
+ ASSERT_EQ(4, loco::shape_get(testcase.decode_node).as<loco::TensorShape>().dim(3));
}
TEST(CanonicalShapeInferenceRuleTest, avgpool2d)
@@ -141,11 +141,11 @@ TEST(CanonicalShapeInferenceRuleTest, avgpool2d)
//
// NOTE AvgPool2D testcase assumes NHWC layout
ASSERT_TRUE(loco::shape_known(testcase.avgpool2d_node));
- ASSERT_EQ(loco::shape_get(testcase.avgpool2d_node).domain(), loco::Domain::Feature);
- ASSERT_EQ(loco::shape_get(testcase.avgpool2d_node).as<FeatureShape>().count(), 1);
- ASSERT_EQ(loco::shape_get(testcase.avgpool2d_node).as<FeatureShape>().depth(), 3);
- ASSERT_EQ(loco::shape_get(testcase.avgpool2d_node).as<FeatureShape>().height(), 4);
- ASSERT_EQ(loco::shape_get(testcase.avgpool2d_node).as<FeatureShape>().width(), 2);
+ ASSERT_EQ(loco::Domain::Feature, loco::shape_get(testcase.avgpool2d_node).domain());
+ ASSERT_EQ(1, loco::shape_get(testcase.avgpool2d_node).as<FeatureShape>().count());
+ ASSERT_EQ(3, loco::shape_get(testcase.avgpool2d_node).as<FeatureShape>().depth());
+ ASSERT_EQ(4, loco::shape_get(testcase.avgpool2d_node).as<FeatureShape>().height());
+ ASSERT_EQ(2, loco::shape_get(testcase.avgpool2d_node).as<FeatureShape>().width());
}
TEST(CanonicalShapeInferenceRuleTest, depthwiseconv2d)
@@ -172,11 +172,45 @@ TEST(CanonicalShapeInferenceRuleTest, depthwiseconv2d)
//
// NOTE DepthwiseConv2D testcase assumes NHWC layout
ASSERT_TRUE(loco::shape_known(testcase.depthwiseconv2d_node));
- ASSERT_EQ(loco::shape_get(testcase.depthwiseconv2d_node).domain(), loco::Domain::Feature);
- ASSERT_EQ(loco::shape_get(testcase.depthwiseconv2d_node).as<FeatureShape>().count(), 1);
- ASSERT_EQ(loco::shape_get(testcase.depthwiseconv2d_node).as<FeatureShape>().depth(), 6);
- ASSERT_EQ(loco::shape_get(testcase.depthwiseconv2d_node).as<FeatureShape>().height(), 3);
- ASSERT_EQ(loco::shape_get(testcase.depthwiseconv2d_node).as<FeatureShape>().width(), 3);
+ ASSERT_EQ(loco::Domain::Feature, loco::shape_get(testcase.depthwiseconv2d_node).domain());
+ ASSERT_EQ(1, loco::shape_get(testcase.depthwiseconv2d_node).as<FeatureShape>().count());
+ ASSERT_EQ(6, loco::shape_get(testcase.depthwiseconv2d_node).as<FeatureShape>().depth());
+ ASSERT_EQ(3, loco::shape_get(testcase.depthwiseconv2d_node).as<FeatureShape>().height());
+ ASSERT_EQ(3, loco::shape_get(testcase.depthwiseconv2d_node).as<FeatureShape>().width());
+}
+
+TEST(CanonicalShapeInferenceRuleTest, transposedconv2d)
+{
+ using namespace loco;
+
+ // Create a sample network
+ GraphTestcase<GraphCode::TransposedConv2D> testcase;
+
+ testcase.pull_node->shape({1, 270, 480, 24}); // NHWC
+
+ testcase.const_node->dtype(loco::DataType::FLOAT32);
+ testcase.const_node->shape({3, 3, 24, 12}); // HWCN (or HWIO)
+
+ testcase.tr_conv2d_node->stride()->vertical(2);
+ testcase.tr_conv2d_node->stride()->horizontal(2);
+
+ testcase.tr_conv2d_node->pad()->top(0);
+ testcase.tr_conv2d_node->pad()->bottom(1);
+ testcase.tr_conv2d_node->pad()->left(0);
+ testcase.tr_conv2d_node->pad()->right(1);
+
+ // Run Inference
+ loco::CanonicalShapeInferenceRule rule;
+
+ loco::apply(&rule).to(testcase.graph());
+
+ // Verify!
+ ASSERT_TRUE(loco::shape_known(testcase.tr_conv2d_node));
+ ASSERT_EQ(loco::Domain::Feature, loco::shape_get(testcase.tr_conv2d_node).domain());
+ ASSERT_EQ(1, loco::shape_get(testcase.tr_conv2d_node).as<FeatureShape>().count());
+ ASSERT_EQ(540, loco::shape_get(testcase.tr_conv2d_node).as<FeatureShape>().height());
+ ASSERT_EQ(960, loco::shape_get(testcase.tr_conv2d_node).as<FeatureShape>().width());
+ ASSERT_EQ(12, loco::shape_get(testcase.tr_conv2d_node).as<FeatureShape>().depth());
}
TEST(CanonicalShapeInferenceRuleTest, maxpool2d)
@@ -209,11 +243,11 @@ TEST(CanonicalShapeInferenceRuleTest, maxpool2d)
//
// NOTE MaxPool2D testcase assumes NHWC layout
ASSERT_TRUE(loco::shape_known(testcase.maxpool2d_node));
- ASSERT_EQ(loco::shape_get(testcase.maxpool2d_node).domain(), loco::Domain::Feature);
- ASSERT_EQ(loco::shape_get(testcase.maxpool2d_node).as<FeatureShape>().count(), 1);
- ASSERT_EQ(loco::shape_get(testcase.maxpool2d_node).as<FeatureShape>().depth(), 3);
- ASSERT_EQ(loco::shape_get(testcase.maxpool2d_node).as<FeatureShape>().height(), 4);
- ASSERT_EQ(loco::shape_get(testcase.maxpool2d_node).as<FeatureShape>().width(), 2);
+ ASSERT_EQ(loco::Domain::Feature, loco::shape_get(testcase.maxpool2d_node).domain());
+ ASSERT_EQ(1, loco::shape_get(testcase.maxpool2d_node).as<FeatureShape>().count());
+ ASSERT_EQ(3, loco::shape_get(testcase.maxpool2d_node).as<FeatureShape>().depth());
+ ASSERT_EQ(4, loco::shape_get(testcase.maxpool2d_node).as<FeatureShape>().height());
+ ASSERT_EQ(2, loco::shape_get(testcase.maxpool2d_node).as<FeatureShape>().width());
}
TEST(CanonicalShapeInferenceRuleTest, tensor_concat)
@@ -234,11 +268,11 @@ TEST(CanonicalShapeInferenceRuleTest, tensor_concat)
// Verify!
ASSERT_TRUE(loco::shape_known(testcase.concat_node));
- ASSERT_EQ(loco::shape_get(testcase.concat_node).domain(), loco::Domain::Tensor);
- ASSERT_EQ(loco::shape_get(testcase.concat_node).as<TensorShape>().rank(), 3);
- ASSERT_EQ(loco::shape_get(testcase.concat_node).as<TensorShape>().dim(0), 1);
- ASSERT_EQ(loco::shape_get(testcase.concat_node).as<TensorShape>().dim(1), 6);
- ASSERT_EQ(loco::shape_get(testcase.concat_node).as<TensorShape>().dim(2), 3);
+ ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(testcase.concat_node).domain());
+ ASSERT_EQ(3, loco::shape_get(testcase.concat_node).as<TensorShape>().rank());
+ ASSERT_EQ(1, loco::shape_get(testcase.concat_node).as<TensorShape>().dim(0));
+ ASSERT_EQ(6, loco::shape_get(testcase.concat_node).as<TensorShape>().dim(1));
+ ASSERT_EQ(3, loco::shape_get(testcase.concat_node).as<TensorShape>().dim(2));
}
TEST(CanonicalShapeInferenceRuleTest, fixed_reshape)
@@ -256,10 +290,10 @@ TEST(CanonicalShapeInferenceRuleTest, fixed_reshape)
// Verify!
ASSERT_TRUE(loco::shape_known(testcase.push_node));
- ASSERT_EQ(loco::shape_get(testcase.push_node).domain(), loco::Domain::Tensor);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().rank(), 2);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(0), 4);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(1), 9);
+ ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(testcase.push_node).domain());
+ ASSERT_EQ(2, loco::shape_get(testcase.push_node).as<loco::TensorShape>().rank());
+ ASSERT_EQ(4, loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(0));
+ ASSERT_EQ(9, loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(1));
}
TEST(CanonicalShapeInferenceRuleTest, tensor_broadcast)
@@ -276,10 +310,38 @@ TEST(CanonicalShapeInferenceRuleTest, tensor_broadcast)
// Verify!
ASSERT_TRUE(loco::shape_known(testcase.push_node));
- ASSERT_EQ(loco::shape_get(testcase.push_node).domain(), loco::Domain::Tensor);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().rank(), 2);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(0), 4);
- ASSERT_EQ(loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(1), 2);
+ ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(testcase.push_node).domain());
+ ASSERT_EQ(2, loco::shape_get(testcase.push_node).as<loco::TensorShape>().rank());
+ ASSERT_EQ(4, loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(0));
+ ASSERT_EQ(2, loco::shape_get(testcase.push_node).as<loco::TensorShape>().dim(1));
+}
+
+TEST(CanonicalShapeInferenceRuleTest, tensor_transpose)
+{
+ // Create a sample network
+ GraphTestcase<GraphCode::TensorTranspose> tc;
+
+ tc.pull_node->shape({10, 20, 30, 40});
+
+ tc.transpose_node->perm()->size(4);
+ tc.transpose_node->perm()->axis(0) = 2;
+ tc.transpose_node->perm()->axis(1) = 3;
+ tc.transpose_node->perm()->axis(2) = 0;
+ tc.transpose_node->perm()->axis(3) = 1;
+
+ // Run Inference
+ loco::CanonicalShapeInferenceRule rule;
+
+ loco::apply(&rule).to(tc.graph());
+
+ // Verify!
+ ASSERT_TRUE(loco::shape_known(tc.push_node));
+ ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(tc.push_node).domain());
+ ASSERT_EQ(4, loco::shape_get(tc.push_node).as<loco::TensorShape>().rank());
+ ASSERT_EQ(30, loco::shape_get(tc.push_node).as<loco::TensorShape>().dim(0));
+ ASSERT_EQ(40, loco::shape_get(tc.push_node).as<loco::TensorShape>().dim(1));
+ ASSERT_EQ(10, loco::shape_get(tc.push_node).as<loco::TensorShape>().dim(2));
+ ASSERT_EQ(20, loco::shape_get(tc.push_node).as<loco::TensorShape>().dim(3));
}
namespace
@@ -331,8 +393,8 @@ TEST(CanonicalShapeInferenceRuleTest, infer_v2)
rule.infer(&ctx, relu_2, &sink);
- ASSERT_EQ(sink.shape.domain(), loco::Domain::Tensor);
- ASSERT_EQ(sink.shape.as<loco::TensorShape>().rank(), 2);
- ASSERT_EQ(sink.shape.as<loco::TensorShape>().dim(0), 4);
- ASSERT_EQ(sink.shape.as<loco::TensorShape>().dim(1), 5);
+ ASSERT_EQ(loco::Domain::Tensor, sink.shape.domain());
+ ASSERT_EQ(2, sink.shape.as<loco::TensorShape>().rank());
+ ASSERT_EQ(4, sink.shape.as<loco::TensorShape>().dim(0));
+ ASSERT_EQ(5, sink.shape.as<loco::TensorShape>().dim(1));
}
diff --git a/compiler/loco/src/Service/GraphBuilder.test.cpp b/compiler/loco/src/Service/GraphBuilder.test.cpp
index 7b2ea5198..812964870 100644
--- a/compiler/loco/src/Service/GraphBuilder.test.cpp
+++ b/compiler/loco/src/Service/GraphBuilder.test.cpp
@@ -41,7 +41,7 @@ TEST(GraphBuilderTest, Usecase_000)
auto node = gbuilder->pop();
- ASSERT_EQ(g->nodes()->size(), 1);
- ASSERT_EQ(node->dialect(), loco::CanonicalDialect::get());
- ASSERT_EQ(node->opnum(), static_cast<uint32_t>(loco::CanonicalOpcode::ConstGen));
+ ASSERT_EQ(1, g->nodes()->size());
+ ASSERT_EQ(loco::CanonicalDialect::get(), node->dialect());
+ ASSERT_EQ(static_cast<uint32_t>(loco::CanonicalOpcode::ConstGen), node->opnum());
}
diff --git a/compiler/loco/src/Service/GraphTestcase.h b/compiler/loco/src/Service/GraphTestcase.h
index 0c8fd45be..27b011f8d 100644
--- a/compiler/loco/src/Service/GraphTestcase.h
+++ b/compiler/loco/src/Service/GraphTestcase.h
@@ -36,6 +36,7 @@ enum class GraphCode
MaxPool2D,
TensorBroadcast,
TensorConcat,
+ TensorTranspose,
FixedReshape,
};
@@ -58,7 +59,7 @@ template <> loco::Permutation<loco::Domain::Feature> make_NHWC_perm(void)
template <loco::Domain D> loco::Permutation<D> make_HWCN_perm(void);
-// @note Also known as HWIO permutation
+/// @note Also known as HWIO permutation
template <> loco::Permutation<loco::Domain::Filter> make_HWCN_perm(void)
{
loco::Permutation<loco::Domain::Filter> perm;
@@ -493,4 +494,48 @@ private:
std::unique_ptr<loco::Graph> _graph;
};
+template <> class GraphTestcase<GraphCode::TensorTranspose> final
+{
+public:
+ GraphTestcase()
+ {
+ using namespace loco;
+
+ // Create a sample network
+ _graph = make_graph();
+
+ // Create Graph Input/Output
+ auto graph_input = _graph->inputs()->create();
+ auto graph_output = _graph->outputs()->create();
+
+ graph_input->name("input");
+ graph_output->name("output");
+
+ // Create and connect nodes
+ pull_node = _graph->nodes()->create<Pull>();
+ pull_node->index(0);
+
+ transpose_node = _graph->nodes()->create<TensorTranspose>();
+ transpose_node->input(pull_node);
+
+ push_node = _graph->nodes()->create<loco::Push>();
+ push_node->index(0);
+ push_node->from(transpose_node);
+
+ // Create a link between input/output and corresponding nodes
+ loco::link(graph_input, pull_node);
+ loco::link(graph_output, push_node);
+ }
+
+public:
+ loco::Graph *graph() { return _graph.get(); }
+
+ loco::Pull *pull_node = nullptr;
+ loco::TensorTranspose *transpose_node = nullptr;
+ loco::Push *push_node = nullptr;
+
+private:
+ std::unique_ptr<loco::Graph> _graph;
+};
+
#endif // __GRAPH_TESTCASE_H__
diff --git a/compiler/loco/src/Service/MultiDialectShapeInferenceRule.test.cpp b/compiler/loco/src/Service/MultiDialectShapeInferenceRule.test.cpp
index ffa9ee5ca..3d5a11ae4 100644
--- a/compiler/loco/src/Service/MultiDialectShapeInferenceRule.test.cpp
+++ b/compiler/loco/src/Service/MultiDialectShapeInferenceRule.test.cpp
@@ -120,15 +120,15 @@ TEST(MultiDialectShapeInferenceRuleTest, test1)
// Verify!
ASSERT_TRUE(loco::shape_known(t23_node));
auto t23_shape = loco::shape_get(t23_node);
- ASSERT_EQ(t23_shape.domain(), loco::Domain::Tensor);
- ASSERT_EQ(t23_shape.as<loco::TensorShape>().rank(), 2);
- ASSERT_EQ(t23_shape.as<loco::TensorShape>().dim(0), 2);
- ASSERT_EQ(t23_shape.as<loco::TensorShape>().dim(1), 3);
+ ASSERT_EQ(loco::Domain::Tensor, t23_shape.domain());
+ ASSERT_EQ(2, t23_shape.as<loco::TensorShape>().rank());
+ ASSERT_EQ(2, t23_shape.as<loco::TensorShape>().dim(0));
+ ASSERT_EQ(3, t23_shape.as<loco::TensorShape>().dim(1));
ASSERT_TRUE(loco::shape_known(t45_node));
auto t45_shape = loco::shape_get(t45_node);
- ASSERT_EQ(t45_shape.domain(), loco::Domain::Tensor);
- ASSERT_EQ(t45_shape.as<loco::TensorShape>().rank(), 2);
- ASSERT_EQ(t45_shape.as<loco::TensorShape>().dim(0), 4);
- ASSERT_EQ(t45_shape.as<loco::TensorShape>().dim(1), 5);
+ ASSERT_EQ(loco::Domain::Tensor, t45_shape.domain());
+ ASSERT_EQ(2, t45_shape.as<loco::TensorShape>().rank());
+ ASSERT_EQ(4, t45_shape.as<loco::TensorShape>().dim(0));
+ ASSERT_EQ(5, t45_shape.as<loco::TensorShape>().dim(1));
}
diff --git a/compiler/loco/src/Service/ShapeInference.test.cpp b/compiler/loco/src/Service/ShapeInference.test.cpp
index e10b98844..20857034e 100644
--- a/compiler/loco/src/Service/ShapeInference.test.cpp
+++ b/compiler/loco/src/Service/ShapeInference.test.cpp
@@ -71,16 +71,16 @@ TEST(ShapeInferenceTest, framework)
loco::apply(&rule).to(testcase.graph());
// Framework SHOULD visit all the nodes
- ASSERT_EQ(nodes.size(), 2);
+ ASSERT_EQ(2, nodes.size());
// Framework SHOULD visit "pull" before "push"
- ASSERT_EQ(nodes.at(0), testcase.pull_node);
- ASSERT_EQ(nodes.at(1), testcase.push_node);
+ ASSERT_EQ(testcase.pull_node, nodes.at(0));
+ ASSERT_EQ(testcase.push_node, nodes.at(1));
// Framework SHOULD make an annotation if "rule" returns TRUE
ASSERT_TRUE(loco::shape_known(testcase.pull_node));
- ASSERT_EQ(loco::shape_get(testcase.pull_node).domain(), loco::Domain::Tensor);
- ASSERT_EQ(loco::shape_get(testcase.pull_node).as<loco::TensorShape>().rank(), 1);
- ASSERT_EQ(loco::shape_get(testcase.pull_node).as<loco::TensorShape>().dim(0), 4);
+ ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(testcase.pull_node).domain());
+ ASSERT_EQ(1, loco::shape_get(testcase.pull_node).as<loco::TensorShape>().rank());
+ ASSERT_EQ(4, loco::shape_get(testcase.pull_node).as<loco::TensorShape>().dim(0));
// Framework SHOULD NOT make any annotation if "rule" returns FALSE
ASSERT_FALSE(loco::shape_known(testcase.push_node));
diff --git a/compiler/loco/src/Service/TypeInference.cpp b/compiler/loco/src/Service/TypeInference.cpp
index 92cdbd51a..27d7d9a29 100644
--- a/compiler/loco/src/Service/TypeInference.cpp
+++ b/compiler/loco/src/Service/TypeInference.cpp
@@ -126,8 +126,13 @@ struct CanonicalTypeForwardAlgorithm final : public loco::CanonicalNodeVisitor<l
{
return loco::dtype_get(node->input());
}
+ loco::DataType visit(const loco::DepthwiseFilterDecode *node)
+ {
+ return loco::dtype_get(node->input());
+ }
loco::DataType visit(const loco::EltwiseAdd *node) { return loco::dtype_get(node->lhs()); }
loco::DataType visit(const loco::EltwiseDiv *node) { return loco::dtype_get(node->lhs()); }
+ loco::DataType visit(const loco::EltwiseMax *node) { return loco::dtype_get(node->lhs()); }
loco::DataType visit(const loco::EltwiseMul *node) { return loco::dtype_get(node->lhs()); }
loco::DataType visit(const loco::EltwiseSqrt *node) { return loco::dtype_get(node->input()); }
loco::DataType visit(const loco::EltwiseSub *node) { return loco::dtype_get(node->lhs()); }
@@ -135,8 +140,12 @@ struct CanonicalTypeForwardAlgorithm final : public loco::CanonicalNodeVisitor<l
loco::DataType visit(const loco::FeatureBiasAdd *node) { return loco::dtype_get(node->value()); }
loco::DataType visit(const loco::FeatureDecode *node) { return loco::dtype_get(node->input()); }
loco::DataType visit(const loco::FeatureEncode *node) { return loco::dtype_get(node->input()); }
+ loco::DataType visit(const loco::FilterDecode *node) { return loco::dtype_get(node->input()); }
loco::DataType visit(const loco::FilterEncode *node) { return loco::dtype_get(node->input()); }
loco::DataType visit(const loco::FixedReshape *node) { return loco::dtype_get(node->input()); }
+ loco::DataType visit(const loco::MatrixDecode *node) { return loco::dtype_get(node->input()); }
+ loco::DataType visit(const loco::MatrixEncode *node) { return loco::dtype_get(node->input()); }
+ loco::DataType visit(const loco::MatMul *node) { return loco::dtype_get(node->lhs()); }
loco::DataType visit(const loco::MaxPool2D *node) { return loco::dtype_get(node->ifm()); }
loco::DataType visit(const loco::Push *node) { return loco::dtype_get(node->from()); }
loco::DataType visit(const loco::Pull *node) { return node->dtype(); }
@@ -144,9 +153,15 @@ struct CanonicalTypeForwardAlgorithm final : public loco::CanonicalNodeVisitor<l
loco::DataType visit(const loco::ReLU6 *node) { return loco::dtype_get(node->input()); }
loco::DataType visit(const loco::Tanh *node) { return loco::dtype_get(node->input()); }
loco::DataType visit(const loco::TensorConcat *node) { return loco::dtype_get(node->lhs()); }
+ loco::DataType visit(const loco::TensorConstantPad *node)
+ {
+ return loco::dtype_get(node->input());
+ }
loco::DataType visit(const loco::TensorBiasAdd *node) { return loco::dtype_get(node->value()); }
loco::DataType visit(const loco::TensorBroadcast *node) { return loco::dtype_get(node->input()); }
+ loco::DataType visit(const loco::TensorReduce *node) { return loco::dtype_get(node->input()); }
loco::DataType visit(const loco::TensorSoftmax *node) { return loco::dtype_get(node->input()); }
+ loco::DataType visit(const loco::TensorTranspose *node) { return loco::dtype_get(node->input()); }
loco::DataType visit(const loco::TransposedConv2D *node) { return loco::dtype_get(node->ifm()); }
};
@@ -167,7 +182,7 @@ bool CanonicalTypeInferenceRule::infer(const Node *node, DataType &dtype) const
assert(dynamic_cast<const loco::CanonicalNode *>(node) != nullptr);
CanonicalTypeForwardAlgorithm alg;
- dtype = dynamic_cast<const loco::CanonicalNode *>(node)->accept(&alg);
+ dtype = loco::must_cast<const loco::CanonicalNode *>(node)->accept(&alg);
return true;
}
diff --git a/compiler/loco/src/Service/TypeInference.test.cpp b/compiler/loco/src/Service/TypeInference.test.cpp
index 4660401db..13bcfa52b 100644
--- a/compiler/loco/src/Service/TypeInference.test.cpp
+++ b/compiler/loco/src/Service/TypeInference.test.cpp
@@ -88,14 +88,14 @@ TEST(TypeInferenceTest, framework)
loco::apply(&rule).to(g.get());
- ASSERT_EQ(nodes.size(), 2); // Framework SHOULD visit all the nodes
- ASSERT_EQ(nodes.at(0), pull_node); // Framework SHOULD visit "pull" before "push"
- ASSERT_EQ(nodes.at(1), push_node);
+ ASSERT_EQ(2, nodes.size()); // Framework SHOULD visit all the nodes
+ ASSERT_EQ(pull_node, nodes.at(0)); // Framework SHOULD visit "pull" before "push"
+ ASSERT_EQ(push_node, nodes.at(1));
// Framework SHOULD NOT make any annotation if "rule" returns FALSE
ASSERT_TRUE(loco::dtype_known(pull_node));
// Framework SHOULD make an annotation if "rule" returns TRUE
- ASSERT_EQ(loco::dtype_get(pull_node), loco::DataType::U8);
+ ASSERT_EQ(loco::DataType::U8, loco::dtype_get(pull_node));
ASSERT_FALSE(loco::dtype_known(push_node));
}
@@ -129,7 +129,7 @@ TEST(CanonicalTypeInferenceRuleTest, minimal)
// Verify!
ASSERT_TRUE(loco::dtype_known(push_node));
- ASSERT_EQ(loco::dtype_get(push_node), loco::DataType::U8);
+ ASSERT_EQ(loco::DataType::U8, loco::dtype_get(push_node));
}
TEST(CanonicalTypeInferenceRuleTest, relu6)
@@ -166,7 +166,7 @@ TEST(CanonicalTypeInferenceRuleTest, relu6)
// Verify!
ASSERT_TRUE(loco::dtype_known(relu6_node));
- ASSERT_EQ(loco::dtype_get(relu6_node), loco::DataType::FLOAT32);
+ ASSERT_EQ(loco::DataType::FLOAT32, loco::dtype_get(relu6_node));
}
TEST(CanonicalTypeInferenceRuleTest, tensor_broadcast)
@@ -183,7 +183,7 @@ TEST(CanonicalTypeInferenceRuleTest, tensor_broadcast)
// Verify!
ASSERT_TRUE(loco::dtype_known(testcase.push_node));
- ASSERT_EQ(loco::dtype_get(testcase.push_node), loco::DataType::U8);
+ ASSERT_EQ(loco::DataType::U8, loco::dtype_get(testcase.push_node));
}
// mockup for MultiDialectTypeInferenceRule
@@ -275,8 +275,8 @@ TEST(MultiDialectTypeInferenceRuleTest, test1)
// Verify!
ASSERT_TRUE(loco::dtype_known(s8_node));
- ASSERT_EQ(loco::dtype_get(s8_node), loco::DataType::S8);
+ ASSERT_EQ(loco::DataType::S8, loco::dtype_get(s8_node));
ASSERT_TRUE(loco::dtype_known(u8_node));
- ASSERT_EQ(loco::dtype_get(u8_node), loco::DataType::U8);
+ ASSERT_EQ(loco::DataType::U8, loco::dtype_get(u8_node));
}
diff --git a/compiler/loco/src/loco.test.cpp b/compiler/loco/src/loco.test.cpp
index 4c4f51aa5..e5668b6d3 100644
--- a/compiler/loco/src/loco.test.cpp
+++ b/compiler/loco/src/loco.test.cpp
@@ -58,11 +58,11 @@ TEST(LOCO, identity_network)
loco::link(graph_output, push_node);
// loco::link SHOULD update "index"
- ASSERT_EQ(pull_node->index(), 0);
- ASSERT_EQ(graph_input->dtype(), loco::DataType::FLOAT32);
+ ASSERT_EQ(0, pull_node->index());
+ ASSERT_EQ(loco::DataType::FLOAT32, graph_input->dtype());
// loco::link SHOULD update "index"
- ASSERT_EQ(push_node->index(), 0);
+ ASSERT_EQ(0, push_node->index());
}
#if 0
@@ -99,10 +99,10 @@ TEST(LOCO, identity_network_V2)
push_node->index(0);
push_node->from(pull_node);
- ASSERT_EQ(pull_node->dtype(), loco::DataType::FLOAT32);
+ ASSERT_EQ(loco::DataType::FLOAT32, pull_node->dtype());
// TODO Check Shape of pull_node
// TODO Check Shape of push_node
- ASSERT_EQ(loco::pull_node(g.get(), 0), pull_node);
- ASSERT_EQ(loco::push_node(g.get(), 0), push_node);
+ ASSERT_EQ(pull_node, loco::pull_node(g.get(), 0));
+ ASSERT_EQ(push_node, loco::push_node(g.get(), 0));
}
diff --git a/compiler/locoex-customop/CMakeLists.txt b/compiler/locoex-customop/CMakeLists.txt
index b42d36764..df1e01526 100644
--- a/compiler/locoex-customop/CMakeLists.txt
+++ b/compiler/locoex-customop/CMakeLists.txt
@@ -5,13 +5,14 @@ list(REMOVE_ITEM SOURCES ${TESTS})
add_library(locoex_customop SHARED ${SOURCES})
target_include_directories(locoex_customop PUBLIC include)
target_link_libraries(locoex_customop PUBLIC loco)
-target_link_libraries(locoex_customop PRIVATE plier_tf stdex locop pepper_str)
+target_link_libraries(locoex_customop PRIVATE stdex locop pepper_str)
+install(TARGETS locoex_customop DESTINATION lib)
if(NOT ENABLE_TEST)
return()
endif(NOT ENABLE_TEST)
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
GTest_AddTest(locoex_customop_test ${TESTS})
target_link_libraries(locoex_customop_test loco locoex_customop stdex)
diff --git a/compiler/locoex-customop/README.md b/compiler/locoex-customop/README.md
index c2b22e3db..3f71140f9 100644
--- a/compiler/locoex-customop/README.md
+++ b/compiler/locoex-customop/README.md
@@ -3,7 +3,7 @@
_locoex_ is an extention of loco. Classes with `COp` prefix enables *Custom Operation*.
In this version, a *custom operation* means one of the following:
-1. an op that is supported by Tensorflow but not supported both by the moco and the neurun
-1. an op that is not supported by Tensorflow, moco, and the neurun
+1. an op that is supported by Tensorflow but not supported both by the moco and the onert
+1. an op that is not supported by Tensorflow, moco, and the onert
`COpCall` node will represent IR entity that calls custom operations and kernels.
diff --git a/compiler/locoex-customop/requires.cmake b/compiler/locoex-customop/requires.cmake
index 12f93a5d8..9127144f2 100644
--- a/compiler/locoex-customop/requires.cmake
+++ b/compiler/locoex-customop/requires.cmake
@@ -1,5 +1,4 @@
require("loco")
require("stdex")
-require("plier-tf")
require("locop")
require("pepper-str")
diff --git a/compiler/locoex-customop/src/Service/COpShapeInferenceRule.cpp b/compiler/locoex-customop/src/Service/COpShapeInferenceRule.cpp
index 4dc8f461f..cf9decc55 100644
--- a/compiler/locoex-customop/src/Service/COpShapeInferenceRule.cpp
+++ b/compiler/locoex-customop/src/Service/COpShapeInferenceRule.cpp
@@ -37,7 +37,7 @@ bool COpShapeInferenceRule::infer(const loco::Node *node, loco::NodeShape &shape
assert(node->dialect() == COpDialect::get());
assert(dynamic_cast<const COpNode *>(node) != nullptr);
- auto cop_call = dynamic_cast<const COpCall *>(node);
+ auto cop_call = loco::must_cast<const COpCall *>(node);
// Note that the shape of custom op is considered as TensorShape
// TODO Decide how to deal with this shape error cases
diff --git a/compiler/locomotiv/CMakeLists.txt b/compiler/locomotiv/CMakeLists.txt
index 2d360d7c7..5c0156b78 100644
--- a/compiler/locomotiv/CMakeLists.txt
+++ b/compiler/locomotiv/CMakeLists.txt
@@ -2,7 +2,8 @@ file(GLOB_RECURSE SOURCES "src/*.cpp")
file(GLOB_RECURSE TESTS "src/*.test.cpp")
list(REMOVE_ITEM SOURCES ${TESTS})
-add_library(locomotiv SHARED ${SOURCES})
+add_library(locomotiv STATIC ${SOURCES})
+set_target_properties(locomotiv PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_include_directories(locomotiv PUBLIC include)
target_include_directories(locomotiv PRIVATE src)
target_link_libraries(locomotiv PUBLIC loco)
@@ -19,7 +20,7 @@ if(NOT ENABLE_TEST)
endif(NOT ENABLE_TEST)
# Google Test is mandatory for internal testing
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
GTest_AddTest(locomotiv_test ${TESTS})
target_include_directories(locomotiv_test PRIVATE src)
diff --git a/compiler/locomotiv/README.md b/compiler/locomotiv/README.md
index f27bf9be2..9569f6ea3 100644
--- a/compiler/locomotiv/README.md
+++ b/compiler/locomotiv/README.md
@@ -67,6 +67,8 @@ For each domain(see `loco::Domain`), `locomotiv` has fixed layout rule on how to
- That is number of filter(N), height(H), width(W) and input channel depth(C)
- DepthwiseFilter is represented as HWCM layout
- That is height(H), width(W), input channel depth(C) and depth multiplier(M)
+- Matrix is represented as HW layout
+ - That is height(H), width(W)
### Notes on step 3
- Mocking Tensorflow lite `reference_op.h` might be a good place to start.
diff --git a/compiler/locomotiv/src/Node.lst b/compiler/locomotiv/src/Node.lst
index 7615c7987..be3b10520 100644
--- a/compiler/locomotiv/src/Node.lst
+++ b/compiler/locomotiv/src/Node.lst
@@ -14,6 +14,7 @@ NODE(DepthwiseConv2D)
NODE(DepthwiseFilterEncode)
NODE(EltwiseAdd)
NODE(EltwiseDiv)
+NODE(EltwiseMax)
NODE(EltwiseMul)
NODE(EltwiseSqrt)
NODE(EltwiseSub)
@@ -21,6 +22,9 @@ NODE(FeatureDecode)
NODE(FeatureEncode)
NODE(FilterEncode)
NODE(Forward)
+NODE(MatrixDecode)
+NODE(MatrixEncode)
+NODE(MatMul)
NODE(MaxPool2D)
NODE(Pull)
NODE(Push)
@@ -30,5 +34,7 @@ NODE(Reshape<loco::ReshapeType::Fixed>)
NODE(Tanh)
NODE(TensorBroadcast)
NODE(TensorConcat)
+NODE(TensorConstantPad)
+NODE(TensorReduce)
NODE(TensorSoftmax)
NODE(TransposedConv2D)
diff --git a/compiler/locomotiv/src/Node/AvgPool2D.cpp b/compiler/locomotiv/src/Node/AvgPool2D.cpp
index ad603badf..5fdf1e725 100644
--- a/compiler/locomotiv/src/Node/AvgPool2D.cpp
+++ b/compiler/locomotiv/src/Node/AvgPool2D.cpp
@@ -129,7 +129,8 @@ nncc::core::ADT::tensor::Buffer<T> avgPool2D(const loco::AvgPool2D *avgpool2d,
}
}
- assert(filter_ele_count > 0);
+ if (filter_ele_count <= 0)
+ throw std::runtime_error("The number of filter element must be greater than zero.");
output_buf.at(Index({batch, out_y, out_x, channel})) = total / filter_ele_count;
}
}
@@ -141,10 +142,12 @@ nncc::core::ADT::tensor::Buffer<T> avgPool2D(const loco::AvgPool2D *avgpool2d,
} // namespace
-namespace locomotiv
+namespace
{
-void NodeExecution::execute(loco::AvgPool2D *avgpool2d)
+using namespace locomotiv;
+
+void exectute_node(loco::AvgPool2D *avgpool2d)
{
auto ifm_data = annot_data(avgpool2d->ifm());
@@ -176,4 +179,11 @@ void NodeExecution::execute(loco::AvgPool2D *avgpool2d)
annot_domain(avgpool2d, loco::Domain::Feature);
}
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::AvgPool2D *avgpool2d) { exectute_node(avgpool2d); }
+
} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/AvgPool2D.test.cpp b/compiler/locomotiv/src/Node/AvgPool2D.test.cpp
index 89e10a35e..f9863b47d 100644
--- a/compiler/locomotiv/src/Node/AvgPool2D.test.cpp
+++ b/compiler/locomotiv/src/Node/AvgPool2D.test.cpp
@@ -88,10 +88,10 @@ void run_test(const float *ifm, const float *expected_ofm, const Shape &ifm_shap
for (nncc::core::ADT::tensor::IndexEnumerator e{ofm_shape}; e.valid(); e.advance())
{
const auto &ind = e.current();
- ASSERT_FLOAT_EQ(avgpool2d_data->as_f32_bufptr()->at(ind), ofm_overlay.at(ind));
+ ASSERT_FLOAT_EQ(ofm_overlay.at(ind), avgpool2d_data->as_f32_bufptr()->at(ind));
}
- ASSERT_EQ(locomotiv::annot_domain(avgpool2d), loco::Domain::Feature);
+ ASSERT_EQ(loco::Domain::Feature, locomotiv::annot_domain(avgpool2d));
}
} // namespace
diff --git a/compiler/locomotiv/src/Node/BiasAdd.cpp b/compiler/locomotiv/src/Node/BiasAdd.cpp
index 0724fb728..b84fa7e3c 100644
--- a/compiler/locomotiv/src/Node/BiasAdd.cpp
+++ b/compiler/locomotiv/src/Node/BiasAdd.cpp
@@ -41,11 +41,15 @@ std::unique_ptr<NodeData> calc(const NodeData *input_data, const NodeData *bias_
} // namespace
-namespace locomotiv
+namespace
{
-void NodeExecution::execute(loco::BiasAdd<loco::Domain::Tensor> *bias_add)
+using namespace locomotiv;
+
+void execute_node(loco::BiasAdd<loco::Domain::Tensor> *bias_add)
{
+ validate(bias_add, "BiasAdd is nullptr");
+
auto input_data = locomotiv::annot_data(bias_add->value());
auto bias_data = locomotiv::annot_data(bias_add->bias());
@@ -61,8 +65,10 @@ void NodeExecution::execute(loco::BiasAdd<loco::Domain::Tensor> *bias_add)
annot_domain(bias_add, annot_domain(bias_add->value()));
}
-void NodeExecution::execute(loco::BiasAdd<loco::Domain::Feature> *bias_add)
+void execute_node(loco::BiasAdd<loco::Domain::Feature> *bias_add)
{
+ validate(bias_add, "BiasAdd is nullptr");
+
auto input_data = locomotiv::annot_data(bias_add->value());
auto bias_data = locomotiv::annot_data(bias_add->bias());
@@ -78,7 +84,7 @@ void NodeExecution::execute(loco::BiasAdd<loco::Domain::Feature> *bias_add)
annot_domain(bias_add, loco::Domain::Feature);
}
-} // namespace locomotiv
+} // namespace
namespace
{
@@ -119,3 +125,18 @@ std::unique_ptr<NodeData> calc(const NodeData *input_data, const NodeData *bias_
}
} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::BiasAdd<loco::Domain::Tensor> *bias_add)
+{
+ execute_node(bias_add);
+}
+
+void NodeExecution::execute(loco::BiasAdd<loco::Domain::Feature> *bias_add)
+{
+ execute_node(bias_add);
+}
+
+} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/BiasAdd.test.cpp b/compiler/locomotiv/src/Node/BiasAdd.test.cpp
index 0ca826673..cba2d414a 100644
--- a/compiler/locomotiv/src/Node/BiasAdd.test.cpp
+++ b/compiler/locomotiv/src/Node/BiasAdd.test.cpp
@@ -107,16 +107,16 @@ TEST(NodeExecution_TensorBiasAdd, f32)
// comparing the result
ASSERT_NE(bias_add_data, nullptr);
- ASSERT_EQ(bias_add_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(bias_add_data->shape()), Shape({1, 3, 3, 2}));
+ ASSERT_EQ(loco::DataType::FLOAT32, bias_add_data->dtype());
+ ASSERT_EQ(Shape({1, 3, 3, 2}), *(bias_add_data->shape()));
uint32_t n = 0;
for (IndexEnumerator e{*(bias_add_data->shape())}; e.valid(); e.advance())
{
- ASSERT_FLOAT_EQ(bias_add_data->as_f32_bufptr()->at(e.current()), out_val[n++]);
+ ASSERT_FLOAT_EQ(out_val[n++], bias_add_data->as_f32_bufptr()->at(e.current()));
}
- ASSERT_EQ(locomotiv::annot_domain(bias_add), loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(bias_add));
}
/*
@@ -191,14 +191,14 @@ TEST(NodeExecution_FeatureBiasAdd, f32)
// comparing the result
ASSERT_NE(bias_add_data, nullptr);
- ASSERT_EQ(bias_add_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(bias_add_data->shape()), Shape({1, 3, 3, 2}));
+ ASSERT_EQ(loco::DataType::FLOAT32, bias_add_data->dtype());
+ ASSERT_EQ(Shape({1, 3, 3, 2}), *(bias_add_data->shape()));
uint32_t n = 0;
for (IndexEnumerator e{*(bias_add_data->shape())}; e.valid(); e.advance())
{
- ASSERT_FLOAT_EQ(bias_add_data->as_f32_bufptr()->at(e.current()), out_val[n++]);
+ ASSERT_FLOAT_EQ(out_val[n++], bias_add_data->as_f32_bufptr()->at(e.current()));
}
- ASSERT_EQ(locomotiv::annot_domain(feature_bias_add), loco::Domain::Feature);
+ ASSERT_EQ(loco::Domain::Feature, locomotiv::annot_domain(feature_bias_add));
}
diff --git a/compiler/locomotiv/src/Node/BiasEncode.cpp b/compiler/locomotiv/src/Node/BiasEncode.cpp
index c2f2b44c0..21f00a495 100644
--- a/compiler/locomotiv/src/Node/BiasEncode.cpp
+++ b/compiler/locomotiv/src/Node/BiasEncode.cpp
@@ -23,10 +23,12 @@
#include <stdexcept>
#include <cassert>
-namespace locomotiv
+namespace
{
-void NodeExecution::execute(loco::BiasEncode *bias_enc)
+using namespace locomotiv;
+
+void execute_node(loco::BiasEncode *bias_enc)
{
auto input_data = annot_data(bias_enc->input());
@@ -60,4 +62,11 @@ void NodeExecution::execute(loco::BiasEncode *bias_enc)
annot_domain(bias_enc, loco::Domain::Bias);
}
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::BiasEncode *bias_enc) { execute_node(bias_enc); }
+
} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/BiasEncode.test.cpp b/compiler/locomotiv/src/Node/BiasEncode.test.cpp
index 73e2af8a8..4680f5c5a 100644
--- a/compiler/locomotiv/src/Node/BiasEncode.test.cpp
+++ b/compiler/locomotiv/src/Node/BiasEncode.test.cpp
@@ -82,14 +82,24 @@ template <typename T> void test()
auto bias_enc_data = locomotiv::annot_data(bias_enc);
ASSERT_NE(bias_enc_data, nullptr);
- ASSERT_EQ(bias_enc_data->dtype(), loco_dtype<T>());
- ASSERT_EQ(*(bias_enc_data->shape()), Shape{1});
- ASSERT_EQ(as_bufptr<T>(bias_enc_data)->at(Index{0}), pull_buf.at(Index{0}));
+ ASSERT_EQ(loco_dtype<T>(), bias_enc_data->dtype());
+ ASSERT_EQ(Shape{1}, *(bias_enc_data->shape()));
+ ASSERT_EQ(pull_buf.at(Index{0}), as_bufptr<T>(bias_enc_data)->at(Index{0}));
- ASSERT_EQ(locomotiv::annot_domain(bias_enc), loco::Domain::Bias);
+ ASSERT_EQ(loco::Domain::Bias, locomotiv::annot_domain(bias_enc));
}
} // namespace
-TEST(NodeExecution_BiasEncode, s32) { test<int32_t>(); }
+TEST(NodeExecution_BiasEncode, s32)
+{
+ test<int32_t>();
+
+ SUCCEED();
+}
-TEST(NodeExecution_BiasEncode, f32) { test<float>(); }
+TEST(NodeExecution_BiasEncode, f32)
+{
+ test<float>();
+
+ SUCCEED();
+}
diff --git a/compiler/locomotiv/src/Node/ConstGen.cpp b/compiler/locomotiv/src/Node/ConstGen.cpp
index 0360b9fef..96ffbc257 100644
--- a/compiler/locomotiv/src/Node/ConstGen.cpp
+++ b/compiler/locomotiv/src/Node/ConstGen.cpp
@@ -53,10 +53,12 @@ inline uint32_t offset_by_index(const Shape &shape, const Index &index)
} // namespace
-namespace locomotiv
+namespace
{
-void NodeExecution::execute(loco::ConstGen *constgen)
+using namespace locomotiv;
+
+void execute_node(loco::ConstGen *constgen)
{
uint32_t volume = 1;
@@ -113,4 +115,11 @@ void NodeExecution::execute(loco::ConstGen *constgen)
annot_domain(constgen, loco::Domain::Tensor);
}
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::ConstGen *constgen) { execute_node(constgen); }
+
} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/ConstGen.test.cpp b/compiler/locomotiv/src/Node/ConstGen.test.cpp
index 838f4c11d..382cc77e1 100644
--- a/compiler/locomotiv/src/Node/ConstGen.test.cpp
+++ b/compiler/locomotiv/src/Node/ConstGen.test.cpp
@@ -53,16 +53,16 @@ TEST(NodeExecution_ConstGen, s32)
// test
auto data = locomotiv::annot_data(&constgen);
ASSERT_NE(data, nullptr);
- ASSERT_EQ(data->dtype(), loco::DataType::S32);
- ASSERT_EQ(*data->shape(), Shape({2, 3}));
- ASSERT_EQ(data->as_s32_bufptr()->at(Index{0, 0}), 0);
- ASSERT_EQ(data->as_s32_bufptr()->at(Index{0, 1}), 1);
- ASSERT_EQ(data->as_s32_bufptr()->at(Index{0, 2}), 2);
- ASSERT_EQ(data->as_s32_bufptr()->at(Index{1, 0}), -3);
- ASSERT_EQ(data->as_s32_bufptr()->at(Index{1, 1}), -4);
- ASSERT_EQ(data->as_s32_bufptr()->at(Index{1, 2}), -5);
-
- ASSERT_EQ(locomotiv::annot_domain(&constgen), loco::Domain::Tensor);
+ ASSERT_EQ(loco::DataType::S32, data->dtype());
+ ASSERT_EQ(Shape({2, 3}), *data->shape());
+ ASSERT_EQ(0, data->as_s32_bufptr()->at(Index{0, 0}));
+ ASSERT_EQ(1, data->as_s32_bufptr()->at(Index{0, 1}));
+ ASSERT_EQ(2, data->as_s32_bufptr()->at(Index{0, 2}));
+ ASSERT_EQ(-3, data->as_s32_bufptr()->at(Index{1, 0}));
+ ASSERT_EQ(-4, data->as_s32_bufptr()->at(Index{1, 1}));
+ ASSERT_EQ(-5, data->as_s32_bufptr()->at(Index{1, 2}));
+
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(&constgen));
}
TEST(NodeExecution_ConstGen, f32)
@@ -87,14 +87,14 @@ TEST(NodeExecution_ConstGen, f32)
// test
auto data = locomotiv::annot_data(&constgen);
ASSERT_NE(data, nullptr);
- ASSERT_EQ(data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*data->shape(), Shape({2, 3}));
- ASSERT_FLOAT_EQ(data->as_f32_bufptr()->at(Index{0, 0}), 0.0f);
- ASSERT_FLOAT_EQ(data->as_f32_bufptr()->at(Index{0, 1}), 1.0f);
- ASSERT_FLOAT_EQ(data->as_f32_bufptr()->at(Index{0, 2}), 2.0f);
- ASSERT_FLOAT_EQ(data->as_f32_bufptr()->at(Index{1, 0}), 3.0f);
- ASSERT_FLOAT_EQ(data->as_f32_bufptr()->at(Index{1, 1}), 4.0f);
- ASSERT_FLOAT_EQ(data->as_f32_bufptr()->at(Index{1, 2}), 5.0f);
-
- ASSERT_EQ(locomotiv::annot_domain(&constgen), loco::Domain::Tensor);
+ ASSERT_EQ(loco::DataType::FLOAT32, data->dtype());
+ ASSERT_EQ(Shape({2, 3}), *data->shape());
+ ASSERT_FLOAT_EQ(0.0f, data->as_f32_bufptr()->at(Index{0, 0}));
+ ASSERT_FLOAT_EQ(1.0f, data->as_f32_bufptr()->at(Index{0, 1}));
+ ASSERT_FLOAT_EQ(2.0f, data->as_f32_bufptr()->at(Index{0, 2}));
+ ASSERT_FLOAT_EQ(3.0f, data->as_f32_bufptr()->at(Index{1, 0}));
+ ASSERT_FLOAT_EQ(4.0f, data->as_f32_bufptr()->at(Index{1, 1}));
+ ASSERT_FLOAT_EQ(5.0f, data->as_f32_bufptr()->at(Index{1, 2}));
+
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(&constgen));
}
diff --git a/compiler/locomotiv/src/Node/Conv2D.cpp b/compiler/locomotiv/src/Node/Conv2D.cpp
index 2e4185574..cdf0dfd56 100644
--- a/compiler/locomotiv/src/Node/Conv2D.cpp
+++ b/compiler/locomotiv/src/Node/Conv2D.cpp
@@ -139,10 +139,12 @@ Buffer<RET_T> calc_conv2D(const loco::Conv2D *conv2d, const Buffer<IFM_T> *input
} // namespace
-namespace locomotiv
+namespace
{
-void NodeExecution::execute(loco::Conv2D *conv2d)
+using namespace locomotiv;
+
+void execute_node(loco::Conv2D *conv2d)
{
auto ifm_data = annot_data(conv2d->ifm());
auto ker_data = annot_data(conv2d->ker());
@@ -176,4 +178,11 @@ void NodeExecution::execute(loco::Conv2D *conv2d)
annot_domain(conv2d, loco::Domain::Feature);
}
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::Conv2D *conv2d) { execute_node(conv2d); }
+
} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/Conv2D.test.cpp b/compiler/locomotiv/src/Node/Conv2D.test.cpp
index 83d7fc268..66e947acc 100644
--- a/compiler/locomotiv/src/Node/Conv2D.test.cpp
+++ b/compiler/locomotiv/src/Node/Conv2D.test.cpp
@@ -101,10 +101,10 @@ void run_test(const float *ifm, const float *ker, const float *expected_ofm, con
for (nncc::core::ADT::tensor::IndexEnumerator e{ofm_shape}; e.valid(); e.advance())
{
const auto &ind = e.current();
- ASSERT_FLOAT_EQ(conv2d_result->as_f32_bufptr()->at(ind), ofm_overlay.at(ind));
+ ASSERT_FLOAT_EQ(ofm_overlay.at(ind), conv2d_result->as_f32_bufptr()->at(ind));
}
- ASSERT_EQ(locomotiv::annot_domain(conv2d), loco::Domain::Feature);
+ ASSERT_EQ(loco::Domain::Feature, locomotiv::annot_domain(conv2d));
}
} // namespace
diff --git a/compiler/locomotiv/src/Node/DepthwiseConv2D.cpp b/compiler/locomotiv/src/Node/DepthwiseConv2D.cpp
index 92d5aa161..f39cd177e 100644
--- a/compiler/locomotiv/src/Node/DepthwiseConv2D.cpp
+++ b/compiler/locomotiv/src/Node/DepthwiseConv2D.cpp
@@ -143,10 +143,12 @@ Buffer<RET_T> calc_dw_conv2d(const loco::DepthwiseConv2D *dw_conv2d, const Buffe
} // namespace
-namespace locomotiv
+namespace
{
-void NodeExecution::execute(loco::DepthwiseConv2D *dw_conv2d)
+using namespace locomotiv;
+
+void execute_node(loco::DepthwiseConv2D *dw_conv2d)
{
auto ifm_data = annot_data(dw_conv2d->ifm());
auto ker_data = annot_data(dw_conv2d->ker());
@@ -182,4 +184,11 @@ void NodeExecution::execute(loco::DepthwiseConv2D *dw_conv2d)
annot_domain(dw_conv2d, loco::Domain::Feature);
}
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::DepthwiseConv2D *dw_conv2d) { execute_node(dw_conv2d); }
+
} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/DepthwiseConv2D.test.cpp b/compiler/locomotiv/src/Node/DepthwiseConv2D.test.cpp
index 48824c2e0..1ff333be0 100644
--- a/compiler/locomotiv/src/Node/DepthwiseConv2D.test.cpp
+++ b/compiler/locomotiv/src/Node/DepthwiseConv2D.test.cpp
@@ -101,10 +101,10 @@ void run_test(const float *ifm, const float *ker, const float *expected_ofm, con
for (nncc::core::ADT::tensor::IndexEnumerator e{ofm_shape}; e.valid(); e.advance())
{
const auto &ind = e.current();
- ASSERT_FLOAT_EQ(dw_conv2d_result->as_f32_bufptr()->at(ind), ofm_overlay.at(ind));
+ ASSERT_FLOAT_EQ(ofm_overlay.at(ind), dw_conv2d_result->as_f32_bufptr()->at(ind));
}
- ASSERT_EQ(locomotiv::annot_domain(dw_conv2d), loco::Domain::Feature);
+ ASSERT_EQ(loco::Domain::Feature, locomotiv::annot_domain(dw_conv2d));
}
} // namespace
diff --git a/compiler/locomotiv/src/Node/DepthwiseFilterEncode.cpp b/compiler/locomotiv/src/Node/DepthwiseFilterEncode.cpp
index 17004901f..03f5bf833 100644
--- a/compiler/locomotiv/src/Node/DepthwiseFilterEncode.cpp
+++ b/compiler/locomotiv/src/Node/DepthwiseFilterEncode.cpp
@@ -79,10 +79,12 @@ std::unique_ptr<locomotiv::NodeData> dw_filter_encode(const loco::DepthwiseFilte
} // namespace
-namespace locomotiv
+namespace
{
-void NodeExecution::execute(loco::DepthwiseFilterEncode *enc)
+using namespace locomotiv;
+
+void execute_node(loco::DepthwiseFilterEncode *enc)
{
auto input_data = annot_data(enc->input());
@@ -110,4 +112,11 @@ void NodeExecution::execute(loco::DepthwiseFilterEncode *enc)
annot_domain(enc, loco::Domain::DepthwiseFilter);
}
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::DepthwiseFilterEncode *enc) { execute_node(enc); }
+
} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/DepthwiseFilterEncode.test.cpp b/compiler/locomotiv/src/Node/DepthwiseFilterEncode.test.cpp
index db828c08b..5b2ec9326 100644
--- a/compiler/locomotiv/src/Node/DepthwiseFilterEncode.test.cpp
+++ b/compiler/locomotiv/src/Node/DepthwiseFilterEncode.test.cpp
@@ -77,14 +77,14 @@ TEST(NodeExecution_DepthwiseFilterEncode, f32)
auto enc_data = locomotiv::annot_data(enc);
ASSERT_NE(enc_data, nullptr);
- ASSERT_EQ(enc_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(enc_data->shape()), (Shape{H, W, C, M})); // locomotiv depthwise filter is HWCM
+ ASSERT_EQ(loco::DataType::FLOAT32, enc_data->dtype());
+ ASSERT_EQ((Shape{H, W, C, M}), *(enc_data->shape())); // locomotiv depthwise filter is HWCM
auto enc_buf = enc_data->as_f32_bufptr();
for (uint32_t h = 0; h < H; ++h)
for (uint32_t w = 0; w < W; ++w)
for (uint32_t c = 0; c < C; ++c)
for (uint32_t m = 0; m < M; ++m)
- ASSERT_FLOAT_EQ(pull_buf.at(Index{m, h, w, c}), enc_buf->at(Index{h, w, c, m}));
+ ASSERT_FLOAT_EQ(enc_buf->at(Index{h, w, c, m}), pull_buf.at(Index{m, h, w, c}));
- ASSERT_EQ(locomotiv::annot_domain(enc), loco::Domain::DepthwiseFilter);
+ ASSERT_EQ(loco::Domain::DepthwiseFilter, locomotiv::annot_domain(enc));
}
diff --git a/compiler/locomotiv/src/Node/EltwiseAdd.test.cpp b/compiler/locomotiv/src/Node/EltwiseAdd.test.cpp
index 2899dccdd..2873a6544 100644
--- a/compiler/locomotiv/src/Node/EltwiseAdd.test.cpp
+++ b/compiler/locomotiv/src/Node/EltwiseAdd.test.cpp
@@ -108,14 +108,14 @@ TEST(NodeExecution_EltwiseAdd, f32)
// comparing the result
ASSERT_NE(eltwise_add_data, nullptr);
- ASSERT_EQ(eltwise_add_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(eltwise_add_data->shape()), Shape({1, 3, 3, 2}));
+ ASSERT_EQ(loco::DataType::FLOAT32, eltwise_add_data->dtype());
+ ASSERT_EQ(Shape({1, 3, 3, 2}), *(eltwise_add_data->shape()));
uint32_t n = 0;
for (IndexEnumerator e{*(eltwise_add_data->shape())}; e.valid(); e.advance())
{
- ASSERT_FLOAT_EQ(eltwise_add_data->as_f32_bufptr()->at(e.current()), out_val[n++]);
+ ASSERT_FLOAT_EQ(out_val[n++], eltwise_add_data->as_f32_bufptr()->at(e.current()));
}
- ASSERT_EQ(locomotiv::annot_domain(eltwise_add), loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(eltwise_add));
}
diff --git a/compiler/locomotiv/src/Node/EltwiseDiv.test.cpp b/compiler/locomotiv/src/Node/EltwiseDiv.test.cpp
index 60950c15b..cc5045073 100644
--- a/compiler/locomotiv/src/Node/EltwiseDiv.test.cpp
+++ b/compiler/locomotiv/src/Node/EltwiseDiv.test.cpp
@@ -108,14 +108,14 @@ TEST(NodeExecution_EltwiseDiv, f32)
// comparing the result
ASSERT_NE(eltwise_div_data, nullptr);
- ASSERT_EQ(eltwise_div_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(eltwise_div_data->shape()), Shape({1, 3, 3, 2}));
+ ASSERT_EQ(loco::DataType::FLOAT32, eltwise_div_data->dtype());
+ ASSERT_EQ(Shape({1, 3, 3, 2}), *(eltwise_div_data->shape()));
uint32_t n = 0;
for (IndexEnumerator e{*(eltwise_div_data->shape())}; e.valid(); e.advance())
{
- ASSERT_FLOAT_EQ(eltwise_div_data->as_f32_bufptr()->at(e.current()), out_val[n++]);
+ ASSERT_FLOAT_EQ(out_val[n++], eltwise_div_data->as_f32_bufptr()->at(e.current()));
}
- ASSERT_EQ(locomotiv::annot_domain(eltwise_div), loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(eltwise_div));
}
diff --git a/compiler/locomotiv/src/Node/EltwiseMax.cpp b/compiler/locomotiv/src/Node/EltwiseMax.cpp
new file mode 100644
index 000000000..ec44fd6fa
--- /dev/null
+++ b/compiler/locomotiv/src/Node/EltwiseMax.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NodeExecution.h"
+
+#include <cstdlib>
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::EltwiseMax *eltwise_max)
+{
+ struct Func final : public BinaryFunc
+ {
+ float apply(float lhs, float rhs) const { return std::max(lhs, rhs); }
+ };
+
+ Func f;
+
+ eltwise_binary(eltwise_max, f);
+}
+
+} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/EltwiseMax.test.cpp b/compiler/locomotiv/src/Node/EltwiseMax.test.cpp
new file mode 100644
index 000000000..94c398212
--- /dev/null
+++ b/compiler/locomotiv/src/Node/EltwiseMax.test.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NodeExecution.h"
+
+#include "locomotiv/NodeData.h"
+#include "NodeDataImpl.h"
+#include "NodeDomain.h"
+
+#include <nncc/core/ADT/tensor/Shape.h>
+#include <nncc/core/ADT/tensor/Buffer.h>
+#include <nncc/core/ADT/tensor/LexicalLayout.h>
+#include <nncc/core/ADT/tensor/Index.h>
+#include <nncc/core/ADT/tensor/IndexEnumerator.h>
+
+#include <gtest/gtest.h>
+
+using nncc::core::ADT::tensor::Shape;
+using nncc::core::ADT::tensor::LexicalLayout;
+using nncc::core::ADT::tensor::make_buffer;
+using nncc::core::ADT::tensor::IndexEnumerator;
+
+/*
+test case generated from the following:
+
+x = tf.constant([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18],
+ shape=[1, 3, 3, 2], dtype=tf.float32)
+y = tf.constant([18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
+ shape=[1, 3, 3, 2], dtype=tf.float32)
+out = tf.math.maximum(x, y)
+
+with tf.Session() as sess:
+ print(sess.run(out))
+*/
+TEST(NodeExecution_EltwiseMax, f32)
+{
+ float x_val[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};
+ float y_val[] = {18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
+ float out_val[] = {18, 17, 16, 15, 14, 13, 12, 11, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18};
+
+ // make EltwiseMax(Pull, Pull)
+ auto g = loco::make_graph();
+ Shape input_shape{1, 3, 3, 2}; // NHWC
+
+ auto inp_lhs = g->nodes()->create<loco::Pull>();
+ {
+ inp_lhs->dtype(loco::DataType::FLOAT32);
+ inp_lhs->shape({1, 3, 3, 2});
+ }
+
+ auto inp_rhs = g->nodes()->create<loco::Pull>();
+ {
+ inp_rhs->dtype(loco::DataType::FLOAT32);
+ inp_rhs->shape({1, 3, 3, 2});
+ }
+
+ auto eltwise_max = g->nodes()->create<loco::EltwiseMax>();
+ {
+ eltwise_max->lhs(inp_lhs);
+ eltwise_max->rhs(inp_rhs);
+ }
+
+ // Make and assign data to two pull nodes
+ auto inp_lhs_buf = make_buffer<float, LexicalLayout>(input_shape);
+ {
+ int n = 0;
+ for (IndexEnumerator e{inp_lhs_buf.shape()}; e.valid(); e.advance())
+ {
+ inp_lhs_buf.at(e.current()) = x_val[n++];
+ }
+ }
+
+ auto inp_rhs_buf = make_buffer<float, LexicalLayout>(input_shape);
+ {
+ int n = 0;
+ for (IndexEnumerator e{inp_rhs_buf.shape()}; e.valid(); e.advance())
+ {
+ inp_rhs_buf.at(e.current()) = y_val[n++];
+ }
+ }
+
+ auto inp_lhs_data = locomotiv::make_data(inp_lhs_buf);
+ locomotiv::annot_data(inp_lhs, std::move(inp_lhs_data));
+ locomotiv::annot_domain(inp_lhs, loco::Domain::Tensor);
+
+ auto inp_rhs_data = locomotiv::make_data(inp_rhs_buf);
+ locomotiv::annot_data(inp_rhs, std::move(inp_rhs_data));
+ locomotiv::annot_domain(inp_rhs, loco::Domain::Tensor);
+
+ // run the network
+ locomotiv::NodeExecution::get().run(eltwise_max);
+
+ // get result
+ auto eltwise_max_data = locomotiv::annot_data(eltwise_max);
+
+ // comparing the result
+ ASSERT_NE(eltwise_max_data, nullptr);
+ ASSERT_EQ(loco::DataType::FLOAT32, eltwise_max_data->dtype());
+ ASSERT_EQ(Shape({1, 3, 3, 2}), *(eltwise_max_data->shape()));
+
+ uint32_t n = 0;
+ for (IndexEnumerator e{*(eltwise_max_data->shape())}; e.valid(); e.advance())
+ {
+ ASSERT_FLOAT_EQ(out_val[n++], eltwise_max_data->as_f32_bufptr()->at(e.current()));
+ }
+
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(eltwise_max));
+}
diff --git a/compiler/locomotiv/src/Node/EltwiseMul.test.cpp b/compiler/locomotiv/src/Node/EltwiseMul.test.cpp
index b76888300..bbe51bce1 100644
--- a/compiler/locomotiv/src/Node/EltwiseMul.test.cpp
+++ b/compiler/locomotiv/src/Node/EltwiseMul.test.cpp
@@ -111,14 +111,14 @@ TEST(NodeExecution_EltwiseMul, f32)
// comparing the result
ASSERT_NE(eltwise_mul_data, nullptr);
- ASSERT_EQ(eltwise_mul_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(eltwise_mul_data->shape()), Shape({1, 3, 3, 2}));
+ ASSERT_EQ(loco::DataType::FLOAT32, eltwise_mul_data->dtype());
+ ASSERT_EQ(Shape({1, 3, 3, 2}), *(eltwise_mul_data->shape()));
uint32_t n = 0;
for (IndexEnumerator e{*(eltwise_mul_data->shape())}; e.valid(); e.advance())
{
- ASSERT_FLOAT_EQ(eltwise_mul_data->as_f32_bufptr()->at(e.current()), out_val[n++]);
+ ASSERT_FLOAT_EQ(out_val[n++], eltwise_mul_data->as_f32_bufptr()->at(e.current()));
}
- ASSERT_EQ(locomotiv::annot_domain(eltwise_mul), loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(eltwise_mul));
}
diff --git a/compiler/locomotiv/src/Node/EltwiseSqrt.test.cpp b/compiler/locomotiv/src/Node/EltwiseSqrt.test.cpp
index adb1b853e..44d0ca654 100644
--- a/compiler/locomotiv/src/Node/EltwiseSqrt.test.cpp
+++ b/compiler/locomotiv/src/Node/EltwiseSqrt.test.cpp
@@ -58,12 +58,12 @@ TEST(NodeExecution_EltwiseSqrt, f32)
auto sqrt_data = locomotiv::annot_data(sqrt);
ASSERT_NE(sqrt_data, nullptr);
- ASSERT_EQ(sqrt_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(sqrt_data->shape()), Shape{4});
- ASSERT_FLOAT_EQ(sqrt_data->as_f32_bufptr()->at(Index{0}), 2.0f);
- ASSERT_FLOAT_EQ(sqrt_data->as_f32_bufptr()->at(Index{1}), 3.0f);
- ASSERT_FLOAT_EQ(sqrt_data->as_f32_bufptr()->at(Index{2}), 0.0f);
+ ASSERT_EQ(loco::DataType::FLOAT32, sqrt_data->dtype());
+ ASSERT_EQ(Shape{4}, *(sqrt_data->shape()));
+ ASSERT_FLOAT_EQ(2.0f, sqrt_data->as_f32_bufptr()->at(Index{0}));
+ ASSERT_FLOAT_EQ(3.0f, sqrt_data->as_f32_bufptr()->at(Index{1}));
+ ASSERT_FLOAT_EQ(0.0f, sqrt_data->as_f32_bufptr()->at(Index{2}));
ASSERT_TRUE(std::isnan(sqrt_data->as_f32_bufptr()->at(Index{3})));
- ASSERT_EQ(locomotiv::annot_domain(sqrt), loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(sqrt));
}
diff --git a/compiler/locomotiv/src/Node/EltwiseSub.test.cpp b/compiler/locomotiv/src/Node/EltwiseSub.test.cpp
index 7eff90f9e..94dc9c9ad 100644
--- a/compiler/locomotiv/src/Node/EltwiseSub.test.cpp
+++ b/compiler/locomotiv/src/Node/EltwiseSub.test.cpp
@@ -108,14 +108,14 @@ TEST(NodeExecution_EltwiseSub, f32)
// comparing the result
ASSERT_NE(eltwise_sub_data, nullptr);
- ASSERT_EQ(eltwise_sub_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(eltwise_sub_data->shape()), Shape({1, 3, 3, 2}));
+ ASSERT_EQ(loco::DataType::FLOAT32, eltwise_sub_data->dtype());
+ ASSERT_EQ(Shape({1, 3, 3, 2}), *(eltwise_sub_data->shape()));
uint32_t n = 0;
for (IndexEnumerator e{*(eltwise_sub_data->shape())}; e.valid(); e.advance())
{
- ASSERT_FLOAT_EQ(eltwise_sub_data->as_f32_bufptr()->at(e.current()), out_val[n++]);
+ ASSERT_FLOAT_EQ(out_val[n++], eltwise_sub_data->as_f32_bufptr()->at(e.current()));
}
- ASSERT_EQ(locomotiv::annot_domain(eltwise_sub), loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(eltwise_sub));
}
diff --git a/compiler/locomotiv/src/Node/FeatureCodec.test.cpp b/compiler/locomotiv/src/Node/FeatureCodec.test.cpp
index c35f0e69a..1b6b06c13 100644
--- a/compiler/locomotiv/src/Node/FeatureCodec.test.cpp
+++ b/compiler/locomotiv/src/Node/FeatureCodec.test.cpp
@@ -128,16 +128,16 @@ TEST_F(NodeExecution_FeatureCodec, s32)
// Test FeatureEncode
auto enc_data = locomotiv::annot_data(enc);
ASSERT_NE(enc_data, nullptr);
- ASSERT_EQ(enc_data->dtype(), loco::DataType::S32);
- ASSERT_EQ(*(enc_data->shape()), (Shape{N, H, W, C})); // locomotiv feature is NHWC
+ ASSERT_EQ(loco::DataType::S32, enc_data->dtype());
+ ASSERT_EQ((Shape{N, H, W, C}), *(enc_data->shape())); // locomotiv feature is NHWC
auto enc_buf = enc_data->as_s32_bufptr();
for (uint32_t n = 0; n < N; ++n)
for (uint32_t h = 0; h < H; ++h)
for (uint32_t w = 0; w < W; ++w)
for (uint32_t c = 0; c < C; ++c)
- ASSERT_EQ(pull_buf.at(Index{n, c, h, w}), enc_buf->at(Index{n, h, w, c}));
+ ASSERT_EQ(enc_buf->at(Index{n, h, w, c}), pull_buf.at(Index{n, c, h, w}));
- ASSERT_EQ(locomotiv::annot_domain(enc), loco::Domain::Feature);
+ ASSERT_EQ(loco::Domain::Feature, locomotiv::annot_domain(enc));
// FeatureDecode
auto dec = feature_decode_layer(enc, NCHW);
@@ -146,16 +146,16 @@ TEST_F(NodeExecution_FeatureCodec, s32)
// Test FeatureDecode: Encode -> Decode == identity
auto dec_data = locomotiv::annot_data(dec);
ASSERT_NE(dec_data, nullptr);
- ASSERT_EQ(dec_data->dtype(), loco::DataType::S32);
- ASSERT_EQ(*(dec_data->shape()), (Shape{N, C, H, W}));
+ ASSERT_EQ(loco::DataType::S32, dec_data->dtype());
+ ASSERT_EQ((Shape{N, C, H, W}), *(dec_data->shape()));
auto dec_buf = dec_data->as_s32_bufptr();
for (uint32_t n = 0; n < N; ++n)
for (uint32_t h = 0; h < H; ++h)
for (uint32_t w = 0; w < W; ++w)
for (uint32_t c = 0; c < C; ++c)
- ASSERT_EQ(pull_buf.at(Index{n, c, h, w}), dec_buf->at(Index{n, c, h, w}));
+ ASSERT_EQ(dec_buf->at(Index{n, c, h, w}), pull_buf.at(Index{n, c, h, w}));
- ASSERT_EQ(locomotiv::annot_domain(dec), loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(dec));
}
TEST_F(NodeExecution_FeatureCodec, f32)
@@ -192,16 +192,16 @@ TEST_F(NodeExecution_FeatureCodec, f32)
// Test FeatureEncode
auto enc_data = locomotiv::annot_data(enc);
ASSERT_NE(enc_data, nullptr);
- ASSERT_EQ(enc_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(enc_data->shape()), (Shape{N, H, W, C})); // locomotiv feature is NHWC
+ ASSERT_EQ(loco::DataType::FLOAT32, enc_data->dtype());
+ ASSERT_EQ((Shape{N, H, W, C}), *(enc_data->shape())); // locomotiv feature is NHWC
auto enc_buf = enc_data->as_f32_bufptr();
for (uint32_t n = 0; n < N; ++n)
for (uint32_t h = 0; h < H; ++h)
for (uint32_t w = 0; w < W; ++w)
for (uint32_t c = 0; c < C; ++c)
- ASSERT_FLOAT_EQ(pull_buf.at(Index{c, h, n, w}), enc_buf->at(Index{n, h, w, c}));
+ ASSERT_FLOAT_EQ(enc_buf->at(Index{n, h, w, c}), pull_buf.at(Index{c, h, n, w}));
- ASSERT_EQ(locomotiv::annot_domain(enc), loco::Domain::Feature);
+ ASSERT_EQ(loco::Domain::Feature, locomotiv::annot_domain(enc));
// FeatureDecode
auto dec = feature_decode_layer(enc, CHNW);
@@ -210,14 +210,14 @@ TEST_F(NodeExecution_FeatureCodec, f32)
// Test FeatureDecode: Encode -> Decode == identity
auto dec_data = locomotiv::annot_data(dec);
ASSERT_NE(dec_data, nullptr);
- ASSERT_EQ(dec_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(dec_data->shape()), (Shape{C, H, N, W}));
+ ASSERT_EQ(loco::DataType::FLOAT32, dec_data->dtype());
+ ASSERT_EQ((Shape{C, H, N, W}), *(dec_data->shape()));
auto dec_buf = dec_data->as_f32_bufptr();
for (uint32_t n = 0; n < N; ++n)
for (uint32_t h = 0; h < H; ++h)
for (uint32_t w = 0; w < W; ++w)
for (uint32_t c = 0; c < C; ++c)
- ASSERT_FLOAT_EQ(pull_buf.at(Index{c, h, n, w}), dec_buf->at(Index{c, h, n, w}));
+ ASSERT_FLOAT_EQ(dec_buf->at(Index{c, h, n, w}), pull_buf.at(Index{c, h, n, w}));
- ASSERT_EQ(locomotiv::annot_domain(dec), loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(dec));
}
diff --git a/compiler/locomotiv/src/Node/FeatureDecode.cpp b/compiler/locomotiv/src/Node/FeatureDecode.cpp
index 8a56a56b2..8776e1b42 100644
--- a/compiler/locomotiv/src/Node/FeatureDecode.cpp
+++ b/compiler/locomotiv/src/Node/FeatureDecode.cpp
@@ -72,10 +72,12 @@ std::unique_ptr<locomotiv::NodeData> feature_decode(const loco::FeatureDecode *n
} // namespace
-namespace locomotiv
+namespace
{
-void NodeExecution::execute(loco::FeatureDecode *dec)
+using namespace locomotiv;
+
+void execute_node(loco::FeatureDecode *dec)
{
auto input_data = annot_data(dec->input());
@@ -109,4 +111,11 @@ void NodeExecution::execute(loco::FeatureDecode *dec)
annot_domain(dec, loco::Domain::Tensor);
}
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::FeatureDecode *dec) { execute_node(dec); }
+
} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/FilterEncode.cpp b/compiler/locomotiv/src/Node/FilterEncode.cpp
index cd9d708dc..0e2ac918f 100644
--- a/compiler/locomotiv/src/Node/FilterEncode.cpp
+++ b/compiler/locomotiv/src/Node/FilterEncode.cpp
@@ -74,10 +74,12 @@ std::unique_ptr<locomotiv::NodeData> filter_encode(const loco::FilterEncode *nod
} // namespace
-namespace locomotiv
+namespace
{
-void NodeExecution::execute(loco::FilterEncode *enc)
+using namespace locomotiv;
+
+void execute_node(loco::FilterEncode *enc)
{
auto input_data = annot_data(enc->input());
@@ -111,4 +113,11 @@ void NodeExecution::execute(loco::FilterEncode *enc)
annot_domain(enc, loco::Domain::Filter);
}
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::FilterEncode *enc) { execute_node(enc); }
+
} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/FilterEncode.test.cpp b/compiler/locomotiv/src/Node/FilterEncode.test.cpp
index 79b8308e2..dcca94993 100644
--- a/compiler/locomotiv/src/Node/FilterEncode.test.cpp
+++ b/compiler/locomotiv/src/Node/FilterEncode.test.cpp
@@ -77,16 +77,16 @@ TEST(NodeExecution_FilterEncode, s32)
auto enc_data = locomotiv::annot_data(enc);
ASSERT_NE(enc_data, nullptr);
- ASSERT_EQ(enc_data->dtype(), loco::DataType::S32);
- ASSERT_EQ(*(enc_data->shape()), (Shape{N, H, W, C})); // locomotiv filter is NHWC
+ ASSERT_EQ(loco::DataType::S32, enc_data->dtype());
+ ASSERT_EQ((Shape{N, H, W, C}), *(enc_data->shape())); // locomotiv filter is NHWC
auto enc_buf = enc_data->as_s32_bufptr();
for (uint32_t n = 0; n < N; ++n)
for (uint32_t h = 0; h < H; ++h)
for (uint32_t w = 0; w < W; ++w)
for (uint32_t c = 0; c < C; ++c)
- ASSERT_EQ(pull_buf.at(Index{n, c, h, w}), enc_buf->at(Index{n, h, w, c}));
+ ASSERT_EQ(enc_buf->at(Index{n, h, w, c}), pull_buf.at(Index{n, c, h, w}));
- ASSERT_EQ(locomotiv::annot_domain(enc), loco::Domain::Filter);
+ ASSERT_EQ(loco::Domain::Filter, locomotiv::annot_domain(enc));
}
TEST(NodeExecution_FilterEncode, f32)
@@ -131,14 +131,14 @@ TEST(NodeExecution_FilterEncode, f32)
auto enc_data = locomotiv::annot_data(enc);
ASSERT_NE(enc_data, nullptr);
- ASSERT_EQ(enc_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(enc_data->shape()), (Shape{N, H, W, C})); // locomotiv filter is NHWC
+ ASSERT_EQ(loco::DataType::FLOAT32, enc_data->dtype());
+ ASSERT_EQ((Shape{N, H, W, C}), *(enc_data->shape())); // locomotiv filter is NHWC
auto enc_buf = enc_data->as_f32_bufptr();
for (uint32_t n = 0; n < N; ++n)
for (uint32_t h = 0; h < H; ++h)
for (uint32_t w = 0; w < W; ++w)
for (uint32_t c = 0; c < C; ++c)
- ASSERT_FLOAT_EQ(pull_buf.at(Index{c, h, n, w}), enc_buf->at(Index{n, h, w, c}));
+ ASSERT_FLOAT_EQ(enc_buf->at(Index{n, h, w, c}), pull_buf.at(Index{c, h, n, w}));
- ASSERT_EQ(locomotiv::annot_domain(enc), loco::Domain::Filter);
+ ASSERT_EQ(loco::Domain::Filter, locomotiv::annot_domain(enc));
}
diff --git a/compiler/locomotiv/src/Node/Forward.cpp b/compiler/locomotiv/src/Node/Forward.cpp
index eb7d44a59..9095ecf00 100644
--- a/compiler/locomotiv/src/Node/Forward.cpp
+++ b/compiler/locomotiv/src/Node/Forward.cpp
@@ -23,10 +23,12 @@
#include <stdexcept>
#include <cassert>
-namespace locomotiv
+namespace
{
-void NodeExecution::execute(loco::Forward *forward)
+using namespace locomotiv;
+
+void execute_node(loco::Forward *forward)
{
auto input_data = annot_data(forward->input());
@@ -59,4 +61,11 @@ void NodeExecution::execute(loco::Forward *forward)
annot_domain(forward, annot_domain(forward->input()));
}
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::Forward *forward) { execute_node(forward); }
+
} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/Forward.test.cpp b/compiler/locomotiv/src/Node/Forward.test.cpp
index 73d37139a..5116a9596 100644
--- a/compiler/locomotiv/src/Node/Forward.test.cpp
+++ b/compiler/locomotiv/src/Node/Forward.test.cpp
@@ -52,11 +52,11 @@ TEST(NodeExecution_Forward, s32)
auto forward_data = locomotiv::annot_data(forward);
ASSERT_NE(forward_data, nullptr);
- ASSERT_EQ(forward_data->dtype(), loco::DataType::S32);
- ASSERT_EQ(*(forward_data->shape()), Shape{1});
- ASSERT_EQ(forward_data->as_s32_bufptr()->at(Index{0}), pull_buf.at(Index{0}));
+ ASSERT_EQ(loco::DataType::S32, forward_data->dtype());
+ ASSERT_EQ(Shape{1}, *(forward_data->shape()));
+ ASSERT_EQ(pull_buf.at(Index{0}), forward_data->as_s32_bufptr()->at(Index{0}));
- ASSERT_EQ(locomotiv::annot_domain(forward), loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(forward));
}
TEST(NodeExecution_Forward, f32)
@@ -80,9 +80,9 @@ TEST(NodeExecution_Forward, f32)
auto forward_data = locomotiv::annot_data(forward);
ASSERT_NE(forward_data, nullptr);
- ASSERT_EQ(forward_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(forward_data->shape()), Shape{1});
- ASSERT_FLOAT_EQ(forward_data->as_f32_bufptr()->at(Index{0}), pull_buf.at(Index{0}));
+ ASSERT_EQ(loco::DataType::FLOAT32, forward_data->dtype());
+ ASSERT_EQ(Shape{1}, *(forward_data->shape()));
+ ASSERT_FLOAT_EQ(pull_buf.at(Index{0}), forward_data->as_f32_bufptr()->at(Index{0}));
- ASSERT_EQ(locomotiv::annot_domain(forward), loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(forward));
}
diff --git a/compiler/locomotiv/src/Node/MatMul.cpp b/compiler/locomotiv/src/Node/MatMul.cpp
new file mode 100644
index 000000000..e5d149ac5
--- /dev/null
+++ b/compiler/locomotiv/src/Node/MatMul.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2019 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 "NodeExecution.h"
+
+#include "NodeDataImpl.h"
+#include "NodeDomain.h"
+#include "Validation.h"
+
+#include <nncc/core/ADT/tensor/Shape.h>
+#include <nncc/core/ADT/tensor/Buffer.h>
+#include <nncc/core/ADT/tensor/Index.h>
+#include <nncc/core/ADT/tensor/IndexEnumerator.h>
+#include <nncc/core/ADT/tensor/LexicalLayout.h>
+
+#include <cassert>
+#include <stdexcept>
+
+namespace
+{
+using nncc::core::ADT::tensor::Buffer;
+using nncc::core::ADT::tensor::Shape;
+using nncc::core::ADT::tensor::Index;
+using nncc::core::ADT::tensor::LexicalLayout;
+using nncc::core::ADT::tensor::make_buffer;
+
+/**
+ * @brief Calculate Matrix Multiplication
+ */
+template <typename T> Buffer<T> calc_mat_mul(const Buffer<T> *lhs_buf, const Buffer<T> *rhs_buf)
+{
+ const auto lhs_shape = lhs_buf->shape();
+ const auto rhs_shape = rhs_buf->shape();
+
+ assert(lhs_shape.rank() == 2 && "lhs rank must be 2");
+ assert(rhs_shape.rank() == 2 && "rhs rank must be 2");
+ // lhs width should be the same as rhs height
+ assert(lhs_shape.dim(1) == rhs_shape.dim(0) && "height/width mismatch");
+
+ const uint32_t lhs_height = lhs_shape.dim(0);
+ const uint32_t lhs_width = lhs_shape.dim(1);
+
+ const uint32_t rhs_width = rhs_shape.dim(1);
+
+ const uint32_t output_height = lhs_height;
+ const uint32_t output_width = rhs_width;
+
+ Shape output_shape{output_height, output_width};
+ auto output_buf = make_buffer<T, LexicalLayout>(output_shape);
+
+ for (uint32_t out_y = 0; out_y < output_height; ++out_y)
+ {
+ for (uint32_t out_x = 0; out_x < output_width; ++out_x)
+ {
+ T total = static_cast<T>(0); // accumulator
+ // Accumulate through axis
+ for (uint32_t axis = 0; axis < lhs_width; ++axis)
+ {
+ total += lhs_buf->at(Index({out_y, axis})) * rhs_buf->at(Index({axis, out_x}));
+ }
+ // Set output value
+ output_buf.at(Index({out_y, out_x})) = total;
+ }
+ }
+
+ return output_buf;
+}
+
+} // namespace
+
+namespace
+{
+
+using namespace locomotiv;
+
+void execute_node(loco::MatMul *mat_mul)
+{
+ auto lhs_data = annot_data(mat_mul->lhs());
+ auto rhs_data = annot_data(mat_mul->rhs());
+
+ validate(lhs_data, "Can't find left matrix data of MatMul");
+ validate(lhs_data->shape()->rank() == 2, "lhs rank must be 2");
+
+ validate(rhs_data, "Can't find right matrix data of MatMul");
+ validate(rhs_data->shape()->rank() == 2, "rhs rank must be 2");
+
+ validate(annot_domain(mat_mul->lhs()) == loco::Domain::Matrix,
+ "Left matrix of MatMul is not a Matrix");
+ validate(annot_domain(mat_mul->rhs()) == loco::Domain::Matrix,
+ "Right matrix of MatMul is not a Matrix");
+
+ std::unique_ptr<NodeData> mat_mul_result = nullptr;
+
+ if (lhs_data->dtype() == loco::DataType::FLOAT32 && rhs_data->dtype() == loco::DataType::FLOAT32)
+ {
+ const auto lhs_buf = lhs_data->as_f32_bufptr();
+ const auto rhs_buf = rhs_data->as_f32_bufptr();
+
+ auto mat_mul_buf = calc_mat_mul<float>(lhs_buf, rhs_buf);
+
+ mat_mul_result = make_data(mat_mul_buf);
+ }
+ else if (lhs_data->dtype() == loco::DataType::S32 && rhs_data->dtype() == loco::DataType::S32)
+ {
+ const auto lhs_buf = lhs_data->as_s32_bufptr();
+ const auto rhs_buf = rhs_data->as_s32_bufptr();
+
+ auto mat_mul_buf = calc_mat_mul<int32_t>(lhs_buf, rhs_buf);
+
+ mat_mul_result = make_data(mat_mul_buf);
+ }
+ else
+ throw std::runtime_error("NYI for these DataTypes");
+
+ assert(mat_mul_result != nullptr);
+
+ annot_data(mat_mul, std::move(mat_mul_result));
+ annot_domain(mat_mul, loco::Domain::Matrix);
+}
+
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::MatMul *mat_mul) { execute_node(mat_mul); }
+
+} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/MatMul.test.cpp b/compiler/locomotiv/src/Node/MatMul.test.cpp
new file mode 100644
index 000000000..7d942e1d0
--- /dev/null
+++ b/compiler/locomotiv/src/Node/MatMul.test.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NodeExecution.h"
+
+#include "locomotiv/NodeData.h"
+#include "NodeDataImpl.h"
+#include "NodeDomain.h"
+
+#include <nncc/core/ADT/tensor/Shape.h>
+#include <nncc/core/ADT/tensor/Buffer.h>
+#include <nncc/core/ADT/tensor/Overlay.h>
+#include <nncc/core/ADT/tensor/LexicalLayout.h>
+#include "nncc/core/ADT/tensor/IndexEnumerator.h"
+
+#include <gtest/gtest.h>
+
+namespace
+{
+using nncc::core::ADT::tensor::Shape;
+using nncc::core::ADT::tensor::LexicalLayout;
+using nncc::core::ADT::tensor::make_buffer;
+using nncc::core::ADT::tensor::make_overlay;
+
+template <typename T>
+void run_test(const T *lhs, const T *rhs, const T *expected_output, const Shape &lhs_shape,
+ const Shape &rhs_shape, const Shape &out_shape, loco::DataType expected_datatype)
+{
+ auto g = loco::make_graph();
+ // Fill lhs MatrixEncode
+ auto lhs_enc = g->nodes()->create<loco::MatrixEncode>();
+ {
+ auto lhs_enc_buf = make_buffer<T, LexicalLayout>(lhs_shape);
+ auto lhs_overlay = make_overlay<T, LexicalLayout>(lhs_shape, const_cast<T *>(lhs));
+ for (nncc::core::ADT::tensor::IndexEnumerator e{lhs_shape}; e.valid(); e.advance())
+ {
+ const auto &ind = e.current();
+ lhs_enc_buf.at(ind) = lhs_overlay.at(ind);
+ }
+
+ auto enc_data = locomotiv::make_data(lhs_enc_buf);
+ locomotiv::annot_data(lhs_enc, std::move(enc_data));
+ locomotiv::annot_domain(lhs_enc, loco::Domain::Matrix);
+ }
+ // Fill rhs MatrixEncode
+ auto rhs_enc = g->nodes()->create<loco::MatrixEncode>();
+ {
+ auto rhs_enc_buf = make_buffer<T, LexicalLayout>(rhs_shape);
+ auto rhs_overlay = make_overlay<T, LexicalLayout>(rhs_shape, const_cast<T *>(rhs));
+ for (nncc::core::ADT::tensor::IndexEnumerator e{rhs_shape}; e.valid(); e.advance())
+ {
+ const auto &ind = e.current();
+ rhs_enc_buf.at(ind) = rhs_overlay.at(ind);
+ }
+
+ auto enc_data = locomotiv::make_data(rhs_enc_buf);
+ locomotiv::annot_data(rhs_enc, std::move(enc_data));
+ locomotiv::annot_domain(rhs_enc, loco::Domain::Matrix);
+ }
+
+ // build MatMul
+ auto mat_mul = g->nodes()->create<loco::MatMul>();
+ mat_mul->lhs(lhs_enc);
+ mat_mul->rhs(rhs_enc);
+
+ // run interpreter
+ locomotiv::NodeExecution::get().run(mat_mul);
+
+ // get result of calculation
+ auto mat_mul_result = locomotiv::annot_data(mat_mul);
+
+ // check the result
+ ASSERT_NE(mat_mul_result, nullptr);
+ ASSERT_TRUE(mat_mul_result->dtype() == expected_datatype);
+ ASSERT_TRUE(*(mat_mul_result->shape()) == out_shape);
+
+ auto out_overlay = make_overlay<T, LexicalLayout>(out_shape, const_cast<T *>(expected_output));
+ for (nncc::core::ADT::tensor::IndexEnumerator e{out_shape}; e.valid(); e.advance())
+ {
+ const auto &ind = e.current();
+ if (expected_datatype == loco::DataType::FLOAT32)
+ ASSERT_FLOAT_EQ(out_overlay.at(ind), mat_mul_result->as_f32_bufptr()->at(ind));
+ else if (expected_datatype == loco::DataType::S32)
+ ASSERT_EQ(out_overlay.at(ind), mat_mul_result->as_s32_bufptr()->at(ind));
+ else
+ throw std::runtime_error("NYI for these DataTypes");
+ }
+
+ ASSERT_EQ(loco::Domain::Matrix, locomotiv::annot_domain(mat_mul));
+}
+
+} // namespace
+
+// clang-format off
+/* from the code below:
+
+import numpy as np
+
+a = [[-0.48850584, 1.4292705, -1.3424522],
+ [1.7021934, -0.39246717, 0.6248314]]
+
+b = [[-0.0830195, 0.21088193, -0.11781317],
+ [0.07755677, 1.6337638, 1.0792778],
+ [-1.6922939, -1.5437212, 0.96667504]]
+
+print(np.array(a) @ np.array(b))
+*/
+TEST(NodeExecution_MatMul, f32_2x3_3x3)
+{
+ using nncc::core::ADT::tensor::Shape;
+
+ const float lhs[] =
+ {
+ -0.48850584, 1.4292705, -1.3424522,
+ 1.7021934, -0.39246717, 0.6248314
+ };
+
+ const float rhs[] =
+ {
+ -0.0830195, 0.21088193, -0.11781317,
+ 0.07755677, 1.6337638, 1.0792778,
+ -1.6922939, -1.5437212, 0.96667504
+ };
+
+ const float out[] =
+ {
+ 2.42322878, 4.30444527, 0.30241731,
+ -1.2291521, -1.2468023, -0.02011299
+ };
+
+ run_test<float>(lhs, rhs, out, Shape{2, 3}, Shape{3, 3}, Shape{2, 3}, loco::DataType::FLOAT32);
+
+ SUCCEED();
+}
+
+/* from the code below:
+
+import numpy as np
+
+a = np.random.randint(10000, size=(4, 2))
+
+b = np.random.randint(10000, size=(2, 6))
+
+print(a)
+print(b)
+print(np.array(a) @ np.array(b))
+*/
+TEST(NodeExecution_MatMul, s32_4x2_2x6)
+{
+ using nncc::core::ADT::tensor::Shape;
+
+ const int32_t lhs[] =
+ {
+ 6392, 4993,
+ 54, 9037,
+ 3947, 5820,
+ 5800, 4181
+ };
+
+ const int32_t rhs[] =
+ {
+ 2694, 8376, 8090, 1285, 7492, 1652,
+ 5427, 8798, 7634, 2229, 5439, 6999
+ };
+
+ const int32_t out[] =
+ {
+ 44317059, 97467806, 89827842, 19343117, 75045791, 45505591,
+ 49189275, 79959830, 69425318, 20212863, 49556811, 63339171,
+ 42218358, 84264432, 76361110, 18044675, 61225904, 47254624,
+ 38315487, 85365238, 78839754, 16772449, 66194059, 38844419
+ };
+
+ run_test<int32_t>(lhs, rhs, out, Shape{4, 2}, Shape{2, 6}, Shape{4, 6}, loco::DataType::S32);
+
+ SUCCEED();
+}
+
+// clang-format on
diff --git a/compiler/locomotiv/src/Node/MatrixCodec.test.cpp b/compiler/locomotiv/src/Node/MatrixCodec.test.cpp
new file mode 100644
index 000000000..da4afeded
--- /dev/null
+++ b/compiler/locomotiv/src/Node/MatrixCodec.test.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NodeExecution.h"
+
+#include "locomotiv/NodeData.h"
+#include "NodeDataImpl.h"
+#include "NodeDomain.h"
+
+#include <loco/IR/PermutingCodec.h>
+
+#include <nncc/core/ADT/tensor/Shape.h>
+#include <nncc/core/ADT/tensor/Buffer.h>
+#include <nncc/core/ADT/tensor/LexicalLayout.h>
+#include <nncc/core/ADT/tensor/IndexEnumerator.h>
+
+#include <gtest/gtest.h>
+
+using nncc::core::ADT::tensor::Index;
+using nncc::core::ADT::tensor::Shape;
+using nncc::core::ADT::tensor::LexicalLayout;
+using nncc::core::ADT::tensor::make_buffer;
+using nncc::core::ADT::tensor::IndexEnumerator;
+using nncc::core::ADT::tensor::Buffer;
+
+// This file is intended to test MatrixEncode and MatrixDecode at once
+namespace
+{
+
+class NodeExecution_MatrixCodec : public ::testing::Test
+{
+private:
+ loco::Graph g;
+
+protected:
+ /// @brief Make Pull node and set data by given buffer and data type
+ template <typename DT> loco::Pull *pull_layer(Buffer<DT> &pull_buf, loco::DataType dtype)
+ {
+ auto pull = g.nodes()->create<loco::Pull>();
+ pull->dtype(dtype);
+
+ auto pull_data = locomotiv::make_data(pull_buf);
+ locomotiv::annot_data(pull, std::move(pull_data));
+ locomotiv::annot_domain(pull, loco::Domain::Tensor);
+
+ return pull;
+ }
+
+ /// @brief Make MatrixEncode node with given input and encoding permutation
+ loco::MatrixEncode *matrix_encode_layer(loco::Node *input,
+ const loco::Permutation<loco::Domain::Matrix> &perm)
+ {
+ auto encoder = std::unique_ptr<loco::PermutingEncoder<loco::Domain::Matrix>>(
+ new loco::PermutingEncoder<loco::Domain::Matrix>);
+
+ encoder->perm(perm);
+
+ auto enc = g.nodes()->create<loco::MatrixEncode>();
+ enc->input(input);
+ enc->encoder(std::move(encoder));
+
+ return enc;
+ }
+
+ /// @brief Make MatrixDecode node with given input and decoding permutation
+ loco::MatrixDecode *matrix_decode_layer(loco::Node *input,
+ const loco::Permutation<loco::Domain::Matrix> &perm)
+ {
+ auto decoder = std::unique_ptr<loco::PermutingDecoder<loco::Domain::Matrix>>(
+ new loco::PermutingDecoder<loco::Domain::Matrix>);
+
+ decoder->perm(perm);
+
+ auto dec = g.nodes()->create<loco::MatrixDecode>();
+ dec->input(input);
+ dec->decoder(std::move(decoder));
+
+ return dec;
+ }
+};
+
+} // namespace
+
+TEST_F(NodeExecution_MatrixCodec, HW_s32)
+{
+ const uint32_t H = 3;
+ const uint32_t W = 4;
+
+ // Make HW data for pull node
+ auto pull_buf = make_buffer<int32_t, LexicalLayout>(Shape{H, W});
+ int32_t i = 0;
+ for (IndexEnumerator e{pull_buf.shape()}; e.valid(); e.advance())
+ {
+ pull_buf.at(e.current()) = i;
+ ++i; // Doesn't matter what it is
+ }
+
+ // Make HW permutation for encoder and decoder
+ loco::Permutation<loco::Domain::Matrix> HW;
+
+ HW.axis(loco::MatrixAxis::Height) = 0;
+ HW.axis(loco::MatrixAxis::Width) = 1;
+
+ // Pull
+ auto pull = pull_layer(pull_buf, loco::DataType::S32);
+
+ // MatrixEncode
+ auto enc = matrix_encode_layer(pull, HW);
+ locomotiv::NodeExecution::get().run(enc);
+
+ // Test MatrixEncode
+ auto enc_data = locomotiv::annot_data(enc);
+ ASSERT_NE(enc_data, nullptr);
+ ASSERT_EQ(loco::DataType::S32, enc_data->dtype());
+ ASSERT_EQ((Shape{H, W}), *(enc_data->shape())); // locomotiv matrix is HW
+ auto enc_buf = enc_data->as_s32_bufptr();
+ for (uint32_t h = 0; h < H; ++h)
+ for (uint32_t w = 0; w < W; ++w)
+ ASSERT_EQ(enc_buf->at(Index{h, w}), pull_buf.at(Index{h, w}));
+
+ ASSERT_EQ(loco::Domain::Matrix, locomotiv::annot_domain(enc));
+
+ // MatrixDecode
+ auto dec = matrix_decode_layer(enc, HW);
+ locomotiv::NodeExecution::get().run(dec);
+
+ // Test MatrixDecode: Encode -> Decode == identity
+ auto dec_data = locomotiv::annot_data(dec);
+ ASSERT_NE(dec_data, nullptr);
+ ASSERT_EQ(loco::DataType::S32, dec_data->dtype());
+ ASSERT_EQ((Shape{H, W}), *(dec_data->shape()));
+ auto dec_buf = dec_data->as_s32_bufptr();
+ for (uint32_t h = 0; h < H; ++h)
+ for (uint32_t w = 0; w < W; ++w)
+ ASSERT_EQ(dec_buf->at(Index{h, w}), pull_buf.at(Index{h, w}));
+
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(dec));
+}
+
+TEST_F(NodeExecution_MatrixCodec, WH_f32)
+{
+ const uint32_t W = 6;
+ const uint32_t H = 5;
+
+ // Make crazy WH data for pull node
+ auto pull_buf = make_buffer<float, LexicalLayout>(Shape{W, H});
+ float f = 0.0f;
+ for (IndexEnumerator e{pull_buf.shape()}; e.valid(); e.advance())
+ {
+ pull_buf.at(e.current()) = f;
+ f += 0.1f; // Doesn't matter what it is
+ }
+
+ // Make WH permutation for encoder and decoder
+ loco::Permutation<loco::Domain::Matrix> WH;
+
+ WH.axis(loco::MatrixAxis::Width) = 0;
+ WH.axis(loco::MatrixAxis::Height) = 1;
+
+ // Pull
+ auto pull = pull_layer(pull_buf, loco::DataType::FLOAT32);
+
+ // MatrixEncode
+ auto enc = matrix_encode_layer(pull, WH);
+ locomotiv::NodeExecution::get().run(enc);
+
+ // Test MatrixEncode
+ auto enc_data = locomotiv::annot_data(enc);
+ ASSERT_NE(enc_data, nullptr);
+ ASSERT_EQ(loco::DataType::FLOAT32, enc_data->dtype());
+ ASSERT_EQ((Shape{H, W}), *(enc_data->shape())); // locomotiv matrix is HW
+ auto enc_buf = enc_data->as_f32_bufptr();
+ for (uint32_t h = 0; h < H; ++h)
+ for (uint32_t w = 0; w < W; ++w)
+ ASSERT_FLOAT_EQ(enc_buf->at(Index{h, w}), pull_buf.at(Index{w, h}));
+
+ ASSERT_EQ(loco::Domain::Matrix, locomotiv::annot_domain(enc));
+
+ // MatrixDecode
+ auto dec = matrix_decode_layer(enc, WH);
+ locomotiv::NodeExecution::get().run(dec);
+
+ // Test MatrixDecode: Encode -> Decode == identity
+ auto dec_data = locomotiv::annot_data(dec);
+ ASSERT_NE(dec_data, nullptr);
+ ASSERT_EQ(loco::DataType::FLOAT32, dec_data->dtype());
+ ASSERT_EQ((Shape{W, H}), *(dec_data->shape()));
+ auto dec_buf = dec_data->as_f32_bufptr();
+ for (uint32_t h = 0; h < H; ++h)
+ for (uint32_t w = 0; w < W; ++w)
+ ASSERT_FLOAT_EQ(dec_buf->at(Index{w, h}), pull_buf.at(Index{w, h}));
+
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(dec));
+}
diff --git a/compiler/locomotiv/src/Node/MatrixDecode.cpp b/compiler/locomotiv/src/Node/MatrixDecode.cpp
new file mode 100644
index 000000000..0310015f1
--- /dev/null
+++ b/compiler/locomotiv/src/Node/MatrixDecode.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NodeExecution.h"
+
+#include "NodeDataImpl.h"
+#include "NodeDomain.h"
+#include "Validation.h"
+
+#include <nncc/core/ADT/tensor/LexicalLayout.h>
+#include <nncc/core/ADT/tensor/IndexEnumerator.h>
+
+#include <stdexcept>
+#include <cassert>
+
+namespace
+{
+
+using nncc::core::ADT::tensor::Buffer;
+using nncc::core::ADT::tensor::make_buffer;
+using nncc::core::ADT::tensor::LexicalLayout;
+using nncc::core::ADT::tensor::Shape;
+using nncc::core::ADT::tensor::IndexEnumerator;
+using nncc::core::ADT::tensor::Index;
+
+template <typename T>
+std::unique_ptr<locomotiv::NodeData> matrix_decode(const loco::MatrixDecode *node,
+ const Buffer<T> *input_buf)
+{
+ auto decoder = node->decoder();
+
+ // Make MatrixShape from input. Note that matrix in locomotiv represented as HW
+ loco::MatrixShape input_shape;
+ assert(input_buf->shape().rank() == 2);
+ input_shape.height() = input_buf->shape().dim(0);
+ input_shape.width() = input_buf->shape().dim(1);
+
+ loco::TensorShape node_shape = decoder->shape(input_shape);
+
+ // Make tensor buffer from TensorShape
+ Buffer<T> node_buf =
+ make_buffer<T, LexicalLayout>(Shape{node_shape.dim(0).value(), node_shape.dim(1).value()});
+
+ // Copy buffer in an order arranged by decoder
+ for (IndexEnumerator e{node_buf.shape()}; e.valid(); e.advance())
+ {
+ loco::MatrixIndex matrix_index = decoder->value(e.current());
+ Index buf_index({matrix_index.row(), matrix_index.column()});
+
+ node_buf.at(e.current()) = input_buf->at(buf_index);
+ }
+
+ return locomotiv::make_data(node_buf);
+}
+
+} // namespace
+
+namespace
+{
+
+using namespace locomotiv;
+
+void execute_node(loco::MatrixDecode *matrix_dec)
+{
+ auto input_data = annot_data(matrix_dec->input());
+
+ validate(input_data, "Input not ready");
+ validate(annot_domain(matrix_dec->input()) == loco::Domain::Matrix,
+ "Input domain should be Matrix");
+ validate(input_data->shape()->rank() == 2, "Input data rank must be 2");
+
+ std::unique_ptr<NodeData> matrix_dec_data = nullptr;
+
+ switch (input_data->dtype())
+ {
+ case loco::DataType::S32:
+ {
+ auto input_buf = input_data->as_s32_bufptr();
+ matrix_dec_data = matrix_decode<int32_t>(matrix_dec, input_buf);
+ break;
+ }
+ case loco::DataType::FLOAT32:
+ {
+ auto input_buf = input_data->as_f32_bufptr();
+ matrix_dec_data = matrix_decode<float>(matrix_dec, input_buf);
+ break;
+ }
+ default:
+ throw std::runtime_error("NYI for this DataType");
+ }
+
+ assert(matrix_dec_data != nullptr);
+
+ annot_data(matrix_dec, std::move(matrix_dec_data));
+ annot_domain(matrix_dec, loco::Domain::Tensor);
+}
+
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::MatrixDecode *matrix_dec) { execute_node(matrix_dec); }
+
+} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/MatrixEncode.cpp b/compiler/locomotiv/src/Node/MatrixEncode.cpp
new file mode 100644
index 000000000..e3554e15a
--- /dev/null
+++ b/compiler/locomotiv/src/Node/MatrixEncode.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NodeExecution.h"
+
+#include "NodeDataImpl.h"
+#include "NodeDomain.h"
+#include "Validation.h"
+
+#include <nncc/core/ADT/tensor/LexicalLayout.h>
+#include <nncc/core/ADT/tensor/IndexEnumerator.h>
+
+#include <stdexcept>
+#include <cassert>
+
+namespace
+{
+
+using nncc::core::ADT::tensor::Buffer;
+using nncc::core::ADT::tensor::make_buffer;
+using nncc::core::ADT::tensor::LexicalLayout;
+using nncc::core::ADT::tensor::Shape;
+using nncc::core::ADT::tensor::IndexEnumerator;
+
+template <typename T>
+std::unique_ptr<locomotiv::NodeData> matrix_encode(const loco::MatrixEncode *node,
+ const Buffer<T> *input_buf)
+{
+ auto encoder = node->encoder();
+
+ // Make TensorShape from input
+ loco::TensorShape input_shape;
+ input_shape.rank(input_buf->shape().rank());
+ assert(input_shape.rank() == 2);
+ for (uint32_t i = 0; i < input_shape.rank(); ++i)
+ {
+ input_shape.dim(i) = input_buf->shape().dim(i);
+ }
+
+ loco::MatrixShape node_shape = encoder->shape(input_shape);
+
+ // Make HW buffer from MatrixShape
+ Buffer<T> node_buf =
+ make_buffer<T, LexicalLayout>(Shape{node_shape.height().value(), node_shape.width().value()});
+
+ // Copy buffer in an order arranged by encoder
+ for (IndexEnumerator e{node_buf.shape()}; e.valid(); e.advance())
+ {
+ loco::MatrixIndex index;
+ index.row() = e.current().at(0);
+ index.column() = e.current().at(1);
+
+ node_buf.at(e.current()) = input_buf->at(encoder->value(index));
+ }
+
+ return locomotiv::make_data(node_buf);
+}
+
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::MatrixEncode *matrix_enc)
+{
+ auto input_data = annot_data(matrix_enc->input());
+
+ validate(input_data, "Input not ready");
+ validate(annot_domain(matrix_enc->input()) == loco::Domain::Tensor,
+ "Input domain should be Tensor");
+ validate(input_data->shape()->rank() == 2, "Input data rank must be 2");
+
+ std::unique_ptr<NodeData> matrix_enc_data = nullptr;
+
+ switch (input_data->dtype())
+ {
+ case loco::DataType::S32:
+ {
+ auto input_buf = input_data->as_s32_bufptr();
+ matrix_enc_data = matrix_encode<int32_t>(matrix_enc, input_buf);
+ break;
+ }
+ case loco::DataType::FLOAT32:
+ {
+ auto input_buf = input_data->as_f32_bufptr();
+ matrix_enc_data = matrix_encode<float>(matrix_enc, input_buf);
+ break;
+ }
+ default:
+ throw std::runtime_error("NYI for this DataType");
+ }
+
+ assert(matrix_enc_data != nullptr);
+
+ annot_data(matrix_enc, std::move(matrix_enc_data));
+ annot_domain(matrix_enc, loco::Domain::Matrix);
+}
+
+} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/MaxPool2D.cpp b/compiler/locomotiv/src/Node/MaxPool2D.cpp
index 5d92f89f5..8dce1cb1e 100644
--- a/compiler/locomotiv/src/Node/MaxPool2D.cpp
+++ b/compiler/locomotiv/src/Node/MaxPool2D.cpp
@@ -129,10 +129,12 @@ nncc::core::ADT::tensor::Buffer<T> maxPool2D(const loco::MaxPool2D *maxpool2d,
} // namespace
-namespace locomotiv
+namespace
{
-void NodeExecution::execute(loco::MaxPool2D *maxpool2d)
+using namespace locomotiv;
+
+void execute_node(loco::MaxPool2D *maxpool2d)
{
auto ifm_data = annot_data(maxpool2d->ifm());
@@ -164,4 +166,11 @@ void NodeExecution::execute(loco::MaxPool2D *maxpool2d)
annot_domain(maxpool2d, loco::Domain::Feature);
}
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::MaxPool2D *maxpool2d) { execute_node(maxpool2d); }
+
} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/MaxPool2D.test.cpp b/compiler/locomotiv/src/Node/MaxPool2D.test.cpp
index 9d877a96b..5046d4a6e 100644
--- a/compiler/locomotiv/src/Node/MaxPool2D.test.cpp
+++ b/compiler/locomotiv/src/Node/MaxPool2D.test.cpp
@@ -86,10 +86,10 @@ void run_test(const float *ifm, const float *expected_ofm, const Shape &ifm_shap
for (nncc::core::ADT::tensor::IndexEnumerator e{ofm_shape}; e.valid(); e.advance())
{
const auto &ind = e.current();
- ASSERT_FLOAT_EQ(maxpool2d_data->as_f32_bufptr()->at(ind), ofm_overlay.at(ind));
+ ASSERT_FLOAT_EQ(ofm_overlay.at(ind), maxpool2d_data->as_f32_bufptr()->at(ind));
}
- ASSERT_EQ(locomotiv::annot_domain(maxpool2d), loco::Domain::Feature);
+ ASSERT_EQ(loco::Domain::Feature, locomotiv::annot_domain(maxpool2d));
}
} // namespace
diff --git a/compiler/locomotiv/src/Node/Pull.cpp b/compiler/locomotiv/src/Node/Pull.cpp
index c482d8b04..fe5d7c2e1 100644
--- a/compiler/locomotiv/src/Node/Pull.cpp
+++ b/compiler/locomotiv/src/Node/Pull.cpp
@@ -24,10 +24,12 @@
#include <cassert>
#include <stdexcept>
-namespace locomotiv
+namespace
{
-void NodeExecution::execute(loco::Pull *pull)
+using namespace locomotiv;
+
+void execute_node(loco::Pull *pull)
{
// TODO Remove deprecated code
#if 0
@@ -69,4 +71,11 @@ void NodeExecution::execute(loco::Pull *pull)
annot_domain(pull, loco::Domain::Tensor);
}
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::Pull *pull) { execute_node(pull); }
+
} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/Push.cpp b/compiler/locomotiv/src/Node/Push.cpp
index fc5808b15..4e1c6c3b8 100644
--- a/compiler/locomotiv/src/Node/Push.cpp
+++ b/compiler/locomotiv/src/Node/Push.cpp
@@ -23,10 +23,12 @@
#include <stdexcept>
#include <cassert>
-namespace locomotiv
+namespace
{
-void NodeExecution::execute(loco::Push *push)
+using namespace locomotiv;
+
+void execute_node(loco::Push *push)
{
auto from_data = annot_data(push->from());
@@ -58,4 +60,11 @@ void NodeExecution::execute(loco::Push *push)
annot_domain(push, loco::Domain::Tensor);
}
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::Push *push) { execute_node(push); }
+
} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/Push.test.cpp b/compiler/locomotiv/src/Node/Push.test.cpp
index be8f1e4e9..e9f56056a 100644
--- a/compiler/locomotiv/src/Node/Push.test.cpp
+++ b/compiler/locomotiv/src/Node/Push.test.cpp
@@ -52,11 +52,11 @@ TEST(NodeExecution_Push, s32)
auto push_data = locomotiv::annot_data(push);
ASSERT_NE(push_data, nullptr);
- ASSERT_EQ(push_data->dtype(), loco::DataType::S32);
- ASSERT_EQ(*(push_data->shape()), Shape{1});
- ASSERT_EQ(push_data->as_s32_bufptr()->at(Index{0}), pull_buf.at(Index{0}));
+ ASSERT_EQ(loco::DataType::S32, push_data->dtype());
+ ASSERT_EQ(Shape{1}, *(push_data->shape()));
+ ASSERT_EQ(pull_buf.at(Index{0}), push_data->as_s32_bufptr()->at(Index{0}));
- ASSERT_EQ(locomotiv::annot_domain(push), loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(push));
}
TEST(NodeExecution_Push, f32)
@@ -80,9 +80,9 @@ TEST(NodeExecution_Push, f32)
auto push_data = locomotiv::annot_data(push);
ASSERT_NE(push_data, nullptr);
- ASSERT_EQ(push_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(push_data->shape()), Shape{1});
- ASSERT_FLOAT_EQ(push_data->as_f32_bufptr()->at(Index{0}), pull_buf.at(Index{0}));
+ ASSERT_EQ(loco::DataType::FLOAT32, push_data->dtype());
+ ASSERT_EQ(Shape{1}, *(push_data->shape()));
+ ASSERT_FLOAT_EQ(pull_buf.at(Index{0}), push_data->as_f32_bufptr()->at(Index{0}));
- ASSERT_EQ(locomotiv::annot_domain(push), loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(push));
}
diff --git a/compiler/locomotiv/src/Node/ReLU.test.cpp b/compiler/locomotiv/src/Node/ReLU.test.cpp
index 0ddd01d0f..d2f928d1d 100644
--- a/compiler/locomotiv/src/Node/ReLU.test.cpp
+++ b/compiler/locomotiv/src/Node/ReLU.test.cpp
@@ -53,10 +53,10 @@ TEST(NodeExecution_ReLU, f32)
auto relu_data = locomotiv::annot_data(relu);
ASSERT_NE(relu_data, nullptr);
- ASSERT_EQ(relu_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(relu_data->shape()), Shape{2});
- ASSERT_FLOAT_EQ(relu_data->as_f32_bufptr()->at(Index{0}), 0.0f);
- ASSERT_FLOAT_EQ(relu_data->as_f32_bufptr()->at(Index{1}), 10.0f);
+ ASSERT_EQ(loco::DataType::FLOAT32, relu_data->dtype());
+ ASSERT_EQ(Shape{2}, *(relu_data->shape()));
+ ASSERT_FLOAT_EQ(0.0f, relu_data->as_f32_bufptr()->at(Index{0}));
+ ASSERT_FLOAT_EQ(10.0f, relu_data->as_f32_bufptr()->at(Index{1}));
- ASSERT_EQ(locomotiv::annot_domain(relu), loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(relu));
}
diff --git a/compiler/locomotiv/src/Node/ReLU6.test.cpp b/compiler/locomotiv/src/Node/ReLU6.test.cpp
index 07f6af23f..b2362b1f4 100644
--- a/compiler/locomotiv/src/Node/ReLU6.test.cpp
+++ b/compiler/locomotiv/src/Node/ReLU6.test.cpp
@@ -55,12 +55,12 @@ TEST(NodeExecution_ReLU6, f32)
auto relu6_data = locomotiv::annot_data(relu6);
ASSERT_NE(relu6_data, nullptr);
- ASSERT_EQ(relu6_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(relu6_data->shape()), Shape({2, 2}));
- ASSERT_FLOAT_EQ(relu6_data->as_f32_bufptr()->at(Index{0, 0}), 0.0f);
- ASSERT_FLOAT_EQ(relu6_data->as_f32_bufptr()->at(Index{0, 1}), 6.0f);
- ASSERT_FLOAT_EQ(relu6_data->as_f32_bufptr()->at(Index{1, 0}), 6.0f);
- ASSERT_FLOAT_EQ(relu6_data->as_f32_bufptr()->at(Index{1, 1}), 0.0f);
+ ASSERT_EQ(loco::DataType::FLOAT32, relu6_data->dtype());
+ ASSERT_EQ(Shape({2, 2}), *(relu6_data->shape()));
+ ASSERT_FLOAT_EQ(0.0f, relu6_data->as_f32_bufptr()->at(Index{0, 0}));
+ ASSERT_FLOAT_EQ(6.0f, relu6_data->as_f32_bufptr()->at(Index{0, 1}));
+ ASSERT_FLOAT_EQ(6.0f, relu6_data->as_f32_bufptr()->at(Index{1, 0}));
+ ASSERT_FLOAT_EQ(0.0f, relu6_data->as_f32_bufptr()->at(Index{1, 1}));
- ASSERT_EQ(locomotiv::annot_domain(relu6), loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(relu6));
}
diff --git a/compiler/locomotiv/src/Node/Reshape.cpp b/compiler/locomotiv/src/Node/Reshape.cpp
index 08be4c7d3..a9c07bee1 100644
--- a/compiler/locomotiv/src/Node/Reshape.cpp
+++ b/compiler/locomotiv/src/Node/Reshape.cpp
@@ -36,10 +36,12 @@ using nncc::core::ADT::tensor::num_elements;
#include <cstring>
#include <vector>
-namespace locomotiv
+namespace
{
-void NodeExecution::execute(loco::Reshape<loco::ReshapeType::Fixed> *reshape)
+using namespace locomotiv;
+
+void execute_node(loco::Reshape<loco::ReshapeType::Fixed> *reshape)
{
auto input_data = annot_data(reshape->input());
@@ -71,9 +73,8 @@ void NodeExecution::execute(loco::Reshape<loco::ReshapeType::Fixed> *reshape)
uint64_t input_len = num_elements(*input_shape) * sizeof(float);
float *output_ptr = reshape_bufptr.base();
- uint64_t output_len = num_elements(*output_shape) * sizeof(float);
- assert(input_len == output_len);
+ assert(input_len == num_elements(*output_shape) * sizeof(float));
memcpy(output_ptr, input_ptr, input_len);
reshape_data = make_data(reshape_bufptr);
@@ -88,4 +89,14 @@ void NodeExecution::execute(loco::Reshape<loco::ReshapeType::Fixed> *reshape)
annot_domain(reshape, annot_domain(reshape->input()));
}
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::Reshape<loco::ReshapeType::Fixed> *reshape)
+{
+ execute_node(reshape);
+}
+
} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/Reshape.test.cpp b/compiler/locomotiv/src/Node/Reshape.test.cpp
index 8e54a16df..8aeb4656f 100644
--- a/compiler/locomotiv/src/Node/Reshape.test.cpp
+++ b/compiler/locomotiv/src/Node/Reshape.test.cpp
@@ -56,12 +56,12 @@ TEST(NodeExecution_Reshape, f32)
auto reshape_data = locomotiv::annot_data(reshape);
ASSERT_NE(reshape_data, nullptr);
- ASSERT_EQ(reshape_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(reshape_data->shape()), (Shape{2, 2}));
- ASSERT_FLOAT_EQ(reshape_data->as_f32_bufptr()->at(Index{0, 0}), 0.0f);
- ASSERT_FLOAT_EQ(reshape_data->as_f32_bufptr()->at(Index{0, 1}), 1.1f);
- ASSERT_FLOAT_EQ(reshape_data->as_f32_bufptr()->at(Index{1, 0}), 2.2f);
- ASSERT_FLOAT_EQ(reshape_data->as_f32_bufptr()->at(Index{1, 1}), 3.3f);
+ ASSERT_EQ(loco::DataType::FLOAT32, reshape_data->dtype());
+ ASSERT_EQ((Shape{2, 2}), *(reshape_data->shape()));
+ ASSERT_FLOAT_EQ(0.0f, reshape_data->as_f32_bufptr()->at(Index{0, 0}));
+ ASSERT_FLOAT_EQ(1.1f, reshape_data->as_f32_bufptr()->at(Index{0, 1}));
+ ASSERT_FLOAT_EQ(2.2f, reshape_data->as_f32_bufptr()->at(Index{1, 0}));
+ ASSERT_FLOAT_EQ(3.3f, reshape_data->as_f32_bufptr()->at(Index{1, 1}));
- ASSERT_EQ(locomotiv::annot_domain(reshape), loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(reshape));
}
diff --git a/compiler/locomotiv/src/Node/Softmax.cpp b/compiler/locomotiv/src/Node/Softmax.cpp
index 352598b27..0018eb66f 100644
--- a/compiler/locomotiv/src/Node/Softmax.cpp
+++ b/compiler/locomotiv/src/Node/Softmax.cpp
@@ -65,10 +65,12 @@ Shape reduce_shape(const Shape &shape, uint32_t axis)
} // namespace
-namespace locomotiv
+namespace
{
-void NodeExecution::execute(loco::TensorSoftmax *softmax)
+using namespace locomotiv;
+
+void execute_node(loco::TensorSoftmax *softmax)
{
auto input_data = annot_data(softmax->input());
@@ -119,4 +121,11 @@ void NodeExecution::execute(loco::TensorSoftmax *softmax)
annot_domain(softmax, annot_domain(softmax->input()));
}
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::TensorSoftmax *softmax) { execute_node(softmax); }
+
} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/Softmax.test.cpp b/compiler/locomotiv/src/Node/Softmax.test.cpp
index 21d240275..257279338 100644
--- a/compiler/locomotiv/src/Node/Softmax.test.cpp
+++ b/compiler/locomotiv/src/Node/Softmax.test.cpp
@@ -57,12 +57,12 @@ TEST(NodeExecution_Softmax, f32)
auto kShape = Shape{2, 2};
auto softmax_data = locomotiv::annot_data(softmax);
ASSERT_NE(softmax_data, nullptr);
- ASSERT_EQ(softmax_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(softmax_data->shape()), kShape);
- ASSERT_FLOAT_EQ(softmax_data->as_f32_bufptr()->at(Index{0, 0}), 0.5f);
- ASSERT_FLOAT_EQ(softmax_data->as_f32_bufptr()->at(Index{0, 1}), 0.5f);
- ASSERT_FLOAT_EQ(softmax_data->as_f32_bufptr()->at(Index{1, 0}), 0.5f);
- ASSERT_FLOAT_EQ(softmax_data->as_f32_bufptr()->at(Index{1, 1}), 0.5f);
+ ASSERT_EQ(loco::DataType::FLOAT32, softmax_data->dtype());
+ ASSERT_EQ(kShape, *(softmax_data->shape()));
+ ASSERT_FLOAT_EQ(0.5f, softmax_data->as_f32_bufptr()->at(Index{0, 0}));
+ ASSERT_FLOAT_EQ(0.5f, softmax_data->as_f32_bufptr()->at(Index{0, 1}));
+ ASSERT_FLOAT_EQ(0.5f, softmax_data->as_f32_bufptr()->at(Index{1, 0}));
+ ASSERT_FLOAT_EQ(0.5f, softmax_data->as_f32_bufptr()->at(Index{1, 1}));
- ASSERT_EQ(locomotiv::annot_domain(softmax), loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(softmax));
}
diff --git a/compiler/locomotiv/src/Node/Tanh.test.cpp b/compiler/locomotiv/src/Node/Tanh.test.cpp
index 78c3a13ba..96c1e7f1f 100644
--- a/compiler/locomotiv/src/Node/Tanh.test.cpp
+++ b/compiler/locomotiv/src/Node/Tanh.test.cpp
@@ -54,11 +54,11 @@ TEST(NodeExecution_Tanh, f32)
auto tanh_data = locomotiv::annot_data(tanh);
ASSERT_NE(tanh_data, nullptr);
- ASSERT_EQ(tanh_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(tanh_data->shape()), Shape{3});
- ASSERT_FLOAT_EQ(tanh_data->as_f32_bufptr()->at(Index{0}), 0.0f);
- ASSERT_FLOAT_EQ(tanh_data->as_f32_bufptr()->at(Index{1}), 0.761594f);
- ASSERT_FLOAT_EQ(tanh_data->as_f32_bufptr()->at(Index{2}), -0.761594f);
+ ASSERT_EQ(loco::DataType::FLOAT32, tanh_data->dtype());
+ ASSERT_EQ(Shape{3}, *(tanh_data->shape()));
+ ASSERT_FLOAT_EQ(0.0f, tanh_data->as_f32_bufptr()->at(Index{0}));
+ ASSERT_FLOAT_EQ(0.761594f, tanh_data->as_f32_bufptr()->at(Index{1}));
+ ASSERT_FLOAT_EQ(-0.761594f, tanh_data->as_f32_bufptr()->at(Index{2}));
- ASSERT_EQ(locomotiv::annot_domain(tanh), loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(tanh));
}
diff --git a/compiler/locomotiv/src/Node/TensorBroadcast.cpp b/compiler/locomotiv/src/Node/TensorBroadcast.cpp
index 010ca6821..38e5a7aa9 100644
--- a/compiler/locomotiv/src/Node/TensorBroadcast.cpp
+++ b/compiler/locomotiv/src/Node/TensorBroadcast.cpp
@@ -34,10 +34,12 @@ using nncc::core::ADT::tensor::Shape;
#include <cassert>
#include <stdexcept>
-namespace locomotiv
+namespace
{
-void NodeExecution::execute(loco::TensorBroadcast *tensor_broadcast)
+using namespace locomotiv;
+
+void execute_node(loco::TensorBroadcast *tensor_broadcast)
{
auto input_data = annot_data(tensor_broadcast->input());
@@ -103,4 +105,14 @@ void NodeExecution::execute(loco::TensorBroadcast *tensor_broadcast)
annot_domain(tensor_broadcast, loco::Domain::Tensor);
}
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::TensorBroadcast *tensor_broadcast)
+{
+ execute_node(tensor_broadcast);
+}
+
} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/TensorBroadcast.test.cpp b/compiler/locomotiv/src/Node/TensorBroadcast.test.cpp
index e8347d737..52f7c8517 100644
--- a/compiler/locomotiv/src/Node/TensorBroadcast.test.cpp
+++ b/compiler/locomotiv/src/Node/TensorBroadcast.test.cpp
@@ -54,10 +54,10 @@ TEST(NodeExecution_TensorBroadcast, f32)
auto broadcast_data = locomotiv::annot_data(broadcast);
ASSERT_NE(broadcast_data, nullptr);
- ASSERT_EQ(broadcast_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ((*(broadcast_data->shape())), (Shape{2, 1}));
- ASSERT_FLOAT_EQ(broadcast_data->as_f32_bufptr()->at(Index{0, 0}), -1.0f);
- ASSERT_FLOAT_EQ(broadcast_data->as_f32_bufptr()->at(Index{1, 0}), -1.0f);
+ ASSERT_EQ(loco::DataType::FLOAT32, broadcast_data->dtype());
+ ASSERT_EQ((Shape{2, 1}), (*(broadcast_data->shape())));
+ ASSERT_FLOAT_EQ(-1.0f, broadcast_data->as_f32_bufptr()->at(Index{0, 0}));
+ ASSERT_FLOAT_EQ(-1.0f, broadcast_data->as_f32_bufptr()->at(Index{1, 0}));
- ASSERT_EQ(locomotiv::annot_domain(broadcast), loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(broadcast));
}
diff --git a/compiler/locomotiv/src/Node/TensorConcat.cpp b/compiler/locomotiv/src/Node/TensorConcat.cpp
index 5097e55c6..188bb635b 100644
--- a/compiler/locomotiv/src/Node/TensorConcat.cpp
+++ b/compiler/locomotiv/src/Node/TensorConcat.cpp
@@ -35,11 +35,15 @@ using nncc::core::ADT::tensor::Shape;
#include <cassert>
#include <stdexcept>
-namespace locomotiv
+namespace
{
-void NodeExecution::execute(loco::TensorConcat *tensor_concat)
+using namespace locomotiv;
+
+void execute_node(loco::TensorConcat *tensor_concat)
{
+ validate(tensor_concat, "TensorConcat is nullptr");
+
auto lhs_data = annot_data(tensor_concat->lhs());
auto rhs_data = annot_data(tensor_concat->rhs());
auto axis = tensor_concat->axis();
@@ -110,4 +114,11 @@ void NodeExecution::execute(loco::TensorConcat *tensor_concat)
annot_domain(tensor_concat, loco::Domain::Tensor);
}
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::TensorConcat *tensor_concat) { execute_node(tensor_concat); }
+
} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/TensorConcat.test.cpp b/compiler/locomotiv/src/Node/TensorConcat.test.cpp
index d71b51524..e9060e36f 100644
--- a/compiler/locomotiv/src/Node/TensorConcat.test.cpp
+++ b/compiler/locomotiv/src/Node/TensorConcat.test.cpp
@@ -65,14 +65,14 @@ TEST(NodeExecution_TensorConcat, f32)
auto concat_data = locomotiv::annot_data(tconcat);
ASSERT_NE(concat_data, nullptr);
- ASSERT_EQ(concat_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ((*(concat_data->shape())), (Shape{2, 2}));
- ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{0, 0}), -1.0f);
- ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{0, 1}), -2.0f);
- ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{1, 0}), 3.0f);
- ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{1, 1}), 4.0f);
-
- ASSERT_EQ(locomotiv::annot_domain(tconcat), loco::Domain::Tensor);
+ ASSERT_EQ(loco::DataType::FLOAT32, concat_data->dtype());
+ ASSERT_EQ((Shape{2, 2}), (*(concat_data->shape())));
+ ASSERT_FLOAT_EQ(-1.0f, concat_data->as_f32_bufptr()->at(Index{0, 0}));
+ ASSERT_FLOAT_EQ(-2.0f, concat_data->as_f32_bufptr()->at(Index{0, 1}));
+ ASSERT_FLOAT_EQ(3.0f, concat_data->as_f32_bufptr()->at(Index{1, 0}));
+ ASSERT_FLOAT_EQ(4.0f, concat_data->as_f32_bufptr()->at(Index{1, 1}));
+
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(tconcat));
}
TEST(NodeExecution_TensorConcat, f32_2)
@@ -113,16 +113,16 @@ TEST(NodeExecution_TensorConcat, f32_2)
auto concat_data = locomotiv::annot_data(tconcat);
ASSERT_NE(concat_data, nullptr);
- ASSERT_EQ(concat_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ((*(concat_data->shape())), (Shape{4, 2}));
- ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{0, 0}), -1.0f);
- ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{0, 1}), -2.0f);
- ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{1, 0}), 3.0f);
- ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{1, 1}), 4.0f);
- ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{2, 0}), -3.0f);
- ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{2, 1}), -4.0f);
- ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{3, 0}), 5.0f);
- ASSERT_FLOAT_EQ(concat_data->as_f32_bufptr()->at(Index{3, 1}), 6.0f);
-
- ASSERT_EQ(locomotiv::annot_domain(tconcat), loco::Domain::Tensor);
+ ASSERT_EQ(loco::DataType::FLOAT32, concat_data->dtype());
+ ASSERT_EQ((Shape{4, 2}), (*(concat_data->shape())));
+ ASSERT_FLOAT_EQ(-1.0f, concat_data->as_f32_bufptr()->at(Index{0, 0}));
+ ASSERT_FLOAT_EQ(-2.0f, concat_data->as_f32_bufptr()->at(Index{0, 1}));
+ ASSERT_FLOAT_EQ(3.0f, concat_data->as_f32_bufptr()->at(Index{1, 0}));
+ ASSERT_FLOAT_EQ(4.0f, concat_data->as_f32_bufptr()->at(Index{1, 1}));
+ ASSERT_FLOAT_EQ(-3.0f, concat_data->as_f32_bufptr()->at(Index{2, 0}));
+ ASSERT_FLOAT_EQ(-4.0f, concat_data->as_f32_bufptr()->at(Index{2, 1}));
+ ASSERT_FLOAT_EQ(5.0f, concat_data->as_f32_bufptr()->at(Index{3, 0}));
+ ASSERT_FLOAT_EQ(6.0f, concat_data->as_f32_bufptr()->at(Index{3, 1}));
+
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(tconcat));
}
diff --git a/compiler/locomotiv/src/Node/TensorConstantPad.cpp b/compiler/locomotiv/src/Node/TensorConstantPad.cpp
new file mode 100644
index 000000000..5d4ad5d24
--- /dev/null
+++ b/compiler/locomotiv/src/Node/TensorConstantPad.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NodeExecution.h"
+
+#include "NodeDataImpl.h"
+#include "NodeDomain.h"
+#include "Validation.h"
+
+#include <nncc/core/ADT/tensor/IndexEnumerator.h>
+#include <nncc/core/ADT/tensor/LexicalLayout.h>
+
+#include <cassert>
+
+using nncc::core::ADT::tensor::Shape;
+using nncc::core::ADT::tensor::Index;
+using nncc::core::ADT::tensor::IndexEnumerator;
+using nncc::core::ADT::tensor::LexicalLayout;
+using nncc::core::ADT::tensor::make_buffer;
+
+namespace
+{
+
+using namespace locomotiv;
+
+void execute_node(loco::TensorConstantPad *pad)
+{
+ validate(pad, "TensorConstantPad is nullptr");
+
+ auto input_data = annot_data(pad->input());
+ auto input_domain = annot_domain(pad->input());
+ validate(input_data, "Input not ready");
+ validate(input_domain == loco::Domain::Tensor, "Input domain of TensorConstantPad is not Tensor");
+
+ auto input_shape = input_data->shape();
+ const uint32_t input_rank = input_shape->rank();
+
+ auto padding = pad->padding();
+ validate(input_rank == padding->rank(), "input and padding should have same rank");
+
+ auto constant_node = pad->constant();
+ auto constant_data = annot_data(constant_node);
+ validate(constant_data->dtype() == input_data->dtype(), "constant and input have same data type");
+ validate(constant_data->shape()->rank() == 1 && constant_data->shape()->dim(0) == 1,
+ "constant should have one rank with one dimension at zero axis");
+
+ std::unique_ptr<NodeData> pad_data = nullptr;
+ Index base_index;
+ base_index.resize(input_rank);
+
+ // Tensor is padded by relocating its base.
+ // padded output index = input index + base index
+ for (uint32_t axis = 0; axis < padding->rank(); axis++)
+ {
+ base_index.at(axis) = padding->front(axis);
+ }
+
+ // calculate output shape
+ Shape output_shape;
+ output_shape.resize(input_rank);
+ for (uint32_t i = 0; i < input_rank; i++)
+ {
+ output_shape.dim(i) = input_shape->dim(i) + padding->front(i) + padding->back(i);
+ }
+
+ switch (input_data->dtype())
+ {
+ case loco::DataType::FLOAT32:
+ {
+ auto input_buf = input_data->as_f32_bufptr();
+ auto constant_data_buf = constant_data->as_f32_bufptr();
+ const auto constant_value = constant_data_buf->at(Index{0});
+
+ auto output_buf = make_buffer<float, LexicalLayout>(output_shape);
+
+ for (IndexEnumerator ie{*input_shape}, oe{output_shape}; oe.valid(); oe.advance())
+ {
+ auto input_index = ie.current();
+ auto output_index = oe.current();
+
+ if ((input_index + base_index) == output_index)
+ {
+ output_buf.at(output_index) = input_buf->at(input_index);
+ ie.advance();
+ }
+ else
+ {
+ output_buf.at(output_index) = constant_value;
+ }
+ }
+
+ pad_data = make_data(output_buf);
+ break;
+ }
+ default:
+ throw std::runtime_error("NYI for this DataType");
+ }
+
+ assert(pad_data != nullptr);
+ annot_data(pad, std::move(pad_data));
+ annot_domain(pad, annot_domain(pad->input()));
+}
+
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::TensorConstantPad *pad) { execute_node(pad); }
+
+} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/TensorConstantPad.test.cpp b/compiler/locomotiv/src/Node/TensorConstantPad.test.cpp
new file mode 100644
index 000000000..64b913014
--- /dev/null
+++ b/compiler/locomotiv/src/Node/TensorConstantPad.test.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NodeExecution.h"
+
+#include "locomotiv/NodeData.h"
+#include "NodeDataImpl.h"
+#include "NodeDomain.h"
+
+#include <nncc/core/ADT/tensor/Shape.h>
+#include <nncc/core/ADT/tensor/LexicalLayout.h>
+
+#include <gtest/gtest.h>
+
+using nncc::core::ADT::tensor::Index;
+using nncc::core::ADT::tensor::LexicalLayout;
+using nncc::core::ADT::tensor::make_buffer;
+using nncc::core::ADT::tensor::Shape;
+
+TEST(NodeExecution_Pad, tensor_constant_pad_4_dim)
+{
+ auto g = loco::make_graph();
+
+ auto inputTensor = g->nodes()->create<loco::Pull>();
+ inputTensor->dtype(loco::DataType::FLOAT32);
+ inputTensor->shape({1, 2, 2, 1});
+ auto inputTensor_buf = make_buffer<float, LexicalLayout>(Shape{1, 2, 2, 1});
+ inputTensor_buf.at(Index{0, 0, 0, 0}) = 1.0f;
+ inputTensor_buf.at(Index{0, 0, 1, 0}) = 2.0f;
+ inputTensor_buf.at(Index{0, 1, 0, 0}) = 3.0f;
+ inputTensor_buf.at(Index{0, 1, 1, 0}) = 4.0f;
+ auto inputTensor_data = locomotiv::make_data(inputTensor_buf);
+ locomotiv::annot_data(inputTensor, std::move(inputTensor_data));
+ locomotiv::annot_domain(inputTensor, loco::Domain::Tensor);
+
+ auto constant = g->nodes()->create<loco::ConstGen>();
+ constant->dtype(loco::DataType::FLOAT32);
+ constant->shape({1});
+ auto constant_buf = make_buffer<float, LexicalLayout>(Shape{1});
+ constant_buf.at(Index{0}) = 0.0f;
+ auto constant_data = locomotiv::make_data(constant_buf);
+ locomotiv::annot_data(constant, std::move(constant_data));
+ locomotiv::annot_domain(constant, loco::Domain::Tensor);
+
+ auto pad = g->nodes()->create<loco::TensorConstantPad>();
+ pad->input(inputTensor);
+ pad->constant(constant);
+
+ auto padding = pad->padding();
+ padding->rank(4);
+ padding->front(0) = 0;
+ padding->back(0) = 0;
+ padding->front(1) = 3;
+ padding->back(1) = 1;
+ padding->front(2) = 1;
+ padding->back(2) = 1;
+ padding->front(3) = 0;
+ padding->back(3) = 0;
+
+ locomotiv::NodeExecution::get().run(pad);
+
+ auto pad_data = locomotiv::annot_data(pad);
+ ASSERT_NE(pad_data, nullptr);
+ ASSERT_EQ(loco::DataType::FLOAT32, pad_data->dtype());
+ ASSERT_EQ(Shape({1, 6, 4, 1}), *(pad_data->shape()));
+
+ ASSERT_FLOAT_EQ(1.0f, pad_data->as_f32_bufptr()->at(Index{0, 3, 1, 0}));
+ ASSERT_FLOAT_EQ(2.0f, pad_data->as_f32_bufptr()->at(Index{0, 3, 2, 0}));
+ ASSERT_FLOAT_EQ(3.0f, pad_data->as_f32_bufptr()->at(Index{0, 4, 1, 0}));
+ ASSERT_FLOAT_EQ(4.0f, pad_data->as_f32_bufptr()->at(Index{0, 4, 2, 0}));
+ ASSERT_FLOAT_EQ(0.0f, pad_data->as_f32_bufptr()->at(Index{0, 0, 0, 0}));
+
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(pad));
+}
+
+TEST(NodeExecution_Pad, tensor_constant_pad_1_dim)
+{
+ auto g = loco::make_graph();
+
+ auto inputTensor = g->nodes()->create<loco::Pull>();
+ inputTensor->dtype(loco::DataType::FLOAT32);
+ inputTensor->shape({3});
+ auto inputTensor_buf = make_buffer<float, LexicalLayout>(Shape{3});
+ inputTensor_buf.at(Index{0}) = 1.0f;
+ inputTensor_buf.at(Index{1}) = 5.0f;
+ inputTensor_buf.at(Index{2}) = 3.0f;
+ auto inputTensor_data = locomotiv::make_data(inputTensor_buf);
+ locomotiv::annot_data(inputTensor, std::move(inputTensor_data));
+ locomotiv::annot_domain(inputTensor, loco::Domain::Tensor);
+
+ auto constant = g->nodes()->create<loco::ConstGen>();
+ constant->dtype(loco::DataType::FLOAT32);
+ constant->shape({1});
+ auto constant_buf = make_buffer<float, LexicalLayout>(Shape{1});
+ constant_buf.at(Index{0}) = 0.0f;
+ auto constant_data = locomotiv::make_data(constant_buf);
+ locomotiv::annot_data(constant, std::move(constant_data));
+ locomotiv::annot_domain(constant, loco::Domain::Tensor);
+
+ auto pad = g->nodes()->create<loco::TensorConstantPad>();
+ pad->input(inputTensor);
+ pad->constant(constant);
+ auto padding = pad->padding();
+ padding->rank(1);
+ padding->front(0) = 2;
+ padding->back(0) = 1;
+
+ locomotiv::NodeExecution::get().run(pad);
+
+ auto pad_data = locomotiv::annot_data(pad);
+ ASSERT_NE(pad_data, nullptr);
+ ASSERT_EQ(loco::DataType::FLOAT32, pad_data->dtype());
+ ASSERT_EQ(Shape({6}), *(pad_data->shape()));
+
+ ASSERT_FLOAT_EQ(0.0f, pad_data->as_f32_bufptr()->at(Index{0}));
+ ASSERT_FLOAT_EQ(0.0f, pad_data->as_f32_bufptr()->at(Index{1}));
+ ASSERT_FLOAT_EQ(1.0f, pad_data->as_f32_bufptr()->at(Index{2}));
+ ASSERT_FLOAT_EQ(5.0f, pad_data->as_f32_bufptr()->at(Index{3}));
+ ASSERT_FLOAT_EQ(3.0f, pad_data->as_f32_bufptr()->at(Index{4}));
+ ASSERT_FLOAT_EQ(0.0f, pad_data->as_f32_bufptr()->at(Index{5}));
+
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(pad));
+}
+
+TEST(NodeExecution_Pad, tensor_constant_pad_6_dim)
+{
+ auto g = loco::make_graph();
+
+ auto inputTensor = g->nodes()->create<loco::Pull>();
+ inputTensor->dtype(loco::DataType::FLOAT32);
+ inputTensor->shape({2, 1, 3, 2, 1, 2});
+ auto inputTensor_buf = make_buffer<float, LexicalLayout>(Shape{2, 1, 3, 2, 1, 2});
+ int a, b, c, d, e, f;
+ float dummy = 1.0f;
+ for (uint32_t a = 0; a < 2; a++)
+ {
+ for (uint32_t b = 0; b < 1; b++)
+ {
+ for (uint32_t c = 0; c < 3; c++)
+ {
+ for (uint32_t d = 0; d < 2; d++)
+ {
+ for (uint32_t e = 0; e < 1; e++)
+ {
+ for (uint32_t f = 0; f < 2; f++)
+ {
+ inputTensor_buf.at(Index{a, b, c, d, e, f}) = dummy++;
+ }
+ }
+ }
+ }
+ }
+ }
+ auto inputTensor_data = locomotiv::make_data(inputTensor_buf);
+ locomotiv::annot_data(inputTensor, std::move(inputTensor_data));
+ locomotiv::annot_domain(inputTensor, loco::Domain::Tensor);
+
+ auto constant = g->nodes()->create<loco::ConstGen>();
+ constant->dtype(loco::DataType::FLOAT32);
+ constant->shape({1});
+ auto constant_buf = make_buffer<float, LexicalLayout>(Shape{1});
+ constant_buf.at(Index{0}) = 0.0f;
+ auto constant_data = locomotiv::make_data(constant_buf);
+ locomotiv::annot_data(constant, std::move(constant_data));
+ locomotiv::annot_domain(constant, loco::Domain::Tensor);
+
+ auto pad = g->nodes()->create<loco::TensorConstantPad>();
+ pad->input(inputTensor);
+ pad->constant(constant);
+ auto padding = pad->padding();
+
+ padding->rank(6);
+ padding->front(0) = 1;
+ padding->back(0) = 1;
+ padding->front(1) = 0;
+ padding->back(1) = 0;
+ padding->front(2) = 1;
+ padding->back(2) = 2;
+ padding->front(3) = 2;
+ padding->back(3) = 1;
+ padding->front(4) = 0;
+ padding->back(4) = 0;
+ padding->front(5) = 1;
+ padding->back(5) = 2;
+
+ locomotiv::NodeExecution::get().run(pad);
+
+ auto pad_data = locomotiv::annot_data(pad);
+ ASSERT_NE(pad_data, nullptr);
+ ASSERT_EQ(loco::DataType::FLOAT32, pad_data->dtype());
+ ASSERT_EQ(Shape({4, 1, 6, 5, 1, 5}), *(pad_data->shape()));
+
+ ASSERT_FLOAT_EQ(1.0f, pad_data->as_f32_bufptr()->at(Index{1, 0, 1, 2, 0, 1}));
+ ASSERT_FLOAT_EQ(2.0f, pad_data->as_f32_bufptr()->at(Index{1, 0, 1, 2, 0, 2}));
+ ASSERT_FLOAT_EQ(3.0f, pad_data->as_f32_bufptr()->at(Index{1, 0, 1, 3, 0, 1}));
+ ASSERT_FLOAT_EQ(4.0f, pad_data->as_f32_bufptr()->at(Index{1, 0, 1, 3, 0, 2}));
+ ASSERT_FLOAT_EQ(5.0f, pad_data->as_f32_bufptr()->at(Index{1, 0, 2, 2, 0, 1}));
+ ASSERT_FLOAT_EQ(6.0f, pad_data->as_f32_bufptr()->at(Index{1, 0, 2, 2, 0, 2}));
+ ASSERT_FLOAT_EQ(7.0f, pad_data->as_f32_bufptr()->at(Index{1, 0, 2, 3, 0, 1}));
+ ASSERT_FLOAT_EQ(8.0f, pad_data->as_f32_bufptr()->at(Index{1, 0, 2, 3, 0, 2}));
+ ASSERT_FLOAT_EQ(9.0f, pad_data->as_f32_bufptr()->at(Index{1, 0, 3, 2, 0, 1}));
+ ASSERT_FLOAT_EQ(10.0f, pad_data->as_f32_bufptr()->at(Index{1, 0, 3, 2, 0, 2}));
+
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(pad));
+}
diff --git a/compiler/locomotiv/src/Node/TensorReduce.cpp b/compiler/locomotiv/src/Node/TensorReduce.cpp
new file mode 100644
index 000000000..1f619a31a
--- /dev/null
+++ b/compiler/locomotiv/src/Node/TensorReduce.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NodeExecution.h"
+#include "NodeDataImpl.h"
+#include "NodeDomain.h"
+#include "Validation.h"
+
+#include <nncc/core/ADT/tensor/Shape.h>
+#include <nncc/core/ADT/tensor/Buffer.h>
+#include <nncc/core/ADT/tensor/Index.h>
+#include <nncc/core/ADT/tensor/IndexEnumerator.h>
+#include <nncc/core/ADT/tensor/LexicalLayout.h>
+
+using nncc::core::ADT::tensor::Index;
+using nncc::core::ADT::tensor::IndexEnumerator;
+using nncc::core::ADT::tensor::LexicalLayout;
+using nncc::core::ADT::tensor::make_buffer;
+using nncc::core::ADT::tensor::Shape;
+using nncc::core::ADT::tensor::Buffer;
+
+#include <cassert>
+#include <stdexcept>
+
+namespace
+{
+
+Index reduced_index(const Index &index, const loco::TensorAxisSet &axes)
+{
+ Index r_index;
+
+ r_index.resize(index.rank());
+ for (uint32_t i = 0; i < index.rank(); ++i)
+ r_index.at(i) = (axes.defined(i)) ? 0 : index.at(i);
+
+ return r_index;
+}
+
+Shape reduced_shape(const Shape &shape, const loco::TensorAxisSet &axes)
+{
+ Shape r_shape;
+
+ r_shape.resize(shape.rank());
+ for (uint32_t i = 0; i < shape.rank(); ++i)
+ r_shape.dim(i) = (axes.defined(i)) ? 1 : shape.dim(i);
+
+ return r_shape;
+}
+
+} // namespace
+
+namespace
+{
+
+template <typename T, loco::ReduceFunc F> struct ReduceFunction
+{
+ static void apply(Buffer<T> &lhs, const Buffer<T> &rhs, const loco::TensorAxisSet &axes)
+ {
+ throw std::runtime_error("Not supported ReduceFunc type");
+ }
+};
+
+template <typename T> struct ReduceFunction<T, loco::ReduceFunc::Mean>
+{
+ static void apply(Buffer<T> &lhs, const Buffer<T> &rhs, const loco::TensorAxisSet &axes)
+ {
+ for (IndexEnumerator e{rhs.shape()}; e.valid(); e.advance())
+ {
+ const auto &index = e.current();
+ const auto r_index = reduced_index(index, axes);
+
+ lhs.at(r_index) += rhs.at(index);
+ }
+
+ uint32_t r_cnt = 1;
+ for (uint32_t i = 0; i < rhs.shape().rank(); ++i)
+ if (axes.defined(i))
+ r_cnt *= rhs.shape().dim(i);
+
+ for (IndexEnumerator e{lhs.shape()}; e.valid(); e.advance())
+ {
+ const auto &index = e.current();
+ lhs.at(index) /= static_cast<T>(r_cnt);
+ }
+ }
+};
+
+template <typename T>
+void apply(Buffer<T> &lhs, const Buffer<T> &rhs, const loco::TensorReduce &node)
+{
+ switch (node.func())
+ {
+ case loco::ReduceFunc::Mean:
+ ReduceFunction<T, loco::ReduceFunc::Mean>::apply(lhs, rhs, *node.axes());
+ break;
+
+ // TODO Support more ReduceFunc type
+ default:
+ break;
+ }
+}
+
+} // namespace
+
+namespace
+{
+
+using namespace locomotiv;
+
+void execute_node(loco::TensorReduce *node)
+{
+ auto input_data = annot_data(node->input());
+ validate(input_data, "Input not ready");
+ auto input_shape = input_data->shape();
+ validate(annot_domain(node->input()) == loco::Domain::Tensor,
+ "Input domain of TensorReduce is not Tensor");
+
+ std::unique_ptr<NodeData> reduce_data = nullptr;
+ Shape r_shape = reduced_shape(*input_shape, *node->axes());
+ switch (input_data->dtype())
+ {
+ case loco::DataType::FLOAT32:
+ {
+ auto input_bufptr = input_data->as_f32_bufptr();
+ auto reduce_buf = make_buffer<float, LexicalLayout>(r_shape);
+
+ apply(reduce_buf, *input_bufptr, *node);
+
+ reduce_data = make_data(reduce_buf);
+ break;
+ }
+ default:
+ throw std::runtime_error("NYI for this DataType");
+ }
+
+ assert(reduce_data != nullptr);
+ annot_data(node, std::move(reduce_data));
+ annot_domain(node, annot_domain(node->input()));
+}
+
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::TensorReduce *node) { execute_node(node); }
+
+} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/TensorReduce.test.cpp b/compiler/locomotiv/src/Node/TensorReduce.test.cpp
new file mode 100644
index 000000000..d0e73a248
--- /dev/null
+++ b/compiler/locomotiv/src/Node/TensorReduce.test.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NodeExecution.h"
+
+#include "locomotiv/NodeData.h"
+#include "NodeDataImpl.h"
+#include "NodeDomain.h"
+
+#include <nncc/core/ADT/tensor/Index.h>
+#include <nncc/core/ADT/tensor/Shape.h>
+#include <nncc/core/ADT/tensor/Buffer.h>
+#include <nncc/core/ADT/tensor/LexicalLayout.h>
+
+#include <gtest/gtest.h>
+
+using nncc::core::ADT::tensor::Index;
+using nncc::core::ADT::tensor::Shape;
+using nncc::core::ADT::tensor::LexicalLayout;
+using nncc::core::ADT::tensor::make_buffer;
+
+TEST(NodeExecution_Fixed_Reduce_Mean, f32_0)
+{
+ // Make pull-TensorReduce(Mean) graph
+ auto g = loco::make_graph();
+ auto pull_input = g->nodes()->create<loco::Pull>();
+ pull_input->dtype(loco::DataType::FLOAT32);
+ pull_input->shape({1, 2, 2});
+ auto reduce_node = g->nodes()->create<loco::TensorReduce>();
+ reduce_node->input(pull_input);
+ reduce_node->axes()->insert(0);
+ reduce_node->axes()->insert(1);
+ reduce_node->func(loco::ReduceFunc::Mean);
+
+ // Make and assign data to pull node
+ auto pull_input_buf = make_buffer<float, LexicalLayout>({1, 2, 2});
+ pull_input_buf.at(Index{0, 0, 0}) = 1.1f;
+ pull_input_buf.at(Index{0, 0, 1}) = 2.2f;
+ pull_input_buf.at(Index{0, 1, 0}) = 5.5f;
+ pull_input_buf.at(Index{0, 1, 1}) = 6.6f;
+ auto pull_input_data = locomotiv::make_data(pull_input_buf);
+ locomotiv::annot_data(pull_input, std::move(pull_input_data));
+ locomotiv::annot_domain(pull_input, loco::Domain::Tensor);
+
+ locomotiv::NodeExecution::get().run(reduce_node);
+
+ auto kShape = Shape{1, 1, 2};
+ auto reduce_data = locomotiv::annot_data(reduce_node);
+ ASSERT_NE(reduce_data, nullptr);
+ ASSERT_EQ(loco::DataType::FLOAT32, reduce_data->dtype());
+ ASSERT_EQ(kShape, *(reduce_data->shape()));
+ ASSERT_FLOAT_EQ(3.3f, reduce_data->as_f32_bufptr()->at(Index{0, 0, 0}));
+ ASSERT_FLOAT_EQ(4.4f, reduce_data->as_f32_bufptr()->at(Index{0, 0, 1}));
+
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(reduce_node));
+}
+
+TEST(NodeExecution_Fixed_Reduce_Mean, f32_1)
+{
+ // Make pull-TensorReduce(Mean) graph
+ auto g = loco::make_graph();
+ auto pull_input = g->nodes()->create<loco::Pull>();
+ pull_input->dtype(loco::DataType::FLOAT32);
+ pull_input->shape({1, 2, 2});
+ auto reduce_node = g->nodes()->create<loco::TensorReduce>();
+ reduce_node->input(pull_input);
+ reduce_node->axes()->insert(1);
+ reduce_node->axes()->insert(2);
+ reduce_node->func(loco::ReduceFunc::Mean);
+
+ // Make and assign data to pull node
+ auto pull_input_buf = make_buffer<float, LexicalLayout>({1, 2, 2});
+ pull_input_buf.at(Index{0, 0, 0}) = 1.1f;
+ pull_input_buf.at(Index{0, 0, 1}) = 2.2f;
+ pull_input_buf.at(Index{0, 1, 0}) = 5.5f;
+ pull_input_buf.at(Index{0, 1, 1}) = 6.6f;
+ auto pull_input_data = locomotiv::make_data(pull_input_buf);
+ locomotiv::annot_data(pull_input, std::move(pull_input_data));
+ locomotiv::annot_domain(pull_input, loco::Domain::Tensor);
+
+ locomotiv::NodeExecution::get().run(reduce_node);
+
+ auto kShape = Shape{1, 1, 1};
+ auto reduce_data = locomotiv::annot_data(reduce_node);
+ ASSERT_NE(reduce_data, nullptr);
+ ASSERT_EQ(loco::DataType::FLOAT32, reduce_data->dtype());
+ ASSERT_EQ(kShape, *(reduce_data->shape()));
+ ASSERT_FLOAT_EQ(3.85f, reduce_data->as_f32_bufptr()->at(Index{0, 0, 0}));
+
+ ASSERT_EQ(loco::Domain::Tensor, locomotiv::annot_domain(reduce_node));
+}
diff --git a/compiler/locomotiv/src/Node/TransposedConv2D.cpp b/compiler/locomotiv/src/Node/TransposedConv2D.cpp
index 3ea4f071d..bec15a5df 100644
--- a/compiler/locomotiv/src/Node/TransposedConv2D.cpp
+++ b/compiler/locomotiv/src/Node/TransposedConv2D.cpp
@@ -147,10 +147,12 @@ Buffer<RET_T> calc_tr_conv2D(const loco::TransposedConv2D *tr_conv2d,
} // namespace
-namespace locomotiv
+namespace
{
-void NodeExecution::execute(loco::TransposedConv2D *tr_conv2d)
+using namespace locomotiv;
+
+void execute_node(loco::TransposedConv2D *tr_conv2d)
{
auto ifm_data = annot_data(tr_conv2d->ifm());
auto ker_data = annot_data(tr_conv2d->ker());
@@ -186,4 +188,11 @@ void NodeExecution::execute(loco::TransposedConv2D *tr_conv2d)
annot_domain(tr_conv2d, loco::Domain::Feature);
}
+} // namespace
+
+namespace locomotiv
+{
+
+void NodeExecution::execute(loco::TransposedConv2D *tr_conv2d) { execute_node(tr_conv2d); }
+
} // namespace locomotiv
diff --git a/compiler/locomotiv/src/Node/TransposedConv2D.test.cpp b/compiler/locomotiv/src/Node/TransposedConv2D.test.cpp
index bd955a06b..ef759f51b 100644
--- a/compiler/locomotiv/src/Node/TransposedConv2D.test.cpp
+++ b/compiler/locomotiv/src/Node/TransposedConv2D.test.cpp
@@ -101,10 +101,10 @@ void run_test(const float *ifm, const float *ker, const float *expected_ofm, con
for (nncc::core::ADT::tensor::IndexEnumerator e{ofm_shape}; e.valid(); e.advance())
{
const auto &ind = e.current();
- ASSERT_FLOAT_EQ(conv2d_result->as_f32_bufptr()->at(ind), ofm_overlay.at(ind));
+ ASSERT_FLOAT_EQ(ofm_overlay.at(ind), conv2d_result->as_f32_bufptr()->at(ind));
}
- ASSERT_EQ(locomotiv::annot_domain(tr_conv2d), loco::Domain::Feature);
+ ASSERT_EQ(loco::Domain::Feature, locomotiv::annot_domain(tr_conv2d));
}
} // namespace
diff --git a/compiler/locomotiv/src/NodeData.test.cpp b/compiler/locomotiv/src/NodeData.test.cpp
index b1c9832d5..65bd3e1a8 100644
--- a/compiler/locomotiv/src/NodeData.test.cpp
+++ b/compiler/locomotiv/src/NodeData.test.cpp
@@ -35,9 +35,9 @@ TEST(NodeData, as_s32_buffer_wrapper)
auto data = locomotiv::make_data(buf);
- ASSERT_EQ(data->dtype(), loco::DataType::S32);
- ASSERT_EQ(*(data->shape()), shape);
- ASSERT_EQ(data->as_s32_bufptr()->at(Index{0}), 42);
+ ASSERT_EQ(loco::DataType::S32, data->dtype());
+ ASSERT_EQ(shape, *(data->shape()));
+ ASSERT_EQ(42, data->as_s32_bufptr()->at(Index{0}));
}
TEST(NodeData, as_f32_buffer_wrapper)
@@ -48,7 +48,7 @@ TEST(NodeData, as_f32_buffer_wrapper)
auto data = locomotiv::make_data(buf);
- ASSERT_EQ(data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(data->shape()), shape);
- ASSERT_FLOAT_EQ(data->as_f32_bufptr()->at(Index{0}), 3.14f);
+ ASSERT_EQ(loco::DataType::FLOAT32, data->dtype());
+ ASSERT_EQ(shape, *(data->shape()));
+ ASSERT_FLOAT_EQ(3.14f, data->as_f32_bufptr()->at(Index{0}));
}
diff --git a/compiler/locomotiv/src/NodeDataImpl.test.cpp b/compiler/locomotiv/src/NodeDataImpl.test.cpp
index b85956063..3fb0cc264 100644
--- a/compiler/locomotiv/src/NodeDataImpl.test.cpp
+++ b/compiler/locomotiv/src/NodeDataImpl.test.cpp
@@ -39,7 +39,7 @@ TEST(NodeDataImpl, as_annotation)
auto g = loco::make_graph();
auto node = g->nodes()->create<loco::Pull>();
- ASSERT_EQ(locomotiv::annot_data(node), nullptr);
+ ASSERT_EQ(nullptr, locomotiv::annot_data(node));
// Set annotation
locomotiv::annot_data(node, std::move(data));
@@ -48,11 +48,11 @@ TEST(NodeDataImpl, as_annotation)
const locomotiv::NodeData *obtained = locomotiv::annot_data(node);
ASSERT_NE(obtained, nullptr);
- ASSERT_EQ(obtained->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(obtained->shape()), shape);
- ASSERT_FLOAT_EQ(obtained->as_f32_bufptr()->at(Index{0}), 3.14f);
+ ASSERT_EQ(loco::DataType::FLOAT32, obtained->dtype());
+ ASSERT_EQ(shape, *(obtained->shape()));
+ ASSERT_FLOAT_EQ(3.14f, obtained->as_f32_bufptr()->at(Index{0}));
// Erase annotation
locomotiv::erase_annot_data(node);
- ASSERT_EQ(locomotiv::annot_data(node), nullptr);
+ ASSERT_EQ(nullptr, locomotiv::annot_data(node));
}
diff --git a/compiler/locomotiv/src/NodeDomain.test.cpp b/compiler/locomotiv/src/NodeDomain.test.cpp
index 9cfcf2eb8..87c8135e5 100644
--- a/compiler/locomotiv/src/NodeDomain.test.cpp
+++ b/compiler/locomotiv/src/NodeDomain.test.cpp
@@ -22,16 +22,16 @@ TEST(NodeDomain, as_annotation)
{
loco::Pull node;
- ASSERT_EQ(locomotiv::annot_domain(&node), loco::Domain::Unknown);
+ ASSERT_EQ(loco::Domain::Unknown, locomotiv::annot_domain(&node));
// Set annotation
locomotiv::annot_domain(&node, loco::Domain::Tensor);
// Get annotation
const loco::Domain obtained = locomotiv::annot_domain(&node);
- ASSERT_EQ(obtained, loco::Domain::Tensor);
+ ASSERT_EQ(loco::Domain::Tensor, obtained);
// Erase annotation
locomotiv::erase_annot_domain(&node);
- ASSERT_EQ(locomotiv::annot_domain(&node), loco::Domain::Unknown);
+ ASSERT_EQ(loco::Domain::Unknown, locomotiv::annot_domain(&node));
}
diff --git a/compiler/locomotiv/src/NodeExecution.cpp b/compiler/locomotiv/src/NodeExecution.cpp
index e532b5af6..2a8697181 100644
--- a/compiler/locomotiv/src/NodeExecution.cpp
+++ b/compiler/locomotiv/src/NodeExecution.cpp
@@ -72,9 +72,9 @@ void NodeExecution::eltwise_unary(loco::Node *node, const UnaryFunc &f)
auto input_node = node->arg(0);
auto input_domain = annot_domain(input_node);
auto input_data = annot_data(input_node);
+ validate(input_data, "Input is not ready");
auto input_dtype = input_data->dtype();
- validate(input_data, "Input is not ready");
validate(input_domain != loco::Domain::Unknown, "Input domain is unknown");
auto output_node = node;
diff --git a/compiler/locomotiv/src/Session.test.cpp b/compiler/locomotiv/src/Session.test.cpp
index 6d4a2414f..b73e4fa8b 100644
--- a/compiler/locomotiv/src/Session.test.cpp
+++ b/compiler/locomotiv/src/Session.test.cpp
@@ -57,8 +57,8 @@ TEST(Session, graph_IO_size)
// Make session
locomotiv::Session s(g.get());
- ASSERT_EQ(s.input_size(), inputs);
- ASSERT_EQ(s.output_size(), outputs);
+ ASSERT_EQ(inputs, s.input_size());
+ ASSERT_EQ(outputs, s.output_size());
}
TEST(Session, set_input)
@@ -173,9 +173,9 @@ TEST(Session, inference_identity)
auto output_data = s.get_output(0);
ASSERT_NE(output_data, nullptr);
- ASSERT_EQ(output_data->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(*(output_data->shape()), Shape{1});
- ASSERT_EQ(output_data->as_f32_bufptr()->at(Index{0}), 3.14f);
+ ASSERT_EQ(loco::DataType::FLOAT32, output_data->dtype());
+ ASSERT_EQ(Shape{1}, *(output_data->shape()));
+ ASSERT_EQ(3.14f, output_data->as_f32_bufptr()->at(Index{0}));
}
}
@@ -234,63 +234,63 @@ TEST(Session, session_for_subgraph)
{
// Session to get t1 only
locomotiv::Session s(g.get(), {t1});
- ASSERT_EQ(s.output_size(), 1);
- ASSERT_EQ(s.get_output_node(0), dynamic_cast<loco::Node *>(t1));
+ ASSERT_EQ(1, s.output_size());
+ ASSERT_EQ(dynamic_cast<loco::Node *>(t1), s.get_output_node(0));
s.infer();
auto t1_data = s.get_output(0);
ASSERT_NE(t1_data, nullptr);
- ASSERT_EQ(*(t1_data->shape()), Shape{2});
+ ASSERT_EQ(Shape{2}, *(t1_data->shape()));
auto t1_buf = t1_data->as_f32_bufptr();
- ASSERT_EQ(t1_buf->at({0}), 0.1f);
- ASSERT_EQ(t1_buf->at({1}), 0.2f);
+ ASSERT_EQ(0.1f, t1_buf->at({0}));
+ ASSERT_EQ(0.2f, t1_buf->at({1}));
}
{
// Session to get t2 only
locomotiv::Session s(g.get(), {t2});
- ASSERT_EQ(s.output_size(), 1);
- ASSERT_EQ(s.get_output_node(0), dynamic_cast<loco::Node *>(t2));
+ ASSERT_EQ(1, s.output_size());
+ ASSERT_EQ(dynamic_cast<loco::Node *>(t2), s.get_output_node(0));
s.infer();
auto t2_data = s.get_output(0);
ASSERT_NE(t2_data, nullptr);
- ASSERT_EQ(*(t2_data->shape()), Shape{2});
+ ASSERT_EQ(Shape{2}, *(t2_data->shape()));
auto t2_buf = t2_data->as_f32_bufptr();
- ASSERT_EQ(t2_buf->at({0}), 0.3f);
- ASSERT_EQ(t2_buf->at({1}), 0.4f);
+ ASSERT_EQ(0.3f, t2_buf->at({0}));
+ ASSERT_EQ(0.4f, t2_buf->at({1}));
}
{
// Session to get t2 and push
locomotiv::Session s(g.get(), {t2, push});
- ASSERT_EQ(s.output_size(), 2);
- ASSERT_EQ(s.get_output_node(0), dynamic_cast<loco::Node *>(t2));
- ASSERT_EQ(s.get_output_node(1), dynamic_cast<loco::Node *>(push));
+ ASSERT_EQ(2, s.output_size());
+ ASSERT_EQ(dynamic_cast<loco::Node *>(t2), s.get_output_node(0));
+ ASSERT_EQ(dynamic_cast<loco::Node *>(push), s.get_output_node(1));
s.infer();
auto t2_data = s.get_output(0);
ASSERT_NE(t2_data, nullptr);
- ASSERT_EQ(*(t2_data->shape()), Shape{2});
+ ASSERT_EQ(Shape{2}, *(t2_data->shape()));
auto t2_buf = t2_data->as_f32_bufptr();
- ASSERT_EQ(t2_buf->at({0}), 0.3f);
- ASSERT_EQ(t2_buf->at({1}), 0.4f);
+ ASSERT_EQ(0.3f, t2_buf->at({0}));
+ ASSERT_EQ(0.4f, t2_buf->at({1}));
auto push_data = s.get_output(1);
ASSERT_NE(push_data, nullptr);
- ASSERT_EQ(*(push_data->shape()), Shape{4});
+ ASSERT_EQ(Shape{4}, *(push_data->shape()));
auto push_buf = push_data->as_f32_bufptr();
- ASSERT_EQ(push_buf->at({0}), 0.1f);
- ASSERT_EQ(push_buf->at({1}), 0.2f);
- ASSERT_EQ(push_buf->at({2}), 0.3f);
- ASSERT_EQ(push_buf->at({3}), 0.4f);
+ ASSERT_EQ(0.1f, push_buf->at({0}));
+ ASSERT_EQ(0.2f, push_buf->at({1}));
+ ASSERT_EQ(0.3f, push_buf->at({2}));
+ ASSERT_EQ(0.4f, push_buf->at({3}));
}
}
@@ -321,19 +321,19 @@ TEST(Session, ctor_by_range)
auto constgen_data = s.get_output(0);
ASSERT_NE(constgen_data, nullptr);
- ASSERT_EQ(*(constgen_data->shape()), Shape{2});
+ ASSERT_EQ(Shape{2}, *(constgen_data->shape()));
auto constgen_buf = constgen_data->as_f32_bufptr();
- ASSERT_EQ(constgen_buf->at({0}), 0.1f);
- ASSERT_EQ(constgen_buf->at({1}), -0.1f);
+ ASSERT_EQ(0.1f, constgen_buf->at({0}));
+ ASSERT_EQ(-0.1f, constgen_buf->at({1}));
auto push_data = s.get_output(1);
ASSERT_NE(push_data, nullptr);
- ASSERT_EQ(*(push_data->shape()), Shape{2});
+ ASSERT_EQ(Shape{2}, *(push_data->shape()));
auto push_buf = push_data->as_f32_bufptr();
- ASSERT_EQ(push_buf->at({0}), 0.1f);
- ASSERT_EQ(push_buf->at({1}), 0.0f);
+ ASSERT_EQ(0.1f, push_buf->at({0}));
+ ASSERT_EQ(0.0f, push_buf->at({1}));
}
// Below here is internal test for locomotiv, i.e. not public usage of locomotiv
@@ -363,17 +363,17 @@ TEST(Session, dtor)
s.set_input(0, std::move(data));
auto data_annotated = locomotiv::annot_data(pull);
- ASSERT_EQ(data_annotated, nullptr);
+ ASSERT_EQ(nullptr, data_annotated);
auto user_data_annotated = locomotiv::user_data(pull);
ASSERT_NE(user_data_annotated, nullptr);
auto domain_annotated = locomotiv::annot_domain(pull);
- ASSERT_EQ(domain_annotated, loco::Domain::Unknown);
+ ASSERT_EQ(loco::Domain::Unknown, domain_annotated);
}
auto data_annotated = locomotiv::annot_data(pull);
- ASSERT_EQ(data_annotated, nullptr);
+ ASSERT_EQ(nullptr, data_annotated);
auto user_data_annotated = locomotiv::user_data(pull);
- ASSERT_EQ(user_data_annotated, nullptr);
+ ASSERT_EQ(nullptr, user_data_annotated);
auto domain_annotated = locomotiv::annot_domain(pull);
- ASSERT_EQ(domain_annotated, loco::Domain::Unknown);
+ ASSERT_EQ(loco::Domain::Unknown, domain_annotated);
}
diff --git a/compiler/locop/CMakeLists.txt b/compiler/locop/CMakeLists.txt
index 8b60295ae..107ee8be8 100644
--- a/compiler/locop/CMakeLists.txt
+++ b/compiler/locop/CMakeLists.txt
@@ -20,7 +20,7 @@ if(NOT ENABLE_TEST)
endif(NOT ENABLE_TEST)
# Google Test is mandatory for internal testing
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
GTest_AddTest(locop_test ${TESTS})
target_link_libraries(locop_test stdex)
diff --git a/compiler/locop/requires.cmake b/compiler/locop/requires.cmake
new file mode 100644
index 000000000..d314ae55d
--- /dev/null
+++ b/compiler/locop/requires.cmake
@@ -0,0 +1 @@
+require("pp")
diff --git a/compiler/locop/src/CanonicalNodeSummaryBuilder.cpp b/compiler/locop/src/CanonicalNodeSummaryBuilder.cpp
index 2c135c8fa..61d9e8ae7 100644
--- a/compiler/locop/src/CanonicalNodeSummaryBuilder.cpp
+++ b/compiler/locop/src/CanonicalNodeSummaryBuilder.cpp
@@ -71,9 +71,7 @@ std::string opname(const loco::Node *node)
{
if (node->dialect() == loco::CanonicalDialect::get())
{
- auto canonical_node = dynamic_cast<const loco::CanonicalNode *>(node);
-
- assert(canonical_node != nullptr);
+ auto canonical_node = loco::must_cast<const loco::CanonicalNode *>(node);
switch (canonical_node->opcode())
{
@@ -217,6 +215,19 @@ public:
return res;
}
+ NodeDesc visit(const loco::TensorReduce *node) final
+ {
+ NodeDesc res{opname(node)};
+
+ // TODO Print TensorAxisSet
+ res.arg("input", nodename(node->input()));
+ res.arg("func", pp::fmt((int32_t)node->func()));
+
+ res.state(NodeDesc::State::PartiallyKnown);
+
+ return res;
+ }
+
NodeDesc visit(const loco::Reshape<loco::ReshapeType::Fixed> *node) final
{
NodeDesc res{opname(node)};
@@ -275,8 +286,7 @@ bool CanonicalNodeSummaryBuilder::build(const loco::Node *node, locop::NodeSumma
return false;
}
- auto canonical_node = dynamic_cast<const loco::CanonicalNode *>(node);
- assert(canonical_node != nullptr);
+ auto canonical_node = loco::must_cast<const loco::CanonicalNode *>(node);
out = canonical_node_desc(*_tbl, canonical_node);
return true;
}
diff --git a/compiler/locop/src/FormattedGraph.cpp b/compiler/locop/src/FormattedGraph.cpp
index a6896b906..bf4175768 100644
--- a/compiler/locop/src/FormattedGraph.cpp
+++ b/compiler/locop/src/FormattedGraph.cpp
@@ -16,6 +16,7 @@
#include "locop/FormattedGraph.h"
#include "locop/FormattedTensorShape.h"
+#include "locop/GenericNodeSummaryBuilder.h"
#include <loco/Service/TypeInference.h>
#include <loco/Service/ShapeInference.h>
@@ -66,6 +67,9 @@ std::string str(const loco::DataType &dtype)
case loco::DataType::FLOAT64:
return "FLOAT64";
+ case loco::DataType::BOOL:
+ return "BOOL";
+
default:
break;
};
@@ -194,43 +198,6 @@ std::ostream &operator<<(std::ostream &os, const NodeDesc &d)
} // namespace locop
-namespace
-{
-
-NodeDesc default_node_desc(const SymbolTable &tbl, const loco::Node *node)
-{
- NodeDesc res{"unknown.Unknown"};
-
- for (uint32_t n = 0; n < node->arity(); ++n)
- {
- res.arg(std::string{"arg"} + std::to_string(n), tbl.lookup(node->arg(n)));
- }
- res.state(NodeDesc::State::PartiallyKnown);
-
- return res;
-}
-
-struct BuiltinNodeSummaryBuilder final : public locop::NodeSummaryBuilder
-{
-public:
- BuiltinNodeSummaryBuilder(const locop::SymbolTable *symtbl) : _symtbl{symtbl}
- {
- // DO NOTHING
- }
-
-public:
- bool build(const loco::Node *node, locop::NodeSummary &summary) const final
- {
- summary = default_node_desc(*_symtbl, node);
- return true;
- }
-
-private:
- const locop::SymbolTable *_symtbl;
-};
-
-} // namespace
-
namespace locop
{
@@ -333,7 +300,7 @@ void FormattedGraphImpl<Formatter::LinearV1>::dump(std::ostream &os) const
else
{
// Use Built-in NodeSummaryBuilder otherwise
- node_summary_builder = stdex::make_unique<BuiltinNodeSummaryBuilder>(&symbols);
+ node_summary_builder = stdex::make_unique<GenericNodeSummaryBuilder>(&symbols);
}
// Print Graph Input(s)
diff --git a/compiler/locop/src/FormattedGraph.test.cpp b/compiler/locop/src/FormattedGraph.test.cpp
index c9808d3a2..aff9ebe5f 100644
--- a/compiler/locop/src/FormattedGraph.test.cpp
+++ b/compiler/locop/src/FormattedGraph.test.cpp
@@ -28,6 +28,8 @@ TEST(LinearV1FormatterTest, simple)
// TODO Validate the output (when the implementation becomes stable)
std::cout << locop::fmt<locop::LinearV1>(g) << std::endl;
+
+ SUCCEED();
}
TEST(LinearV1FormatterTest, user_defined_node_summary_builder)
diff --git a/compiler/locop/src/FormattedTensorShape.test.cpp b/compiler/locop/src/FormattedTensorShape.test.cpp
index 0f0017ab4..fc85df3a6 100644
--- a/compiler/locop/src/FormattedTensorShape.test.cpp
+++ b/compiler/locop/src/FormattedTensorShape.test.cpp
@@ -30,4 +30,6 @@ TEST(FormattedTensorShapeTest, BracketFormat)
tensor_shape->dim(0) = 4;
std::cout << fmt<TensorShapeFormat::Bracket>(tensor_shape.get()) << std::endl;
+
+ SUCCEED();
}
diff --git a/compiler/logo-core/CMakeLists.txt b/compiler/logo-core/CMakeLists.txt
new file mode 100644
index 000000000..3bc71dbd0
--- /dev/null
+++ b/compiler/logo-core/CMakeLists.txt
@@ -0,0 +1,19 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(logo_core STATIC ${SOURCES})
+set_target_properties(logo_core PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(logo_core PRIVATE src)
+target_include_directories(logo_core PUBLIC include)
+target_link_libraries(logo_core PUBLIC loco)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(logo_core_test ${TESTS})
+target_include_directories(logo_core_test PRIVATE src)
+target_link_libraries(logo_core_test logo_core)
diff --git a/compiler/logo-core/README.md b/compiler/logo-core/README.md
new file mode 100644
index 000000000..0dee3954b
--- /dev/null
+++ b/compiler/logo-core/README.md
@@ -0,0 +1,3 @@
+# logo-core
+
+_logo-core_ provides _loco_ General Graph Pass Core for Transformation and Optimization
diff --git a/compiler/logo/include/logo/Pass.h b/compiler/logo-core/include/logo/Pass.h
index 4f667f156..4f667f156 100644
--- a/compiler/logo/include/logo/Pass.h
+++ b/compiler/logo-core/include/logo/Pass.h
diff --git a/compiler/logo/include/logo/Phase.h b/compiler/logo-core/include/logo/Phase.h
index d1b7ccd5f..d1b7ccd5f 100644
--- a/compiler/logo/include/logo/Phase.h
+++ b/compiler/logo-core/include/logo/Phase.h
diff --git a/compiler/logo-core/requires.cmake b/compiler/logo-core/requires.cmake
new file mode 100644
index 000000000..44f6870da
--- /dev/null
+++ b/compiler/logo-core/requires.cmake
@@ -0,0 +1 @@
+require("loco")
diff --git a/compiler/logo/src/Pass.cpp b/compiler/logo-core/src/Pass.cpp
index a44010760..a44010760 100644
--- a/compiler/logo/src/Pass.cpp
+++ b/compiler/logo-core/src/Pass.cpp
diff --git a/compiler/logo/src/Pass.test.cpp b/compiler/logo-core/src/Pass.test.cpp
index b6bebff62..b6bebff62 100644
--- a/compiler/logo/src/Pass.test.cpp
+++ b/compiler/logo-core/src/Pass.test.cpp
diff --git a/compiler/logo/src/Phase.cpp b/compiler/logo-core/src/Phase.cpp
index b929a31ba..b929a31ba 100644
--- a/compiler/logo/src/Phase.cpp
+++ b/compiler/logo-core/src/Phase.cpp
diff --git a/compiler/logo/CMakeLists.txt b/compiler/logo/CMakeLists.txt
index fa824822e..399cb7586 100644
--- a/compiler/logo/CMakeLists.txt
+++ b/compiler/logo/CMakeLists.txt
@@ -7,6 +7,7 @@ set_target_properties(logo PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_include_directories(logo PRIVATE src)
target_include_directories(logo PUBLIC include)
target_link_libraries(logo PUBLIC loco)
+target_link_libraries(logo PUBLIC logo_core)
target_link_libraries(logo PRIVATE locomotiv)
target_link_libraries(logo PRIVATE stdex)
@@ -14,8 +15,9 @@ if(NOT ENABLE_TEST)
return()
endif(NOT ENABLE_TEST)
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
GTest_AddTest(logo_test ${TESTS})
target_include_directories(logo_test PRIVATE src)
target_link_libraries(logo_test logo)
+target_link_libraries(logo_test stdex)
diff --git a/compiler/logo/README.md b/compiler/logo/README.md
index 67f764c34..0cf1ba313 100644
--- a/compiler/logo/README.md
+++ b/compiler/logo/README.md
@@ -1,3 +1,3 @@
# logo
-_logo_ privides _loco_ General Graph Passes for Transformation and Optimization
+_logo_ provides _loco_ General Graph Passes for Transformation and Optimization
diff --git a/compiler/logo/include/logo/DeadNodeQueryService.h b/compiler/logo/include/logo/DeadNodeQueryService.h
new file mode 100644
index 000000000..e74a4bc58
--- /dev/null
+++ b/compiler/logo/include/logo/DeadNodeQueryService.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOGO_DEAD_NODE_QUERY_SERVICE_H__
+#define __LOGO_DEAD_NODE_QUERY_SERVICE_H__
+
+#include <loco.h>
+#include <loco/IR/DialectService.h>
+
+namespace logo
+{
+
+struct DeadNodeQueryService : public ::loco::DialectService
+{
+ virtual ~DeadNodeQueryService() = default;
+ /// @brief Check if the node is dead node
+ virtual bool isDeadNode(loco::Node *node) = 0;
+};
+
+} // namespace logo
+
+#endif // __LOGO_DEAD_NODE_QUERY_SERVICE_H__
diff --git a/compiler/logo/include/logo/RemoveDeadNodeWithQueryPass.h b/compiler/logo/include/logo/RemoveDeadNodeWithQueryPass.h
new file mode 100644
index 000000000..de0867117
--- /dev/null
+++ b/compiler/logo/include/logo/RemoveDeadNodeWithQueryPass.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOGO_REMOVE_DEAD_NODE_WITH_QUERY_PASS_H__
+#define __LOGO_REMOVE_DEAD_NODE_WITH_QUERY_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace logo
+{
+
+struct RemoveDeadNodeWithQueryPass final : public Pass
+{
+ const char *name(void) const final { return "RemoveDeadNodeWithQueryPass"; }
+
+ bool run(loco::Graph *g);
+};
+
+} // namespace logo
+
+#endif // __LOGO_REMOVE_DEAD_NODE_WITH_QUERY_PASS_H__
diff --git a/compiler/logo/include/logo/SimplifyDomainConversionPass.h b/compiler/logo/include/logo/SimplifyDomainConversionPass.h
index 524c36438..551806f60 100644
--- a/compiler/logo/include/logo/SimplifyDomainConversionPass.h
+++ b/compiler/logo/include/logo/SimplifyDomainConversionPass.h
@@ -28,6 +28,11 @@ namespace logo
* SimplifyDomainConversionPass recognizes the following patterns:
* - FeatureDecode followed by FeatureEncode (Feature -> Tensor -> Feature)
* - FeatureEncode followed by FeatureDecode (Tensor -> Feature -> Tensor)
+ * - FilterEncode followed by FilterDecode (Tensor -> Filter -> Tensor)
+ * - BiasEncode followed by BiasDecode (Tensor -> Bias -> Tensor)
+ * - DepthwiseFilterEncode followed by DepthwiseFilterDecode (Tensor -> DepthwiseFilter -> Tensor)
+ * - MatrixDecode followed by MatrixEncode (Matrix -> Tensor -> Matrix)
+ * - MatrixEncode followed by MatrixDecode (Tensor -> Matrix -> Tensor)
* - (TO BE ADDED)
*/
struct SimplifyDomainConversionPass final : public Pass
diff --git a/compiler/logo/requires.cmake b/compiler/logo/requires.cmake
index 362a4e2c4..9a7d14788 100644
--- a/compiler/logo/requires.cmake
+++ b/compiler/logo/requires.cmake
@@ -1,3 +1,4 @@
require("loco")
+require("logo-core")
require("locomotiv")
require("stdex")
diff --git a/compiler/logo/src/Passes/ConstantFoldingPass.cpp b/compiler/logo/src/Passes/ConstantFoldingPass.cpp
index c31b3c9d4..e038e7140 100644
--- a/compiler/logo/src/Passes/ConstantFoldingPass.cpp
+++ b/compiler/logo/src/Passes/ConstantFoldingPass.cpp
@@ -61,6 +61,10 @@ bool skip(const loco::Node *node)
// We don't perform constant folding for Push
static_cast<uint32_t>(loco::CanonicalOpcode::Push),
+
+ // TensorBroadcast is a good hint for optimization
+ // TODO Let this option be controlled by driver using logo
+ static_cast<uint32_t>(loco::CanonicalOpcode::TensorBroadcast),
};
if (node->dialect() == loco::CanonicalDialect::get())
diff --git a/compiler/logo/src/Passes/ConstantFoldingPass.test.cpp b/compiler/logo/src/Passes/ConstantFoldingPass.test.cpp
index 824027762..b9c4942c4 100644
--- a/compiler/logo/src/Passes/ConstantFoldingPass.test.cpp
+++ b/compiler/logo/src/Passes/ConstantFoldingPass.test.cpp
@@ -82,7 +82,7 @@ TEST(ConstantFolding, const_relu_to_const)
}
auto push = logo::test::find_first_node_by_type<loco::Push>(graph.get());
- auto const_gen = dynamic_cast<loco::ConstGen *>(push->from());
+ auto const_gen = loco::must_cast<loco::ConstGen *>(push->from());
ASSERT_NE(const_gen, nullptr);
ASSERT_EQ(const_gen->size<loco::DataType::FLOAT32>(), 2);
@@ -168,7 +168,7 @@ TEST(ConstantFolding, const_relu_to_concat)
}
auto push = logo::test::find_first_node_by_type<loco::Push>(graph.get());
- auto const_gen = dynamic_cast<loco::ConstGen *>(push->from());
+ auto const_gen = loco::must_cast<loco::ConstGen *>(push->from());
ASSERT_NE(const_gen, nullptr);
ASSERT_EQ(const_gen->size<loco::DataType::FLOAT32>(), 4);
diff --git a/compiler/logo/src/Passes/RemoveDeadNodeWithQueryPass.cpp b/compiler/logo/src/Passes/RemoveDeadNodeWithQueryPass.cpp
new file mode 100644
index 000000000..5c745212a
--- /dev/null
+++ b/compiler/logo/src/Passes/RemoveDeadNodeWithQueryPass.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <logo/RemoveDeadNodeWithQueryPass.h>
+#include <logo/DeadNodeQueryService.h>
+
+#include <loco/IR/Algorithm.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/IR/CanonicalNode.h>
+
+#include <set>
+
+namespace logo
+{
+
+bool RemoveDeadNodeWithQueryPass::run(loco::Graph *g)
+{
+ // Let's enumerate nodes required to compute output nodes
+ auto active_nodes = loco::active_nodes(loco::output_nodes(g));
+
+ // List dead(= non-active) nodes candidates
+ std::set<loco::Node *> candidates;
+
+ for (auto node : loco::all_nodes(g))
+ {
+ if (active_nodes.find(node) == active_nodes.end())
+ {
+ candidates.insert(node);
+ }
+ }
+
+ // Find the nodes that should not be dead node in candidates
+ for (auto node : candidates)
+ {
+ if (auto service = node->dialect()->service<DeadNodeQueryService>())
+ {
+ if (!service->isDeadNode(node))
+ {
+ candidates.erase(node);
+ }
+ }
+ }
+
+ for (auto node : candidates)
+ {
+ node->drop();
+ }
+
+ for (auto node : candidates)
+ {
+ g->nodes()->destroy(node);
+ }
+
+ return candidates.size() > 0;
+}
+
+} // namespace logo
diff --git a/compiler/logo/src/Passes/RemoveForwardNodePass.cpp b/compiler/logo/src/Passes/RemoveForwardNodePass.cpp
index c951cfac4..966b270f6 100644
--- a/compiler/logo/src/Passes/RemoveForwardNodePass.cpp
+++ b/compiler/logo/src/Passes/RemoveForwardNodePass.cpp
@@ -47,7 +47,7 @@ bool RemoveForwardNodePass::run(loco::Graph *g)
{
if (node->dialect() == loco::CanonicalDialect::get())
{
- auto canonical_node = dynamic_cast<loco::CanonicalNode *>(node);
+ auto canonical_node = loco::must_cast<loco::CanonicalNode *>(node);
canonical_node->accept(&collector);
}
}
diff --git a/compiler/logo/src/Passes/ReorderDecodePass.cpp b/compiler/logo/src/Passes/ReorderDecodePass.cpp
index 724db5780..863f180e7 100644
--- a/compiler/logo/src/Passes/ReorderDecodePass.cpp
+++ b/compiler/logo/src/Passes/ReorderDecodePass.cpp
@@ -37,49 +37,54 @@ bool isReLU(const loco::Node *node)
return node->opnum() == static_cast<uint32_t>(loco::CanonicalOpcode::ReLU);
}
-} // namespace
-
-namespace logo
-{
-
-bool ReorderDecodePass<loco::TensorBiasAdd>::run(loco::Graph *g)
+// Update queue
+class Collector final : public loco::CanonicalNodeMutableVisitor<void>
{
- std::queue<loco::FeatureDecode *> q;
-
- // Update queue
- class Collector final : public loco::CanonicalNodeMutableVisitor<void>
+public:
+ Collector(std::queue<loco::FeatureDecode *> *out) : _out{out}
{
- public:
- Collector(std::queue<loco::FeatureDecode *> *out) : _out{out}
- {
- // DO NOTHING
- }
+ // DO NOTHING
+ }
- void visit(loco::FeatureDecode *node) final
+ void visit(loco::FeatureDecode *node) final
+ {
+ if (node->input() != nullptr)
{
- if (node->input() != nullptr)
- {
- _out->push(node);
- }
+ _out->push(node);
}
+ }
- void visit(loco::Node *) final { return; }
+ void visit(loco::Node *) final { return; }
- private:
- // TODO This definition should be revised to support other decode operations
- std::queue<loco::FeatureDecode *> *_out;
- };
+private:
+ // TODO This definition should be revised to support other decode operations
+ std::queue<loco::FeatureDecode *> *_out;
+};
+void gather_candidates(loco::Graph *g, std::queue<loco::FeatureDecode *> &q)
+{
Collector collector{&q};
for (auto node : loco::all_nodes(g))
{
if (node->dialect() == loco::CanonicalDialect::get())
{
- auto canonical_node = dynamic_cast<loco::CanonicalNode *>(node);
+ auto canonical_node = loco::must_cast<loco::CanonicalNode *>(node);
canonical_node->accept(&collector);
}
}
+}
+
+} // namespace
+
+namespace logo
+{
+
+bool ReorderDecodePass<loco::TensorBiasAdd>::run(loco::Graph *g)
+{
+ std::queue<loco::FeatureDecode *> q;
+
+ gather_candidates(g, q);
bool changed = false;
@@ -125,9 +130,7 @@ bool ReorderDecodePass<loco::TensorBiasAdd>::run(loco::Graph *g)
// Q. Is it better to create an independent transform for this rewriting rule?
if (isTensorBiasAdd(u))
{
- auto old_badd = dynamic_cast<loco::TensorBiasAdd *>(u);
-
- assert(old_badd != nullptr);
+ auto old_badd = loco::must_cast<loco::TensorBiasAdd *>(u);
/**
* Let us consider the following example:
@@ -182,40 +185,7 @@ bool ReorderDecodePass<loco::ReLU>::run(loco::Graph *g)
{
std::queue<loco::FeatureDecode *> q;
- // Update queue
- class Collector final : public loco::CanonicalNodeMutableVisitor<void>
- {
- public:
- Collector(std::queue<loco::FeatureDecode *> *out) : _out{out}
- {
- // DO NOTHING
- }
-
- void visit(loco::FeatureDecode *node) final
- {
- if (node->input() != nullptr)
- {
- _out->push(node);
- }
- }
-
- void visit(loco::Node *) final { return; }
-
- private:
- // TODO This definition should be revised to support other decode operations
- std::queue<loco::FeatureDecode *> *_out;
- };
-
- Collector collector{&q};
-
- for (auto node : loco::all_nodes(g))
- {
- if (node->dialect() == loco::CanonicalDialect::get())
- {
- auto canonical_node = dynamic_cast<loco::CanonicalNode *>(node);
- canonical_node->accept(&collector);
- }
- }
+ gather_candidates(g, q);
bool changed = false;
diff --git a/compiler/logo/src/Passes/ResolveDuplicateReshapePass.cpp b/compiler/logo/src/Passes/ResolveDuplicateReshapePass.cpp
index d3c74cb77..94ff6291d 100644
--- a/compiler/logo/src/Passes/ResolveDuplicateReshapePass.cpp
+++ b/compiler/logo/src/Passes/ResolveDuplicateReshapePass.cpp
@@ -61,7 +61,7 @@ bool is_duplicate_reshape(loco::Node *node)
*/
void remap_input(loco::FixedReshape *reshape)
{
- auto input_reshape = dynamic_cast<loco::FixedReshape *>(reshape->input());
+ auto input_reshape = loco::must_cast<loco::FixedReshape *>(reshape->input());
auto volume = [](loco::FixedReshape *node) {
uint32_t vol = 1;
@@ -94,7 +94,7 @@ bool ResolveDuplicateReshapePass::run(loco::Graph *graph)
{
if (is_duplicate_reshape(node))
{
- auto node_as_reshape = dynamic_cast<loco::FixedReshape *>(node);
+ auto node_as_reshape = loco::must_cast<loco::FixedReshape *>(node);
remap_input(node_as_reshape);
diff --git a/compiler/logo/src/Passes/SimplifyDomainConversionPass.cpp b/compiler/logo/src/Passes/SimplifyDomainConversionPass.cpp
index f4343c70a..0bda85b6f 100644
--- a/compiler/logo/src/Passes/SimplifyDomainConversionPass.cpp
+++ b/compiler/logo/src/Passes/SimplifyDomainConversionPass.cpp
@@ -20,7 +20,10 @@
#include <loco/IR/CanonicalDialect.h>
#include <loco/IR/CanonicalNode.h>
+#include <stdex/Memory.h>
+
#include <set>
+#include <vector>
#include <cassert>
namespace
@@ -42,6 +45,45 @@ bool equal(const Permutation<Domain::Feature> *lhs, const Permutation<Domain::Fe
return true;
}
+bool equal(const Permutation<Domain::Filter> *lhs, const Permutation<Domain::Filter> *rhs)
+{
+ for (const auto &axis :
+ {FilterAxis::Count, FilterAxis::Depth, FilterAxis::Height, FilterAxis::Width})
+ {
+ if (lhs->axis(axis) != rhs->axis(axis))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool equal(const Permutation<Domain::DepthwiseFilter> *lhs,
+ const Permutation<Domain::DepthwiseFilter> *rhs)
+{
+ for (const auto &axis : {DepthwiseFilterAxis::Depth, DepthwiseFilterAxis::Multiplier,
+ DepthwiseFilterAxis::Height, DepthwiseFilterAxis::Width})
+ {
+ if (lhs->axis(axis) != rhs->axis(axis))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool equal(const Permutation<Domain::Matrix> *lhs, const Permutation<Domain::Matrix> *rhs)
+{
+ for (const auto &axis : {MatrixAxis::Height, MatrixAxis::Width})
+ {
+ if (lhs->axis(axis) != rhs->axis(axis))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
void set_input_null(loco::Node *node)
{
if (auto casted = dynamic_cast<loco::FeatureEncode *>(node))
@@ -50,6 +92,18 @@ void set_input_null(loco::Node *node)
casted->input(nullptr);
else if (auto casted = dynamic_cast<loco::BiasDecode *>(node))
casted->input(nullptr);
+ else if (auto casted = dynamic_cast<loco::FilterEncode *>(node))
+ casted->input(nullptr);
+ else if (auto casted = dynamic_cast<loco::FilterDecode *>(node))
+ casted->input(nullptr);
+ else if (auto casted = dynamic_cast<loco::DepthwiseFilterEncode *>(node))
+ casted->input(nullptr);
+ else if (auto casted = dynamic_cast<loco::DepthwiseFilterDecode *>(node))
+ casted->input(nullptr);
+ else if (auto casted = dynamic_cast<loco::MatrixEncode *>(node))
+ casted->input(nullptr);
+ else if (auto casted = dynamic_cast<loco::MatrixDecode *>(node))
+ casted->input(nullptr);
else
assert(false && "not supported node type");
}
@@ -93,7 +147,7 @@ bool SimplifyDomainConversionPass::run(loco::Graph *g)
if (equal(perm_encoder->perm(), perm_decoder->perm()))
{
- candidates.insert({encode_node, decode_node->input()});
+ forwardCandidates.insert({encode_node, decode_node->input()});
}
}
@@ -126,7 +180,59 @@ bool SimplifyDomainConversionPass::run(loco::Graph *g)
if (equal(perm_encoder->perm(), perm_decoder->perm()))
{
- candidates.insert({decode_node, encode_node->input()});
+ forwardCandidates.insert({decode_node, encode_node->input()});
+ }
+ }
+
+ // Let's find `FilterEncode -- FilterDecode` pattern
+ void visit(loco::FilterDecode *decode_node) final
+ {
+ using namespace loco;
+
+ auto encode_node = dynamic_cast<loco::FilterEncode *>(decode_node->input());
+ if (encode_node == nullptr)
+ {
+ return;
+ }
+ assert(encode_node->input() != nullptr);
+
+ auto encoder = encode_node->encoder();
+ assert(encoder != nullptr);
+
+ auto decoder = decode_node->decoder();
+ assert(decoder != nullptr);
+
+ // NOTE Work only for permuting codec
+ auto perm_decoder = dynamic_cast<const PermutingDecoder<Domain::Filter> *>(decoder);
+ auto perm_encoder = dynamic_cast<const PermutingEncoder<Domain::Filter> *>(encoder);
+
+ if (perm_encoder == nullptr || perm_decoder == nullptr)
+ {
+ return;
+ }
+
+ if (equal(perm_encoder->perm(), perm_decoder->perm()))
+ {
+ forwardCandidates.insert({decode_node, encode_node->input()});
+ }
+ else
+ {
+ std::vector<loco::TensorAxis> perm_vec;
+ perm_vec.resize(4);
+
+ auto enc_perm = perm_encoder->perm();
+ auto dec_perm = perm_decoder->perm();
+
+ for (const auto &axis :
+ {FilterAxis::Count, FilterAxis::Height, FilterAxis::Width, FilterAxis::Depth})
+ {
+ auto from = enc_perm->axis(axis);
+ auto to = dec_perm->axis(axis);
+ perm_vec[to] = from;
+ }
+
+ transposeCandidates.insert(stdex::make_unique<TransposeCtx>(
+ encode_node, decode_node, encode_node->input(), perm_vec));
}
}
@@ -136,7 +242,143 @@ bool SimplifyDomainConversionPass::run(loco::Graph *g)
if (auto encode_node = dynamic_cast<loco::BiasEncode *>(decode_node->input()))
{
assert(encode_node->input() != nullptr);
- candidates.insert({decode_node, encode_node->input()});
+ forwardCandidates.insert({decode_node, encode_node->input()});
+ }
+ }
+
+ // Let's find `DepthwiseFilterEncode -- DepthwiseFilterDecode` pattern
+ void visit(loco::DepthwiseFilterDecode *decode_node) final
+ {
+ using namespace loco;
+
+ auto encode_node = dynamic_cast<loco::DepthwiseFilterEncode *>(decode_node->input());
+ if (encode_node == nullptr)
+ {
+ return;
+ }
+ assert(encode_node->input() != nullptr);
+
+ auto encoder = encode_node->encoder();
+ assert(encoder != nullptr);
+
+ auto decoder = decode_node->decoder();
+ assert(decoder != nullptr);
+
+ // NOTE Work only for permuting codec
+ auto perm_decoder = dynamic_cast<const PermutingDecoder<Domain::DepthwiseFilter> *>(decoder);
+ auto perm_encoder = dynamic_cast<const PermutingEncoder<Domain::DepthwiseFilter> *>(encoder);
+
+ if (perm_encoder == nullptr || perm_decoder == nullptr)
+ {
+ return;
+ }
+
+ if (equal(perm_encoder->perm(), perm_decoder->perm()))
+ {
+ forwardCandidates.insert({decode_node, encode_node->input()});
+ }
+ else
+ {
+ std::vector<TensorAxis> perm_vec;
+ perm_vec.resize(4);
+
+ auto enc_perm = perm_encoder->perm();
+ auto dec_perm = perm_decoder->perm();
+
+ for (const auto &axis : {DepthwiseFilterAxis::Depth, DepthwiseFilterAxis::Height,
+ DepthwiseFilterAxis::Width, DepthwiseFilterAxis::Multiplier})
+ {
+ auto from = enc_perm->axis(axis);
+ auto to = dec_perm->axis(axis);
+ perm_vec[to] = from;
+ }
+
+ transposeCandidates.insert(stdex::make_unique<TransposeCtx>(
+ encode_node, decode_node, encode_node->input(), perm_vec));
+ }
+ }
+
+ // Let's find MatrixDecode followed by MatrixEncode
+ void visit(loco::MatrixEncode *encode_node) final
+ {
+ using namespace loco;
+
+ auto encoder = encode_node->encoder();
+ assert(encoder != nullptr);
+
+ auto decode_node = dynamic_cast<loco::MatrixDecode *>(encode_node->input());
+ if (decode_node == nullptr)
+ {
+ return;
+ }
+ assert(decode_node->input() != nullptr);
+
+ auto decoder = decode_node->decoder();
+ assert(decoder != nullptr);
+
+ // NOTE Work only for permuting codec
+ auto perm_decoder = dynamic_cast<const PermutingDecoder<Domain::Matrix> *>(decoder);
+ auto perm_encoder = dynamic_cast<const PermutingEncoder<Domain::Matrix> *>(encoder);
+
+ if (perm_encoder == nullptr || perm_decoder == nullptr)
+ {
+ return;
+ }
+
+ if (equal(perm_encoder->perm(), perm_decoder->perm()))
+ {
+ forwardCandidates.insert({encode_node, decode_node->input()});
+ }
+ }
+
+ // Let's find MatrixEncode followed by MatrixDecode
+ void visit(loco::MatrixDecode *decode_node) final
+ {
+ using namespace loco;
+
+ auto encode_node = dynamic_cast<loco::MatrixEncode *>(decode_node->input());
+ if (encode_node == nullptr)
+ {
+ return;
+ }
+ assert(encode_node->input() != nullptr);
+
+ auto encoder = encode_node->encoder();
+ assert(encoder != nullptr);
+
+ auto decoder = decode_node->decoder();
+ assert(decoder != nullptr);
+
+ // NOTE Work only for permuting codec
+ auto perm_decoder = dynamic_cast<const PermutingDecoder<Domain::Matrix> *>(decoder);
+ auto perm_encoder = dynamic_cast<const PermutingEncoder<Domain::Matrix> *>(encoder);
+
+ if (perm_encoder == nullptr || perm_decoder == nullptr)
+ {
+ return;
+ }
+
+ if (equal(perm_encoder->perm(), perm_decoder->perm()))
+ {
+ forwardCandidates.insert({decode_node, encode_node->input()});
+ }
+ else
+ {
+ std::vector<loco::TensorAxis> perm_vec;
+ perm_vec.resize(2);
+
+ auto enc_perm = perm_encoder->perm();
+ auto dec_perm = perm_decoder->perm();
+
+ for (const auto &axis : {MatrixAxis::Height, MatrixAxis::Width})
+ {
+ auto from = enc_perm->axis(axis);
+ auto to = dec_perm->axis(axis);
+ perm_vec[to] = from;
+ }
+
+ transposeCandidates.insert(stdex::make_unique<TransposeCtx>(
+ encode_node, decode_node, encode_node->input(), perm_vec));
}
}
@@ -144,21 +386,37 @@ bool SimplifyDomainConversionPass::run(loco::Graph *g)
using SimplifyingInfo = std::pair<loco::Node * /* end node of subgraph that will be replaced*/,
loco::Node * /* input of subgraph */>;
- std::set<SimplifyingInfo> candidates;
+ std::set<SimplifyingInfo> forwardCandidates;
+
+ struct TransposeCtx
+ {
+ loco::Node *first_node; // starting node of subgraph that will be replaced
+ loco::Node *last_node; // end node of subgraph that will be replaced
+ loco::Node *input_node; // input of subgraph
+ std::vector<loco::TensorAxis> perm_vec; // perm vector for transpose
+
+ TransposeCtx(loco::Node *first, loco::Node *last, loco::Node *input,
+ std::vector<loco::TensorAxis> perm)
+ : first_node(first), last_node(last), input_node(input), perm_vec(perm)
+ { /* empty */
+ }
+ };
+
+ std::set<std::unique_ptr<TransposeCtx>> transposeCandidates;
};
Collector collector;
- for (auto node : loco::all_nodes(g))
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
{
if (node->dialect() == loco::CanonicalDialect::get())
{
- auto canonical_node = dynamic_cast<loco::CanonicalNode *>(node);
+ auto canonical_node = loco::must_cast<loco::CanonicalNode *>(node);
canonical_node->accept(&collector);
}
}
- for (auto p : collector.candidates)
+ for (auto p : collector.forwardCandidates)
{
auto forward_node = g->nodes()->create<loco::Forward>();
forward_node->input(p.second);
@@ -166,7 +424,22 @@ bool SimplifyDomainConversionPass::run(loco::Graph *g)
set_input_null(p.first);
}
- return collector.candidates.size() > 0;
+ for (auto &ctx : collector.transposeCandidates)
+ {
+ auto transpose_node = g->nodes()->create<loco::TensorTranspose>();
+ {
+ transpose_node->perm()->size(ctx->perm_vec.size());
+
+ for (loco::TensorAxis axis = 0; axis < ctx->perm_vec.size(); axis++)
+ transpose_node->perm()->axis(axis) = ctx->perm_vec[axis];
+ }
+
+ transpose_node->input(ctx->input_node);
+ replace(ctx->last_node).with(transpose_node);
+ set_input_null(ctx->first_node);
+ }
+
+ return (collector.forwardCandidates.size() > 0 or collector.transposeCandidates.size() > 0);
}
} // namespace logo
diff --git a/compiler/logo/src/Passes/SimplifyDomainConversionPass.test.cpp b/compiler/logo/src/Passes/SimplifyDomainConversionPass.test.cpp
new file mode 100644
index 000000000..9a05763b4
--- /dev/null
+++ b/compiler/logo/src/Passes/SimplifyDomainConversionPass.test.cpp
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <logo/SimplifyDomainConversionPass.h>
+
+#include "TestHelper.h"
+
+#include <loco.h>
+#include <stdex/Memory.h>
+
+#include <gtest/gtest.h>
+
+namespace
+{
+
+// code borrowed from GraphBlock.h/cpp in exo-tflite
+enum class FilterLayout
+{
+ OHWI, // a.k.a., NHWC, Tensorflow Lite uses this layout
+ HWIO, // Tensorflow format
+};
+
+template <FilterLayout T> loco::Permutation<loco::Domain::Filter> perm();
+
+template <> loco::Permutation<loco::Domain::Filter> perm<FilterLayout::OHWI>()
+{
+ // Make NHWC permutation for encoder and decoder
+ loco::Permutation<loco::Domain::Filter> OHWI; // a.k.a., NHWC
+
+ OHWI.axis(loco::FilterAxis::Count) = 0;
+ OHWI.axis(loco::FilterAxis::Height) = 1;
+ OHWI.axis(loco::FilterAxis::Width) = 2;
+ OHWI.axis(loco::FilterAxis::Depth) = 3;
+
+ return OHWI;
+}
+
+template <> loco::Permutation<loco::Domain::Filter> perm<FilterLayout::HWIO>()
+{
+ // Make NHWC permutation for encoder and decoder
+ loco::Permutation<loco::Domain::Filter> HWIO;
+
+ HWIO.axis(loco::FilterAxis::Height) = 0;
+ HWIO.axis(loco::FilterAxis::Width) = 1;
+ HWIO.axis(loco::FilterAxis::Depth) = 2;
+ HWIO.axis(loco::FilterAxis::Count) = 3;
+
+ return HWIO;
+}
+
+template <FilterLayout T> loco::FilterDecode *make_filter_decode(loco::Node *input_for_decode)
+{
+ loco::Graph *g = input_for_decode->graph();
+
+ auto decoder = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Filter>>();
+
+ decoder->perm(perm<T>());
+
+ auto dec = g->nodes()->create<loco::FilterDecode>();
+ dec->input(input_for_decode);
+ dec->decoder(std::move(decoder));
+
+ return dec;
+}
+
+template <FilterLayout T> loco::FilterEncode *make_filter_encode(loco::Node *input_for_encode)
+{
+ loco::Graph *g = input_for_encode->graph();
+
+ auto encoder = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>();
+
+ encoder->perm(perm<T>());
+
+ auto enc = g->nodes()->create<loco::FilterEncode>();
+ enc->input(input_for_encode);
+ enc->encoder(std::move(encoder));
+
+ return enc;
+}
+
+/*
+ test case:
+ ConstGen (2x3x4x5) ---- FeatureEncode ---- FeatureDecode --- Push
+ 0 H O 0
+ 1 W H 1
+ 2 I(depth) W 2
+ 3 O(count) I 3
+
+ axis 0 ---------------------> H --------------> H -----------> 1
+ axis 1 ---------------------> W --------------> W -----------> 2
+ axis 2 ---------------------> I --------------> I -----------> 3
+ axis 3 ---------------------> O --------------> O -----------> 0
+
+ so perm vector of Tranpose = [3, 0, 1, 2]
+*/
+void create_net_FilterEncode_FilterDecode_different_perms(loco::Graph *graph)
+{
+ assert(graph);
+
+ auto const_node = graph->nodes()->create<loco::ConstGen>();
+ {
+ const_node->dtype(loco::DataType::FLOAT32);
+ const_node->rank(4);
+ int count = 1;
+ for (int i = 0; i < 4; ++i)
+ {
+ const_node->dim(i) = i + 2;
+ count *= i + 2;
+ }
+ const_node->size<loco::DataType::FLOAT32>(count);
+ for (uint32_t i = 0; i < count; i++)
+ const_node->at<loco::DataType::FLOAT32>(i) = 3.14f; // any number
+ }
+
+ auto encoder = make_filter_encode<FilterLayout::HWIO>(const_node);
+ auto decoder = make_filter_decode<FilterLayout::OHWI>(encoder);
+
+ auto push_node = graph->nodes()->create<loco::Push>();
+ {
+ push_node->from(decoder);
+ }
+
+ auto graph_output = graph->outputs()->create();
+ {
+ graph_output->name("output");
+ graph_output->dtype(loco::DataType::FLOAT32);
+ loco::link(graph_output, push_node);
+ }
+}
+
+/*
+ test case:
+ ConstGen (2x3x4x5) ---- FeatureEncode ---- FeatureDecode --- Push
+ 0 H H 0
+ 1 W W 1
+ 2 I(depth) I 2
+ 3 O(count) O 3
+
+ axis 0 ---------------------> H --------------> H -----------> 0
+ axis 1 ---------------------> W --------------> W -----------> 1
+ axis 2 ---------------------> I --------------> I -----------> 2
+ axis 3 ---------------------> O --------------> O -----------> 3
+
+ so perm vector of Tranpose = [0, 1, 2, 3] and transposes should be eliminated
+*/
+void create_net_FilterEncode_FilterDecode_equal_perms(loco::Graph *graph)
+{
+ assert(graph);
+
+ auto const_node = graph->nodes()->create<loco::ConstGen>();
+ {
+ const_node->dtype(loco::DataType::FLOAT32);
+ const_node->rank(4);
+ int count = 1;
+ for (int i = 0; i < 4; ++i)
+ {
+ const_node->dim(i) = i + 2;
+ count *= i + 2;
+ }
+ const_node->size<loco::DataType::FLOAT32>(count);
+ for (uint32_t i = 0; i < count; i++)
+ const_node->at<loco::DataType::FLOAT32>(i) = 3.14f; // any number
+ }
+
+ auto encoder = make_filter_encode<FilterLayout::HWIO>(const_node);
+ auto decoder = make_filter_decode<FilterLayout::HWIO>(encoder);
+
+ auto push_node = graph->nodes()->create<loco::Push>();
+ {
+ push_node->from(decoder);
+ }
+
+ auto graph_output = graph->outputs()->create();
+ {
+ graph_output->name("output");
+ graph_output->dtype(loco::DataType::FLOAT32);
+ loco::link(graph_output, push_node);
+ }
+}
+
+} // namespace
+
+TEST(SimplifyDomainConversionPass, FilterEncode_FilterDecode_different_perms)
+{
+ auto graph = loco::make_graph();
+ create_net_FilterEncode_FilterDecode_different_perms(graph.get());
+
+ logo::SimplifyDomainConversionPass pass;
+ while (pass.run(graph.get()) == true)
+ ;
+
+ auto tr = logo::test::find_first_node_by_type<loco::TensorTranspose>(graph.get());
+ {
+ ASSERT_EQ(tr->perm()->size(), 4);
+ ASSERT_EQ(tr->perm()->axis(0), 3);
+ ASSERT_EQ(tr->perm()->axis(1), 0);
+ ASSERT_EQ(tr->perm()->axis(2), 1);
+ ASSERT_EQ(tr->perm()->axis(3), 2);
+ }
+
+ auto const_gen = dynamic_cast<loco::ConstGen *>(tr->input());
+ ASSERT_NE(const_gen, nullptr);
+}
+
+TEST(SimplifyDomainConversionPass, FilterEncode_FilterDecode_equal_perms)
+{
+ auto graph = loco::make_graph();
+ create_net_FilterEncode_FilterDecode_equal_perms(graph.get());
+
+ logo::SimplifyDomainConversionPass pass;
+ while (pass.run(graph.get()) == true)
+ ;
+
+ ASSERT_EQ(loco::output_nodes(graph.get()).size(), 1);
+ loco::Node *output_node = loco::output_nodes(graph.get())[0];
+
+ auto forward = loco::must_cast<loco::Forward *>(output_node->arg(0));
+ ASSERT_NE(forward, nullptr);
+ auto const_gen = dynamic_cast<loco::ConstGen *>(forward->arg(0));
+ ASSERT_NE(const_gen, nullptr);
+}
diff --git a/compiler/luci-interpreter/CMakeLists.txt b/compiler/luci-interpreter/CMakeLists.txt
new file mode 100644
index 000000000..33fdc52aa
--- /dev/null
+++ b/compiler/luci-interpreter/CMakeLists.txt
@@ -0,0 +1,4 @@
+set(LUCI_INTERPRETER_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include")
+set(LUCI_INTERPRETER_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src")
+
+add_subdirectory(src)
diff --git a/compiler/luci-interpreter/include/luci_interpreter/Interpreter.h b/compiler/luci-interpreter/include/luci_interpreter/Interpreter.h
new file mode 100644
index 000000000..7a14bf6f8
--- /dev/null
+++ b/compiler/luci-interpreter/include/luci_interpreter/Interpreter.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_INTERPRETER_H
+#define LUCI_INTERPRETER_INTERPRETER_H
+
+#include "luci_interpreter/core/Tensor.h"
+
+#include <luci/IR/Nodes/CircleInput.h>
+#include <luci/IR/Nodes/CircleOutput.h>
+
+#include <luci/IR/Module.h>
+
+#include <memory>
+#include <vector>
+#include <unordered_map>
+
+namespace luci_interpreter
+{
+
+class ExecutionObserver
+{
+public:
+ virtual ~ExecutionObserver();
+
+ // Called when the value of a tensor has been updated during execution.
+ virtual void postTensorWrite(const luci::CircleNode *node, const Tensor *tensor);
+
+ // Called before / after executing an operator.
+ // Note that these methods are not called for auxiliary operators (CircleInput, CircleOutput,
+ // CircleConst and Circle*Out).
+ virtual void preOperatorExecute(const luci::CircleNode *node);
+ virtual void postOperatorExecute(const luci::CircleNode *node);
+};
+
+class Interpreter
+{
+public:
+ explicit Interpreter(const luci::Module *module);
+
+ ~Interpreter();
+
+ void writeInputTensor(const luci::CircleInput *input_node, const void *data, size_t data_size);
+
+ void readOutputTensor(const luci::CircleOutput *output_node, void *data, size_t data_size);
+
+ void interpret();
+
+ void attachObserver(ExecutionObserver *observer);
+
+ const Tensor *getTensor(const loco::Node *node) { return _node_to_tensor[node]; }
+
+private:
+ std::unique_ptr<class RuntimeModule> _runtime_module;
+
+ // Observer functionality support.
+ std::unique_ptr<struct RuntimeToIR> _runtime_to_ir;
+ std::unordered_map<const loco::Node *, Tensor *> _node_to_tensor;
+ std::unique_ptr<class EventNotifier> _event_notifier;
+ std::vector<ExecutionObserver *> _observers;
+};
+
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_INTERPRETER_H
diff --git a/compiler/luci-interpreter/include/luci_interpreter/core/DataType.h b/compiler/luci-interpreter/include/luci_interpreter/core/DataType.h
new file mode 100644
index 000000000..27bf719b5
--- /dev/null
+++ b/compiler/luci-interpreter/include/luci_interpreter/core/DataType.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_CORE_DATATYPE_H
+#define LUCI_INTERPRETER_CORE_DATATYPE_H
+
+#include <loco/IR/DataType.h>
+#include <loco/IR/DataTypeTraits.h>
+
+#include <cstddef>
+
+namespace luci_interpreter
+{
+
+using DataType = loco::DataType;
+
+template <DataType DT> using DataTypeImpl = loco::DataTypeImpl<DT>;
+
+inline size_t getDataTypeSize(DataType data_type) { return loco::size(data_type); }
+
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_CORE_DATATYPE_H
diff --git a/compiler/luci-interpreter/include/luci_interpreter/core/Tensor.h b/compiler/luci-interpreter/include/luci_interpreter/core/Tensor.h
new file mode 100644
index 000000000..e356bce92
--- /dev/null
+++ b/compiler/luci-interpreter/include/luci_interpreter/core/Tensor.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_CORE_TENSOR_H
+#define LUCI_INTERPRETER_CORE_TENSOR_H
+
+#include "luci_interpreter/core/DataType.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace luci_interpreter
+{
+
+class Shape
+{
+public:
+ explicit Shape(int rank) : _dims(rank, 0) {}
+
+ Shape(std::initializer_list<int32_t> dims) : _dims(dims.begin(), dims.end()) {}
+
+ int num_dims() const { return _dims.size(); }
+
+ int32_t dim(int i) const
+ {
+ assert(i >= 0 && i < static_cast<int>(_dims.size()));
+ return _dims[i];
+ }
+
+ int32_t &dim(int i)
+ {
+ assert(i >= 0 && i < static_cast<int>(_dims.size()));
+ return _dims[i];
+ }
+
+ int32_t num_elements() const
+ {
+ int32_t result = 1;
+ for (const int32_t dim : _dims)
+ {
+ result *= dim;
+ }
+ return result;
+ }
+
+ bool operator==(const Shape &other) const { return _dims == other._dims; }
+
+ bool operator!=(const Shape &other) const { return !operator==(other); }
+
+private:
+ std::vector<int32_t> _dims;
+};
+
+// Tensor affine quantization parameters.
+//
+// The relationship between real and quantized values:
+// real_value = (quantized_value - zero_point) * scale
+//
+// In per-tensor case, 'scale' and 'zero_point' are one element each.
+// In per-channel case, 'scale' and 'zero_point' are N elements each, where N is the size
+// of the quantized dimension.
+//
+// Note that due to historical and performance reasons, per-tensor quantization uses unsigned
+// integer types, while per-channel uses signed types assuming 'zero_point' == 0.
+struct AffineQuantization
+{
+ std::vector<float> scale;
+ std::vector<int32_t> zero_point;
+ int32_t quantized_dimension;
+};
+
+class Tensor
+{
+public:
+ Tensor(DataType element_type, Shape shape, AffineQuantization quantization, std::string name);
+
+ DataType element_type() const { return _element_type; }
+
+ const Shape &shape() const { return _shape; }
+
+ float scale() const
+ {
+ assert(_quantization.scale.size() == 1);
+ return _quantization.scale[0];
+ }
+
+ int32_t zero_point() const
+ {
+ assert(_quantization.zero_point.size() == 1);
+ return _quantization.zero_point[0];
+ }
+
+ void allocate();
+ void deallocate();
+
+ const std::vector<float> &scales() const { return _quantization.scale; }
+
+ const std::vector<int32_t> &zero_points() const { return _quantization.zero_point; }
+
+ int32_t quantized_dimension() const { return _quantization.quantized_dimension; }
+
+ template <typename T> const T *data() const
+ {
+ assert(_data_allocated);
+ return reinterpret_cast<const T *>(_data.get());
+ }
+
+ template <typename T> T *data()
+ {
+ if (!_data_allocated)
+ allocate();
+ return reinterpret_cast<T *>(_data.get());
+ }
+
+ const std::string &name() const { return _name; }
+
+ void readData(void *data_ptr, size_t data_size) const;
+
+ void writeData(const void *data_ptr, size_t data_size);
+
+ void resize(const Shape &new_shape);
+
+private:
+ DataType _element_type;
+ Shape _shape;
+ AffineQuantization _quantization;
+ std::unique_ptr<uint8_t[]> _data;
+ std::string _name;
+ bool _data_allocated;
+};
+
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_CORE_TENSOR_H
diff --git a/compiler/luci-interpreter/requires.cmake b/compiler/luci-interpreter/requires.cmake
new file mode 100644
index 000000000..f411f387a
--- /dev/null
+++ b/compiler/luci-interpreter/requires.cmake
@@ -0,0 +1 @@
+require(luci)
diff --git a/compiler/luci-interpreter/src/CMakeLists.txt b/compiler/luci-interpreter/src/CMakeLists.txt
new file mode 100644
index 000000000..47b68fa40
--- /dev/null
+++ b/compiler/luci-interpreter/src/CMakeLists.txt
@@ -0,0 +1,41 @@
+nnas_find_package(TensorFlowSource EXACT 2.3.0 QUIET)
+nnas_find_package(TensorFlowGEMMLowpSource EXACT 2.3.0 QUIET)
+nnas_find_package(TensorFlowEigenSource EXACT 2.3.0 QUIET)
+nnas_find_package(TensorFlowRuySource EXACT 2.3.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 ()
+
+add_subdirectory(core)
+add_subdirectory(kernels)
+add_subdirectory(loader)
+
+set(SOURCES
+ "${LUCI_INTERPRETER_INCLUDE_DIR}/luci_interpreter/Interpreter.h"
+ Interpreter.cpp)
+
+add_library(luci_interpreter SHARED ${SOURCES})
+target_include_directories(luci_interpreter PUBLIC "${LUCI_INTERPRETER_INCLUDE_DIR}")
+target_include_directories(luci_interpreter PRIVATE "${LUCI_INTERPRETER_SOURCE_DIR}")
+target_link_libraries(luci_interpreter
+ PUBLIC luci_lang luci_interpreter_loader luci_interpreter_core
+ PRIVATE nncc_common)
+
+install(TARGETS luci_interpreter DESTINATION lib)
diff --git a/compiler/luci-interpreter/src/Interpreter.cpp b/compiler/luci-interpreter/src/Interpreter.cpp
new file mode 100644
index 000000000..639ffc1f0
--- /dev/null
+++ b/compiler/luci-interpreter/src/Interpreter.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci_interpreter/Interpreter.h"
+
+#include "loader/ModuleLoader.h"
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace
+{
+
+class EventNotifierImpl final : public EventNotifier
+{
+public:
+ EventNotifierImpl(const RuntimeToIR &runtime_to_ir,
+ const std::vector<ExecutionObserver *> &observers)
+ : _runtime_to_ir(runtime_to_ir), _observers(observers)
+ {
+ }
+
+ void postTensorWrite(const Tensor *tensor) override
+ {
+ assert(tensor != nullptr);
+ for (const auto &observer : _observers)
+ {
+ observer->postTensorWrite(_runtime_to_ir.tensor_to_node.at(tensor), tensor);
+ }
+ }
+
+ void preOperatorExecute(const Kernel *kernel) override
+ {
+ assert(kernel != nullptr);
+ for (const auto &observer : _observers)
+ {
+ observer->preOperatorExecute(_runtime_to_ir.kernel_to_node.at(kernel));
+ }
+ }
+
+ void postOperatorExecute(const Kernel *kernel) override
+ {
+ assert(kernel != nullptr);
+ for (const auto &observer : _observers)
+ {
+ observer->postOperatorExecute(_runtime_to_ir.kernel_to_node.at(kernel));
+ }
+ }
+
+private:
+ const RuntimeToIR &_runtime_to_ir;
+ const std::vector<ExecutionObserver *> &_observers;
+};
+
+} // namespace
+
+Interpreter::Interpreter(const luci::Module *module)
+{
+ _runtime_to_ir = std::make_unique<RuntimeToIR>();
+ _event_notifier = std::make_unique<EventNotifierImpl>(*_runtime_to_ir, _observers);
+ _runtime_module = std::make_unique<RuntimeModule>(_event_notifier.get());
+ ModuleLoader loader(module, _runtime_module.get(), *_runtime_to_ir, _node_to_tensor);
+ loader.load();
+}
+
+Interpreter::~Interpreter() = default;
+
+void Interpreter::writeInputTensor(const luci::CircleInput *input_node, const void *data,
+ size_t data_size)
+{
+ Tensor *tensor = _runtime_module->getInputTensors()[input_node->index()];
+ if (tensor == nullptr)
+ {
+ const std::string &name = input_node->name();
+ throw std::runtime_error("Cannot find tensor for input node named \"" + name + "\".");
+ }
+ if (data != nullptr)
+ tensor->writeData(data, data_size);
+}
+
+void Interpreter::readOutputTensor(const luci::CircleOutput *output_node, void *data,
+ size_t data_size)
+{
+ Tensor *tensor = _runtime_module->getOutputTensors()[output_node->index()];
+ if (tensor == nullptr)
+ {
+ const std::string &name = output_node->name();
+ throw std::runtime_error("Cannot find tensor for output node named \"" + name + "\".");
+ }
+ if (data != nullptr)
+ tensor->readData(data, data_size);
+}
+
+void Interpreter::interpret() { _runtime_module->execute(); }
+
+void Interpreter::attachObserver(ExecutionObserver *observer)
+{
+ if (std::find(_observers.cbegin(), _observers.cend(), observer) != _observers.cend())
+ throw std::runtime_error("Observer is already attached.");
+ _observers.push_back(observer);
+}
+
+ExecutionObserver::~ExecutionObserver() = default;
+
+void ExecutionObserver::postTensorWrite(const luci::CircleNode *, const Tensor *) {}
+
+void ExecutionObserver::preOperatorExecute(const luci::CircleNode *) {}
+
+void ExecutionObserver::postOperatorExecute(const luci::CircleNode *) {}
+
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/core/CMakeLists.txt b/compiler/luci-interpreter/src/core/CMakeLists.txt
new file mode 100644
index 000000000..e576dbd94
--- /dev/null
+++ b/compiler/luci-interpreter/src/core/CMakeLists.txt
@@ -0,0 +1,17 @@
+set(SOURCES
+ "${LUCI_INTERPRETER_INCLUDE_DIR}/luci_interpreter/core/DataType.h"
+ "${LUCI_INTERPRETER_INCLUDE_DIR}/luci_interpreter/core/Tensor.h"
+ EventNotifier.h
+ Kernel.h
+ KernelParams.h
+ RuntimeGraph.h
+ RuntimeGraph.cpp
+ RuntimeModule.h
+ Tensor.cpp)
+
+add_library(luci_interpreter_core STATIC ${SOURCES})
+set_target_properties(luci_interpreter_core PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(luci_interpreter_core PUBLIC "${LUCI_INTERPRETER_INCLUDE_DIR}")
+target_include_directories(luci_interpreter_core PUBLIC "${LUCI_INTERPRETER_SOURCE_DIR}")
+target_link_libraries(luci_interpreter_core PUBLIC luci_lang)
+target_link_libraries(luci_interpreter_core PRIVATE nncc_common)
diff --git a/compiler/luci-interpreter/src/core/EventNotifier.h b/compiler/luci-interpreter/src/core/EventNotifier.h
new file mode 100644
index 000000000..5c4fbd3be
--- /dev/null
+++ b/compiler/luci-interpreter/src/core/EventNotifier.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_CORE_EVENTNOTIFIER_H
+#define LUCI_INTERPRETER_CORE_EVENTNOTIFIER_H
+
+namespace luci_interpreter
+{
+
+// Used at execution stage to tell the interpreter that the runtime state has changed in some way.
+class EventNotifier
+{
+public:
+ virtual ~EventNotifier() = default;
+
+ virtual void postTensorWrite(const Tensor *tensor) = 0;
+ virtual void preOperatorExecute(const Kernel *kernel) = 0;
+ virtual void postOperatorExecute(const Kernel *kernel) = 0;
+};
+
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_CORE_EVENTNOTIFIER_H
diff --git a/compiler/luci-interpreter/src/core/Kernel.h b/compiler/luci-interpreter/src/core/Kernel.h
new file mode 100644
index 000000000..5f5efb219
--- /dev/null
+++ b/compiler/luci-interpreter/src/core/Kernel.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_CORE_KERNEL_H
+#define LUCI_INTERPRETER_CORE_KERNEL_H
+
+#include "luci_interpreter/core/Tensor.h"
+
+#include <vector>
+
+namespace luci_interpreter
+{
+
+// Base class for all kernels.
+class Kernel
+{
+protected:
+ Kernel(std::vector<const Tensor *> inputs, std::vector<Tensor *> outputs)
+ : _inputs(std::move(inputs)), _outputs(std::move(outputs))
+ {
+ }
+
+public:
+ virtual ~Kernel() = default;
+
+ std::vector<const Tensor *> getInputTensors() const { return _inputs; }
+ std::vector<Tensor *> getOutputTensors() const { return _outputs; }
+
+ // Configures the kernel.
+ // This function is currently called once for each kernel during interpreter construction,
+ // which makes it a convenient place for preparing (resizing) output tensors.
+ virtual void configure() = 0;
+
+ // Executes the kernel.
+ virtual void execute() const = 0;
+
+protected:
+ // NOTE Prefer not to use these in derived classes.
+ const std::vector<const Tensor *> _inputs;
+ const std::vector<Tensor *> _outputs;
+};
+
+// Base class for kernels with parameters.
+template <typename Params> class KernelWithParams : public Kernel
+{
+protected:
+ KernelWithParams(std::vector<const Tensor *> inputs, std::vector<Tensor *> outputs,
+ const Params &params)
+ : Kernel(std::move(inputs), std::move(outputs)), _params(params)
+ {
+ }
+
+public:
+ const Params &params() const { return _params; }
+
+protected:
+ const Params _params;
+};
+
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_CORE_KERNEL_H
diff --git a/compiler/luci-interpreter/src/core/KernelParams.h b/compiler/luci-interpreter/src/core/KernelParams.h
new file mode 100644
index 000000000..b74be797b
--- /dev/null
+++ b/compiler/luci-interpreter/src/core/KernelParams.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_CORE_KERNELPARAMS_H
+#define LUCI_INTERPRETER_CORE_KERNELPARAMS_H
+
+#include <luci/IR/AttrPadding.h>
+#include <luci/IR/AttrFusedActFunc.h>
+#include <luci_interpreter/core/DataType.h>
+
+#include <cstdint>
+#include <vector>
+
+namespace luci_interpreter
+{
+
+// Inject commonly used types into `luci_interpreter` namespace for convenience.
+using Activation = luci::FusedActFunc;
+using Padding = luci::Padding;
+
+struct AddParams
+{
+ Activation activation;
+};
+
+struct ArgMaxParams
+{
+ DataType output_type;
+};
+
+struct ConcatenationParams
+{
+ int axis;
+};
+
+struct Conv2DParams
+{
+ Padding padding;
+ int32_t stride_height;
+ int32_t stride_width;
+ int32_t dilation_height_factor;
+ int32_t dilation_width_factor;
+ Activation activation;
+};
+
+struct DepthToSpaceParams
+{
+ int block_size;
+};
+
+struct DepthwiseConv2DParams
+{
+ Padding padding;
+ int32_t depth_multiplier; // TODO Remove, as it can be calculated.
+ int32_t stride_height;
+ int32_t stride_width;
+ int32_t dilation_height_factor;
+ int32_t dilation_width_factor;
+ Activation activation;
+};
+
+struct DivParams
+{
+ Activation activation;
+};
+
+struct FullyConnectedParams
+{
+ Activation activation;
+};
+
+struct InstanceNormParams
+{
+ float epsilon;
+ Activation activation;
+};
+
+struct L2NormParams
+{
+ Activation activation;
+};
+
+struct LeakyReluParams
+{
+ float alpha;
+};
+
+struct LocalResponseNormalizationParams
+{
+ int32_t radius;
+ float bias;
+ float alpha;
+ float beta;
+};
+
+struct MulParams
+{
+ Activation activation;
+};
+
+struct Pool2DParams
+{
+ Padding padding;
+ int32_t filter_height;
+ int32_t filter_width;
+ int32_t stride_height;
+ int32_t stride_width;
+ Activation activation;
+};
+
+struct ReducerParams
+{
+ bool keep_dims;
+};
+
+struct ResizeBilinearParams
+{
+ bool align_corners;
+ bool half_pixel_centers;
+};
+
+struct ResizeNearestNeighborParams
+{
+ bool align_corners;
+ bool half_pixel_centers;
+};
+
+struct SubParams
+{
+ Activation activation;
+};
+
+struct SpaceToDepthParams
+{
+ int block_size;
+};
+
+struct SoftmaxParams
+{
+ float beta;
+};
+
+struct StridedSliceParams
+{
+ int32_t begin_mask;
+ int32_t end_mask;
+ int32_t ellipsis_mask;
+ int32_t new_axis_mask;
+ int32_t shrink_axis_mask;
+};
+
+struct SqueezeParams
+{
+ std::vector<int32_t> squeeze_dims;
+};
+
+struct TransposeConvParams
+{
+ Padding padding;
+ int32_t stride_height;
+ int32_t stride_width;
+};
+
+struct UnpackParams
+{
+ int axis;
+};
+
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_CORE_KERNELPARAMS_H
diff --git a/compiler/luci-interpreter/src/core/RuntimeGraph.cpp b/compiler/luci-interpreter/src/core/RuntimeGraph.cpp
new file mode 100644
index 000000000..57f6fed44
--- /dev/null
+++ b/compiler/luci-interpreter/src/core/RuntimeGraph.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/RuntimeGraph.h"
+
+#include "core/RuntimeModule.h"
+
+#include <algorithm>
+#include <unordered_map>
+
+namespace luci_interpreter
+{
+
+class RuntimeGraph::TensorAllocPlan
+{
+ std::vector<std::vector<Tensor *>> _alloc_plan;
+ std::vector<std::vector<Tensor *>> _dealloc_plan;
+ bool _valid = false;
+
+public:
+ void invalidate() { _valid = false; }
+ bool isValid() const { return _valid; }
+ void build(const RuntimeGraph &graph);
+ void allocate(size_t kernel_index) const;
+ void deallocate(size_t kernel_index) const;
+};
+
+void RuntimeGraph::TensorAllocPlan::build(const RuntimeGraph &graph)
+{
+ invalidate();
+ using Lifetime = std::pair<size_t, size_t>;
+ std::unordered_map<Tensor *, Lifetime> lifetimes;
+ const size_t num_kernels = graph._kernels.size();
+ for (size_t index = 0; index < num_kernels; ++index)
+ {
+ const auto &kernel = graph._kernels[index];
+ for (const Tensor *tensor : kernel->getInputTensors())
+ {
+ auto nc_tensor = const_cast<Tensor *>(tensor);
+ if (lifetimes.count(nc_tensor) > 0)
+ lifetimes.at(nc_tensor).second = index;
+ }
+ for (Tensor *tensor : kernel->getOutputTensors())
+ {
+ assert(lifetimes.count(tensor) == 0);
+ lifetimes[tensor] = Lifetime(index, index);
+ }
+ }
+ for (const Tensor *tensor : graph.getOutputTensors())
+ {
+ auto nc_tensor = const_cast<Tensor *>(tensor);
+ if (lifetimes.count(nc_tensor) > 0)
+ lifetimes.at(nc_tensor).second = num_kernels;
+ }
+ _alloc_plan.assign(num_kernels, std::vector<Tensor *>());
+ _dealloc_plan.assign(num_kernels + 1, std::vector<Tensor *>());
+ for (const auto &item : lifetimes)
+ {
+ _alloc_plan[item.second.first].push_back(item.first);
+ _dealloc_plan[item.second.second].push_back(item.first);
+ }
+ _valid = true;
+}
+
+void RuntimeGraph::TensorAllocPlan::allocate(size_t kernel_index) const
+{
+ assert(_valid && kernel_index < _alloc_plan.size());
+ for (Tensor *tensor : _alloc_plan[kernel_index])
+ {
+ tensor->allocate();
+ }
+}
+
+void RuntimeGraph::TensorAllocPlan::deallocate(size_t kernel_index) const
+{
+ assert(_valid && kernel_index < _dealloc_plan.size());
+ for (Tensor *tensor : _dealloc_plan[kernel_index])
+ {
+ tensor->deallocate();
+ }
+}
+
+RuntimeGraph::RuntimeGraph(RuntimeModule *owning_module)
+ : _owning_module(owning_module), _tensor_alloc_plan(std::make_unique<TensorAllocPlan>())
+{
+}
+
+RuntimeGraph::~RuntimeGraph() {}
+
+Tensor *RuntimeGraph::addTensor(std::unique_ptr<Tensor> &&tensor)
+{
+ assert(tensor != nullptr);
+ _tensors.push_back(std::move(tensor));
+ return _tensors.back().get();
+}
+
+void RuntimeGraph::setInputTensors(const std::vector<Tensor *> &input_tensors)
+{
+ assert(std::all_of(input_tensors.cbegin(), input_tensors.cend(),
+ [](Tensor *tensor) { return tensor != nullptr; }));
+ _input_tensors = input_tensors;
+}
+
+void RuntimeGraph::setOutputTensors(const std::vector<Tensor *> &output_tensors)
+{
+ assert(std::all_of(output_tensors.cbegin(), output_tensors.cend(),
+ [](Tensor *tensor) { return tensor != nullptr; }));
+ _output_tensors = output_tensors;
+}
+
+void RuntimeGraph::addKernel(std::unique_ptr<Kernel> &&kernel)
+{
+ assert(kernel != nullptr);
+ _kernels.push_back(std::move(kernel));
+ _tensor_alloc_plan->invalidate();
+}
+
+void RuntimeGraph::execute() const
+{
+ if (!_tensor_alloc_plan->isValid())
+ _tensor_alloc_plan->build(*this);
+
+ EventNotifier *event_notifier = _owning_module->getEventNotifier();
+
+ // Notify the observers that the input tensors have changed.
+ if (event_notifier != nullptr)
+ {
+ for (const Tensor *input_tensor : getInputTensors())
+ {
+ event_notifier->postTensorWrite(input_tensor);
+ }
+ }
+
+ for (size_t index = 0; index < _kernels.size(); ++index)
+ {
+ const auto &kernel = _kernels[index];
+ if (event_notifier != nullptr)
+ {
+ event_notifier->preOperatorExecute(kernel.get());
+ }
+
+ // TODO The `configure` method should only be called if the outputs of an operator need to be
+ // resized.
+ kernel->configure();
+// TODO decide where to allocate memory, and uncomment/remove this if
+#if 0
+ _tensor_alloc_plan->allocate(
+ index); // Preallocate outputs in advance instead of relying on automatic allocation
+#endif
+ kernel->execute();
+
+ if (event_notifier != nullptr)
+ {
+ event_notifier->postOperatorExecute(kernel.get());
+ }
+
+ for (const Tensor *tensor : kernel->getOutputTensors())
+ {
+ if (event_notifier != nullptr)
+ {
+ event_notifier->postTensorWrite(tensor);
+ }
+ }
+ _tensor_alloc_plan->deallocate(index);
+ }
+}
+
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/core/RuntimeGraph.h b/compiler/luci-interpreter/src/core/RuntimeGraph.h
new file mode 100644
index 000000000..5f732025d
--- /dev/null
+++ b/compiler/luci-interpreter/src/core/RuntimeGraph.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_CORE_RUNTIMEGRAPH_H
+#define LUCI_INTERPRETER_CORE_RUNTIMEGRAPH_H
+
+#include "luci_interpreter/core/Tensor.h"
+#include "core/Kernel.h"
+
+#include <memory>
+#include <vector>
+
+namespace luci_interpreter
+{
+
+class RuntimeModule;
+
+class RuntimeGraph
+{
+private:
+ class TensorAllocPlan;
+ friend class TensorAllocPlan;
+
+public:
+ explicit RuntimeGraph(RuntimeModule *owning_module);
+ ~RuntimeGraph();
+
+ Tensor *addTensor(std::unique_ptr<Tensor> &&tensor);
+
+ void setInputTensors(const std::vector<Tensor *> &input_tensors);
+ void setOutputTensors(const std::vector<Tensor *> &output_tensors);
+
+ const std::vector<Tensor *> &getInputTensors() const { return _input_tensors; }
+ const std::vector<Tensor *> &getOutputTensors() const { return _output_tensors; }
+
+ void addKernel(std::unique_ptr<Kernel> &&kernel);
+
+ void execute() const;
+
+private:
+ RuntimeModule *_owning_module;
+ std::vector<std::unique_ptr<Tensor>> _tensors;
+ std::vector<Tensor *> _input_tensors;
+ std::vector<Tensor *> _output_tensors;
+
+ // Kernels in execution order.
+ std::vector<std::unique_ptr<Kernel>> _kernels;
+ // Tensors that are not used anymore after given op
+ std::unique_ptr<TensorAllocPlan> _tensor_alloc_plan;
+};
+
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_CORE_RUNTIMEGRAPH_H
diff --git a/compiler/luci-interpreter/src/core/RuntimeModule.h b/compiler/luci-interpreter/src/core/RuntimeModule.h
new file mode 100644
index 000000000..dccc3a173
--- /dev/null
+++ b/compiler/luci-interpreter/src/core/RuntimeModule.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_CORE_RUNTIMEMODULE_H
+#define LUCI_INTERPRETER_CORE_RUNTIMEMODULE_H
+
+#include "core/RuntimeGraph.h"
+#include "core/EventNotifier.h"
+
+#include <memory>
+#include <vector>
+
+namespace luci_interpreter
+{
+
+class RuntimeModule
+{
+public:
+ explicit RuntimeModule(EventNotifier *event_notifier) : _event_notifier(event_notifier) {}
+
+ EventNotifier *getEventNotifier() const { return _event_notifier; }
+
+ RuntimeGraph *addGraph()
+ {
+ _graphs.push_back(std::make_unique<RuntimeGraph>(this));
+ return _graphs.back().get();
+ }
+
+ const std::vector<Tensor *> &getInputTensors() const { return getMainGraph()->getInputTensors(); }
+ const std::vector<Tensor *> &getOutputTensors() const
+ {
+ return getMainGraph()->getOutputTensors();
+ }
+
+ void execute() const { getMainGraph()->execute(); }
+
+private:
+ RuntimeGraph *getMainGraph() const { return _graphs[0].get(); }
+
+ EventNotifier *const _event_notifier;
+ std::vector<std::unique_ptr<RuntimeGraph>> _graphs;
+};
+
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_CORE_RUNTIMEMODULE_H
diff --git a/compiler/luci-interpreter/src/core/Tensor.cpp b/compiler/luci-interpreter/src/core/Tensor.cpp
new file mode 100644
index 000000000..6e0424ffa
--- /dev/null
+++ b/compiler/luci-interpreter/src/core/Tensor.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci_interpreter/core/Tensor.h"
+
+#include <cstring>
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+Tensor::Tensor(DataType element_type, Shape shape, AffineQuantization quantization,
+ std::string name)
+ : _element_type(element_type), _shape(std::move(shape)), _quantization(std::move(quantization)),
+ _name(std::move(name)), _data_allocated(false)
+{
+}
+
+void Tensor::allocate()
+{
+ deallocate();
+ const size_t element_size = getDataTypeSize(_element_type);
+ const int32_t num_elements = _shape.num_elements();
+ _data = std::make_unique<uint8_t[]>(num_elements * element_size);
+ _data_allocated = true;
+}
+
+void Tensor::deallocate()
+{
+ _data_allocated = false;
+ _data.reset();
+}
+
+void Tensor::readData(void *data_ptr, size_t data_size) const
+{
+ const size_t element_size = getDataTypeSize(element_type());
+ const int32_t num_elements = shape().num_elements();
+ if (data_size != num_elements * element_size)
+ {
+ throw std::invalid_argument("Invalid data size.");
+ }
+ assert(data_ptr != nullptr);
+ std::memcpy(data_ptr, data<void>(), data_size);
+}
+
+void Tensor::writeData(const void *data_ptr, size_t data_size)
+{
+ const size_t element_size = getDataTypeSize(element_type());
+ const int32_t num_elements = shape().num_elements();
+ if (data_size != num_elements * element_size)
+ {
+ throw std::invalid_argument("Invalid data size.");
+ }
+ assert(data_ptr != nullptr);
+ std::memcpy(data<void>(), data_ptr, data_size);
+}
+
+void Tensor::resize(const Shape &new_shape)
+{
+ deallocate();
+ _shape = new_shape;
+}
+
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Add.cpp b/compiler/luci-interpreter/src/kernels/Add.cpp
new file mode 100644
index 000000000..8d119d516
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Add.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Add.h"
+
+#include "kernels/BinaryOpCommon.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/add.h>
+#include <tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Add::Add(const Tensor *input1, const Tensor *input2, Tensor *output, const AddParams &params)
+ : KernelWithParams<AddParams>({input1, input2}, {output}, params)
+{
+}
+
+void Add::configure()
+{
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type());
+ if (input1()->element_type() == DataType::S16)
+ {
+ LUCI_INTERPRETER_CHECK(input1()->zero_point() == 0 && input2()->zero_point() == 0 &&
+ output()->zero_point() == 0);
+ }
+
+ output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape()));
+}
+
+void Add::execute() const
+{
+ switch (input1()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ case DataType::S16:
+ evalQuantizedS16();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Add::evalFloat() const
+{
+ float activation_min{};
+ float activation_max{};
+ calculateActivationRange(_params.activation, &activation_min, &activation_max);
+
+ tflite::ArithmeticParams params{};
+ params.float_activation_min = activation_min;
+ params.float_activation_max = activation_max;
+
+ const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes(
+ getTensorShape(input1()), getTensorShape(input2()), &params);
+
+ if (need_broadcast)
+ {
+ tflite::reference_ops::BroadcastAdd4DSlow(
+ params, getTensorShape(input1()), getTensorData<float>(input1()), getTensorShape(input2()),
+ getTensorData<float>(input2()), getTensorShape(output()), getTensorData<float>(output()));
+ }
+ else
+ {
+ tflite::reference_ops::Add(params, getTensorShape(input1()), getTensorData<float>(input1()),
+ getTensorShape(input2()), getTensorData<float>(input2()),
+ getTensorShape(output()), getTensorData<float>(output()));
+ }
+}
+
+void Add::evalQuantized() const
+{
+ const auto input1_scale = static_cast<double>(input1()->scale());
+ const auto input2_scale = static_cast<double>(input2()->scale());
+ const auto output_scale = static_cast<double>(output()->scale());
+
+ const int left_shift = 20;
+ const double twice_max_input_scale = 2 * std::max(input1_scale, input2_scale);
+ const double real_input1_multiplier = input1_scale / twice_max_input_scale;
+ const double real_input2_multiplier = input2_scale / twice_max_input_scale;
+ const double real_output_multiplier = twice_max_input_scale / ((1 << left_shift) * output_scale);
+
+ int32_t input1_multiplier{}, input2_multiplier{}, output_multiplier{};
+ int input1_shift{}, input2_shift{}, output_shift{};
+ quantizeMultiplierSmallerThanOneExp(real_input1_multiplier, &input1_multiplier, &input1_shift);
+ quantizeMultiplierSmallerThanOneExp(real_input2_multiplier, &input2_multiplier, &input2_shift);
+ quantizeMultiplierSmallerThanOneExp(real_output_multiplier, &output_multiplier, &output_shift);
+
+ int32_t activation_min{};
+ int32_t activation_max{};
+ calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max);
+
+ tflite::ArithmeticParams params{};
+ params.left_shift = left_shift;
+ // The kernel expects inputs' zero points to be negated.
+ params.input1_offset = -input1()->zero_point(); // Note the '-'.
+ params.input1_multiplier = input1_multiplier;
+ params.input1_shift = input1_shift;
+ params.input2_offset = -input2()->zero_point(); // Note the '-'.
+ params.input2_multiplier = input2_multiplier;
+ params.input2_shift = input2_shift;
+ params.output_offset = output()->zero_point();
+ params.output_multiplier = output_multiplier;
+ params.output_shift = output_shift;
+ params.quantized_activation_min = activation_min;
+ params.quantized_activation_max = activation_max;
+
+ const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes(
+ getTensorShape(input1()), getTensorShape(input2()), &params);
+
+ if (need_broadcast)
+ {
+ tflite::reference_ops::BroadcastAdd4DSlow(
+ params, getTensorShape(input1()), getTensorData<uint8_t>(input1()),
+ getTensorShape(input2()), getTensorData<uint8_t>(input2()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ }
+ else
+ {
+ tflite::reference_ops::Add(params, getTensorShape(input1()), getTensorData<uint8_t>(input1()),
+ getTensorShape(input2()), getTensorData<uint8_t>(input2()),
+ getTensorShape(output()), getTensorData<uint8_t>(output()));
+ }
+}
+
+void Add::evalQuantizedS16() const
+{
+ const auto input1_scale = static_cast<double>(input1()->scale());
+ const auto input2_scale = static_cast<double>(input2()->scale());
+ const auto output_scale = static_cast<double>(output()->scale());
+
+ constexpr int left_shift = 12;
+ const double twice_max_input_scale = 2 * std::max(input1_scale, input2_scale);
+ const double real_input1_multiplier = input1_scale / twice_max_input_scale;
+ const double real_input2_multiplier = input2_scale / twice_max_input_scale;
+ const double real_output_multiplier = twice_max_input_scale / ((1 << left_shift) * output_scale);
+
+ int32_t input1_multiplier{}, input2_multiplier{}, output_multiplier{};
+ int input1_shift{}, input2_shift{}, output_shift{};
+ quantizeMultiplierSmallerThanOneExp(real_input1_multiplier, &input1_multiplier, &input1_shift);
+ quantizeMultiplierSmallerThanOneExp(real_input2_multiplier, &input2_multiplier, &input2_shift);
+ quantizeMultiplierSmallerThanOneExp(real_output_multiplier, &output_multiplier, &output_shift);
+
+ int32_t activation_min{};
+ int32_t activation_max{};
+ calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max);
+
+ auto fn = [input1_multiplier, input1_shift, //
+ input2_multiplier, input2_shift, //
+ output_multiplier, output_shift, //
+ activation_min, activation_max](int16_t input1_val, int16_t input2_val) {
+ const int32_t shifted_input1_val = static_cast<int32_t>(input1_val) << left_shift;
+ const int32_t shifted_input2_val = static_cast<int32_t>(input2_val) << left_shift;
+ const int32_t scaled_input1_val = tflite::MultiplyByQuantizedMultiplierSmallerThanOneExp(
+ shifted_input1_val, input1_multiplier, input1_shift);
+ const int32_t scaled_input2_val = tflite::MultiplyByQuantizedMultiplierSmallerThanOneExp(
+ shifted_input2_val, input2_multiplier, input2_shift);
+ const int32_t raw_sum = scaled_input1_val + scaled_input2_val;
+ const int32_t raw_output = tflite::MultiplyByQuantizedMultiplierSmallerThanOneExp(
+ raw_sum, output_multiplier, output_shift);
+ const int32_t clamped_output = std::min(activation_max, std::max(activation_min, raw_output));
+ return static_cast<int16_t>(clamped_output);
+ };
+
+ BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData<int16_t>(input1()),
+ getTensorShape(input2()), getTensorData<int16_t>(input2()),
+ getTensorShape(output()), getTensorData<int16_t>(output()), fn);
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Add.h b/compiler/luci-interpreter/src/kernels/Add.h
new file mode 100644
index 000000000..79518845d
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Add.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_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<AddParams>
+{
+public:
+ Add(const Tensor *input1, const Tensor *input2, Tensor *output, const AddParams &params);
+
+ const Tensor *input1() const { return _inputs[0]; }
+ const Tensor *input2() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+ void evalQuantizedS16() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_ADD_H
diff --git a/compiler/luci-interpreter/src/kernels/Add.test.cpp b/compiler/luci-interpreter/src/kernels/Add.test.cpp
new file mode 100644
index 000000000..de8a3bbb0
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Add.test.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Add.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+// for quantized Add, the error shouldn't exceed step
+float GetTolerance(float min, float max)
+{
+ float kQuantizedStep = (max - min) / 255.0;
+ return kQuantizedStep;
+}
+
+TEST(AddTest, Uint8)
+{
+ std::initializer_list<int32_t> base_shape = {2, 3, 1, 2};
+ std::initializer_list<float> base_data = {-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f,
+ 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f};
+ std::initializer_list<int32_t> test_shapes[] = {
+ {1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}};
+ std::initializer_list<float> test_data = {0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f};
+ std::initializer_list<int32_t> output_shapes[] = {
+ {2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}};
+ std::vector<std::vector<float>> 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<float, int32_t> quant_param = quantizationParams<uint8_t>(-3.f, 3.f);
+ for (int i = 0; i < output_data.size(); i++)
+ {
+ Tensor input1_tensor =
+ makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, base_data);
+ Tensor input2_tensor = makeInputTensor<DataType::U8>(test_shapes[i], quant_param.first,
+ quant_param.second, test_data);
+ Tensor output_tensor =
+ makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second);
+
+ AddParams params{};
+ params.activation = Activation::NONE;
+
+ Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data[i], kQuantizedTolerance));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i]));
+ }
+ // Re-run with exchanged inputs.
+ for (int i = 0; i < output_data.size(); i++)
+ {
+ Tensor input1_tensor = makeInputTensor<DataType::U8>(test_shapes[i], quant_param.first,
+ quant_param.second, test_data);
+ Tensor input2_tensor =
+ makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, base_data);
+ Tensor output_tensor =
+ makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second);
+
+ AddParams params{};
+ params.activation = Activation::NONE;
+
+ Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data[i], kQuantizedTolerance));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i]));
+ }
+}
+
+TEST(AddTest, Float)
+{
+ Shape base_shape = {2, 3, 1, 2};
+ std::vector<Shape> test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}};
+ std::vector<std::vector<float>> 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<float> input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f,
+ 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f};
+ std::vector<float> input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f};
+ for (size_t i = 0; i < test_shapes.size(); ++i)
+ {
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>(base_shape, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>(test_shapes[i], input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ AddParams params{};
+ params.activation = Activation::RELU;
+
+ Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f))
+ << "With shape number " << i;
+ }
+ // Re-run with exchanged inputs.
+ for (size_t i = 0; i < test_shapes.size(); ++i)
+ {
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>(test_shapes[i], input2_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>(base_shape, input1_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ AddParams params{};
+ params.activation = Activation::RELU;
+
+ Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f))
+ << "With shape number " << i;
+ }
+}
+
+TEST(AddTest, SInt16)
+{
+ Shape base_shape = {2, 3, 1, 2};
+ std::vector<Shape> test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}};
+ std::vector<std::vector<int32_t>> ref_output_shapes{
+ {2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}};
+
+ std::vector<float> input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f,
+ 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f};
+ std::vector<float> input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f};
+ std::vector<std::vector<float>> ref_outputs = {
+ {0.0f, 2.6f, 0.0f, 2.8f, 0.7f, 3.2f, 1.1f, 0.8f, 0.5f, 1.0f, 1.9f, 1.4f,
+ 1.0f, 0.0f, 0.4f, 0.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.8f, 3.3f, 2.2f, 3.7f,
+ 0.0f, 0.3f, 0.0f, 0.5f, 0.0f, 0.9f, 0.9f, 0.0f, 0.3f, 0.0f, 1.7f, 0.0f},
+ {0.0f, 2.6f, 0.5f, 1.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.0f, 0.5f, 1.7f, 0.0f},
+ {0.0f, 2.5f, 0.0f, 2.6f, 0.0f, 1.9f, 1.1f, 0.7f, 1.2f, 0.8f, 0.5f, 0.1f,
+ 1.0f, 0.0f, 1.1f, 0.0f, 0.4f, 0.0f, 1.7f, 3.3f, 2.2f, 3.8f, 2.1f, 3.7f,
+ 0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.9f, 1.2f, 0.0f, 1.7f, 0.0f, 1.6f, 0.0f},
+ {0.0f, 2.5f, 1.2f, 0.8f, 0.4f, 0.0f, 1.7f, 3.3f, 0.0f, 1.0f, 1.6f, 0.0f}};
+
+ for (size_t i = 0; i < test_shapes.size(); ++i)
+ {
+ Tensor input1_tensor = makeInputTensor<DataType::S16>(base_shape, 3.0 / 32767, 0, input1_data);
+ Tensor input2_tensor =
+ makeInputTensor<DataType::S16>(test_shapes[i], 1.0 / 32767, 0, input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 4.0 / 32767, 0);
+ const float tolerance = output_tensor.scale();
+
+ AddParams params{};
+ params.activation = Activation::RELU;
+
+ Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor),
+ ::testing::ElementsAreArray(ref_output_shapes[i]))
+ << "With shape number " << i;
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance))
+ << "With shape number " << i;
+ }
+ // Re-run with exchanged inputs and different scales.
+ for (size_t i = 0; i < test_shapes.size(); ++i)
+ {
+ Tensor input1_tensor =
+ makeInputTensor<DataType::S16>(test_shapes[i], 2.0 / 32767, 0, input2_data);
+ Tensor input2_tensor = makeInputTensor<DataType::S16>(base_shape, 4.0 / 32767, 0, input1_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 5.0 / 32767, 0);
+ const float tolerance = output_tensor.scale();
+
+ AddParams params{};
+ params.activation = Activation::RELU;
+
+ Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor),
+ ::testing::ElementsAreArray(ref_output_shapes[i]))
+ << "With shape number " << i;
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance))
+ << "With shape number " << i;
+ }
+}
+
+TEST(AddTest, Input_Output_Type_NEG)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor input2_tensor = makeInputTensor<DataType::S32>({1}, {2});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ AddParams params{};
+ params.activation = Activation::RELU;
+
+ Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(AddTest, Invalid_Input_Type_NEG)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::S64>({1}, {1});
+ Tensor input2_tensor = makeInputTensor<DataType::S64>({1}, {2});
+ Tensor output_tensor = makeOutputTensor(DataType::S64);
+
+ AddParams params{};
+ params.activation = Activation::RELU;
+
+ Add kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ EXPECT_ANY_THROW(kernel.execute());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/ArgMax.cpp b/compiler/luci-interpreter/src/kernels/ArgMax.cpp
new file mode 100644
index 000000000..5c464ed09
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/ArgMax.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/ArgMax.h"
+#include "kernels/Utils.h"
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+ArgMax::ArgMax(const Tensor *input, const Tensor *axis, Tensor *output, const ArgMaxParams &params)
+ : KernelWithParams<ArgMaxParams>({input, axis}, {output}, params)
+{
+}
+
+void ArgMax::configure()
+{
+ assert(axis()->element_type() == DataType::S32 || axis()->element_type() == DataType::S64);
+ assert(input()->shape().num_dims() >= 1);
+ const Shape &input_shape = input()->shape();
+ const int num_dims = input_shape.num_dims();
+ Shape output_shape(num_dims - 1);
+
+ // If axis value is negative, then update by adding input_shape's num_dims.
+ // If updated value also negative, then assert.
+ assert(axis()->shape().num_elements() == 1);
+ int axis_value = getTensorData<int32_t>(axis())[0];
+ if (axis_value < 0)
+ axis_value = axis_value + num_dims;
+ assert(axis_value >= 0);
+
+ int j = 0;
+ for (int i = 0; i < num_dims; i++)
+ {
+ if (i == axis_value)
+ continue;
+ output_shape.dim(j++) = input_shape.dim(i);
+ }
+
+ assert(output()->element_type() == _params.output_type);
+
+ output()->resize(output_shape);
+}
+
+void ArgMax::execute() const
+{
+
+#define TF_LITE_ARG_MAX(data_type, axis_type, output_type) \
+ tflite::optimized_ops::ArgMinMax(getTensorShape(input()), getTensorData<data_type>(input()), \
+ getTensorData<axis_type>(axis()), getTensorShape(output()), \
+ getTensorData<output_type>(output()), \
+ std::greater<data_type>())
+ if (axis()->element_type() == DataType::S32)
+ {
+ switch (_params.output_type)
+ {
+ case DataType::S32:
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ TF_LITE_ARG_MAX(float, int32_t, int32_t);
+ break;
+ case DataType::U8:
+ TF_LITE_ARG_MAX(uint8_t, int32_t, int32_t);
+ break;
+ default:
+ throw std::runtime_error("Unsupported input type.");
+ }
+ break;
+ case DataType::S64:
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ TF_LITE_ARG_MAX(float, int32_t, int64_t);
+ break;
+ case DataType::U8:
+ TF_LITE_ARG_MAX(uint8_t, int32_t, int64_t);
+ break;
+ default:
+ throw std::runtime_error("Unsupported input type.");
+ }
+ break;
+ default:
+ throw std::runtime_error("Unsupported output type.");
+ }
+ }
+ else
+ {
+ switch (_params.output_type)
+ {
+ case DataType::S32:
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ TF_LITE_ARG_MAX(float, int64_t, int32_t);
+ break;
+ case DataType::U8:
+ TF_LITE_ARG_MAX(uint8_t, int64_t, int32_t);
+ break;
+ default:
+ throw std::runtime_error("Unsupported input type.");
+ }
+ break;
+ case DataType::S64:
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ TF_LITE_ARG_MAX(float, int64_t, int64_t);
+ break;
+ case DataType::U8:
+ TF_LITE_ARG_MAX(uint8_t, int64_t, int64_t);
+ break;
+ default:
+ throw std::runtime_error("Unsupported input type.");
+ }
+ break;
+ default:
+ throw std::runtime_error("Unsupported output type.");
+ }
+ }
+#undef TF_LITE_ARG_MAX
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/ArgMax.h b/compiler/luci-interpreter/src/kernels/ArgMax.h
new file mode 100644
index 000000000..c851b5891
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/ArgMax.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_ARGMAX_H
+#define LUCI_INTERPRETER_KERNELS_ARGMAX_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class ArgMax : public KernelWithParams<ArgMaxParams>
+{
+public:
+ ArgMax(const Tensor *input, const Tensor *axis, Tensor *output, const ArgMaxParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ const Tensor *axis() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_ARGMAX_H
diff --git a/compiler/luci-interpreter/src/kernels/ArgMax.test.cpp b/compiler/luci-interpreter/src/kernels/ArgMax.test.cpp
new file mode 100644
index 000000000..c6734a114
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/ArgMax.test.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/ArgMax.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T1, typename T2>
+void Check(std::initializer_list<int32_t> input_shape,
+ std::initializer_list<int32_t> dimension_shape,
+ std::initializer_list<int32_t> output_shape, std::initializer_list<T1> input_data,
+ std::initializer_list<int32_t> dimension_data, std::initializer_list<T2> output_data)
+{
+ constexpr DataType element_type = getElementType<T1>();
+ Tensor input_tensor = makeInputTensor<element_type>(input_shape, input_data);
+ Tensor dimension_tensor = makeInputTensor<DataType::S32>(dimension_shape, dimension_data);
+ Tensor output_tensor = makeOutputTensor(getElementType<T2>());
+
+ ArgMaxParams params{};
+ params.output_type = getElementType<T2>();
+ ArgMax kernel(&input_tensor, &dimension_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<T2>(output_tensor), ::testing::ElementsAreArray(output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), output_shape);
+}
+
+template <typename T> class ArgMaxTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(ArgMaxTest, DataTypes);
+
+TYPED_TEST(ArgMaxTest, Simple)
+{
+ Check<TypeParam, int32_t>(/*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<TypeParam, int64_t>(/*input_shape=*/{1, 1, 1, 4}, /*dimension_shape=*/{},
+ /*output_shape=*/{1, 1, 1},
+ /*input_data=*/
+ {
+ 1, 9, 7, 3,
+ },
+ /*dimension_data=*/{3}, /*output_data=*/{1});
+}
+
+TYPED_TEST(ArgMaxTest, MultiDimensions)
+{
+ Check<TypeParam, int32_t>(/*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<TypeParam, int64_t>(/*input_shape=*/{1, 1, 2, 4}, /*dimension_shape=*/{},
+ /*output_shape=*/{1, 1, 2},
+ /*input_data=*/
+ {
+ 1, 2, 7, 8, 1, 9, 7, 3,
+ },
+ /*dimension_data=*/{3}, /*output_data=*/{3, 1});
+}
+
+TEST(ArgMaxTest, UnsupportedType_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1, 1, 2, 4}, {
+ 1, 2, 7, 8, 1, 9, 7, 3,
+ });
+ Tensor dimension_tensor = makeInputTensor<DataType::S32>({}, {3});
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ ArgMaxParams params{};
+ params.output_type = DataType::U8;
+ ArgMax kernel(&input_tensor, &dimension_tensor, &output_tensor, params);
+ kernel.configure();
+ EXPECT_ANY_THROW(kernel.execute());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/AveragePool2D.cpp b/compiler/luci-interpreter/src/kernels/AveragePool2D.cpp
new file mode 100644
index 000000000..df54f9786
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/AveragePool2D.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/AveragePool2D.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/integer_ops/pooling.h>
+#include <tensorflow/lite/kernels/internal/reference/pooling.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+AveragePool2D::AveragePool2D(const Tensor *input, Tensor *output, const Pool2DParams &params)
+ : KernelWithParams<Pool2DParams>({input}, {output}, params)
+{
+}
+
+void AveragePool2D::configure()
+{
+ if (input()->element_type() != output()->element_type())
+ {
+ throw std::runtime_error("Input Tensor and Output Tensor Type must be same");
+ }
+ if (input()->shape().num_dims() != 4)
+ {
+ throw std::runtime_error("Input Tensor Shape must be 4-D");
+ }
+ const Shape &input_shape = input()->shape();
+
+ const int32_t batches = input_shape.dim(0);
+ const int32_t input_height = input_shape.dim(1);
+ const int32_t input_width = input_shape.dim(2);
+ const int32_t depth = input_shape.dim(3);
+
+ const int32_t output_height = computeOutputSize(_params.padding, input_height,
+ _params.filter_height, _params.stride_height);
+ const int32_t output_width =
+ computeOutputSize(_params.padding, input_width, _params.filter_width, _params.stride_width);
+
+ _padding_height =
+ computePadding(_params.stride_height, 1, input_height, _params.filter_height, output_height);
+ _padding_width =
+ computePadding(_params.stride_width, 1, input_width, _params.filter_width, output_width);
+ if (input()->element_type() == DataType::U8)
+ {
+ LUCI_INTERPRETER_CHECK(std::abs(output()->scale() - input()->scale()) <= 1.0e-6);
+ LUCI_INTERPRETER_CHECK(output()->zero_point() == input()->zero_point());
+ }
+ else if (input()->element_type() == DataType::S16)
+ {
+ LUCI_INTERPRETER_CHECK(std::abs(output()->scale() - input()->scale()) <= 1.0e-6);
+ LUCI_INTERPRETER_CHECK(input()->zero_point() == 0 && output()->zero_point() == 0);
+ }
+ output()->resize({batches, output_height, output_width, depth});
+}
+
+void AveragePool2D::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ case DataType::S16:
+ evalSInt16();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void AveragePool2D::evalFloat() const
+{
+ float activation_min{};
+ float activation_max{};
+ calculateActivationRange(_params.activation, &activation_min, &activation_max);
+
+ tflite::PoolParams params{};
+ params.padding_values.height = _padding_height;
+ params.padding_values.width = _padding_width;
+ params.stride_height = _params.stride_height;
+ params.stride_width = _params.stride_width;
+ params.filter_height = _params.filter_height;
+ params.filter_width = _params.filter_width;
+ params.float_activation_min = activation_min;
+ params.float_activation_max = activation_max;
+
+ tflite::reference_ops::AveragePool(params, getTensorShape(input()), getTensorData<float>(input()),
+ getTensorShape(output()), getTensorData<float>(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<uint8_t>(input()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+}
+
+void AveragePool2D::evalSInt16() const
+{
+ int32_t activation_min{};
+ int32_t activation_max{};
+ calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max);
+
+ tflite::PoolParams params{};
+ params.padding_values.height = _padding_height;
+ params.padding_values.width = _padding_width;
+ params.stride_height = _params.stride_height;
+ params.stride_width = _params.stride_width;
+ params.filter_height = _params.filter_height;
+ params.filter_width = _params.filter_width;
+ params.quantized_activation_min = activation_min;
+ params.quantized_activation_max = activation_max;
+
+ tflite::reference_integer_ops::AveragePool(
+ params, getTensorShape(input()), getTensorData<int16_t>(input()), //
+ getTensorShape(output()), getTensorData<int16_t>(output()));
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/AveragePool2D.h b/compiler/luci-interpreter/src/kernels/AveragePool2D.h
new file mode 100644
index 000000000..282a58797
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/AveragePool2D.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_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<Pool2DParams>
+{
+public:
+ AveragePool2D(const Tensor *input, Tensor *output, const Pool2DParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+ void evalSInt16() const;
+
+private:
+ int32_t _padding_height{};
+ int32_t _padding_width{};
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_AVERAGEPOOL2D_H
diff --git a/compiler/luci-interpreter/src/kernels/AveragePool2D.test.cpp b/compiler/luci-interpreter/src/kernels/AveragePool2D.test.cpp
new file mode 100644
index 000000000..83e48c89d
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/AveragePool2D.test.cpp
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/AveragePool2D.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(AveragePool2DTest, Float)
+{
+ Shape input_shape{1, 3, 5, 1};
+ std::vector<float> input_data{
+ -4, -3, -2, -1, 0, //
+ 1, 2, 3, 4, 5, //
+ 6, 7, 8, 9, 10, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ 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;
+
+ AveragePool2D kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{
+ 0, 1.5, //
+ 4.5, 6, //
+ };
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 2, 1}));
+}
+
+TEST(AveragePool2DTest, Uint8_0)
+{
+ std::vector<float> input_data{
+ 0, -6, 12, 4, //
+ -3, -2, 10, 7, //
+ };
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-15.9375f, 15.9375f);
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first,
+ quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
+
+ 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, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear({0.0, 6.0}));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 1, 2, 1}));
+}
+
+TEST(AveragePool2DTest, Uint8_1)
+{
+ std::vector<float> input_data{
+ 0, 6, 12, 4, //
+ 3, 2, 10, 7, //
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-15.9375f, 15.9375f);
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first,
+ quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
+
+ 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, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear({2.75, 6.0}));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 1, 2, 1}));
+}
+
+TEST(AveragePool2DTest, SInt16)
+{
+ Shape input_shape{1, 3, 5, 1};
+ std::vector<int32_t> ref_output_shape{1, 2, 2, 1};
+ std::vector<float> input_data{
+ -4, -3, -2, -1, 0, //
+ 1, 2, 3, 4, 5, //
+ 6, 7, 8, 9, 10, //
+ };
+ std::vector<float> ref_output_data{
+ 0, 1.5, //
+ 4.5, 6, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::S16>(input_shape, 0.5, 0, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 0.5, 0);
+
+ Pool2DParams params{};
+ params.padding = Padding::VALID;
+ params.filter_height = 2;
+ params.filter_width = 3;
+ params.stride_height = 1;
+ params.stride_width = 2;
+ params.activation = Activation::RELU6;
+
+ AveragePool2D kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(AveragePool2DTest, Invalid_Input_Shape_NEG)
+{
+ Shape input_shape{1, 3, 5};
+ std::vector<float> input_data{
+ -4, -3, -2, -1, 0, //
+ 1, 2, 3, 4, 5, //
+ 6, 7, 8, 9, 10, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ 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;
+
+ AveragePool2D kernel(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(AveragePool2DTest, In_Out_Type_NEG)
+{
+ Shape input_shape{1, 3, 5, 1};
+ std::vector<float> input_data{
+ -4, -3, -2, -1, 0, //
+ 1, 2, 3, 4, 5, //
+ 6, 7, 8, 9, 10, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ Pool2DParams params{};
+ params.padding = Padding::VALID;
+ params.filter_height = 2;
+ params.filter_width = 3;
+ params.stride_height = 1;
+ params.stride_width = 2;
+ params.activation = Activation::RELU6;
+
+ AveragePool2D kernel(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(AveragePool2DTest, Quant_Param_NEG)
+{
+ std::vector<float> input_data{
+ 0, -6, 12, 4, //
+ -3, -2, 10, 7, //
+ };
+
+ std::pair<float, int32_t> quant_param1 = quantizationParams<uint8_t>(-15.9375f, 15.9375f);
+ std::pair<float, int32_t> quant_param2 = quantizationParams<uint8_t>(-7.875f, 7.875f);
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param1.first,
+ quant_param1.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param2.first, quant_param2.second);
+
+ 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, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/BinaryOpCommon.h b/compiler/luci-interpreter/src/kernels/BinaryOpCommon.h
new file mode 100644
index 000000000..62bd4158e
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/BinaryOpCommon.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_BINARYOPUTILS_H
+#define LUCI_INTERPRETER_KERNELS_BINARYOPUTILS_H
+
+#include "tensorflow/lite/kernels/internal/common.h"
+#include "tensorflow/lite/kernels/internal/types.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+// Derived from tensorflow/lite/kernels/internal/reference/maximum_minimum.h (v2.3.0).
+template <typename T, typename Op, int N = 5>
+void BinaryOpBroadcastSlow(const tflite::RuntimeShape &unextended_input1_shape,
+ const T *input1_data,
+ const tflite::RuntimeShape &unextended_input2_shape,
+ const T *input2_data,
+ const tflite::RuntimeShape &unextended_output_shape, T *output_data,
+ Op op)
+{
+ if (unextended_input1_shape == unextended_input2_shape)
+ {
+ const int flat_size = tflite::MatchingElementsSize(
+ unextended_input1_shape, unextended_input2_shape, unextended_output_shape);
+ for (int i = 0; i < flat_size; ++i)
+ {
+ output_data[i] = op(input1_data[i], input2_data[i]);
+ }
+ }
+ else
+ {
+ assert(unextended_input1_shape.DimensionsCount() <= N);
+ assert(unextended_input2_shape.DimensionsCount() <= N);
+ assert(unextended_output_shape.DimensionsCount() <= N);
+
+ tflite::NdArrayDesc<N> desc1{};
+ tflite::NdArrayDesc<N> desc2{};
+ tflite::NdArrayDesc<N> output_desc{};
+ tflite::NdArrayDescsForElementwiseBroadcast(unextended_input1_shape, unextended_input2_shape,
+ &desc1, &desc2);
+ tflite::CopyDimsToDesc(tflite::RuntimeShape::ExtendedShape(N, unextended_output_shape),
+ &output_desc);
+
+ auto fn = [&](int indexes[N]) {
+ output_data[SubscriptToIndex(output_desc, indexes)] =
+ op(input1_data[SubscriptToIndex(desc1, indexes)],
+ input2_data[SubscriptToIndex(desc2, indexes)]);
+ };
+ tflite::NDOpsHelper<N>(output_desc, fn);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_BINARYOPUTILS_H
diff --git a/compiler/luci-interpreter/src/kernels/CMakeLists.txt b/compiler/luci-interpreter/src/kernels/CMakeLists.txt
new file mode 100644
index 000000000..a07589dca
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/CMakeLists.txt
@@ -0,0 +1,201 @@
+find_package(Threads REQUIRED)
+nnas_find_package(GTest REQUIRED)
+
+set(SOURCES
+ Add.h
+ Add.cpp
+ ArgMax.h
+ ArgMax.cpp
+ AveragePool2D.h
+ AveragePool2D.cpp
+ Concatenation.h
+ Concatenation.cpp
+ Conv2D.h
+ Conv2D.cpp
+ DepthToSpace.h
+ DepthToSpace.cpp
+ DepthwiseConv2D.h
+ DepthwiseConv2D.cpp
+ Div.h
+ Div.cpp
+ Elu.h
+ Elu.cpp
+ Exp.h
+ Exp.cpp
+ Floor.h
+ Floor.cpp
+ FloorDiv.h
+ FloorDiv.cpp
+ Equal.h
+ Equal.cpp
+ FullyConnected.h
+ FullyConnected.cpp
+ Greater.h
+ Greater.cpp
+ GreaterEqual.h
+ GreaterEqual.cpp
+ If.h
+ If.cpp
+ InstanceNorm.h
+ InstanceNorm.cpp
+ L2Normalize.h
+ L2Normalize.cpp
+ L2Pool2D.h
+ L2Pool2D.cpp
+ LeakyRelu.h
+ LeakyRelu.cpp
+ Less.h
+ Less.cpp
+ LessEqual.h
+ LessEqual.cpp
+ LocalResponseNormalization.h
+ LocalResponseNormalization.cpp
+ LogicalAnd.h
+ LogicalAnd.cpp
+ LogicalNot.h
+ LogicalNot.cpp
+ LogicalOr.h
+ LogicalOr.cpp
+ Logistic.h
+ Logistic.cpp
+ LogSoftmax.h
+ LogSoftmax.cpp
+ Maximum.h
+ Maximum.cpp
+ MaxPool2D.h
+ MaxPool2D.cpp
+ Mean.h
+ Mean.cpp
+ Minimum.h
+ Minimum.cpp
+ Mul.h
+ Mul.cpp
+ NotEqual.h
+ NotEqual.cpp
+ Pad.h
+ Pad.cpp
+ Pow.h
+ Pow.cpp
+ Prelu.h
+ Prelu.cpp
+ Relu.h
+ Relu.cpp
+ Relu6.h
+ Relu6.cpp
+ Reshape.h
+ Reshape.cpp
+ ResizeBilinear.h
+ ResizeBilinear.cpp
+ ResizeNearestNeighbor.h
+ ResizeNearestNeighbor.cpp
+ Reverse.h
+ Reverse.cpp
+ Rsqrt.h
+ Rsqrt.cpp
+ Slice.h
+ Slice.cpp
+ Softmax.h
+ Softmax.cpp
+ SpaceToDepth.h
+ SpaceToDepth.cpp
+ Split.h
+ Split.cpp
+ StridedSlice.h
+ StridedSlice.cpp
+ Sqrt.h
+ Sqrt.cpp
+ Squeeze.h
+ Squeeze.cpp
+ Sub.h
+ Sub.cpp
+ Tanh.h
+ Tanh.cpp
+ Transpose.h
+ Transpose.cpp
+ TransposeConv.h
+ TransposeConv.cpp
+ Unpack.h
+ Unpack.cpp)
+
+list(APPEND SOURCES
+ BinaryOpCommon.h
+ Utils.h
+ Utils.cpp
+ ${TensorFlowSource_DIR}/tensorflow/lite/kernels/internal/quantization_util.cc)
+
+add_library(luci_interpreter_kernels STATIC ${SOURCES})
+set_target_properties(luci_interpreter_kernels PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(luci_interpreter_kernels PUBLIC ${LUCI_INTERPRETER_SOURCE_DIR})
+target_include_directories(luci_interpreter_kernels SYSTEM PRIVATE
+ "${TensorFlowRuySource_DIR}"
+ "${TensorFlowGEMMLowpSource_DIR}"
+ "${TensorFlowEigenSource_DIR}"
+ "${TensorFlowSource_DIR}")
+target_link_libraries(luci_interpreter_kernels
+ PUBLIC luci_interpreter_core
+ PRIVATE nncc_common Threads::Threads)
+
+
+set(TEST_SOURCES
+ Add.test.cpp
+ ArgMax.test.cpp
+ AveragePool2D.test.cpp
+ Concatenation.test.cpp
+ Conv2D.test.cpp
+ DepthToSpace.test.cpp
+ DepthwiseConv2D.test.cpp
+ Div.test.cpp
+ Elu.test.cpp
+ Exp.test.cpp
+ Floor.test.cpp
+ FloorDiv.test.cpp
+ Equal.test.cpp
+ FullyConnected.test.cpp
+ Greater.test.cpp
+ GreaterEqual.test.cpp
+ If.test.cpp
+ InstanceNorm.test.cpp
+ L2Normalize.test.cpp
+ L2Pool2D.test.cpp
+ LeakyRelu.test.cpp
+ Less.test.cpp
+ LessEqual.test.cpp
+ LocalResponseNormalization.test.cpp
+ LogicalAnd.test.cpp
+ LogicalNot.test.cpp
+ LogicalOr.test.cpp
+ Logistic.test.cpp
+ LogSoftmax.test.cpp
+ Maximum.test.cpp
+ MaxPool2D.test.cpp
+ Mean.test.cpp
+ Minimum.test.cpp
+ Mul.test.cpp
+ NotEqual.test.cpp
+ Pad.test.cpp
+ Pow.test.cpp
+ Prelu.test.cpp
+ Relu.test.cpp
+ Relu6.test.cpp
+ Reshape.test.cpp
+ ResizeBilinear.test.cpp
+ ResizeNearestNeighbor.test.cpp
+ Reverse.test.cpp
+ Rsqrt.test.cpp
+ Slice.test.cpp
+ Softmax.test.cpp
+ SpaceToDepth.test.cpp
+ Split.test.cpp
+ StridedSlice.test.cpp
+ Sqrt.test.cpp
+ Squeeze.test.cpp
+ Sub.test.cpp
+ Tanh.test.cpp
+ Transpose.test.cpp
+ TransposeConv.test.cpp
+ Unpack.test.cpp)
+
+list(APPEND TEST_SOURCES TestUtils.h TestUtils.cpp)
+
+GTest_AddTest(luci_interpreter_kernels_test ${TEST_SOURCES})
+target_link_libraries(luci_interpreter_kernels_test luci_interpreter_kernels)
diff --git a/compiler/luci-interpreter/src/kernels/Concatenation.cpp b/compiler/luci-interpreter/src/kernels/Concatenation.cpp
new file mode 100644
index 000000000..6f8820446
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Concatenation.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Concatenation.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Concatenation::Concatenation(std::vector<const Tensor *> inputs, Tensor *output,
+ const ConcatenationParams &params)
+ : KernelWithParams<ConcatenationParams>(std::move(inputs), {output}, params)
+{
+}
+
+void Concatenation::configure()
+{
+ const int num_inputs = _inputs.size();
+ LUCI_INTERPRETER_CHECK(num_inputs > 0);
+ const Tensor *t0 = _inputs[0];
+
+ int axis = _params.axis;
+ if (axis < 0)
+ axis += t0->shape().num_dims();
+ LUCI_INTERPRETER_CHECK(axis >= 0 && axis < t0->shape().num_dims());
+
+ int32_t sum_axis = t0->shape().dim(axis);
+ for (int i = 1; i < num_inputs; ++i)
+ {
+ const Tensor *tensor = _inputs[i];
+ LUCI_INTERPRETER_CHECK(tensor->element_type() == t0->element_type());
+ LUCI_INTERPRETER_CHECK(tensor->shape().num_dims() == t0->shape().num_dims());
+ for (int d = 0; d < t0->shape().num_dims(); ++d)
+ {
+ if (d == axis)
+ {
+ sum_axis += tensor->shape().dim(axis);
+ }
+ else
+ {
+ LUCI_INTERPRETER_CHECK(tensor->shape().dim(d) == t0->shape().dim(d));
+ }
+ }
+ }
+
+ Shape output_shape = t0->shape();
+ output_shape.dim(axis) = sum_axis;
+
+ // TODO S8 type needs more checking: quantization parameters of all input tensors and the output
+ // tensor should be the same. Note that there is no such requirement for U8 type.
+ if (t0->element_type() == DataType::S8)
+ throw std::runtime_error("Unsupported type.");
+
+ output()->resize(output_shape);
+}
+
+void Concatenation::execute() const
+{
+ switch (_inputs[0]->element_type())
+ {
+ case DataType::FLOAT32:
+ evalGeneric<float>();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ case DataType::S8:
+ evalGeneric<int8_t>();
+ break;
+ case DataType::S32:
+ evalGeneric<int32_t>();
+ break;
+ case DataType::S64:
+ evalGeneric<int64_t>();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+template <typename T> void Concatenation::evalGeneric() const
+{
+ int axis = _params.axis;
+ if (axis < 0)
+ axis += output()->shape().num_dims();
+
+ VectorOfTensors<T, true> inputs(_inputs);
+ tflite::ConcatenationParams params{};
+ params.axis = axis;
+ params.inputs_count = _inputs.size();
+ tflite::reference_ops::Concatenation(params, inputs.shapes(), inputs.data(),
+ getTensorShape(output()), getTensorData<T>(output()));
+}
+
+void Concatenation::evalQuantized() const
+{
+ int axis = _params.axis;
+ if (axis < 0)
+ axis += output()->shape().num_dims();
+
+ VectorOfQuantizedTensors<true> inputs(_inputs);
+ tflite::ConcatenationParams params{};
+ params.axis = axis;
+ params.input_zeropoint = inputs.zero_point();
+ params.input_scale = inputs.scale();
+ params.inputs_count = _inputs.size();
+ params.output_zeropoint = output()->zero_point();
+ params.output_scale = output()->scale();
+
+ tflite::reference_ops::ConcatenationWithScaling(params, inputs.shapes(), inputs.data(),
+ getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Concatenation.h b/compiler/luci-interpreter/src/kernels/Concatenation.h
new file mode 100644
index 000000000..b48c8ed1e
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Concatenation.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_CONCATENATION_H
+#define LUCI_INTERPRETER_KERNELS_CONCATENATION_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Concatenation : public KernelWithParams<ConcatenationParams>
+{
+public:
+ Concatenation(std::vector<const Tensor *> inputs, Tensor *output,
+ const ConcatenationParams &params);
+
+ const Tensor *input(int index) const { return _inputs[index]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ template <typename T> void evalGeneric() const;
+ void evalQuantized() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_CONCATENATION_H
diff --git a/compiler/luci-interpreter/src/kernels/Concatenation.test.cpp b/compiler/luci-interpreter/src/kernels/Concatenation.test.cpp
new file mode 100644
index 000000000..91707a256
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Concatenation.test.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Concatenation.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(ConcatenationTest, Float)
+{
+ std::vector<float> input1_data{1, 2, 3, 4, 5, 6};
+ std::vector<float> input2_data{7, 8, 9, 10, 11, 12};
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+ ConcatenationParams params{};
+
+ // Try different 'axis' and expect different results.
+ {
+ params.axis = 0;
+
+ Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor),
+ FloatArrayNear({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}));
+ }
+ {
+ params.axis = -2; // Same as '0'.
+
+ Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor),
+ FloatArrayNear({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}));
+ }
+ {
+ params.axis = 1;
+
+ Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor),
+ FloatArrayNear({1, 2, 3, 7, 8, 9, 4, 5, 6, 10, 11, 12}));
+ }
+ {
+ params.axis = -1; // Same as '1'.
+
+ Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor),
+ FloatArrayNear({1, 2, 3, 7, 8, 9, 4, 5, 6, 10, 11, 12}));
+ }
+}
+
+TEST(ConcatenationTest, Input_Number_Check_NEG)
+{
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+ ConcatenationParams params{};
+
+ params.axis = -1;
+
+ Concatenation kernel({}, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ConcatenationTest, Invalid_Axis_NEG)
+{
+ std::vector<float> input1_data{1, 2, 3, 4, 5, 6};
+ std::vector<float> input2_data{7, 8, 9, 10, 11, 12};
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+ ConcatenationParams params{};
+
+ params.axis = -3;
+
+ Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ConcatenationTest, Mismatching_Input_Type_NEG)
+{
+ std::vector<float> input1_data{1, 2, 3, 4, 5, 6};
+ std::vector<uint8_t> input2_data{7, 8, 9, 10, 11, 12};
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::U8>({2, 3}, input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+ ConcatenationParams params{};
+
+ params.axis = -1;
+
+ Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ConcatenationTest, Mismatching_Input_Dimension_Num_NEG)
+{
+ std::vector<float> input1_data{1, 2, 3, 4, 5, 6};
+ std::vector<float> input2_data{7, 8, 9, 10, 11, 12};
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>({1, 2, 3}, input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+ ConcatenationParams params{};
+
+ params.axis = -1;
+
+ Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ConcatenationTest, Mismatching_Input_Dimension_NEG)
+{
+ std::vector<float> input1_data{1, 2, 3, 4, 5, 6};
+ std::vector<float> input2_data{7, 8, 9, 10, 11, 12, 13, 14, 15};
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>({3, 3}, input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+ ConcatenationParams params{};
+
+ params.axis = -1;
+
+ Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ConcatenationTest, Unsupported_Configure_Type_NEG)
+{
+ std::vector<int8_t> input1_data{1, 2, 3, 4, 5, 6};
+ std::vector<int8_t> input2_data{7, 8, 9, 10, 11, 12};
+ Tensor input1_tensor = makeInputTensor<DataType::S8>({2, 3}, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::S8>({2, 3}, input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S8);
+ ConcatenationParams params{};
+
+ params.axis = -1;
+
+ Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Conv2D.cpp b/compiler/luci-interpreter/src/kernels/Conv2D.cpp
new file mode 100644
index 000000000..47e2498f1
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Conv2D.cpp
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Conv2D.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/optimized/legacy_optimized_ops.h>
+
+#include <stdexcept>
+#include <thread>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Conv2D::Conv2D(const Tensor *input, const Tensor *filter, const Tensor *bias, Tensor *output,
+ const Conv2DParams &params)
+ : KernelWithParams<Conv2DParams>({input, filter, bias}, {output}, params)
+{
+}
+
+void Conv2D::configure()
+{
+ // TensorFlow Lite (as of v2.2.0) supports the following combinations of types:
+ // | input filter bias output |
+ // ----+---------------------------+
+ // (1) | float float float float |
+ // (2) | float int8 float float | hybrid
+ // (3) | uint8 uint8 int32 uint8 | quantized
+ // (4) | int8 int8 int32 int8 | quantized per channel
+ //
+ // We only support (1) and (3) for now, and additionally the following:
+ // | input filter bias output |
+ // ----+---------------------------+
+ // (5) | int16 int16 int64 int16 |
+ //
+ if (input()->element_type() == DataType::FLOAT32 && filter()->element_type() == DataType::FLOAT32)
+ {
+ LUCI_INTERPRETER_CHECK(bias() == nullptr || bias()->element_type() == DataType::FLOAT32);
+ }
+ else if (input()->element_type() == DataType::U8 && filter()->element_type() == DataType::U8)
+ {
+ LUCI_INTERPRETER_CHECK(bias() == nullptr || bias()->element_type() == DataType::S32);
+ }
+ else if (input()->element_type() == DataType::S16 && filter()->element_type() == DataType::S16)
+ {
+ LUCI_INTERPRETER_CHECK(bias() == nullptr || bias()->element_type() == DataType::S64);
+ }
+ else
+ {
+ throw std::runtime_error("Unsupported type.");
+ }
+ LUCI_INTERPRETER_CHECK(output()->element_type() == input()->element_type());
+
+ const Shape &input_shape = input()->shape();
+ const Shape &filter_shape = filter()->shape();
+ LUCI_INTERPRETER_CHECK(input_shape.num_dims() == 4 && filter_shape.num_dims() == 4);
+
+ const int32_t batches = input_shape.dim(0);
+ const int32_t input_height = input_shape.dim(1);
+ const int32_t input_width = input_shape.dim(2);
+ const int32_t output_depth = filter_shape.dim(0);
+ const int32_t filter_height = filter_shape.dim(1);
+ const int32_t filter_width = filter_shape.dim(2);
+ LUCI_INTERPRETER_CHECK(filter_shape.dim(3) == input_shape.dim(3));
+
+ LUCI_INTERPRETER_CHECK(bias() == nullptr || (bias()->shape().num_dims() == 1 &&
+ bias()->shape().dim(0) == output_depth));
+
+ const int32_t output_height =
+ computeOutputSize(_params.padding, input_height, filter_height, _params.stride_height,
+ _params.dilation_height_factor);
+ const int32_t output_width =
+ computeOutputSize(_params.padding, input_width, filter_width, _params.stride_width,
+ _params.dilation_width_factor);
+
+ _padding_height = computePadding(_params.stride_height, _params.dilation_height_factor,
+ input_height, filter_height, output_height);
+ _padding_width = computePadding(_params.stride_width, _params.dilation_width_factor, input_width,
+ filter_width, output_width);
+
+ output()->resize({batches, output_height, output_width, output_depth});
+
+ // Allocate tensor for Im2Col, if needed.
+ // The checks here should be aligned with the actual implementation.
+ const bool need_dilated_im2col =
+ _params.dilation_height_factor != 1 || _params.dilation_width_factor != 1;
+ const bool need_non_dilated_im2col = _params.stride_height != 1 || _params.stride_width != 1 ||
+ filter_height != 1 || filter_width != 1;
+ const bool need_im2col =
+ input()->element_type() != DataType::S16 && (need_dilated_im2col || need_non_dilated_im2col);
+ if (need_im2col)
+ {
+ const int input_depth = input_shape.dim(3);
+ Shape im2col_shape{batches, output_height, output_width,
+ input_depth * filter_height * filter_width};
+ try
+ {
+ _im2col =
+ std::make_unique<Tensor>(input()->element_type(), im2col_shape, AffineQuantization{}, "");
+ }
+ catch (std::bad_alloc &ba)
+ {
+ // Failed memory allocation
+ _im2col = nullptr;
+ }
+ }
+}
+
+void Conv2D::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ if (filter()->element_type() == DataType::FLOAT32)
+ {
+ evalFloat();
+ break;
+ }
+ throw std::runtime_error("Unsupported type.");
+ case DataType::U8:
+ evalQuantized();
+ break;
+ case DataType::S16:
+ evalQuantizedS16();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+ if (!!_im2col)
+ _im2col->deallocate();
+}
+
+void Conv2D::evalFloat() const
+{
+ float activation_min{};
+ float activation_max{};
+ calculateActivationRange(_params.activation, &activation_min, &activation_max);
+
+ tflite::ConvParams params{};
+ params.padding_values.height = _padding_height;
+ params.padding_values.width = _padding_width;
+ params.stride_height = _params.stride_height;
+ params.stride_width = _params.stride_width;
+ params.dilation_height_factor = _params.dilation_height_factor;
+ params.dilation_width_factor = _params.dilation_width_factor;
+ params.float_activation_min = activation_min;
+ params.float_activation_max = activation_max;
+
+ if (_im2col)
+ tflite::optimized_ops::Conv(params, getTensorShape(input()), getTensorData<float>(input()),
+ getTensorShape(filter()), getTensorData<float>(filter()),
+ getTensorShape(bias()), getTensorData<float>(bias()),
+ getTensorShape(output()), getTensorData<float>(output()),
+ getTensorShape(_im2col.get()), getTensorData<float>(_im2col.get()));
+ else
+ tflite::reference_ops::Conv(
+ params, getTensorShape(input()), getTensorData<float>(input()), getTensorShape(filter()),
+ getTensorData<float>(filter()), getTensorShape(bias()), getTensorData<float>(bias()),
+ getTensorShape(output()), getTensorData<float>(output()), tflite::RuntimeShape(), nullptr);
+}
+
+void Conv2D::evalQuantized() const
+{
+ const auto input_scale = static_cast<double>(input()->scale());
+ const auto filter_scale = static_cast<double>(filter()->scale());
+ const auto output_scale = static_cast<double>(output()->scale());
+
+ const double real_multiplier = input_scale * filter_scale / output_scale;
+ int32_t output_multiplier{};
+ int output_shift{};
+ quantizeMultiplier(real_multiplier, &output_multiplier, &output_shift);
+
+ int32_t activation_min{};
+ int32_t activation_max{};
+ calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max);
+
+ tflite::ConvParams params{};
+ params.padding_values.height = _padding_height;
+ params.padding_values.width = _padding_width;
+ params.stride_height = _params.stride_height;
+ params.stride_width = _params.stride_width;
+ params.dilation_height_factor = _params.dilation_height_factor;
+ params.dilation_width_factor = _params.dilation_width_factor;
+ // The kernel expects input and filter zero points to be negated.
+ params.input_offset = -input()->zero_point(); // Note the '-'.
+ params.weights_offset = -filter()->zero_point(); // Note the '-'.
+ params.output_offset = output()->zero_point();
+ params.output_multiplier = output_multiplier;
+ params.output_shift = output_shift;
+ params.quantized_activation_min = activation_min;
+ params.quantized_activation_max = activation_max;
+
+ // 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::GemmContext>();
+ gemmlowp_context->set_max_num_threads(static_cast<int>(std::thread::hardware_concurrency()));
+
+ tflite::optimized_ops::Conv(
+ params, getTensorShape(input()), getTensorData<uint8_t>(input()), getTensorShape(filter()),
+ getTensorData<uint8_t>(filter()), getTensorShape(bias()), getTensorData<int32_t>(bias()),
+ getTensorShape(output()), getTensorData<uint8_t>(output()), getTensorShape(_im2col.get()),
+ getTensorData<uint8_t>(_im2col.get()), gemmlowp_context.get());
+}
+
+void Conv2D::evalQuantizedS16() const
+{
+ const auto *input_data = getTensorData<int16_t>(input());
+ const auto *filter_data = getTensorData<int16_t>(filter());
+ const auto *bias_data = getTensorData<int64_t>(bias());
+ auto *output_data = getTensorData<int16_t>(output());
+
+ const Shape &input_shape = input()->shape();
+ const Shape &filter_shape = filter()->shape();
+ const Shape &output_shape = output()->shape();
+
+ const int32_t batches = input_shape.dim(0);
+ const int32_t input_height = input_shape.dim(1);
+ const int32_t input_width = input_shape.dim(2);
+ const int32_t input_depth = input_shape.dim(3);
+ const int32_t output_depth = filter_shape.dim(0);
+ const int32_t filter_height = filter_shape.dim(1);
+ const int32_t filter_width = filter_shape.dim(2);
+ const int32_t output_height = output_shape.dim(1);
+ const int32_t output_width = output_shape.dim(2);
+
+ const int32_t stride_height = _params.stride_height;
+ const int32_t stride_width = _params.stride_width;
+ const int32_t dilation_height_factor = _params.dilation_height_factor;
+ const int32_t dilation_width_factor = _params.dilation_width_factor;
+
+ int32_t activation_min{};
+ int32_t activation_max{};
+ calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max);
+
+ const std::vector<double> effective_output_scale =
+ getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale());
+
+ const std::vector<ChannelQuantMultipliers> multipliers_raw =
+ quantizeMultipliers(effective_output_scale);
+ BroadcastableWrapper<ChannelQuantMultipliers> multipliers(multipliers_raw);
+
+ for (int32_t batch = 0; batch < batches; ++batch)
+ {
+ for (int32_t out_y = 0; out_y < output_height; ++out_y)
+ {
+ for (int32_t out_x = 0; out_x < output_width; ++out_x)
+ {
+ for (int32_t out_c = 0; out_c < output_depth; ++out_c)
+ {
+ const int32_t in_y_origin = out_y * stride_height - _padding_height;
+ const int32_t in_x_origin = out_x * stride_width - _padding_width;
+ int64_t acc = 0;
+ for (int32_t filter_y = 0; filter_y < filter_height; ++filter_y)
+ {
+ for (int32_t filter_x = 0; filter_x < filter_width; ++filter_x)
+ {
+ const int32_t in_y = in_y_origin + dilation_height_factor * filter_y;
+ const int32_t in_x = in_x_origin + dilation_width_factor * filter_x;
+ if ((in_y >= 0 && in_y < input_height) && (in_x >= 0 && in_x < input_width))
+ {
+ for (int32_t in_c = 0; in_c < input_depth; ++in_c)
+ {
+ const int16_t input_val =
+ input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)];
+ const int16_t filter_val =
+ filter_data[calcOffset(filter_shape, out_c, filter_y, filter_x, in_c)];
+ acc += static_cast<int64_t>(input_val) * static_cast<int64_t>(filter_val);
+ }
+ }
+ }
+ }
+ if (bias_data)
+ {
+ acc += bias_data[out_c];
+ }
+
+ int32_t scaled_acc = tflite::MultiplyByQuantizedMultiplier(
+ acc, multipliers[out_c].multiplier, multipliers[out_c].shift);
+
+ scaled_acc = std::max(scaled_acc, activation_min);
+ scaled_acc = std::min(scaled_acc, activation_max);
+
+ output_data[calcOffset(output_shape, batch, out_y, out_x, out_c)] = scaled_acc;
+ }
+ }
+ }
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Conv2D.h b/compiler/luci-interpreter/src/kernels/Conv2D.h
new file mode 100644
index 000000000..83ac67d3d
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Conv2D.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_CONV2D_H
+#define LUCI_INTERPRETER_KERNELS_CONV2D_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+#include <memory>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Conv2D : public KernelWithParams<Conv2DParams>
+{
+public:
+ Conv2D(const Tensor *input, const Tensor *filter, const Tensor *bias, Tensor *output,
+ const Conv2DParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ const Tensor *filter() const { return _inputs[1]; }
+ const Tensor *bias() const { return _inputs[2]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+ void evalQuantizedS16() const;
+
+private:
+ std::unique_ptr<Tensor> _im2col;
+ int32_t _padding_height{};
+ int32_t _padding_width{};
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_CONV2D_H
diff --git a/compiler/luci-interpreter/src/kernels/Conv2D.test.cpp b/compiler/luci-interpreter/src/kernels/Conv2D.test.cpp
new file mode 100644
index 000000000..7aa66a898
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Conv2D.test.cpp
@@ -0,0 +1,416 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Conv2D.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(Conv2DTest, Float)
+{
+ Shape input_shape{1, 4, 3, 2};
+ Shape filter_shape{2, 2, 2, 2};
+ Shape bias_shape{2};
+ std::vector<float> 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<float> 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<float> bias_data{1, 2};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ 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, params);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{
+ 11, 16, 7, 20, // row = 0
+ 0, 40, 0, 44, // row = 1
+ };
+ std::vector<int32_t> ref_output_shape{1, 2, 2, 2};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+}
+
+TEST(Conv2DTest, FloatCheck)
+{
+ Shape input_shape{2, 2, 4, 1};
+ Shape filter_shape{3, 2, 2, 1};
+ Shape bias_shape{3};
+ std::vector<float> input_data{
+ // First batch
+ 1, 1, 1, 1, // row = 1
+ 2, 2, 2, 2, // row = 2
+ // Second batch
+ 1, 2, 3, 4, // row = 1
+ 1, 2, 3, 4, // row = 2
+ };
+ std::vector<float> filter_data{
+ 1, 2, 3, 4, // first 2x2 filter
+ -1, 1, -1, 1, // second 2x2 filter
+ -1, -1, 1, 1, // third 2x2 filter
+ };
+ std::vector<float> bias_data{1, 2, 3};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ 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, params);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> 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<int32_t> ref_output_shape{2, 1, 2, 3};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+}
+
+TEST(Conv2DTest, Uint8)
+{
+ std::vector<float> input_data{
+ // First batch
+ 1, 1, 1, 1, // row = 1
+ 2, 2, 2, 2, // row = 2
+ // Second batch
+ 1, 2, 3, 4, // row = 1
+ 1, 2, 3, 4, // row = 2
+ };
+ std::vector<float> filter_data{
+ 1, 2, 3, 4, // first 2x2 filter
+ -1, 1, -1, 1, // second 2x2 filter
+ -1, -1, 1, 1, // third 2x2 filter
+ };
+ std::vector<float> bias_data{1, 2, 3};
+
+ std::pair<float, int32_t> input_quant_param = quantizationParams<uint8_t>(-63.5, 64);
+ std::pair<float, int32_t> output_quant_param = quantizationParams<uint8_t>(-127, 128);
+
+ Tensor input_tensor = makeInputTensor<DataType::U8>({2, 2, 4, 1}, input_quant_param.first,
+ input_quant_param.second, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::U8>({3, 2, 2, 1}, input_quant_param.first,
+ input_quant_param.second, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::S32>(
+ {3}, input_quant_param.first * input_quant_param.first, 0, bias_data);
+ Tensor output_tensor =
+ makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second);
+
+ 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, params);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> 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<int32_t> 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(Conv2DTest, SInt16)
+{
+ Shape input_shape{1, 4, 3, 2};
+ Shape filter_shape{2, 2, 2, 2};
+ Shape bias_shape{2};
+ std::vector<int32_t> ref_output_shape{1, 2, 2, 2};
+
+ std::vector<float> 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<float> 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<float> bias_data{1, 2};
+ std::vector<float> ref_output_data{
+ 11, 16, 7, 20, // row = 0
+ 0, 40, 0, 44, // row = 1
+ };
+
+ Tensor input_tensor = makeInputTensor<DataType::S16>(input_shape, 0.25, 0, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::S16>(filter_shape, 0.2, 0, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::S64>(bias_shape, 0.25 * 0.2, 0, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 0.5, 0);
+
+ Conv2DParams params{};
+ params.padding = Padding::VALID;
+ params.stride_height = 2;
+ params.stride_width = 1;
+ params.dilation_height_factor = 1;
+ params.dilation_width_factor = 1;
+ params.activation = Activation::RELU;
+
+ Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(Conv2DTest, SInt16_CWQ_weights)
+{
+ Shape input_shape{1, 2, 2, 2}; // Batch x H x W x C
+ Shape filter_shape{3, 1, 1, 2}; // Out channels x H x W x In Channels
+ Shape bias_shape{3};
+ std::vector<int32_t> ref_output_shape{1, 2, 2, 3};
+
+ std::vector<float> 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<float> filter_data{
+ 4, -3, // out = 0
+ 1, -3, // out = 1
+ 5, -3, // out = 2
+ };
+ std::vector<float> bias_data{1, 10, 5};
+ std::vector<float> 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<float> filter_scales = {0.25f, 0.2f, 0.1f};
+ std::vector<float> bias_scales;
+ for (int i = 0; i < filter_scales.size(); ++i)
+ bias_scales.push_back(filter_scales[i] * input_scale);
+ std::vector<int32_t> zerop = {0, 0, 0};
+
+ Tensor input_tensor = makeInputTensor<DataType::S16>(input_shape, input_scale, 0, input_data);
+ Tensor filter_tensor =
+ makeInputTensor<DataType::S16>(filter_shape, filter_scales, zerop, 0, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::S64>(bias_shape, bias_scales, zerop, 0, bias_data);
+ 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, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(Conv2DTest, Unsupported_Type_Configure_NEG)
+{
+ Shape input_shape{1, 4, 3, 2};
+ Shape filter_shape{2, 2, 2, 2};
+ Shape bias_shape{2};
+ std::vector<int32_t> 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<float> 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<float> bias_data{1, 2};
+ Tensor input_tensor = makeInputTensor<DataType::S32>(input_shape, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ 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, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(Conv2DTest, Invalid_Bias_Type_NEG)
+{
+ Shape input_shape{1, 4, 3, 2};
+ Shape filter_shape{2, 2, 2, 2};
+ Shape bias_shape{2};
+ std::vector<float> 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<float> 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<uint8_t> bias_data{1, 2};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::U8>(bias_shape, bias_data);
+ 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, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(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<float> 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<float> 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<float> bias_data{1, 2, 3};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ 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, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(Conv2DTest, Invalid_Input_Shape_NEG)
+{
+ Shape input_shape{1, 4, 6, 1};
+ Shape filter_shape{2, 2, 2, 2};
+ Shape bias_shape{2};
+ std::vector<float> 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<float> 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<float> bias_data{1, 2};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ 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, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/DepthToSpace.cpp b/compiler/luci-interpreter/src/kernels/DepthToSpace.cpp
new file mode 100644
index 000000000..57238313c
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/DepthToSpace.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DepthToSpace.h"
+#include "Utils.h"
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+DepthToSpace::DepthToSpace(const Tensor *input, Tensor *output, const DepthToSpaceParams &params)
+ : KernelWithParams<DepthToSpaceParams>({input}, {output}, params)
+{
+}
+
+void DepthToSpace::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->shape().num_dims() == 4);
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::FLOAT32 ||
+ output()->element_type() == DataType::U8)
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type())
+ const int block_size = params().block_size;
+ const int32_t input_height = input()->shape().dim(1);
+ const int32_t input_width = input()->shape().dim(2);
+ const int32_t input_channels = input()->shape().dim(3);
+ int32_t output_height = input_height * block_size;
+ int32_t output_width = input_width * block_size;
+ int32_t output_channels = input_channels / block_size / block_size;
+
+ LUCI_INTERPRETER_CHECK(input_height == output_height / block_size);
+ LUCI_INTERPRETER_CHECK(input_width == output_width / block_size);
+ LUCI_INTERPRETER_CHECK(input_channels == output_channels * block_size * block_size);
+
+ Shape output_shape(4);
+ output_shape.dim(0) = input()->shape().dim(0);
+ output_shape.dim(1) = output_height;
+ output_shape.dim(2) = output_width;
+ output_shape.dim(3) = output_channels;
+
+ output()->resize(output_shape);
+}
+
+void DepthToSpace::execute() const
+{
+ tflite::DepthToSpaceParams op_params;
+ op_params.block_size = params().block_size;
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ tflite::optimized_ops::DepthToSpace(op_params, getTensorShape(input()),
+ getTensorData<float>(input()), getTensorShape(output()),
+ getTensorData<float>(output()));
+ break;
+ case DataType::U8:
+ tflite::optimized_ops::DepthToSpace(op_params, getTensorShape(input()),
+ getTensorData<uint8_t>(input()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ break;
+ default:
+ throw std::runtime_error("Unsupported Type.");
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/DepthToSpace.h b/compiler/luci-interpreter/src/kernels/DepthToSpace.h
new file mode 100644
index 000000000..63ce37610
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/DepthToSpace.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_DEPTHTOSPACE_H
+#define LUCI_INTERPRETER_KERNELS_DEPTHTOSPACE_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+#include <vector>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class DepthToSpace : public KernelWithParams<DepthToSpaceParams>
+{
+public:
+ DepthToSpace(const Tensor *input, Tensor *output, const DepthToSpaceParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_DEPTHTOSPACE_H
diff --git a/compiler/luci-interpreter/src/kernels/DepthToSpace.test.cpp b/compiler/luci-interpreter/src/kernels/DepthToSpace.test.cpp
new file mode 100644
index 000000000..3dee4ad36
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/DepthToSpace.test.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/DepthToSpace.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T> class DepthToSpaceTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(DepthToSpaceTest, DataTypes);
+
+TYPED_TEST(DepthToSpaceTest, SimpleCase)
+{
+ std::vector<TypeParam> input_data{1, 2, 3, 4, 5, 6, 7, 8};
+ Shape input_shape{1, 1, 2, 4};
+ std::vector<TypeParam> output_data{1, 2, 5, 6, 3, 4, 7, 8};
+ std::vector<int32_t> output_shape{1, 2, 4, 1};
+
+ Tensor input_tensor = makeInputTensor<getElementType<TypeParam>()>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(getElementType<TypeParam>());
+
+ DepthToSpaceParams params{};
+ params.block_size = 2;
+
+ DepthToSpace kernel = DepthToSpace(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<TypeParam>(output_tensor),
+ ::testing::ElementsAreArray(output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+TEST(DepthToSpaceTest, InvalidInputShape_NEG)
+{
+ std::vector<float> input_data{1, 2, 3, 4, 5, 6, 7, 8};
+ Shape input_shape{1, 2, 4};
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DepthToSpaceParams params{};
+ params.block_size = 2;
+
+ DepthToSpace kernel = DepthToSpace(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(DepthToSpaceTest, InOutTypeMismatch_NEG)
+{
+ std::vector<float> input_data{1, 2, 3, 4, 5, 6, 7, 8};
+ Shape input_shape{1, 1, 2, 4};
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ DepthToSpaceParams params{};
+ params.block_size = 2;
+
+ DepthToSpace kernel = DepthToSpace(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(DepthToSpaceTest, InvalidBlockSize_NEG)
+{
+ std::vector<float> input_data{1, 2, 3, 4, 5, 6, 7, 8};
+ Shape input_shape{1, 1, 2, 4};
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DepthToSpaceParams params{};
+ params.block_size = 3;
+
+ DepthToSpace kernel = DepthToSpace(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.cpp b/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.cpp
new file mode 100644
index 000000000..1957f3c9d
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.cpp
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/DepthwiseConv2D.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/depthwiseconv_float.h>
+#include <tensorflow/lite/kernels/internal/reference/depthwiseconv_uint8.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+DepthwiseConv2D::DepthwiseConv2D(const Tensor *input, const Tensor *filter, const Tensor *bias,
+ Tensor *output, const DepthwiseConv2DParams &params)
+ : KernelWithParams<DepthwiseConv2DParams>({input, filter, bias}, {output}, params)
+{
+}
+
+void DepthwiseConv2D::configure()
+{
+ // TensorFlow Lite (as of v2.2.0) supports the following combinations of types:
+ // | input filter bias output |
+ // ----+---------------------------+
+ // (1) | float float float float |
+ // (2) | float int8 float float | hybrid
+ // (3) | uint8 uint8 int32 uint8 | quantized
+ // (4) | int8 int8 int32 int8 | quantized per channel
+ // (5) | int16 int8 int64 int16 | quantized per channel 16x8
+ //
+ // We only support (1) and (3) for now, and additionally the following:
+ // | input filter bias output |
+ // ----+---------------------------+
+ // (5) | int16 int16 int64 int16 |
+ //
+ if (input()->element_type() == DataType::FLOAT32 && filter()->element_type() == DataType::FLOAT32)
+ {
+ LUCI_INTERPRETER_CHECK(bias() == nullptr || bias()->element_type() == DataType::FLOAT32);
+ }
+ else if (input()->element_type() == DataType::U8 && filter()->element_type() == DataType::U8)
+ {
+ LUCI_INTERPRETER_CHECK(bias() == nullptr || bias()->element_type() == DataType::S32);
+ }
+ else if (input()->element_type() == DataType::S16 && filter()->element_type() == DataType::S16)
+ {
+ LUCI_INTERPRETER_CHECK(bias() == nullptr || bias()->element_type() == DataType::S64);
+ }
+ else
+ {
+ throw std::runtime_error("Unsupported type.");
+ }
+ LUCI_INTERPRETER_CHECK(output()->element_type() == input()->element_type());
+
+ const Shape &input_shape = input()->shape();
+ const Shape &filter_shape = filter()->shape();
+ LUCI_INTERPRETER_CHECK(input_shape.num_dims() == 4 && filter_shape.num_dims() == 4);
+
+ const int32_t batches = input_shape.dim(0);
+ const int32_t input_height = input_shape.dim(1);
+ const int32_t input_width = input_shape.dim(2);
+ // Filter format: [1, H, W, O].
+ LUCI_INTERPRETER_CHECK(filter_shape.dim(0) == 1);
+ const int32_t filter_height = filter_shape.dim(1);
+ const int32_t filter_width = filter_shape.dim(2);
+ const int32_t channels_out = filter_shape.dim(3);
+
+ LUCI_INTERPRETER_CHECK(bias() == nullptr || (bias()->shape().num_dims() == 1 &&
+ bias()->shape().dim(0) == channels_out));
+
+ const int32_t output_height =
+ computeOutputSize(_params.padding, input_height, filter_height, _params.stride_height,
+ _params.dilation_height_factor);
+ const int32_t output_width =
+ computeOutputSize(_params.padding, input_width, filter_width, _params.stride_width,
+ _params.dilation_width_factor);
+
+ _padding_height = computePadding(_params.stride_height, _params.dilation_height_factor,
+ input_height, filter_height, output_height);
+ _padding_width = computePadding(_params.stride_width, _params.dilation_width_factor, input_width,
+ filter_width, output_width);
+
+ output()->resize({batches, output_height, output_width, channels_out});
+}
+
+void DepthwiseConv2D::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ if (filter()->element_type() == DataType::FLOAT32)
+ {
+ evalFloat();
+ break;
+ }
+ throw std::runtime_error("Unsupported type.");
+ case DataType::U8:
+ evalQuantized();
+ break;
+ case DataType::S16:
+ evalQuantizedS16();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void DepthwiseConv2D::evalFloat() const
+{
+ float activation_min{};
+ float activation_max{};
+ calculateActivationRange(_params.activation, &activation_min, &activation_max);
+
+ tflite::DepthwiseParams params{};
+ params.padding_values.height = _padding_height;
+ params.padding_values.width = _padding_width;
+ params.stride_height = _params.stride_height;
+ params.stride_width = _params.stride_width;
+ params.dilation_height_factor = _params.dilation_height_factor;
+ params.dilation_width_factor = _params.dilation_width_factor;
+ params.depth_multiplier = _params.depth_multiplier;
+ params.float_activation_min = activation_min;
+ params.float_activation_max = activation_max;
+
+ tflite::reference_ops::DepthwiseConv(
+ params, getTensorShape(input()), getTensorData<float>(input()), getTensorShape(filter()),
+ getTensorData<float>(filter()), getTensorShape(bias()), getTensorData<float>(bias()),
+ getTensorShape(output()), getTensorData<float>(output()));
+}
+
+void DepthwiseConv2D::evalQuantized() const
+{
+ const auto input_scale = static_cast<double>(input()->scale());
+ const auto filter_scale = static_cast<double>(filter()->scale());
+ const auto output_scale = static_cast<double>(output()->scale());
+
+ const double real_multiplier = input_scale * filter_scale / output_scale;
+ int32_t output_multiplier{};
+ int output_shift{};
+ quantizeMultiplier(real_multiplier, &output_multiplier, &output_shift);
+
+ int32_t activation_min{};
+ int32_t activation_max{};
+ calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max);
+
+ tflite::DepthwiseParams params{};
+ params.padding_values.height = _padding_height;
+ params.padding_values.width = _padding_width;
+ params.stride_height = _params.stride_height;
+ params.stride_width = _params.stride_width;
+ params.dilation_height_factor = _params.dilation_height_factor;
+ params.dilation_width_factor = _params.dilation_width_factor;
+ params.depth_multiplier = _params.depth_multiplier;
+ // The kernel expects input and filter zero points to be negated.
+ params.input_offset = -input()->zero_point(); // Note the '-'.
+ params.weights_offset = -filter()->zero_point(); // Note the '-'.
+ params.output_offset = output()->zero_point();
+ params.output_multiplier = output_multiplier;
+ params.output_shift = output_shift;
+ params.quantized_activation_min = activation_min;
+ params.quantized_activation_max = activation_max;
+
+ tflite::reference_ops::DepthwiseConv(
+ params, getTensorShape(input()), getTensorData<uint8_t>(input()), getTensorShape(filter()),
+ getTensorData<uint8_t>(filter()), getTensorShape(bias()), getTensorData<int32_t>(bias()),
+ getTensorShape(output()), getTensorData<uint8_t>(output()));
+}
+
+void DepthwiseConv2D::evalQuantizedS16() const
+{
+ const auto *input_data = getTensorData<int16_t>(input());
+ const auto *filter_data = getTensorData<int16_t>(filter());
+ const auto *bias_data = getTensorData<int64_t>(bias());
+ auto *output_data = getTensorData<int16_t>(output());
+
+ const Shape &input_shape = input()->shape();
+ const Shape &filter_shape = filter()->shape();
+ const Shape &output_shape = output()->shape();
+
+ const int32_t batches = input_shape.dim(0);
+ const int32_t input_height = input_shape.dim(1);
+ const int32_t input_width = input_shape.dim(2);
+ const int32_t input_depth = input_shape.dim(3);
+ const int32_t filter_height = filter_shape.dim(1);
+ const int32_t filter_width = filter_shape.dim(2);
+ const int32_t output_height = output_shape.dim(1);
+ const int32_t output_width = output_shape.dim(2);
+
+ const int32_t stride_height = _params.stride_height;
+ const int32_t stride_width = _params.stride_width;
+ const int32_t dilation_height_factor = _params.dilation_height_factor;
+ const int32_t dilation_width_factor = _params.dilation_width_factor;
+ const int32_t depth_multiplier = _params.depth_multiplier;
+
+ const std::vector<double> effective_output_scales =
+ getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale());
+
+ std::vector<ChannelQuantMultipliers> quant_multipliers_raw =
+ quantizeMultipliers(effective_output_scales);
+
+ BroadcastableWrapper<ChannelQuantMultipliers> quant_multipliers(quant_multipliers_raw);
+
+ int32_t activation_min{};
+ int32_t activation_max{};
+ calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max);
+
+ for (int32_t batch = 0; batch < batches; ++batch)
+ {
+ for (int32_t out_y = 0; out_y < output_height; ++out_y)
+ {
+ for (int32_t out_x = 0; out_x < output_width; ++out_x)
+ {
+ for (int32_t in_c = 0; in_c < input_depth; ++in_c)
+ {
+ for (int32_t m = 0; m < depth_multiplier; ++m)
+ {
+ const int32_t out_c = m + in_c * depth_multiplier;
+ const int32_t in_y_origin = out_y * stride_height - _padding_height;
+ const int32_t in_x_origin = out_x * stride_width - _padding_width;
+ int64_t acc = 0;
+ for (int32_t filter_y = 0; filter_y < filter_height; ++filter_y)
+ {
+ for (int32_t filter_x = 0; filter_x < filter_width; ++filter_x)
+ {
+ const int32_t in_y = in_y_origin + dilation_height_factor * filter_y;
+ const int32_t in_x = in_x_origin + dilation_width_factor * filter_x;
+ if ((in_y >= 0 && in_y < input_height) && (in_x >= 0 && in_x < input_width))
+ {
+ const int16_t input_val =
+ input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)];
+ const int16_t filter_val =
+ filter_data[calcOffset(filter_shape, 0, filter_y, filter_x, out_c)];
+ acc += static_cast<int64_t>(input_val) * static_cast<int64_t>(filter_val);
+ }
+ }
+ }
+ if (bias_data != nullptr)
+ {
+ acc += bias_data[out_c];
+ }
+
+ int32_t output_multiplier = quant_multipliers[out_c].multiplier;
+ int output_shift = quant_multipliers[out_c].shift;
+ int32_t scaled_acc =
+ tflite::MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift);
+
+ scaled_acc = std::max(scaled_acc, activation_min);
+ scaled_acc = std::min(scaled_acc, activation_max);
+
+ output_data[calcOffset(output_shape, batch, out_y, out_x, out_c)] = scaled_acc;
+ }
+ }
+ }
+ }
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.h b/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.h
new file mode 100644
index 000000000..400bebe5a
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_DEPTHWISECONV2D_H
+#define LUCI_INTERPRETER_KERNELS_DEPTHWISECONV2D_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class DepthwiseConv2D : public KernelWithParams<DepthwiseConv2DParams>
+{
+public:
+ DepthwiseConv2D(const Tensor *input, const Tensor *filter, const Tensor *bias, Tensor *output,
+ const DepthwiseConv2DParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ const Tensor *filter() const { return _inputs[1]; }
+ const Tensor *bias() const { return _inputs[2]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+ void evalQuantizedS16() const;
+
+private:
+ int32_t _padding_height{};
+ int32_t _padding_width{};
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_DEPTHWISECONV2D_H
diff --git a/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp b/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp
new file mode 100644
index 000000000..0c76b585e
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/DepthwiseConv2D.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(DepthwiseConv2DTest, Float)
+{
+ Shape input_shape{1, 4, 2, 2};
+ Shape filter_shape{1, 2, 2, 4};
+ Shape bias_shape{4};
+ std::vector<float> input_data{
+ 1, 2, 7, 8, //
+ 3, 4, 9, 10, //
+ 5, 6, 11, 12, //
+ 13, 14, 15, 16, //
+ };
+ std::vector<float> filter_data{
+ 1, 2, 3, 4, //
+ -9, 10, -11, 12, //
+ 5, 6, 7, 8, //
+ 13, -14, 15, -16, //
+ };
+ std::vector<float> bias_data{1, 2, 3, 4};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DepthwiseConv2DParams params{};
+ params.padding = Padding::VALID;
+ params.depth_multiplier = 2;
+ params.stride_height = 2;
+ params.stride_width = 1;
+ params.dilation_height_factor = 1;
+ params.dilation_width_factor = 1;
+ params.activation = Activation::RELU;
+
+ DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{
+ 71, 0, 99, 0, //
+ 167, 0, 227, 28, //
+ };
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 1, 4}));
+}
+
+TEST(DepthwiseConv2DTest, Uint8)
+{
+ std::vector<float> input_data{
+ 1, 2, 7, 8, // column 1
+ 3, 4, 9, 10, // column 2
+ 5, 6, 11, 12, // column 3
+ };
+ std::vector<float> filter_data{
+ 1, 2, 3, 4, //
+ -9, 10, -11, 12, //
+ 5, 6, 7, 8, //
+ 13, -14, 15, -16, //
+ };
+ std::vector<float> bias_data{1, 2, 3, 4};
+
+ std::pair<float, int32_t> input_quant_param = quantizationParams<uint8_t>(-63.5, 64);
+ std::pair<float, int32_t> output_quant_param = quantizationParams<uint8_t>(-127, 128);
+
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 3, 2, 2}, input_quant_param.first,
+ input_quant_param.second, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::U8>({1, 2, 2, 4}, input_quant_param.first,
+ input_quant_param.second, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::S32>(
+ {4}, input_quant_param.first * input_quant_param.first, 0, bias_data);
+ Tensor output_tensor =
+ makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second);
+
+ DepthwiseConv2DParams params{};
+ params.padding = Padding::VALID;
+ params.depth_multiplier = 2;
+ params.stride_height = 1;
+ params.stride_width = 1;
+ params.dilation_height_factor = 1;
+ params.dilation_width_factor = 1;
+ params.activation = Activation::NONE;
+
+ DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{
+ 71, -34, 99, -20, //
+ 91, -26, 127, -4, //
+ };
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 1, 4}));
+}
+
+TEST(DepthwiseConv2DTest, SInt16)
+{
+ Shape input_shape{1, 4, 2, 2};
+ Shape filter_shape{1, 2, 2, 4};
+ Shape bias_shape{4};
+ std::vector<int32_t> ref_output_shape{1, 2, 1, 4};
+
+ std::vector<float> input_data{
+ 1, 2, 7, 8, //
+ 3, 4, 9, 10, //
+ 5, 6, 11, 12, //
+ 13, 14, 15, 16, //
+ };
+ std::vector<float> filter_data{
+ 1, 2, 3, 4, //
+ -9, 10, -11, 12, //
+ 5, 6, 7, 8, //
+ 13, -14, 15, -16, //
+ };
+ std::vector<float> bias_data{1, 2, 3, 4};
+ std::vector<float> ref_output_data{
+ 71, 0, 99, 0, //
+ 167, 0, 227, 28, //
+ };
+
+ Tensor input_tensor = makeInputTensor<DataType::S16>(input_shape, 0.25, 0, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::S16>(filter_shape, 0.2, 0, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::S64>(bias_shape, 0.25 * 0.2, 0, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 0.5, 0);
+
+ DepthwiseConv2DParams params{};
+ params.padding = Padding::VALID;
+ params.depth_multiplier = 2;
+ params.stride_height = 2;
+ params.stride_width = 1;
+ params.dilation_height_factor = 1;
+ params.dilation_width_factor = 1;
+ params.activation = Activation::RELU;
+
+ DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(DepthwiseConv2DTest, SInt16_CWQ_weights)
+{
+ const int output_channels = 4;
+ Shape input_shape{1, 4, 2, 2};
+ Shape filter_shape{1, 2, 2, output_channels};
+ Shape bias_shape{4};
+ std::vector<int32_t> ref_output_shape{1, 2, 1, output_channels};
+
+ std::vector<float> input_data{
+ 1, 2, 7, 8, //
+ 3, 4, 9, 10, //
+ 5, 6, 11, 12, //
+ 13, 14, 15, 16, //
+ };
+ std::vector<float> filter_data{
+ 1, 2, 3, 4, //
+ -9, 10, -11, 12, //
+ 5, 6, 7, 8, //
+ 13, -14, 15, -16, //
+ };
+ std::vector<float> bias_data{1, 2, 3, 4};
+ std::vector<float> ref_output_data{
+ 71, 0, 99, 0, //
+ 167, 0, 227, 28, //
+ };
+
+ float input_scale = 0.25;
+ std::vector<float> filter_scales{0.2f, 1.f, 0.5f, 0.1f};
+ std::vector<float> bias_scales;
+ for (int i = 0; i < output_channels; ++i)
+ bias_scales.push_back(filter_scales[i] * input_scale);
+ std::vector<int32_t> zerop(4, 0);
+ Tensor input_tensor = makeInputTensor<DataType::S16>(input_shape, input_scale, 0, input_data);
+ Tensor filter_tensor =
+ makeInputTensor<DataType::S16>(filter_shape, filter_scales, zerop, 3, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::S64>(bias_shape, bias_scales, zerop, 0, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 0.5, 0);
+
+ DepthwiseConv2DParams params{};
+ params.padding = Padding::VALID;
+ params.depth_multiplier = 2;
+ params.stride_height = 2;
+ params.stride_width = 1;
+ params.dilation_height_factor = 1;
+ params.dilation_width_factor = 1;
+ params.activation = Activation::RELU;
+
+ DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(DepthwiseConv2DTest, InvalidBiasType_NEG)
+{
+ Shape input_shape{1, 4, 2, 2};
+ Shape filter_shape{1, 2, 2, 4};
+ Shape bias_shape{4};
+ std::vector<float> input_data{
+ 1, 2, 7, 8, //
+ 3, 4, 9, 10, //
+ 5, 6, 11, 12, //
+ 13, 14, 15, 16, //
+ };
+ std::vector<float> filter_data{
+ 1, 2, 3, 4, //
+ -9, 10, -11, 12, //
+ 5, 6, 7, 8, //
+ 13, -14, 15, -16, //
+ };
+ std::vector<int32_t> bias_data{1, 2, 3, 4};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::S32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DepthwiseConv2DParams params{};
+ params.padding = Padding::VALID;
+ params.depth_multiplier = 2;
+ params.stride_height = 2;
+ params.stride_width = 1;
+ params.dilation_height_factor = 1;
+ params.dilation_width_factor = 1;
+ params.activation = Activation::RELU;
+
+ DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(DepthwiseConv2DTest, InOutTypeMismatch_NEG)
+{
+ Shape input_shape{1, 4, 2, 2};
+ Shape filter_shape{1, 2, 2, 4};
+ Shape bias_shape{4};
+ std::vector<float> input_data{
+ 1, 2, 7, 8, //
+ 3, 4, 9, 10, //
+ 5, 6, 11, 12, //
+ 13, 14, 15, 16, //
+ };
+ std::vector<float> filter_data{
+ 1, 2, 3, 4, //
+ -9, 10, -11, 12, //
+ 5, 6, 7, 8, //
+ 13, -14, 15, -16, //
+ };
+ std::vector<float> bias_data{1, 2, 3, 4};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ DepthwiseConv2DParams params{};
+ params.padding = Padding::VALID;
+ params.depth_multiplier = 2;
+ params.stride_height = 2;
+ params.stride_width = 1;
+ params.dilation_height_factor = 1;
+ params.dilation_width_factor = 1;
+ params.activation = Activation::RELU;
+
+ DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(DepthwiseConv2DTest, InvalidInputShape_NEG)
+{
+ Shape input_shape{4, 2, 2};
+ Shape filter_shape{2, 2, 4};
+ Shape bias_shape{4};
+ std::vector<float> input_data{
+ 1, 2, 7, 8, //
+ 3, 4, 9, 10, //
+ 5, 6, 11, 12, //
+ 13, 14, 15, 16, //
+ };
+ std::vector<float> filter_data{
+ 1, 2, 3, 4, //
+ -9, 10, -11, 12, //
+ 5, 6, 7, 8, //
+ 13, -14, 15, -16, //
+ };
+ std::vector<float> bias_data{1, 2, 3, 4};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DepthwiseConv2DParams params{};
+ params.padding = Padding::VALID;
+ params.depth_multiplier = 2;
+ params.stride_height = 2;
+ params.stride_width = 1;
+ params.dilation_height_factor = 1;
+ params.dilation_width_factor = 1;
+ params.activation = Activation::RELU;
+
+ DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(DepthwiseConv2DTest, InvalidFilterShape_NEG)
+{
+ Shape input_shape{1, 4, 2, 2};
+ Shape filter_shape{2, 1, 2, 4};
+ Shape bias_shape{4};
+ std::vector<float> input_data{
+ 1, 2, 7, 8, //
+ 3, 4, 9, 10, //
+ 5, 6, 11, 12, //
+ 13, 14, 15, 16, //
+ };
+ std::vector<float> filter_data{
+ 1, 2, 3, 4, //
+ -9, 10, -11, 12, //
+ 5, 6, 7, 8, //
+ 13, -14, 15, -16, //
+ };
+ std::vector<float> bias_data{1, 2, 3, 4};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DepthwiseConv2DParams params{};
+ params.padding = Padding::VALID;
+ params.depth_multiplier = 2;
+ params.stride_height = 2;
+ params.stride_width = 1;
+ params.dilation_height_factor = 1;
+ params.dilation_width_factor = 1;
+ params.activation = Activation::RELU;
+
+ DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(DepthwiseConv2DTest, InvalidBiasDim_NEG)
+{
+ Shape input_shape{1, 4, 2, 2};
+ Shape filter_shape{1, 2, 4, 2};
+ Shape bias_shape{4};
+ std::vector<float> input_data{
+ 1, 2, 7, 8, //
+ 3, 4, 9, 10, //
+ 5, 6, 11, 12, //
+ 13, 14, 15, 16, //
+ };
+ std::vector<float> filter_data{
+ 1, 2, 3, 4, //
+ -9, 10, -11, 12, //
+ 5, 6, 7, 8, //
+ 13, -14, 15, -16, //
+ };
+ std::vector<float> bias_data{1, 2, 3, 4};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DepthwiseConv2DParams params{};
+ params.padding = Padding::VALID;
+ params.depth_multiplier = 2;
+ params.stride_height = 2;
+ params.stride_width = 1;
+ params.dilation_height_factor = 1;
+ params.dilation_width_factor = 1;
+ params.activation = Activation::RELU;
+
+ DepthwiseConv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Div.cpp b/compiler/luci-interpreter/src/kernels/Div.cpp
new file mode 100644
index 000000000..e75876b3a
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Div.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Div.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Div::Div(const Tensor *input1, const Tensor *input2, Tensor *output, const DivParams &params)
+ : KernelWithParams<DivParams>({input1, input2}, {output}, params)
+{
+}
+
+void Div::configure()
+{
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type());
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type());
+
+ output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape()));
+}
+
+void Div::execute() const
+{
+ switch (input1()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Div::evalFloat() const
+{
+ float activation_min{};
+ float activation_max{};
+ calculateActivationRange(_params.activation, &activation_min, &activation_max);
+
+ tflite::ArithmeticParams params{};
+ params.float_activation_min = activation_min;
+ params.float_activation_max = activation_max;
+ const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes(
+ getTensorShape(input1()), getTensorShape(input2()), &params);
+
+ if (need_broadcast)
+ {
+ tflite::reference_ops::BroadcastDivSlow(
+ params, getTensorShape(input1()), getTensorData<float>(input1()), getTensorShape(input2()),
+ getTensorData<float>(input2()), getTensorShape(output()), getTensorData<float>(output()));
+ }
+ else
+ {
+ tflite::reference_ops::Div(params, getTensorShape(input1()), getTensorData<float>(input1()),
+ getTensorShape(input2()), getTensorData<float>(input2()),
+ getTensorShape(output()), getTensorData<float>(output()));
+ }
+}
+
+void Div::evalQuantized() const
+{
+ const auto input1_scale = static_cast<double>(input1()->scale());
+ const auto input2_scale = static_cast<double>(input2()->scale());
+ const auto output_scale = static_cast<double>(output()->scale());
+
+ const double real_output_multiplier = input1_scale / (input2_scale * output_scale);
+
+ int32_t output_multiplier{};
+ int output_shift{};
+
+ quantizeMultiplier(real_output_multiplier, &output_multiplier, &output_shift);
+
+ int32_t activation_min{};
+ int32_t activation_max{};
+ calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max);
+
+ tflite::ArithmeticParams params{};
+
+ params.input1_offset = -input1()->zero_point(); // Note the '-'.
+ params.input2_offset = -input2()->zero_point(); // Note the '-'.
+ params.output_offset = output()->zero_point();
+ params.output_multiplier = output_multiplier;
+ params.output_shift = output_shift;
+ params.quantized_activation_min = activation_min;
+ params.quantized_activation_max = activation_max;
+
+ const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes(
+ getTensorShape(input1()), getTensorShape(input2()), &params);
+
+ if (need_broadcast)
+ {
+ tflite::reference_ops::BroadcastDivSlow(
+ params, getTensorShape(input1()), getTensorData<uint8_t>(input1()),
+ getTensorShape(input2()), getTensorData<uint8_t>(input2()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ }
+ else
+ {
+ tflite::reference_ops::Div(params, getTensorShape(input1()), getTensorData<uint8_t>(input1()),
+ getTensorShape(input2()), getTensorData<uint8_t>(input2()),
+ getTensorShape(output()), getTensorData<uint8_t>(output()));
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Div.h b/compiler/luci-interpreter/src/kernels/Div.h
new file mode 100644
index 000000000..6040cdd02
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Div.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_DIV_H
+#define LUCI_INTERPRETER_KERNELS_DIV_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Div : public KernelWithParams<DivParams>
+{
+public:
+ Div(const Tensor *input1, const Tensor *input2, Tensor *output, const DivParams &params);
+
+ const Tensor *input1() const { return _inputs[0]; }
+ const Tensor *input2() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_DIV_H
diff --git a/compiler/luci-interpreter/src/kernels/Div.test.cpp b/compiler/luci-interpreter/src/kernels/Div.test.cpp
new file mode 100644
index 000000000..77eb2e9c1
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Div.test.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Div.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+float GetTolerance(float min, float max)
+{
+ const float kQuantizedStep = (max - min) / 255.0f;
+ const float kQuantizedTolerance = 2.0f * kQuantizedStep + kQuantizedStep * kQuantizedStep;
+ return kQuantizedTolerance;
+}
+
+TEST(DivTest, Float)
+{
+ Shape base_shape = {2, 3, 1, 1};
+
+ std::vector<int32_t> output_shape = {2, 3, 1, 1};
+
+ std::vector<float> input1_data{0.3f, 2.3f, 0.9f, 0.5f, 0.8f, 1.1f};
+ std::vector<float> input2_data{0.2f, 1.6f, 0.5f, 0.4f, 1.6f, 0.4f};
+ std::vector<float> test_outputs{1.5f, 1.4375f, 1.8f, 1.25f, 0.5f, 2.75f};
+
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>(base_shape, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>(base_shape, input2_data);
+
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DivParams params{};
+ params.activation = Activation::RELU;
+
+ Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs, 0.0001f));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+TEST(DivTest, FloatBroadcast)
+{
+ Shape input1_shape = {1, 3};
+ Shape input2_shape = {3, 1};
+
+ std::vector<float> input1_data{-0.3f, 2.3f, 0.9f};
+ std::vector<float> input2_data{0.2f, 1.6f, 0.5f};
+ std::vector<float> test_outputs{0.f, 11.5f, 4.5f, 0.f, 1.4375f, 0.5625f, 0.f, 4.6f, 1.8f};
+
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>(input1_shape, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>(input2_shape, input2_data);
+
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DivParams params{};
+ params.activation = Activation::RELU;
+
+ Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs, 0.0001f));
+}
+
+TEST(DivTest, Uint8)
+{
+ Shape base_shape = {1, 2, 2, 1};
+
+ std::vector<int32_t> output_shape = {1, 2, 2, 1};
+
+ std::vector<float> input1_data = {-0.8f, -0.2f, 0.3f, 0.7f};
+ std::vector<float> input2_data = {-0.8f, 0.4f, 0.8f, 1.0f};
+ std::vector<float> test_outputs{1.0f, 0.f, 0.375f, 0.7f};
+
+ const float kQuantizedTolerance = GetTolerance(-1.0, 1.0);
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-1.f, 1.f);
+
+ Tensor input1_tensor =
+ makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, input1_data);
+ Tensor input2_tensor =
+ makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, input2_data);
+
+ Tensor output_tensor =
+ makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second);
+
+ DivParams params{};
+ params.activation = Activation::RELU;
+
+ Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(test_outputs, kQuantizedTolerance));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+TEST(DivTest, Input_Output_Type_NEG)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor input2_tensor = makeInputTensor<DataType::S32>({1}, {2});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ DivParams params{};
+ params.activation = Activation::RELU;
+
+ Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(DivTest, Invalid_Input_Type_NEG)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::S64>({1}, {1});
+ Tensor input2_tensor = makeInputTensor<DataType::S64>({1}, {2});
+ Tensor output_tensor = makeOutputTensor(DataType::S64);
+
+ DivParams params{};
+ params.activation = Activation::RELU;
+
+ Div kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ EXPECT_ANY_THROW(kernel.execute());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Elu.cpp b/compiler/luci-interpreter/src/kernels/Elu.cpp
new file mode 100644
index 000000000..456396055
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Elu.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Elu.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Elu::Elu(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {}
+
+void Elu::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ output()->resize(input()->shape());
+}
+
+void Elu::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ tflite::optimized_ops::Elu(getTensorShape(input()), getTensorData<float>(input()),
+ getTensorShape(output()), getTensorData<float>(output()));
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Elu.h b/compiler/luci-interpreter/src/kernels/Elu.h
new file mode 100644
index 000000000..c844ab57f
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Elu.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_ELU_H
+#define LUCI_INTERPRETER_KERNELS_ELU_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Elu : public Kernel
+{
+public:
+ Elu(const Tensor *input, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_ELU_H
diff --git a/compiler/luci-interpreter/src/kernels/Elu.test.cpp b/compiler/luci-interpreter/src/kernels/Elu.test.cpp
new file mode 100644
index 000000000..0235d6552
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Elu.test.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Elu.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data, std::initializer_list<float> output_data)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Elu kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ (void)output_shape;
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(output_data));
+}
+
+TEST(EluTest, SimpleElu)
+{
+ Check(
+ /*input_shape=*/{1, 2, 4, 1}, /*output_shape=*/{1, 2, 4, 1},
+ /*input_data=*/
+ {
+ 0, -6, 2, -4, //
+ 3, -2, 10, -0.1, //
+ },
+ /*output_data=*/
+ {
+ 0.0, -0.997521, 2.0, -0.981684, //
+ 3.0, -0.864665, 10.0, -0.0951626, //
+ });
+}
+
+TEST(EluTest, InOutTypeMismatch_NEG)
+{
+ Shape input_shape{1, 2, 4, 1};
+ std::vector<float> input_data{
+ 0, -6, 2, -4, //
+ 3, -2, 10, -0.1, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ Elu kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Equal.cpp b/compiler/luci-interpreter/src/kernels/Equal.cpp
new file mode 100644
index 000000000..f58de1250
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Equal.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Equal.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/comparisons.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Equal::Equal(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {}
+
+void Equal::configure()
+{
+ LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type());
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL);
+
+ if (x()->element_type() == DataType::U8)
+ {
+ quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift);
+ quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift);
+ }
+ output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape()));
+}
+
+void Equal::execute() const
+{
+ switch (x()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Equal::evalFloat() const
+{
+ const auto x_data = getTensorData<float>(x());
+ const auto y_data = getTensorData<float>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowEqual(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::Equal(op_params, getTensorShape(x()), x_data, getTensorShape(y()),
+ y_data, getTensorShape(output()), output_data);
+ }
+}
+
+void Equal::evalQuantized() const
+{
+ const auto x_data = getTensorData<uint8_t>(x());
+ const auto y_data = getTensorData<uint8_t>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.left_shift = 8;
+ op_params.input1_offset = -x()->zero_point(); // Note the '-'
+ op_params.input1_shift = _x_shift;
+ op_params.input1_multiplier = _x_multiplier;
+ op_params.input2_offset = -y()->zero_point(); // Note the '-'
+ op_params.input2_shift = _y_shift;
+ op_params.input2_multiplier = _y_multiplier;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowEqualWithScaling(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::EqualWithScaling(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data, getTensorShape(output()),
+ output_data);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Equal.h b/compiler/luci-interpreter/src/kernels/Equal.h
new file mode 100644
index 000000000..69b3be774
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Equal.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_EQUAL_H
+#define LUCI_INTERPRETER_KERNELS_EQUAL_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Equal : public Kernel
+{
+public:
+ Equal(const Tensor *x, const Tensor *y, Tensor *output);
+
+ const Tensor *x() const { return _inputs[0]; }
+ const Tensor *y() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+
+private:
+ int32_t _x_multiplier = 0;
+ int32_t _x_shift = 0;
+ int32_t _y_multiplier = 0;
+ int32_t _y_shift = 0;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_EQUAL_H
diff --git a/compiler/luci-interpreter/src/kernels/Equal.test.cpp b/compiler/luci-interpreter/src/kernels/Equal.test.cpp
new file mode 100644
index 000000000..fb0de8bbf
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Equal.test.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Equal.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(EqualTest, FloatSimple)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ -1, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ false, true, false, // Row 1
+ false, true, false, // Row 2
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Equal kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3}));
+}
+
+TEST(EqualTest, FloatBroardcast)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ -1, 0, 1, // Row 3
+ 0.9, 0.7, 0.5, // Row 4
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ false, true, false, // Row 1
+ false, false, false, // Row 2
+ false, false, false, // Row 3
+ true, true, true, // Row 4
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({4, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Equal kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3}));
+}
+
+// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+const float F_MIN = -128.0 / 128.0;
+const float F_MAX = 127.0 / 128.0;
+
+TEST(EqualTest, Uint8Quantized)
+{
+ std::vector<float> x_data{
+ 0.5, 0.5, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.5, 0.55, 0.5, // Row 1
+ -1, 0, 0.05, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ false, true, false, false, // Row 1
+ false, true, true, false, // Row 2
+ };
+
+ std::pair<float, int32_t> x_quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first,
+ x_quant_param.second, x_data);
+
+ std::pair<float, int32_t> y_quant_param = quantizationParams<uint8_t>(F_MIN * 2, F_MAX * 2);
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first,
+ y_quant_param.second, y_data);
+
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Equal kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(EqualTest, Uint8QuantizedBroadcast)
+{
+ std::vector<float> x_data{
+ 0.4, -0.8, 0.7, 0.3, // Row 1
+ -0.5, 0.1, 0, 0.5, // Row 2
+ 1, 0, 0.05, -1, // Row 3
+ -1, 0.05, 0, 1, // Row 4
+ };
+
+ std::vector<float> y_data{
+ -1, 0.05, 0, 1, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ false, false, false, false, // Row 1
+ false, false, true, false, // Row 2
+ false, false, false, false, // Row 3
+ true, true, true, true, // Row 4
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 4, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Equal kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 4, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(EqualTest, Input_Type_Mismatch_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Equal kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(EqualTest, Input_Output_Type_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Equal kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Exp.cpp b/compiler/luci-interpreter/src/kernels/Exp.cpp
new file mode 100644
index 000000000..f7b115ab3
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Exp.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Exp.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Exp::Exp(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {}
+
+void Exp::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ output()->resize(input()->shape());
+}
+
+void Exp::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Exp::evalFloat() const
+{
+ const int size = tflite::MatchingFlatSize(getTensorShape(input()), getTensorShape(output()));
+ tflite::reference_ops::Exp(getTensorData<float>(input()), size, getTensorData<float>(output()));
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Exp.h b/compiler/luci-interpreter/src/kernels/Exp.h
new file mode 100644
index 000000000..429177375
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Exp.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_EXP_H
+#define LUCI_INTERPRETER_KERNELS_EXP_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Exp : public Kernel
+{
+public:
+ Exp(const Tensor *input, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_EXP_H
diff --git a/compiler/luci-interpreter/src/kernels/Exp.test.cpp b/compiler/luci-interpreter/src/kernels/Exp.test.cpp
new file mode 100644
index 000000000..19b2c141a
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Exp.test.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Exp.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(ExpTest, Float)
+{
+ Shape input_shape{1, 1, 7};
+ std::vector<float> input_data{0.0f, 1.0f, -1.0f, 100.0f, -100.0f, 0.01f, -0.01f};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Exp kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<int32_t> ref_output_shape{1, 1, 7};
+ std::vector<float> 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<float>(output_tensor), FloatArrayNear(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Floor.cpp b/compiler/luci-interpreter/src/kernels/Floor.cpp
new file mode 100644
index 000000000..e3c4246cc
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Floor.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Floor.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/floor.h>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Floor::Floor(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {}
+
+void Floor::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ output()->resize(input()->shape());
+}
+
+void Floor::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Floor::evalFloat() const
+{
+ tflite::reference_ops::Floor(getTensorShape(input()), getTensorData<float>(input()),
+ getTensorShape(output()), getTensorData<float>(output()));
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Floor.h b/compiler/luci-interpreter/src/kernels/Floor.h
new file mode 100644
index 000000000..ca3ad5997
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Floor.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_FLOOR_H
+#define LUCI_INTERPRETER_KERNELS_FLOOR_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Floor : public Kernel
+{
+public:
+ Floor(const Tensor *input, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_FLOOR_H
diff --git a/compiler/luci-interpreter/src/kernels/Floor.test.cpp b/compiler/luci-interpreter/src/kernels/Floor.test.cpp
new file mode 100644
index 000000000..3e1ab6f3a
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Floor.test.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Floor.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(FloorTest, SimpleFloat)
+{
+ std::initializer_list<int32_t> input_shape{1, 2, 4, 1};
+ std::vector<float> input_data{
+ 0.2, 8.6, 2.4, 4.3, // Row 1
+ 3, 7.1, 10.5, -0.9, // Row 2
+ };
+
+ std::initializer_list<int32_t> ref_output_shape{1, 2, 4, 1};
+ std::vector<float> ref_output_data{
+ 0, 8, 2, 4, // Row 1
+ 3, 7, 10, -1, // Row 2
+ };
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Floor kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+}
+
+TEST(FloorTest, Input_Output_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::S32);
+
+ Floor kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/FloorDiv.cpp b/compiler/luci-interpreter/src/kernels/FloorDiv.cpp
new file mode 100644
index 000000000..b6f36cea3
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/FloorDiv.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/FloorDiv.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/binary_function.h>
+#include <cmath>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+FloorDiv::FloorDiv(const Tensor *input, const Tensor *alpha, Tensor *output)
+ : Kernel({input, alpha}, {output})
+{
+}
+
+void FloorDiv::configure()
+{
+ LUCI_INTERPRETER_CHECK(x()->element_type() == output()->element_type());
+ LUCI_INTERPRETER_CHECK(y()->element_type() == output()->element_type());
+
+ output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape()));
+}
+
+void FloorDiv::execute() const
+{
+ switch (x()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void FloorDiv::evalFloat() const
+{
+ auto FloorDivFunc = [](float x, float y) -> float {
+ return std::floor(static_cast<double>(x) / static_cast<double>(y));
+ };
+
+ const auto x_data = getTensorData<float>(x());
+ const auto y_data = getTensorData<float>(y());
+
+ // Check the denominator
+ for (int i = 0; i < getTensorShape(y()).FlatSize(); ++i)
+ {
+ LUCI_INTERPRETER_CHECK(y_data[i] != 0);
+ }
+
+ if (x()->shape() != y()->shape())
+ {
+ tflite::reference_ops::BroadcastBinaryFunction4DSlow<float, float, float>(
+ getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()),
+ getTensorData<float>(output()), FloorDivFunc);
+ }
+ else
+ {
+ tflite::reference_ops::BinaryFunction<float, float, float>(
+ getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()),
+ getTensorData<float>(output()), FloorDivFunc);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/FloorDiv.h b/compiler/luci-interpreter/src/kernels/FloorDiv.h
new file mode 100644
index 000000000..e9c47d81a
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/FloorDiv.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_FLOOR_DIV_H
+#define LUCI_INTERPRETER_KERNELS_FLOOR_DIV_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class FloorDiv : public Kernel
+{
+public:
+ FloorDiv(const Tensor *x, const Tensor *y, Tensor *output);
+
+ const Tensor *x() const { return _inputs[0]; }
+ const Tensor *y() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_FLOOR_DIV_H
diff --git a/compiler/luci-interpreter/src/kernels/FloorDiv.test.cpp b/compiler/luci-interpreter/src/kernels/FloorDiv.test.cpp
new file mode 100644
index 000000000..a5bc700f7
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/FloorDiv.test.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/FloorDiv.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(FloorDivTest, FloatSimple)
+{
+ Shape x_shape{2, 3};
+ std::vector<float> x_data{
+ 0.5, 2.4, 3.1, // Row 1
+ 1.9, -1.9, -2.8, // Row 2
+ };
+
+ Shape y_shape = x_shape;
+ std::vector<float> y_data{
+ 2.0, 0.5, 3.0, // Row 1
+ 1.0, -1.0, -2.0, // Row 2
+ };
+
+ std::vector<int32_t> ref_output_shape{2, 3};
+ std::vector<float> ref_output_data{
+ 0, 4, 1, // Row 1
+ 1, 1, 1, // Row 2
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>(x_shape, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>(y_shape, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ FloorDiv kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor),
+ ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+}
+
+TEST(FloorDivTest, FloatBroadcast)
+{
+ Shape x_shape{1, 3};
+ std::vector<float> x_data{
+ 0.5, 2.4, -3.1, // Row 1
+ };
+
+ Shape y_shape{3, 3};
+ std::vector<float> y_data{
+ 1.0, 1.0, 1.0, // Row 1
+ 2.0, -0.5, -2.0, // Row 2
+ 0.3, 0.7, 0.9, // Row 3
+ };
+
+ std::vector<int32_t> ref_output_shape{3, 3};
+ std::vector<float> ref_output_data{
+ 0, 2, -4, // Row 1
+ 0, -5, 1, // Row 2
+ 1, 3, -4, // Row 3
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>(x_shape, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>(y_shape, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ FloorDiv kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor),
+ ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+}
+
+TEST(FloorDivTest, DivByZero_NEG)
+{
+ Shape shape{3};
+ std::vector<float> x_data{1, 0, -1};
+ std::vector<float> y_data{0, 0, 0};
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>(shape, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>(shape, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ FloorDiv kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+
+ EXPECT_ANY_THROW(kernel.execute());
+}
+
+TEST(FloorDivTest, Input_Output_Type_Mismatch_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ FloorDiv kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(FloorDivTest, Input_Type_Mismatch_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1});
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ FloorDiv kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/FullyConnected.cpp b/compiler/luci-interpreter/src/kernels/FullyConnected.cpp
new file mode 100644
index 000000000..7fa76d5e7
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/FullyConnected.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/FullyConnected.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/fully_connected.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+FullyConnected::FullyConnected(const Tensor *input, const Tensor *weights, const Tensor *bias,
+ Tensor *output, const FullyConnectedParams &params)
+ : KernelWithParams<FullyConnectedParams>({input, weights, bias}, {output}, params)
+{
+}
+
+void FullyConnected::configure()
+{
+ if (weights()->element_type() == DataType::U8)
+ {
+ LUCI_INTERPRETER_CHECK(input()->element_type() == DataType::U8);
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::U8);
+ LUCI_INTERPRETER_CHECK(!bias() || bias()->element_type() == DataType::S32)
+ }
+ else if (weights()->element_type() == DataType::FLOAT32)
+ {
+ LUCI_INTERPRETER_CHECK(input()->element_type() == DataType::FLOAT32);
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::FLOAT32);
+ LUCI_INTERPRETER_CHECK(!bias() || bias()->element_type() == DataType::FLOAT32)
+ }
+ else
+ {
+ throw std::runtime_error("Unsupported type.");
+ }
+
+ const Shape &input_shape = input()->shape();
+ const Shape &weights_shape = weights()->shape();
+
+ LUCI_INTERPRETER_CHECK(weights_shape.num_dims() == 2);
+ LUCI_INTERPRETER_CHECK(bias() == nullptr ||
+ bias()->shape().num_elements() == weights_shape.dim(0));
+
+ LUCI_INTERPRETER_CHECK(input_shape.num_elements() % weights_shape.dim(1) == 0);
+ const int32_t batch_size = input_shape.num_elements() / weights_shape.dim(1);
+ const int32_t num_units = weights_shape.dim(0);
+
+ if (bias())
+ LUCI_INTERPRETER_CHECK(bias()->shape().num_elements() == weights()->shape().dim(0));
+
+ output()->resize({batch_size, num_units});
+}
+
+void FullyConnected::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::U8:
+ evalQuantized();
+ break;
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void FullyConnected::evalFloat() const
+{
+ float activation_min{};
+ float activation_max{};
+ calculateActivationRange(_params.activation, &activation_min, &activation_max);
+
+ tflite::FullyConnectedParams params{};
+ params.float_activation_min = activation_min;
+ params.float_activation_max = activation_max;
+ params.weights_format = tflite::FullyConnectedWeightsFormat::kDefault;
+
+ tflite::reference_ops::FullyConnected(
+ params, getTensorShape(input()), getTensorData<float>(input()), getTensorShape(weights()),
+ getTensorData<float>(weights()), getTensorShape(bias()), getTensorData<float>(bias()),
+ getTensorShape(output()), getTensorData<float>(output()));
+}
+
+void FullyConnected::evalQuantized() const
+{
+ double real_multiplier = 0.0;
+ int output_shift;
+ int32_t output_activation_min;
+ int32_t output_activation_max;
+ int32_t output_multiplier;
+ real_multiplier =
+ getQuantizedConvolutionMultipler(input()->scale(), weights()->scale(), output()->scale());
+ quantizeMultiplier(real_multiplier, &output_multiplier, &output_shift);
+ calculateActivationRangeQuantized(params().activation, output(), &output_activation_min,
+ &output_activation_max);
+
+ int32_t input_offset = -input()->zero_point();
+ int32_t filter_offset = -weights()->zero_point();
+ int32_t output_offset = output()->zero_point();
+
+ tflite::FullyConnectedParams op_params{};
+ op_params.input_offset = input_offset;
+ op_params.weights_offset = filter_offset;
+ op_params.output_offset = output_offset;
+ op_params.output_multiplier = output_multiplier;
+ op_params.output_shift = output_shift;
+ op_params.quantized_activation_min = output_activation_min;
+ op_params.quantized_activation_max = output_activation_max;
+ op_params.lhs_cacheable = false;
+ op_params.rhs_cacheable = false;
+ tflite::reference_ops::FullyConnected(
+ op_params, getTensorShape(input()), getTensorData<uint8_t>(input()),
+ getTensorShape(weights()), getTensorData<uint8_t>(weights()), getTensorShape(bias()),
+ getTensorData<int32_t>(bias()), getTensorShape(output()), getTensorData<uint8_t>(output()));
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/FullyConnected.h b/compiler/luci-interpreter/src/kernels/FullyConnected.h
new file mode 100644
index 000000000..204f11ebb
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/FullyConnected.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_FULLYCONNECTED_H
+#define LUCI_INTERPRETER_KERNELS_FULLYCONNECTED_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class FullyConnected : public KernelWithParams<FullyConnectedParams>
+{
+public:
+ FullyConnected(const Tensor *input, const Tensor *weights, const Tensor *bias, Tensor *output,
+ const FullyConnectedParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ const Tensor *weights() const { return _inputs[1]; }
+ const Tensor *bias() const { return _inputs[2]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_FULLYCONNECTED_H
diff --git a/compiler/luci-interpreter/src/kernels/FullyConnected.test.cpp b/compiler/luci-interpreter/src/kernels/FullyConnected.test.cpp
new file mode 100644
index 000000000..d194ce1a0
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/FullyConnected.test.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/FullyConnected.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T>
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> weights_shape,
+ std::initializer_list<int32_t> bias_shape, std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data, std::initializer_list<float> weights_data,
+ std::initializer_list<float> bias_data, std::initializer_list<float> output_data)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor weights_tensor = makeInputTensor<DataType::FLOAT32>(weights_shape, weights_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ FullyConnectedParams params{};
+ params.activation = Activation::RELU;
+
+ FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+ EXPECT_THAT(extractTensorData<T>(output_tensor), FloatArrayNear(output_data));
+}
+
+template <>
+void Check<uint8_t>(
+ std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> weights_shape,
+ std::initializer_list<int32_t> bias_shape, std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data, std::initializer_list<float> weights_data,
+ std::initializer_list<float> bias_data, std::initializer_list<float> output_data)
+{
+ const float quantized_tolerance = getTolerance(-127, 128, 255);
+ std::pair<float, int32_t> input_quant_param = quantizationParams<uint8_t>(-63.5, 64);
+ std::pair<float, int32_t> output_quant_param = quantizationParams<uint8_t>(-127, 128);
+ Tensor input_tensor = makeInputTensor<DataType::U8>(input_shape, input_quant_param.first,
+ input_quant_param.second, input_data);
+ Tensor weights_tensor = makeInputTensor<DataType::U8>(weights_shape, input_quant_param.first,
+ input_quant_param.second, weights_data);
+ Tensor bias_tensor = makeInputTensor<DataType::S32>(
+ bias_shape, input_quant_param.first * input_quant_param.first, 0, bias_data);
+ Tensor output_tensor =
+ makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second);
+
+ FullyConnectedParams params{};
+ params.activation = Activation::RELU;
+
+ FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data, quantized_tolerance));
+}
+
+template <typename T> class FullyConnectedTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(FullyConnectedTest, DataTypes);
+
+TYPED_TEST(FullyConnectedTest, Simple)
+{
+ Check<TypeParam>({3, 2, 2, 1}, {3, 6}, {3}, {2, 3},
+ {
+ -3, -5, 5, 4, 9, -2, // batch = 0
+ -3, -2, -4, 9, -8, 1, // batch = 1
+ },
+ {
+ -3, -7, 4, -4, -6, 4, // unit = 0
+ 3, 5, 2, 3, -3, -8, // unit = 1
+ -3, 7, 4, 9, 0, -5, // unit = 2
+ },
+ {-1, -5, -8}, {
+ 0, 0, 32, // batch = 0
+ 22, 11, 47, // batch = 1
+ });
+}
+
+TEST(FullyConnectedTest, InvalidBiasType_NEG)
+{
+ Shape input_shape{3, 2, 2, 1};
+ std::vector<float> input_data{
+ -3, -5, 5, 4, 9, -2, // batch = 0
+ -3, -2, -4, 9, -8, 1, // batch = 1
+ };
+ Shape weights_shape{3, 6};
+ std::vector<float> weights_data{
+ -3, -7, 4, -4, -6, 4, // unit = 0
+ 3, 5, 2, 3, -3, -8, // unit = 1
+ -3, 7, 4, 9, 0, -5, // unit = 2
+ };
+ Shape bias_shape{3};
+ std::vector<int32_t> bias_data{-1, -5, -8};
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor weights_tensor = makeInputTensor<DataType::FLOAT32>(weights_shape, weights_data);
+ Tensor bias_tensor = makeInputTensor<DataType::S32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ FullyConnectedParams params{};
+ params.activation = Activation::RELU;
+
+ FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(FullyConnectedTest, InvalidWeightShapeDim_NEG)
+{
+ Shape input_shape{3, 2, 2, 1};
+ std::vector<float> input_data{
+ -3, -5, 5, 4, 9, -2, // batch = 0
+ -3, -2, -4, 9, -8, 1, // batch = 1
+ };
+ Shape weights_shape{1, 3, 6};
+ std::vector<float> weights_data{
+ -3, -7, 4, -4, -6, 4, // unit = 0
+ 3, 5, 2, 3, -3, -8, // unit = 1
+ -3, 7, 4, 9, 0, -5, // unit = 2
+ };
+ Shape bias_shape{3};
+ std::vector<float> bias_data{-1, -5, -8};
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor weights_tensor = makeInputTensor<DataType::FLOAT32>(weights_shape, weights_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ FullyConnectedParams params{};
+ params.activation = Activation::RELU;
+
+ FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(FullyConnectedTest, BiasElementNumWeightDimMismatch_NEG)
+{
+ Shape input_shape{3, 2, 2, 1};
+ std::vector<float> input_data{
+ -3, -5, 5, 4, 9, -2, // batch = 0
+ -3, -2, -4, 9, -8, 1, // batch = 1
+ };
+ Shape weights_shape{6, 3};
+ std::vector<float> weights_data{
+ -3, -7, 4, // unit = 0
+ -4, -6, 4, // unit = 1
+ 3, 5, 2, // unit = 2
+ 3, -3, -8, // unit = 3
+ -3, 7, 4, // unit = 4
+ 9, 0, -5, // unit = 5
+ };
+ Shape bias_shape{3};
+ std::vector<float> bias_data{-1, -5, -8};
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor weights_tensor = makeInputTensor<DataType::FLOAT32>(weights_shape, weights_data);
+ Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ FullyConnectedParams params{};
+ params.activation = Activation::RELU;
+
+ FullyConnected kernel(&input_tensor, &weights_tensor, &bias_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Greater.cpp b/compiler/luci-interpreter/src/kernels/Greater.cpp
new file mode 100644
index 000000000..f0dd2db36
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Greater.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Greater.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/comparisons.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Greater::Greater(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {}
+
+void Greater::configure()
+{
+ LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type());
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL);
+
+ if (x()->element_type() == DataType::U8)
+ {
+ quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift);
+ quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift);
+ }
+ output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape()));
+}
+
+void Greater::execute() const
+{
+ switch (x()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Greater::evalFloat() const
+{
+ const auto x_data = getTensorData<float>(x());
+ const auto y_data = getTensorData<float>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowGreater(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::Greater(op_params, getTensorShape(x()), x_data, getTensorShape(y()),
+ y_data, getTensorShape(output()), output_data);
+ }
+}
+
+void Greater::evalQuantized() const
+{
+ const auto x_data = getTensorData<uint8_t>(x());
+ const auto y_data = getTensorData<uint8_t>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.left_shift = 8;
+ op_params.input1_offset = -x()->zero_point(); // Note the '-'
+ op_params.input1_shift = _x_shift;
+ op_params.input1_multiplier = _x_multiplier;
+ op_params.input2_offset = -y()->zero_point(); // Note the '-'
+ op_params.input2_shift = _y_shift;
+ op_params.input2_multiplier = _y_multiplier;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowGreaterWithScaling(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::GreaterWithScaling(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data, getTensorShape(output()),
+ output_data);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Greater.h b/compiler/luci-interpreter/src/kernels/Greater.h
new file mode 100644
index 000000000..a65d29f5c
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Greater.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_GREATER_H
+#define LUCI_INTERPRETER_KERNELS_GREATER_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Greater : public Kernel
+{
+public:
+ Greater(const Tensor *x, const Tensor *y, Tensor *output);
+
+ const Tensor *x() const { return _inputs[0]; }
+ const Tensor *y() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+
+private:
+ int32_t _x_multiplier = 0;
+ int32_t _x_shift = 0;
+ int32_t _y_multiplier = 0;
+ int32_t _y_shift = 0;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_GREATER_H
diff --git a/compiler/luci-interpreter/src/kernels/Greater.test.cpp b/compiler/luci-interpreter/src/kernels/Greater.test.cpp
new file mode 100644
index 000000000..3122fa840
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Greater.test.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Greater.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(GreaterTest, FloatSimple)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ -1, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ false, false, true, // Row 1
+ true, false, false, // Row 2
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Greater kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3}));
+}
+
+TEST(GreaterTest, FloatBroardcast)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ -1, 0, 1, // Row 3
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ false, false, true, // Row 1
+ true, false, false, // Row 2
+ false, false, true, // Row 3
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({3, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Greater kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3, 3}));
+}
+
+// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+const float F_MIN = -128.0 / 128.0;
+const float F_MAX = 127.0 / 128.0;
+
+TEST(GreaterTest, Uint8Quantized)
+{
+ std::vector<float> x_data{
+ 0.5, 0.6, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.6, 0.6, 0.5, // Row 1
+ -1, 0.05, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ false, false, true, true, // Row 1
+ true, false, true, false, // Row 2
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Greater kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(GreaterTest, Uint8QuantizedRescale)
+{
+ std::vector<float> x_data{
+ 0.5, 0.6, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.6, 0.6, 0.5, // Row 1
+ -1, 0.05, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ false, false, true, true, // Row 1
+ true, false, true, false, // Row 2
+ };
+
+ std::pair<float, int32_t> x_quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ std::pair<float, int32_t> y_quant_param = quantizationParams<uint8_t>(F_MIN * 2, F_MAX * 3);
+
+ Tensor x_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first,
+ x_quant_param.second, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first,
+ y_quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Greater kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(GreaterTest, Uint8QuantizedBroadcast)
+{
+ std::vector<float> x_data{
+ 0.4, -0.8, 0.7, 0.3, // Row 1
+ -0.5, 0.1, 0, 0.5, // Row 2
+ 1, 0, 0.05, -1, // Row 3
+ };
+
+ std::vector<float> y_data{
+ -1, 0.05, 0, 1, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ true, false, true, false, // Row 1
+ true, true, false, false, // Row 2
+ true, false, true, false, // Row 3
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 3, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Greater kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 3, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(GreaterTest, Input_Type_Mismatch_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Greater kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(GreaterTest, Input_Output_Type_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Greater kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/GreaterEqual.cpp b/compiler/luci-interpreter/src/kernels/GreaterEqual.cpp
new file mode 100644
index 000000000..68135e27c
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/GreaterEqual.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/GreaterEqual.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/comparisons.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+GreaterEqual::GreaterEqual(const Tensor *x, const Tensor *y, Tensor *output)
+ : Kernel({x, y}, {output})
+{
+}
+
+void GreaterEqual::configure()
+{
+ LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type());
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL);
+
+ if (x()->element_type() == DataType::U8)
+ {
+ quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift);
+ quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift);
+ }
+ output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape()));
+}
+
+void GreaterEqual::execute() const
+{
+ switch (x()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void GreaterEqual::evalFloat() const
+{
+ const auto x_data = getTensorData<float>(x());
+ const auto y_data = getTensorData<float>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowGreaterEqual(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::GreaterEqual(op_params, getTensorShape(x()), x_data, getTensorShape(y()),
+ y_data, getTensorShape(output()), output_data);
+ }
+}
+
+void GreaterEqual::evalQuantized() const
+{
+ const auto x_data = getTensorData<uint8_t>(x());
+ const auto y_data = getTensorData<uint8_t>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.left_shift = 8;
+ op_params.input1_offset = -x()->zero_point(); // Note the '-'
+ op_params.input1_shift = _x_shift;
+ op_params.input1_multiplier = _x_multiplier;
+ op_params.input2_offset = -y()->zero_point(); // Note the '-'
+ op_params.input2_shift = _y_shift;
+ op_params.input2_multiplier = _y_multiplier;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowGreaterEqualWithScaling(
+ op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::GreaterEqualWithScaling(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/GreaterEqual.h b/compiler/luci-interpreter/src/kernels/GreaterEqual.h
new file mode 100644
index 000000000..e948d698f
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/GreaterEqual.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_GREATER_EQUAL_H
+#define LUCI_INTERPRETER_KERNELS_GREATER_EQUAL_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class GreaterEqual : public Kernel
+{
+public:
+ GreaterEqual(const Tensor *x, const Tensor *y, Tensor *output);
+
+ const Tensor *x() const { return _inputs[0]; }
+ const Tensor *y() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+
+private:
+ int32_t _x_multiplier = 0;
+ int32_t _x_shift = 0;
+ int32_t _y_multiplier = 0;
+ int32_t _y_shift = 0;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_GREATER_EQUAL_H
diff --git a/compiler/luci-interpreter/src/kernels/GreaterEqual.test.cpp b/compiler/luci-interpreter/src/kernels/GreaterEqual.test.cpp
new file mode 100644
index 000000000..11e62644c
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/GreaterEqual.test.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/GreaterEqual.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(GreaterEqualTest, FloatSimple)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ -1, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ false, true, true, // Row 1
+ true, true, false, // Row 2
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3}));
+}
+
+TEST(GreaterEqualTest, FloatBroardcast)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ -1, 0, 1, // Row 3
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ false, true, true, // Row 1
+ true, false, false, // Row 2
+ false, false, true, // Row 3
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({3, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3, 3}));
+}
+
+// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+const float F_MIN = -128.0 / 128.0;
+const float F_MAX = 127.0 / 128.0;
+
+TEST(GreaterEqualTest, Uint8Quantized)
+{
+ std::vector<float> x_data{
+ 0.5, 0.6, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.6, 0.55, 0.5, // Row 1
+ -1, 0.05, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ false, true, true, true, // Row 1
+ true, false, true, false, // Row 2
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(GreaterEqualTest, Uint8QuantizedRescale)
+{
+ std::vector<float> x_data{
+ 0.5, 0.5, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.5, 0.6, 0.5, // Row 1
+ -1, 0.05, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ false, true, true, true, // Row 1
+ true, false, true, false, // Row 2
+ };
+
+ std::pair<float, int32_t> x_quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ std::pair<float, int32_t> y_quant_param = quantizationParams<uint8_t>(F_MIN * 1.2, F_MAX * 1.5);
+
+ Tensor x_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first,
+ x_quant_param.second, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first,
+ y_quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(GreaterEqualTest, Uint8QuantizedBroadcast)
+{
+ std::vector<float> x_data{
+ 0.4, -0.8, 0.7, 0.3, // Row 1
+ -0.5, 0.1, 0, 0.5, // Row 2
+ 1, 0, 0.05, -1, // Row 3
+ };
+
+ std::vector<float> y_data{
+ -1, 0.05, 0, 1, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ true, false, true, false, // Row 1
+ true, true, true, false, // Row 2
+ true, false, true, false, // Row 3
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 3, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 3, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(GreaterEqualTest, Input_Type_Mismatch_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(GreaterEqualTest, Input_Output_Type_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/If.cpp b/compiler/luci-interpreter/src/kernels/If.cpp
new file mode 100644
index 000000000..ca982d591
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/If.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/If.h"
+#include "kernels/Utils.h"
+
+#include <cstring>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+static std::vector<const Tensor *> joinInputs(const Tensor *cond,
+ const std::vector<const Tensor *> &inputs)
+{
+ std::vector<const Tensor *> result{cond};
+ result.insert(result.cend(), inputs.cbegin(), inputs.cend());
+ return result;
+}
+
+If::If(const Tensor *cond, const std::vector<const Tensor *> &inputs, std::vector<Tensor *> outputs,
+ RuntimeGraph *then_graph, RuntimeGraph *else_graph)
+ : Kernel(joinInputs(cond, inputs), std::move(outputs)), _then_graph(then_graph),
+ _else_graph(else_graph)
+{
+}
+
+void If::configure()
+{
+ LUCI_INTERPRETER_CHECK(cond()->element_type() == DataType::BOOL);
+ LUCI_INTERPRETER_CHECK(cond()->shape().num_elements() == 1);
+
+ for (RuntimeGraph *graph : {_then_graph, _else_graph})
+ {
+ (void)graph;
+ LUCI_INTERPRETER_CHECK(graph->getInputTensors().size() == getInputTensors().size() - 1);
+ LUCI_INTERPRETER_CHECK(graph->getOutputTensors().size() == getOutputTensors().size());
+ }
+}
+
+void If::execute() const
+{
+ const bool cond_value = cond()->data<bool>()[0];
+
+ RuntimeGraph *active_graph = cond_value ? _then_graph : _else_graph;
+ const auto &graph_inputs = active_graph->getInputTensors();
+ const auto &graph_outputs = active_graph->getOutputTensors();
+
+ // Copy kernel inputs to active graph inputs.
+ for (size_t i = 0; i < getInputTensors().size() - 1; ++i)
+ {
+ LUCI_INTERPRETER_CHECK(graph_inputs[i]->element_type() == input(i)->element_type());
+ graph_inputs[i]->resize(input(i)->shape());
+
+ const int32_t num_elements = input(i)->shape().num_elements();
+ const std::size_t element_size = getDataTypeSize(input(i)->element_type());
+ std::memcpy(graph_inputs[i]->data<void>(), input(i)->data<void>(), num_elements * element_size);
+ }
+
+ active_graph->execute();
+
+ // Copy graph outputs to kernel outputs.
+ for (size_t i = 0; i < getOutputTensors().size(); ++i)
+ {
+ LUCI_INTERPRETER_CHECK(graph_outputs[i]->element_type() == output(i)->element_type());
+ output(i)->resize(graph_outputs[i]->shape());
+
+ const int32_t num_elements = output(i)->shape().num_elements();
+ const std::size_t element_size = getDataTypeSize(output(i)->element_type());
+ std::memcpy(output(i)->data<void>(), graph_outputs[i]->data<void>(),
+ num_elements * element_size);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/If.h b/compiler/luci-interpreter/src/kernels/If.h
new file mode 100644
index 000000000..fa6ab371a
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/If.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_IF_H
+#define LUCI_INTERPRETER_KERNELS_IF_H
+
+#include "core/Kernel.h"
+#include "core/RuntimeGraph.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class If : public Kernel
+{
+public:
+ If(const Tensor *cond, const std::vector<const Tensor *> &inputs, std::vector<Tensor *> outputs,
+ RuntimeGraph *then_graph, RuntimeGraph *else_graph);
+
+ const Tensor *cond() const { return _inputs[0]; }
+ const Tensor *input(int index) const { return _inputs[1 + index]; }
+ Tensor *output(int index) const { return _outputs[index]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ RuntimeGraph *const _then_graph;
+ RuntimeGraph *const _else_graph;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_IF_H
diff --git a/compiler/luci-interpreter/src/kernels/If.test.cpp b/compiler/luci-interpreter/src/kernels/If.test.cpp
new file mode 100644
index 000000000..6967407fb
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/If.test.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/RuntimeModule.h"
+#include "kernels/Add.h"
+#include "kernels/If.h"
+#include "kernels/Mul.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+RuntimeGraph *buildAddSubgraph(RuntimeModule *module)
+{
+ RuntimeGraph *graph = module->addGraph();
+ Tensor *input1 = graph->addTensor(
+ std::make_unique<Tensor>(DataType::FLOAT32, Shape{}, AffineQuantization{}, ""));
+ Tensor *input2 = graph->addTensor(
+ std::make_unique<Tensor>(DataType::FLOAT32, Shape{}, AffineQuantization{}, ""));
+ Tensor *output = graph->addTensor(
+ std::make_unique<Tensor>(DataType::FLOAT32, Shape{}, AffineQuantization{}, ""));
+
+ graph->setInputTensors({input1, input2});
+ graph->setOutputTensors({output});
+
+ AddParams params{};
+ params.activation = Activation::NONE;
+ graph->addKernel(std::make_unique<Add>(input1, input2, output, params));
+
+ return graph;
+}
+
+RuntimeGraph *buildMulSubgraph(RuntimeModule *module)
+{
+ RuntimeGraph *graph = module->addGraph();
+ Tensor *input1 = graph->addTensor(
+ std::make_unique<Tensor>(DataType::FLOAT32, Shape{}, AffineQuantization{}, ""));
+ Tensor *input2 = graph->addTensor(
+ std::make_unique<Tensor>(DataType::FLOAT32, Shape{}, AffineQuantization{}, ""));
+ Tensor *output = graph->addTensor(
+ std::make_unique<Tensor>(DataType::FLOAT32, Shape{}, AffineQuantization{}, ""));
+
+ graph->setInputTensors({input1, input2});
+ graph->setOutputTensors({output});
+
+ MulParams params{};
+ params.activation = Activation::NONE;
+ graph->addKernel(std::make_unique<Mul>(input1, input2, output, params));
+
+ return graph;
+}
+
+TEST(IfTest, CondTrue)
+{
+ Tensor cond = makeInputTensor<DataType::BOOL>({1}, {true});
+ Tensor input1 = makeInputTensor<DataType::FLOAT32>({2}, {5, 7});
+ Tensor input2 = makeInputTensor<DataType::FLOAT32>({1, 2}, {1, 2});
+ Tensor output = makeOutputTensor(DataType::FLOAT32);
+
+ RuntimeModule module(nullptr);
+ RuntimeGraph *then_graph = buildAddSubgraph(&module);
+ RuntimeGraph *else_graph = buildMulSubgraph(&module);
+
+ If kernel(&cond, {&input1, &input2}, {&output}, then_graph, else_graph);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output), FloatArrayNear({6, 9}));
+}
+
+TEST(IfTest, CondFalse)
+{
+ Tensor cond = makeInputTensor<DataType::BOOL>({1}, {false});
+ Tensor input1 = makeInputTensor<DataType::FLOAT32>({2}, {5, 7});
+ Tensor input2 = makeInputTensor<DataType::FLOAT32>({1, 2}, {1, 2});
+ Tensor output = makeOutputTensor(DataType::FLOAT32);
+
+ RuntimeModule module(nullptr);
+ RuntimeGraph *then_graph = buildAddSubgraph(&module);
+ RuntimeGraph *else_graph = buildMulSubgraph(&module);
+
+ If kernel(&cond, {&input1, &input2}, {&output}, then_graph, else_graph);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output), FloatArrayNear({5, 14}));
+}
+
+TEST(IfTest, InvalidCondType_NEG)
+{
+ Tensor cond = makeInputTensor<DataType::FLOAT32>({1}, {1});
+ Tensor input1 = makeInputTensor<DataType::FLOAT32>({2}, {5, 7});
+ Tensor input2 = makeInputTensor<DataType::FLOAT32>({1, 2}, {1, 2});
+ Tensor output = makeOutputTensor(DataType::FLOAT32);
+
+ RuntimeModule module(nullptr);
+ RuntimeGraph *then_graph = buildAddSubgraph(&module);
+ RuntimeGraph *else_graph = buildMulSubgraph(&module);
+
+ If kernel(&cond, {&input1, &input2}, {&output}, then_graph, else_graph);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(IfTest, InvalidCondElementNum_NEG)
+{
+ Tensor cond = makeInputTensor<DataType::BOOL>({2}, {false, true});
+ Tensor input1 = makeInputTensor<DataType::FLOAT32>({2}, {5, 7});
+ Tensor input2 = makeInputTensor<DataType::FLOAT32>({1, 2}, {1, 2});
+ Tensor output = makeOutputTensor(DataType::FLOAT32);
+
+ RuntimeModule module(nullptr);
+ RuntimeGraph *then_graph = buildAddSubgraph(&module);
+ RuntimeGraph *else_graph = buildMulSubgraph(&module);
+
+ If kernel(&cond, {&input1, &input2}, {&output}, then_graph, else_graph);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/InstanceNorm.cpp b/compiler/luci-interpreter/src/kernels/InstanceNorm.cpp
new file mode 100644
index 000000000..8e8241a28
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/InstanceNorm.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/InstanceNorm.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/common.h>
+#include <cmath>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+InstanceNorm::InstanceNorm(const Tensor *input, const Tensor *gamma, const Tensor *beta,
+ Tensor *output, const InstanceNormParams &params)
+ : KernelWithParams<InstanceNormParams>({input, gamma, beta}, {output}, params)
+{
+}
+
+void InstanceNorm::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->shape().num_dims() == 4);
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ LUCI_INTERPRETER_CHECK(gamma()->element_type() == input()->element_type());
+ LUCI_INTERPRETER_CHECK(beta()->element_type() == input()->element_type());
+ output()->resize(input()->shape());
+}
+
+void InstanceNorm::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void InstanceNorm::evalFloat() const
+{
+ float activation_min, activation_max;
+ calculateActivationRange(params().activation, &activation_min, &activation_max);
+ auto input_shape = getTensorShape(input());
+ auto output_shape = getTensorShape(output());
+ const int32_t batches = tflite::MatchingDim(input_shape, 0, output_shape, 0);
+ const int32_t heights = tflite::MatchingDim(input_shape, 1, output_shape, 1);
+ const int32_t widths = tflite::MatchingDim(input_shape, 2, output_shape, 2);
+ const int32_t channels = tflite::MatchingDim(input_shape, 3, output_shape, 3);
+ const float *input_data = getTensorData<float>(input());
+ const float *gamma_data = getTensorData<float>(gamma());
+ const float *beta_data = getTensorData<float>(beta());
+ float *output_data = getTensorData<float>(output());
+ for (int32_t batch = 0; batch < batches; batch++)
+ {
+ for (int32_t channel = 0; channel < channels; channel++)
+ {
+ double sum = 0.0f;
+ double square_sum = 0.0f;
+ int32_t size = heights * widths;
+ for (int32_t height = 0; height < heights; height++)
+ {
+ for (int32_t width = 0; width < widths; width++)
+ {
+ double input_val = input_data[tflite::Offset(input_shape, batch, height, width, channel)];
+ sum += input_val;
+ square_sum += (input_val * input_val);
+ }
+ }
+ double mean = sum / size;
+ double var = square_sum / size - mean * mean;
+
+ double gamma = gamma_data[channel];
+ double beta = beta_data[channel];
+ double a = gamma / (std::sqrt(var + params().epsilon));
+ double b = -mean * a + beta;
+
+ for (int32_t height = 0; height < heights; height++)
+ {
+ for (int32_t width = 0; width < widths; width++)
+ {
+ double input_value =
+ input_data[tflite::Offset(output_shape, batch, height, width, channel)];
+ double output_value = input_value * a + b;
+ output_data[tflite::Offset(output_shape, batch, height, width, channel)] =
+ tflite::ActivationFunctionWithMinMax((float)output_value, activation_min,
+ activation_max);
+ }
+ }
+ }
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/InstanceNorm.h b/compiler/luci-interpreter/src/kernels/InstanceNorm.h
new file mode 100644
index 000000000..a70a84e0a
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/InstanceNorm.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_INSTANCENORM_H
+#define LUCI_INTERPRETER_KERNELS_INSTANCENORM_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class InstanceNorm : public KernelWithParams<InstanceNormParams>
+{
+public:
+ InstanceNorm(const Tensor *input, const Tensor *gamma, const Tensor *beta, Tensor *output,
+ const InstanceNormParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ const Tensor *gamma() const { return _inputs[1]; }
+ const Tensor *beta() const { return _inputs[2]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_INSTANCENORM_H
diff --git a/compiler/luci-interpreter/src/kernels/InstanceNorm.test.cpp b/compiler/luci-interpreter/src/kernels/InstanceNorm.test.cpp
new file mode 100644
index 000000000..19f863544
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/InstanceNorm.test.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "kernels/InstanceNorm.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+TEST(InstanceNormTest, Simple)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1, 2, 2, 1}, {1, 1, 1, 1});
+ Tensor gamma_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1});
+ Tensor beta_tensor = makeInputTensor<DataType::FLOAT32>({1}, {2});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ InstanceNormParams params{};
+ params.epsilon = 0.1f;
+ params.activation = Activation::NONE;
+
+ InstanceNorm kernel(&input_tensor, &gamma_tensor, &beta_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear({2, 2, 2, 2}));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 2, 1}));
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/L2Normalize.cpp b/compiler/luci-interpreter/src/kernels/L2Normalize.cpp
new file mode 100644
index 000000000..0bf133d9c
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/L2Normalize.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/L2Normalize.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+L2Normalize::L2Normalize(const Tensor *input, Tensor *output, const L2NormParams &params)
+ : KernelWithParams<L2NormParams>({input}, {output}, params)
+{
+}
+
+void L2Normalize::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->shape().num_dims() <= 4);
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::FLOAT32 ||
+ output()->element_type() == DataType::U8);
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ if (output()->element_type() == DataType::U8)
+ {
+ LUCI_INTERPRETER_CHECK(output()->scale() == (1. / 128.));
+ LUCI_INTERPRETER_CHECK(output()->zero_point() == 128);
+ }
+ LUCI_INTERPRETER_CHECK(params().activation == Activation::NONE);
+ output()->resize(input()->shape());
+}
+
+void L2Normalize::execute() const
+{
+ switch (output()->element_type())
+ {
+ case DataType::FLOAT32:
+ eval<float>(0);
+ break;
+ case DataType::U8:
+ eval<uint8_t>(input()->zero_point());
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+template <typename T> void L2Normalize::eval(int32_t zero_point) const
+{
+ tflite::L2NormalizationParams op_params{};
+ op_params.input_zero_point = zero_point;
+ tflite::optimized_ops::L2Normalization(op_params, getTensorShape(input()),
+ getTensorData<T>(input()), getTensorShape(output()),
+ getTensorData<T>(output()));
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/L2Normalize.h b/compiler/luci-interpreter/src/kernels/L2Normalize.h
new file mode 100644
index 000000000..6c7dac698
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/L2Normalize.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_L2NORMALIZE_H
+#define LUCI_INTERPRETER_KERNELS_L2NORMALIZE_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class L2Normalize : public KernelWithParams<L2NormParams>
+{
+public:
+ L2Normalize(const Tensor *input, Tensor *output, const L2NormParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ template <typename T> void eval(int32_t zero_point) const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_L2NORMALIZE_H
diff --git a/compiler/luci-interpreter/src/kernels/L2Normalize.test.cpp b/compiler/luci-interpreter/src/kernels/L2Normalize.test.cpp
new file mode 100644
index 000000000..8f9431182
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/L2Normalize.test.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "kernels/L2Normalize.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T>
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data, std::initializer_list<float> output_data)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ L2NormParams params{};
+ params.activation = Activation::NONE;
+
+ L2Normalize kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+template <>
+void Check<uint8_t>(std::initializer_list<int32_t> input_shape,
+ std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data,
+ std::initializer_list<float> output_data)
+{
+ std::pair<float, int32_t> quant_param =
+ quantizationParams<uint8_t>(std::min(input_data) < 0 ? std::min(input_data) : 0.f,
+ std::max(input_data) > 0 ? std::max(input_data) : 0.f);
+
+ Tensor input_tensor =
+ makeInputTensor<DataType::U8>(input_shape, quant_param.first, quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 128., 128);
+
+ L2NormParams params{};
+ params.activation = Activation::NONE;
+
+ L2Normalize kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data, output_tensor.scale()));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+template <typename T> class L2NormalizeTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(L2NormalizeTest, DataTypes);
+
+TYPED_TEST(L2NormalizeTest, Simple)
+{
+ Check<TypeParam>({1, 1, 1, 6}, {1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1},
+ {-0.55, 0.3, 0.35, 0.6, -0.35, 0.05});
+}
+
+TEST(L2NormalizeTest, ActivationType_NEG)
+{
+ std::vector<float> input_data = {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1};
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ L2NormParams params{};
+ params.activation = Activation::RELU6;
+
+ L2Normalize kernel(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(L2NormalizeTest, InvalidOutputQuantParam_NEG)
+{
+ std::vector<float> input_data = {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1};
+
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 1, 1, 6}, 1. / 64., 127, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 64., 127);
+
+ L2NormParams params{};
+ params.activation = Activation::NONE;
+
+ L2Normalize kernel(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/L2Pool2D.cpp b/compiler/luci-interpreter/src/kernels/L2Pool2D.cpp
new file mode 100644
index 000000000..979364a7f
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/L2Pool2D.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/L2Pool2D.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+L2Pool2D::L2Pool2D(const Tensor *input, Tensor *output, const Pool2DParams &params)
+ : KernelWithParams<Pool2DParams>({input}, {output}, params)
+{
+}
+
+void L2Pool2D::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->shape().num_dims() == 4);
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+
+ int batches = input()->shape().dim(0);
+ int height = input()->shape().dim(1);
+ int width = input()->shape().dim(2);
+ int channels_out = input()->shape().dim(3);
+
+ // Matching GetWindowedOutputSize in TensorFlow.
+ auto padding = params().padding;
+ int out_width, out_height;
+ out_width = computeOutputSize(padding, width, params().filter_width, params().stride_width, 1);
+ out_height =
+ computeOutputSize(padding, height, params().filter_height, params().stride_height, 1);
+ _padding_width =
+ computePadding(params().stride_width, 1, width, params().filter_width, out_width);
+ _padding_height =
+ computePadding(params().stride_height, 1, height, params().filter_height, out_height);
+
+ LUCI_INTERPRETER_CHECK(input()->element_type() == DataType::FLOAT32);
+ output()->resize({batches, out_height, out_width, channels_out});
+}
+
+void L2Pool2D::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ float activation_min, activation_max;
+ calculateActivationRange(params().activation, &activation_min, &activation_max);
+ tflite::PoolParams op_params;
+ op_params.stride_height = params().stride_height;
+ op_params.stride_width = params().stride_width;
+ op_params.filter_height = params().filter_height;
+ op_params.filter_width = params().filter_width;
+ op_params.padding_values.height = _padding_height;
+ op_params.padding_values.width = _padding_width;
+ op_params.float_activation_min = activation_min;
+ op_params.float_activation_max = activation_max;
+ tflite::optimized_ops::L2Pool(op_params, getTensorShape(input()),
+ getTensorData<float>(input()), getTensorShape(output()),
+ getTensorData<float>(output()));
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/L2Pool2D.h b/compiler/luci-interpreter/src/kernels/L2Pool2D.h
new file mode 100644
index 000000000..d40f5f478
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/L2Pool2D.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_L2POOL2D_H
+#define LUCI_INTERPRETER_KERNELS_L2POOL2D_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+#include <memory>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class L2Pool2D : public KernelWithParams<Pool2DParams>
+{
+public:
+ L2Pool2D(const Tensor *input, Tensor *output, const Pool2DParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ int32_t _padding_height = 0;
+ int32_t _padding_width = 0;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_L2POOL2D_H
diff --git a/compiler/luci-interpreter/src/kernels/L2Pool2D.test.cpp b/compiler/luci-interpreter/src/kernels/L2Pool2D.test.cpp
new file mode 100644
index 000000000..5f834e3c1
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/L2Pool2D.test.cpp
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/L2Pool2D.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(L2Pool2DTest, FloatNone)
+{
+ Shape input_shape{1, 2, 4, 1};
+ std::vector<float> input_data{
+ 0, 6, 2, 4, //
+ 3, 2, 10, 7, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Pool2DParams params{};
+ params.padding = Padding::VALID;
+ params.activation = Activation::NONE;
+ params.filter_height = 2;
+ params.filter_width = 2;
+ params.stride_height = 2;
+ params.stride_width = 2;
+
+ L2Pool2D kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{3.5, 6.5};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+ // TODO make a Shape checking of output_tensor.
+}
+
+TEST(L2Pool2DTest, FloatRelu)
+{
+ Shape input_shape{1, 2, 4, 1};
+ std::vector<float> input_data{
+ -1, -6, 2, 4, //
+ -3, -2, 10, 7, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Pool2DParams params{};
+ params.padding = Padding::VALID;
+ params.activation = Activation::RELU;
+ params.filter_height = 2;
+ params.filter_width = 2;
+ params.stride_height = 2;
+ params.stride_width = 2;
+
+ L2Pool2D kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{3.53553, 6.5};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+ // TODO make a Shape checking of output_tensor.
+}
+
+TEST(L2Pool2DTest, FloatRelu1)
+{
+ Shape input_shape{1, 2, 4, 1};
+ std::vector<float> input_data{
+ -0.1, -0.6, 2, 4, //
+ -0.3, -0.2, 10, 7, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Pool2DParams params{};
+ params.padding = Padding::VALID;
+ params.activation = Activation::RELU_N1_TO_1;
+ params.filter_height = 2;
+ params.filter_width = 2;
+ params.stride_height = 2;
+ params.stride_width = 2;
+
+ L2Pool2D kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{0.353553, 1.0};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+ // TODO make a Shape checking of output_tensor.
+}
+
+TEST(L2Pool2DTest, FloatRelu6)
+{
+ Shape input_shape{1, 2, 4, 1};
+ std::vector<float> input_data{
+ -0.1, -0.6, 2, 4, //
+ -0.3, -0.2, 10, 7, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Pool2DParams params{};
+ params.padding = Padding::VALID;
+ params.activation = Activation::RELU6;
+ params.filter_height = 2;
+ params.filter_width = 2;
+ params.stride_height = 2;
+ params.stride_width = 2;
+
+ L2Pool2D kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{0.353553, 6.0};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+ // TODO make a Shape checking of output_tensor.
+}
+
+TEST(L2Pool2DTest, FloatPaddingSame)
+{
+ Shape input_shape{1, 2, 4, 1};
+ std::vector<float> input_data{
+ 0, 6, 2, 4, //
+ 3, 2, 10, 7, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Pool2DParams params{};
+ params.padding = Padding::SAME;
+ params.activation = Activation::NONE;
+ params.filter_height = 2;
+ params.filter_width = 2;
+ params.stride_height = 2;
+ params.stride_width = 2;
+
+ L2Pool2D kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{3.5, 6.5};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+ // TODO make a Shape checking of output_tensor.
+}
+
+TEST(L2Pool2DTest, FloatPaddingSameStride)
+{
+ Shape input_shape{1, 2, 4, 1};
+ std::vector<float> input_data{
+ 0, 6, 2, 4, //
+ 3, 2, 10, 7, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Pool2DParams params{};
+ params.padding = Padding::SAME;
+ params.activation = Activation::NONE;
+ params.filter_height = 2;
+ params.filter_width = 2;
+ params.stride_height = 1;
+ params.stride_width = 1;
+
+ L2Pool2D kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{3.5, 6.0, 6.5, 5.70088, 2.54951, 7.2111, 8.63134, 7.0};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+ // TODO make a Shape checking of output_tensor.
+}
+
+TEST(L2Pool2DTest, FloatPaddingValidStride)
+{
+ Shape input_shape{1, 2, 4, 1};
+ std::vector<float> input_data{
+ 0, 6, 2, 4, //
+ 3, 2, 10, 7, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Pool2DParams params{};
+ params.padding = Padding::VALID;
+ params.activation = Activation::NONE;
+ params.filter_height = 2;
+ params.filter_width = 2;
+ params.stride_height = 1;
+ params.stride_width = 1;
+
+ L2Pool2D kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{3.5, 6.0, 6.5};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+ // TODO make a Shape checking of output_tensor.
+}
+
+TEST(L2Pool2DTest, InvalidInputShape_NEG)
+{
+ Shape input_shape{1, 2, 4};
+ std::vector<float> input_data{
+ 0, 6, 2, 4, //
+ 3, 2, 10, 7, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Pool2DParams params{};
+ params.padding = Padding::VALID;
+ params.activation = Activation::NONE;
+ params.filter_height = 2;
+ params.filter_width = 2;
+ params.stride_height = 1;
+ params.stride_width = 1;
+
+ L2Pool2D kernel(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(L2Pool2DTest, InvalidInputOutputType_NEG)
+{
+ Shape input_shape{1, 2, 4};
+ std::vector<float> input_data{
+ 0, 6, 2, 4, //
+ 3, 2, 10, 7, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ Pool2DParams params{};
+ params.padding = Padding::VALID;
+ params.activation = Activation::NONE;
+ params.filter_height = 2;
+ params.filter_width = 2;
+ params.stride_height = 1;
+ params.stride_width = 1;
+
+ L2Pool2D kernel(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LeakyRelu.cpp b/compiler/luci-interpreter/src/kernels/LeakyRelu.cpp
new file mode 100644
index 000000000..919b12792
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LeakyRelu.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/LeakyRelu.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+LeakyRelu::LeakyRelu(const Tensor *input, Tensor *output, const LeakyReluParams &params)
+ : KernelWithParams<LeakyReluParams>({input}, {output}, params)
+{
+}
+
+void LeakyRelu::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ if (input()->element_type() == DataType::U8)
+ {
+ double alpha_multiplier = input()->scale() * params().alpha / output()->scale();
+ quantizeMultiplier(alpha_multiplier, &_output_multiplier_alpha, &_output_shift_alpha);
+ double identity_multiplier = input()->scale() / output()->scale();
+ quantizeMultiplier(identity_multiplier, &_output_multiplier_identity, &_output_shift_identity);
+ }
+ output()->resize(input()->shape());
+}
+
+void LeakyRelu::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void LeakyRelu::evalFloat() const
+{
+ tflite::LeakyReluParams op_params{};
+ op_params.alpha = params().alpha;
+ tflite::optimized_ops::LeakyRelu(op_params, getTensorShape(input()),
+ getTensorData<float>(input()), getTensorShape(output()),
+ getTensorData<float>(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<uint8_t>(input()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LeakyRelu.h b/compiler/luci-interpreter/src/kernels/LeakyRelu.h
new file mode 100644
index 000000000..e66f404df
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LeakyRelu.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_LEAKYRELU_H
+#define LUCI_INTERPRETER_KERNELS_LEAKYRELU_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class LeakyRelu : public KernelWithParams<LeakyReluParams>
+{
+public:
+ LeakyRelu(const Tensor *input, Tensor *output, const LeakyReluParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+
+private:
+ int32_t _output_multiplier_alpha = 0;
+ int _output_shift_alpha = 0;
+ int32_t _output_multiplier_identity = 0;
+ int _output_shift_identity = 0;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_LEAKYRELU_H
diff --git a/compiler/luci-interpreter/src/kernels/LeakyRelu.test.cpp b/compiler/luci-interpreter/src/kernels/LeakyRelu.test.cpp
new file mode 100644
index 000000000..2778549ed
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LeakyRelu.test.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/LeakyRelu.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T>
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data, std::initializer_list<float> output_data,
+ float alpha)
+{
+ constexpr DataType element_type = getElementType<T>();
+ Tensor input_tensor = makeInputTensor<element_type>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(element_type);
+
+ LeakyReluParams params{};
+ params.alpha = alpha;
+
+ LeakyRelu kernel(&input_tensor, &output_tensor, params);
+
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+ EXPECT_THAT(extractTensorData<T>(output_tensor), ::testing::ElementsAreArray(output_data));
+}
+
+template <>
+void Check<uint8_t>(std::initializer_list<int32_t> input_shape,
+ std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data,
+ std::initializer_list<float> output_data, float alpha)
+{
+ const float quantized_tolerance = getTolerance(-8, 127.f / 16.f, 255);
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-8, 127.f / 16.f);
+ Tensor input_tensor =
+ makeInputTensor<DataType::U8>(input_shape, quant_param.first, quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
+
+ LeakyReluParams params{};
+ params.alpha = alpha;
+
+ LeakyRelu kernel(&input_tensor, &output_tensor, params);
+
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data, quantized_tolerance));
+}
+
+template <typename T> class LeakReluTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(LeakReluTest, DataTypes);
+
+TYPED_TEST(LeakReluTest, Simple)
+{
+ Check<TypeParam>(/*input_shape=*/{2, 3}, /*output_shape=*/{2, 3},
+ /*input_data=*/
+ {
+ 0.0f, 1.0f, 3.0f, // Row 1
+ 1.0f, -1.0f, -2.0f, // Row 2
+ },
+ /*output_data=*/
+ {
+ 0.0f, 1.0f, 3.0f, // Row 1
+ 1.0f, -0.5f, -1.0f, // Row 2
+ },
+ /*alpha=*/0.5f);
+
+ SUCCEED();
+}
+
+TEST(LeakReluTest, IvalidInputOutputType_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, {
+ 0.0f, 1.0f, 3.0f, // Row 1
+ 1.0f, -1.0f, -2.0f, // Row 2
+ });
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ LeakyReluParams params{};
+ params.alpha = 0.5f;
+
+ LeakyRelu kernel(&input_tensor, &output_tensor, params);
+
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Less.cpp b/compiler/luci-interpreter/src/kernels/Less.cpp
new file mode 100644
index 000000000..041444926
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Less.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Less.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/comparisons.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Less::Less(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {}
+
+void Less::configure()
+{
+ LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type());
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL);
+
+ if (x()->element_type() == DataType::U8)
+ {
+ quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift);
+ quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift);
+ }
+ output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape()));
+}
+
+void Less::execute() const
+{
+ switch (x()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Less::evalFloat() const
+{
+ const auto x_data = getTensorData<float>(x());
+ const auto y_data = getTensorData<float>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowLess(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::Less(op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+}
+
+void Less::evalQuantized() const
+{
+ const auto x_data = getTensorData<uint8_t>(x());
+ const auto y_data = getTensorData<uint8_t>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.left_shift = 8;
+ op_params.input1_offset = -x()->zero_point(); // Note the '-'
+ op_params.input1_shift = _x_shift;
+ op_params.input1_multiplier = _x_multiplier;
+ op_params.input2_offset = -y()->zero_point(); // Note the '-'
+ op_params.input2_shift = _y_shift;
+ op_params.input2_multiplier = _y_multiplier;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowLessWithScaling(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::LessWithScaling(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data, getTensorShape(output()),
+ output_data);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Less.h b/compiler/luci-interpreter/src/kernels/Less.h
new file mode 100644
index 000000000..fe03e10b1
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Less.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_LESS_H
+#define LUCI_INTERPRETER_KERNELS_LESS_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Less : public Kernel
+{
+public:
+ Less(const Tensor *x, const Tensor *y, Tensor *output);
+
+ const Tensor *x() const { return _inputs[0]; }
+ const Tensor *y() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+
+private:
+ int32_t _x_multiplier = 0;
+ int32_t _x_shift = 0;
+ int32_t _y_multiplier = 0;
+ int32_t _y_shift = 0;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_LESS_H
diff --git a/compiler/luci-interpreter/src/kernels/Less.test.cpp b/compiler/luci-interpreter/src/kernels/Less.test.cpp
new file mode 100644
index 000000000..73aa30b36
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Less.test.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Less.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(LessTest, FloatSimple)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ -1, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ true, false, false, // Row 1
+ false, false, true, // Row 2
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Less kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3}));
+}
+
+TEST(LessTest, FloatBroardcast)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ -1, 0, 1, // Row 3
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ true, false, false, // Row 1
+ false, true, true, // Row 2
+ true, true, false, // Row 3
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({3, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Less kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3, 3}));
+}
+
+// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+const float F_MIN = -128.0 / 128.0;
+const float F_MAX = 127.0 / 128.0;
+
+TEST(LessTest, Uint8Quantized)
+{
+ std::vector<float> x_data{
+ 0.5, 0.6, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.6, 0.55, 0.5, // Row 1
+ -1, 0.05, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ true, false, false, false, // Row 1
+ false, true, false, true, // Row 2
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Less kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(LessTest, Uint8QuantizedRescale)
+{
+ std::vector<float> x_data{
+ 0.5, 0.6, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.6, 0.6, 0.5, // Row 1
+ -1, 0.05, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ true, false, false, false, // Row 1
+ false, true, false, true, // Row 2
+ };
+
+ std::pair<float, int32_t> x_quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ std::pair<float, int32_t> y_quant_param = quantizationParams<uint8_t>(F_MIN * 1.2, F_MAX * 1.5);
+
+ Tensor x_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first,
+ x_quant_param.second, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first,
+ y_quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Less kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(LessTest, Uint8QuantizedBroadcast)
+{
+ std::vector<float> x_data{
+ 0.4, -0.8, 0.7, 0.3, // Row 1
+ -0.5, 0.1, 0, 0.5, // Row 2
+ 1, 0, 0.05, -1, // Row 3
+ };
+
+ std::vector<float> y_data{
+ -1, 0.05, 0, 1, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ false, true, false, true, // Row 1
+ false, false, false, true, // Row 2
+ false, true, false, true, // Row 3
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 3, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Less kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 3, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(LessTest, Input_Type_Mismatch_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Less kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(LessTest, Input_Output_Type_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Less kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LessEqual.cpp b/compiler/luci-interpreter/src/kernels/LessEqual.cpp
new file mode 100644
index 000000000..b8aaba178
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LessEqual.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/LessEqual.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/comparisons.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+LessEqual::LessEqual(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {}
+
+void LessEqual::configure()
+{
+ LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type());
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL);
+
+ if (x()->element_type() == DataType::U8)
+ {
+ quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift);
+ quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift);
+ }
+ output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape()));
+}
+
+void LessEqual::execute() const
+{
+ switch (x()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void LessEqual::evalFloat() const
+{
+ const auto x_data = getTensorData<float>(x());
+ const auto y_data = getTensorData<float>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowLessEqual(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::LessEqual(op_params, getTensorShape(x()), x_data, getTensorShape(y()),
+ y_data, getTensorShape(output()), output_data);
+ }
+}
+
+void LessEqual::evalQuantized() const
+{
+ const auto x_data = getTensorData<uint8_t>(x());
+ const auto y_data = getTensorData<uint8_t>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.left_shift = 8;
+ op_params.input1_offset = -x()->zero_point(); // Note the '-'
+ op_params.input1_shift = _x_shift;
+ op_params.input1_multiplier = _x_multiplier;
+ op_params.input2_offset = -y()->zero_point(); // Note the '-'
+ op_params.input2_shift = _y_shift;
+ op_params.input2_multiplier = _y_multiplier;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowLessEqualWithScaling(
+ op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::LessEqualWithScaling(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LessEqual.h b/compiler/luci-interpreter/src/kernels/LessEqual.h
new file mode 100644
index 000000000..ed4b0f1ea
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LessEqual.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_LESS_EQUAL_H
+#define LUCI_INTERPRETER_KERNELS_LESS_EQUAL_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class LessEqual : public Kernel
+{
+public:
+ LessEqual(const Tensor *x, const Tensor *y, Tensor *output);
+
+ const Tensor *x() const { return _inputs[0]; }
+ const Tensor *y() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+
+private:
+ int32_t _x_multiplier = 0;
+ int32_t _x_shift = 0;
+ int32_t _y_multiplier = 0;
+ int32_t _y_shift = 0;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_LESS_EQUAL_H
diff --git a/compiler/luci-interpreter/src/kernels/LessEqual.test.cpp b/compiler/luci-interpreter/src/kernels/LessEqual.test.cpp
new file mode 100644
index 000000000..9184c061f
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LessEqual.test.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/LessEqual.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(LessEqualTest, FloatSimple)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ -1, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ true, true, false, // Row 1
+ false, true, true, // Row 2
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LessEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3}));
+}
+
+TEST(LessEqualTest, FloatBroardcast)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ -1, 0, 1, // Row 3
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ true, true, false, // Row 1
+ false, true, true, // Row 2
+ true, true, false, // Row 3
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({3, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LessEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({3, 3}));
+}
+
+// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+const float F_MIN = -128.0 / 128.0;
+const float F_MAX = 127.0 / 128.0;
+
+TEST(LessEqualTest, Uint8Quantized)
+{
+ std::vector<float> x_data{
+ 0.5, 0.6, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.6, 0.55, 0.5, // Row 1
+ -1, 0.05, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ true, true, false, false, // Row 1
+ false, true, false, true, // Row 2
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LessEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(LessEqualTest, Uint8QuantizedRescale)
+{
+ std::vector<float> x_data{
+ 0.5, 0.6, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.6, 0.6, 0.5, // Row 1
+ -1, 0.05, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ true, true, false, false, // Row 1
+ false, true, false, true, // Row 2
+ };
+
+ std::pair<float, int32_t> x_quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ std::pair<float, int32_t> y_quant_param = quantizationParams<uint8_t>(F_MIN * 1.2, F_MAX * 1.5);
+
+ Tensor x_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first,
+ x_quant_param.second, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first,
+ y_quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LessEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(LessEqualTest, Uint8QuantizedBroadcast)
+{
+ std::vector<float> x_data{
+ 0.4, -0.8, 0.7, 0.3, // Row 1
+ -0.5, 0.1, 0, 0.5, // Row 2
+ 1, 0, 0.05, -1, // Row 3
+ };
+
+ std::vector<float> y_data{
+ -1, 0.05, 0, 1, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ false, true, false, true, // Row 1
+ false, false, true, true, // Row 2
+ false, true, false, true, // Row 3
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 3, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LessEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 3, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(LessEqualTest, Input_Type_Mismatch_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LessEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(LessEqualTest, Input_Output_Type_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ LessEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.cpp b/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.cpp
new file mode 100644
index 000000000..b78e27128
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/LocalResponseNormalization.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+LocalResponseNormalization::LocalResponseNormalization(
+ const Tensor *input, Tensor *output, const LocalResponseNormalizationParams &params)
+ : KernelWithParams<LocalResponseNormalizationParams>({input}, {output}, params)
+{
+}
+
+void LocalResponseNormalization::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->shape().num_dims() == 4);
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::FLOAT32);
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ output()->resize(input()->shape());
+}
+
+void LocalResponseNormalization::execute() const
+{
+ switch (output()->element_type())
+ {
+ case DataType::FLOAT32:
+ tflite::LocalResponseNormalizationParams op_params;
+ op_params.range = params().radius;
+ op_params.bias = params().bias;
+ op_params.alpha = params().alpha;
+ op_params.beta = params().beta;
+ tflite::optimized_ops::LocalResponseNormalization(
+ op_params, getTensorShape(input()), getTensorData<float>(input()),
+ getTensorShape(output()), getTensorData<float>(output()));
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.h b/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.h
new file mode 100644
index 000000000..60408a104
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_LOCALRESPONSENORMALIZATION_H
+#define LUCI_INTERPRETER_KERNELS_LOCALRESPONSENORMALIZATION_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class LocalResponseNormalization : public KernelWithParams<LocalResponseNormalizationParams>
+{
+public:
+ LocalResponseNormalization(const Tensor *input, Tensor *output,
+ const LocalResponseNormalizationParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_LOCALRESPONSENORMALIZATION_H
diff --git a/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.test.cpp b/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.test.cpp
new file mode 100644
index 000000000..d98305c1a
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.test.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/LocalResponseNormalization.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(LocalResponseNormalizationTest, SameAsL2Norm)
+{
+ Tensor input_tensor =
+ makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ LocalResponseNormalizationParams params{};
+ params.radius = 20;
+ params.bias = 0.0;
+ params.alpha = 1.0;
+ params.beta = 0.5;
+
+ LocalResponseNormalization kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor),
+ FloatArrayNear({-0.55, 0.3, 0.35, 0.6, -0.35, 0.05}));
+}
+
+TEST(LocalResponseNormalizationTest, WithAlpha)
+{
+ Tensor input_tensor =
+ makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ LocalResponseNormalizationParams params{};
+ params.radius = 20;
+ params.bias = 0.0;
+ params.alpha = 4.0;
+ params.beta = 0.5;
+
+ LocalResponseNormalization kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor),
+ FloatArrayNear({-0.275, 0.15, 0.175, 0.3, -0.175, 0.025}));
+}
+
+TEST(LocalResponseNormalizationTest, WithBias)
+{
+ Tensor input_tensor =
+ makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ LocalResponseNormalizationParams params{};
+ params.radius = 20;
+ params.bias = 9.0;
+ params.alpha = 4.0;
+ params.beta = 0.5;
+
+ LocalResponseNormalization kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor),
+ FloatArrayNear({-0.22, 0.12, 0.14, 0.24, -0.14, 0.02}));
+}
+
+TEST(LocalResponseNormalizationTest, SmallRadius)
+{
+ Tensor input_tensor =
+ makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ LocalResponseNormalizationParams params{};
+ params.radius = 2;
+ params.bias = 9.0;
+ params.alpha = 4.0;
+ params.beta = 0.5;
+
+ LocalResponseNormalization kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor),
+ FloatArrayNear({-0.264926, 0.125109, 0.140112, 0.267261, -0.161788, 0.0244266}));
+}
+
+TEST(LocalResponseNormalizationTest, InvalidInputDimension_NEG)
+{
+ Tensor input_tensor =
+ makeInputTensor<DataType::FLOAT32>({1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ LocalResponseNormalizationParams params{};
+ params.radius = 20;
+ params.bias = 0.0;
+ params.alpha = 1.0;
+ params.beta = 0.5;
+
+ LocalResponseNormalization kernel(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(LocalResponseNormalizationTest, InvalidInputOutputType_NEG)
+{
+ Tensor input_tensor =
+ makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1});
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ LocalResponseNormalizationParams params{};
+ params.radius = 20;
+ params.bias = 0.0;
+ params.alpha = 1.0;
+ params.beta = 0.5;
+
+ LocalResponseNormalization kernel(&input_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LogSoftmax.cpp b/compiler/luci-interpreter/src/kernels/LogSoftmax.cpp
new file mode 100644
index 000000000..03d13e4ce
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LogSoftmax.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/LogSoftmax.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+LogSoftmax::LogSoftmax(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {}
+
+void LogSoftmax::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ if (input()->element_type() == DataType::U8)
+ {
+ LUCI_INTERPRETER_CHECK(output()->scale() == 16. / 256);
+ LUCI_INTERPRETER_CHECK(output()->zero_point() == 255);
+
+ tflite::SoftmaxParams params{};
+
+ params.table = _table;
+ params.beta = 1.0;
+
+ tflite::optimized_ops::PopulateSoftmaxLookupTable(&params, input()->scale(), params.beta);
+ }
+ output()->resize(input()->shape());
+}
+
+void LogSoftmax::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void LogSoftmax::evalFloat() const
+{
+ tflite::SoftmaxParams params{};
+ tflite::reference_ops::LogSoftmax(params, getTensorShape(input()), getTensorData<float>(input()),
+ getTensorShape(output()), getTensorData<float>(output()));
+}
+
+void LogSoftmax::evalQuantized() const
+{
+ const auto input_shape = getTensorShape(input());
+ const auto output_shape = getTensorShape(output());
+ const auto input_scale = input()->scale();
+ uint8_t *output_data = getTensorData<uint8_t>(output());
+ const uint8_t *input_data = getTensorData<uint8_t>(input());
+
+ tflite::SoftmaxParams params{};
+
+ params.table = const_cast<float *>(_table);
+ params.zero_point = output()->zero_point();
+ params.scale = output()->scale();
+
+ tflite::optimized_ops::LogSoftmax(params, input_scale, input_shape, input_data, output_shape,
+ output_data);
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LogSoftmax.h b/compiler/luci-interpreter/src/kernels/LogSoftmax.h
new file mode 100644
index 000000000..18477fbe3
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LogSoftmax.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_LOGSOFTMAX_H
+#define LUCI_INTERPRETER_KERNELS_LOGSOFTMAX_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class LogSoftmax : public Kernel
+{
+public:
+ LogSoftmax(const Tensor *input, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+
+ float _table[256];
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_LOGSOFTMAX_H
diff --git a/compiler/luci-interpreter/src/kernels/LogSoftmax.test.cpp b/compiler/luci-interpreter/src/kernels/LogSoftmax.test.cpp
new file mode 100644
index 000000000..d3b331dfe
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LogSoftmax.test.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/LogSoftmax.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(LogSoftmaxTest, Float)
+{
+ Shape input_shape{2, 4};
+ std::vector<float> input_data{
+ 0, -6, 2, 4, //
+ 3, -2, 10, 1, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ LogSoftmax kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{
+ -4.14297, -10.14297, -2.14297, -.142971, //
+ -7.00104, -12.00104, -.00104087, -9.00104, //
+ };
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(LogSoftmaxTest, Uint8)
+{
+ float kMin = -10;
+ float kMax = 10;
+ float kLogSoftmaxQuantizedTolerance = 16. / 256;
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(kMin, kMax);
+ std::vector<float> input_data{
+ 0, -6, 2, 4, //
+ 3, -2, 10, 1, //
+ };
+ Tensor input_tensor =
+ makeInputTensor<DataType::U8>({2, 4}, quant_param.first, quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 16. / 256, 255);
+
+ LogSoftmax kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{
+ -4.14297, -10.14297, -2.14297, -.142971, //
+ -7.00104, -12.00104, -.00104087, -9.00104, //
+ };
+ std::vector<int32_t> ref_output_shape{2, 4};
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(ref_output_data, kLogSoftmaxQuantizedTolerance));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+ EXPECT_THAT(extractTensorData<uint8_t>(output_tensor),
+ ::testing::ElementsAreArray({189, 93, 221, 253, 142, 63, 255, 111}));
+}
+
+TEST(LogSoftmaxTest, InvalidInputOutputType_NEG)
+{
+ std::vector<float> input_data{
+ 0, -6, 2, 4, //
+ 3, -2, 10, 1, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 4}, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 16. / 256, 255);
+
+ LogSoftmax kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(LogSoftmaxTest, InvalidOutputQuantParam_NEG)
+{
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-10, 10);
+ std::vector<float> input_data{
+ 0, -6, 2, 4, //
+ 3, -2, 10, 1, //
+ };
+ Tensor input_tensor =
+ makeInputTensor<DataType::U8>({2, 4}, quant_param.first, quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 20. / 256, 255);
+
+ LogSoftmax kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LogicalAnd.cpp b/compiler/luci-interpreter/src/kernels/LogicalAnd.cpp
new file mode 100644
index 000000000..d50d50472
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LogicalAnd.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/LogicalAnd.h"
+
+#include "kernels/Utils.h"
+
+#include "kernels/BinaryOpCommon.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+LogicalAnd::LogicalAnd(const Tensor *input1, const Tensor *input2, Tensor *output)
+ : Kernel({input1, input2}, {output})
+{
+}
+
+void LogicalAnd::configure()
+{
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type());
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type());
+ output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape()));
+}
+
+void LogicalAnd::execute() const
+{
+ switch (input1()->element_type())
+ {
+ case DataType::BOOL:
+ evalLogicalAnd();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+inline void LogicalAnd::evalLogicalAnd() const
+{
+ BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData<bool>(input1()),
+ getTensorShape(input2()), getTensorData<bool>(input2()),
+ getTensorShape(output()), getTensorData<bool>(output()),
+ [](bool x, bool y) { return x && y; });
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LogicalAnd.h b/compiler/luci-interpreter/src/kernels/LogicalAnd.h
new file mode 100644
index 000000000..46b889986
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LogicalAnd.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_LOGICALAND_H
+#define LUCI_INTERPRETER_KERNELS_LOGICALAND_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class LogicalAnd : public Kernel
+{
+public:
+ LogicalAnd(const Tensor *input1, const Tensor *input2, Tensor *output);
+
+ const Tensor *input1() const { return _inputs[0]; }
+ const Tensor *input2() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ inline void evalLogicalAnd() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_LOGICALAND_H
diff --git a/compiler/luci-interpreter/src/kernels/LogicalAnd.test.cpp b/compiler/luci-interpreter/src/kernels/LogicalAnd.test.cpp
new file mode 100644
index 000000000..564f191d5
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LogicalAnd.test.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/LogicalAnd.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(LogicalAndTest, Basic)
+{
+ Shape input_shape{1, 1, 1, 4};
+ Tensor input_tensor1 = makeInputTensor<DataType::BOOL>(input_shape, {true, false, false, true});
+ Tensor input_tensor2 = makeInputTensor<DataType::BOOL>(input_shape, {true, false, true, false});
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LogicalAnd kernel(&input_tensor1, &input_tensor2, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor),
+ ::testing::ElementsAre(true, false, false, false));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAre(1, 1, 1, 4));
+}
+
+TEST(LogicalAndTest, Broadcast)
+{
+ Tensor input_tensor1 = makeInputTensor<DataType::BOOL>({1, 1, 1, 4}, {true, false, false, true});
+ Tensor input_tensor2 = makeInputTensor<DataType::BOOL>({1, 1, 1, 1}, {true});
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LogicalAnd kernel(&input_tensor1, &input_tensor2, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor),
+ ::testing::ElementsAre(true, false, false, true));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAre(1, 1, 1, 4));
+}
+
+TEST(LogicalAndTest, MismatchInputType_NEG)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::S32>({1, 1, 1, 4}, {1, 0, 0, 1});
+ Tensor input2_tensor = makeInputTensor<DataType::BOOL>({1, 1, 1, 1}, {false});
+ Tensor output_tensor = makeOutputTensor(DataType::S32);
+
+ LogicalAnd kernel(&input1_tensor, &input2_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(LogicalAndTest, InputTypeInvalid_NEG)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::S32>({1, 1, 1, 4}, {1, 0, 0, 1});
+ Tensor input2_tensor = makeInputTensor<DataType::S32>({1, 1, 1, 1}, {0});
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LogicalAnd kernel(&input1_tensor, &input2_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LogicalNot.cpp b/compiler/luci-interpreter/src/kernels/LogicalNot.cpp
new file mode 100644
index 000000000..65ab961aa
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LogicalNot.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/LogicalNot.h"
+
+#include "kernels/Utils.h"
+
+#include "kernels/BinaryOpCommon.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+LogicalNot::LogicalNot(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {}
+
+void LogicalNot::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ output()->resize(input()->shape());
+}
+
+void LogicalNot::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::BOOL:
+ evalLogicalNot();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+inline void LogicalNot::evalLogicalNot() const
+{
+ const int size = tflite::MatchingFlatSize(getTensorShape(input()), getTensorShape(output()));
+ bool *output_data = getTensorData<bool>(output());
+ const bool *input_data = getTensorData<bool>(input());
+ for (int i = 0; i < size; ++i)
+ {
+ output_data[i] = !input_data[i];
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LogicalNot.h b/compiler/luci-interpreter/src/kernels/LogicalNot.h
new file mode 100644
index 000000000..1608fafa5
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LogicalNot.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_LOGICALNOT_H
+#define LUCI_INTERPRETER_KERNELS_LOGICALNOT_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class LogicalNot : public Kernel
+{
+public:
+ LogicalNot(const Tensor *input, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ inline void evalLogicalNot() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_LOGICALNOT_H
diff --git a/compiler/luci-interpreter/src/kernels/LogicalNot.test.cpp b/compiler/luci-interpreter/src/kernels/LogicalNot.test.cpp
new file mode 100644
index 000000000..dccb81102
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LogicalNot.test.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/LogicalNot.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(LogicalNotTest, Basic)
+{
+ Shape input_shape{1, 1, 1, 4};
+ Tensor input_tensor = makeInputTensor<DataType::BOOL>(input_shape, {true, false, false, true});
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LogicalNot kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor),
+ ::testing::ElementsAre(false, true, true, false));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAre(1, 1, 1, 4));
+}
+
+TEST(LogicalNotTest, OutputTypeInvalid_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::BOOL>({1, 1, 1, 4}, {true, false, false, true});
+ Tensor output_tensor = makeOutputTensor(DataType::S32);
+
+ LogicalNot kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(LogicalNotTest, InputTypeInvalid_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::S32>({1, 1, 1, 4}, {1, 0, 0, 1});
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LogicalNot kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LogicalOr.cpp b/compiler/luci-interpreter/src/kernels/LogicalOr.cpp
new file mode 100644
index 000000000..bd2208a4b
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LogicalOr.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/LogicalOr.h"
+
+#include "kernels/Utils.h"
+#include "kernels/BinaryOpCommon.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+LogicalOr::LogicalOr(const Tensor *input1, const Tensor *input2, Tensor *output)
+ : Kernel({input1, input2}, {output})
+{
+}
+
+void LogicalOr::configure()
+{
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type());
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == DataType::BOOL);
+ output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape()));
+}
+
+void LogicalOr::execute() const
+{
+ BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData<bool>(input1()),
+ getTensorShape(input2()), getTensorData<bool>(input2()),
+ getTensorShape(output()), getTensorData<bool>(output()),
+ [](bool x, bool y) { return x || y; });
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/LogicalOr.h b/compiler/luci-interpreter/src/kernels/LogicalOr.h
new file mode 100644
index 000000000..88606483f
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LogicalOr.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_LOGICALOR_H
+#define LUCI_INTERPRETER_KERNELS_LOGICALOR_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class LogicalOr : public Kernel
+{
+public:
+ LogicalOr(const Tensor *input1, const Tensor *input2, Tensor *output);
+
+ const Tensor *input1() const { return _inputs[0]; }
+ const Tensor *input2() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_LOGICALOR_H
diff --git a/compiler/luci-interpreter/src/kernels/LogicalOr.test.cpp b/compiler/luci-interpreter/src/kernels/LogicalOr.test.cpp
new file mode 100644
index 000000000..677eac96a
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/LogicalOr.test.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/LogicalOr.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(LogicalOrTest, Basic)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::BOOL>({1, 1, 1, 4}, {true, false, false, true});
+ Tensor input2_tensor = makeInputTensor<DataType::BOOL>({1, 1, 1, 4}, {true, false, true, false});
+
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LogicalOr kernel(&input1_tensor, &input2_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor),
+ ::testing::ElementsAre(true, false, true, true));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAre(1, 1, 1, 4));
+}
+
+TEST(LogicalOrTest, Broadcast)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::BOOL>({1, 1, 1, 4}, {true, false, false, true});
+ Tensor input2_tensor = makeInputTensor<DataType::BOOL>({1, 1, 1, 1}, {false});
+
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LogicalOr kernel(&input1_tensor, &input2_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor),
+ ::testing::ElementsAre(true, false, false, true));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAre(1, 1, 1, 4));
+}
+
+TEST(LogicalOrTest, MismatchInputType_NEG)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::S32>({1, 1, 1, 4}, {1, 0, 0, 1});
+ Tensor input2_tensor = makeInputTensor<DataType::BOOL>({1, 1, 1, 1}, {false});
+
+ Tensor output_tensor = makeOutputTensor(DataType::S32);
+
+ LogicalOr kernel(&input1_tensor, &input2_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(LogicalOrTest, InputTypeInvalid_NEG)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::S32>({1, 1, 1, 4}, {1, 0, 0, 1});
+ Tensor input2_tensor = makeInputTensor<DataType::S32>({1, 1, 1, 1}, {0});
+
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ LogicalOr kernel(&input1_tensor, &input2_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Logistic.cpp b/compiler/luci-interpreter/src/kernels/Logistic.cpp
new file mode 100644
index 000000000..97d7bf13d
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Logistic.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Logistic.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Logistic::Logistic(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {}
+
+void Logistic::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ if (input()->element_type() == DataType::U8)
+ {
+ LUCI_INTERPRETER_CHECK(output()->scale() == 1. / 256);
+ populateLookupTable();
+ }
+ output()->resize(input()->shape());
+}
+
+void Logistic::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Logistic::evalFloat() const
+{
+ tflite::reference_ops::Logistic(getTensorShape(input()), getTensorData<float>(input()),
+ getTensorShape(output()), getTensorData<float>(output()));
+}
+
+void Logistic::evalQuantized() const
+{
+ const int size = tflite::MatchingFlatSize(getTensorShape(input()), getTensorShape(output()));
+ uint8_t *output_data = getTensorData<uint8_t>(output());
+ const uint8_t *input_data = getTensorData<uint8_t>(input());
+ for (int i = 0; i < size; ++i)
+ {
+ output_data[i] = getTableValue(input_data[i]);
+ }
+}
+
+void Logistic::populateLookupTable()
+{
+ const auto input_scale = static_cast<double>(input()->scale());
+ const auto input_zero_point = static_cast<int32_t>(input()->zero_point());
+ const auto output_scale = static_cast<double>(output()->scale());
+ const auto output_zero_point = static_cast<int32_t>(output()->zero_point());
+ const float inverse_scale = 1 / output_scale;
+ int32_t maxval = std::numeric_limits<uint8_t>::max();
+ int32_t minval = std::numeric_limits<uint8_t>::min();
+ for (int32_t val = minval; val <= maxval; ++val)
+ {
+ const float dequantized = input_scale * (val - input_zero_point);
+ const float transformed = 1.0f / (1.0f + std::exp(-dequantized));
+ const float rescaled = std::round(transformed * inverse_scale);
+ const int32_t quantized = static_cast<int32_t>(rescaled + output_zero_point);
+ setTableValue(static_cast<uint8_t>(std::max(std::min(maxval, quantized), minval)),
+ static_cast<uint8_t>(val));
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Logistic.h b/compiler/luci-interpreter/src/kernels/Logistic.h
new file mode 100644
index 000000000..31de6adf0
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Logistic.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_LOGISTIC_H
+#define LUCI_INTERPRETER_KERNELS_LOGISTIC_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Logistic : public Kernel
+{
+public:
+ Logistic(const Tensor *input, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+ void populateLookupTable();
+ void setTableValue(uint8_t value, uint8_t idx) { _table[idx] = value; };
+ uint8_t getTableValue(uint8_t idx) const { return _table[idx]; };
+
+private:
+ uint8_t _table[256]{};
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_LOGISTIC_H
diff --git a/compiler/luci-interpreter/src/kernels/Logistic.test.cpp b/compiler/luci-interpreter/src/kernels/Logistic.test.cpp
new file mode 100644
index 000000000..d3bbb330d
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Logistic.test.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Logistic.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T>
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data, std::initializer_list<float> output_data)
+{
+ Tensor input_tensor = makeInputTensor<getElementType<T>()>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(getElementType<T>());
+
+ Logistic kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+template <>
+void Check<uint8_t>(std::initializer_list<int32_t> input_shape,
+ std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data,
+ std::initializer_list<float> output_data)
+{
+ std::pair<float, int32_t> input_quant_param =
+ quantizationParams<uint8_t>(std::min(input_data), std::max(input_data));
+ Tensor input_tensor = makeInputTensor<DataType::U8>(input_shape, input_quant_param.first,
+ input_quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 256, 0);
+
+ Logistic kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data, output_tensor.scale() * 2));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+template <typename T> class LogisticTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(LogisticTest, DataTypes);
+
+TYPED_TEST(LogisticTest, Simple)
+{
+ Check<TypeParam>(
+ {89}, {89},
+ {-10.0000000000, -9.7727272727, -9.5454545455, -9.3181818182, -9.0909090909, -8.8636363636,
+ -8.6363636364, -8.4090909091, -8.1818181818, -7.9545454545, -7.7272727273, -7.5000000000,
+ -7.2727272727, -7.0454545455, -6.8181818182, -6.5909090909, -6.3636363636, -6.1363636364,
+ -5.9090909091, -5.6818181818, -5.4545454545, -5.2272727273, -5.0000000000, -4.7727272727,
+ -4.5454545455, -4.3181818182, -4.0909090909, -3.8636363636, -3.6363636364, -3.4090909091,
+ -3.1818181818, -2.9545454545, -2.7272727273, -2.5000000000, -2.2727272727, -2.0454545455,
+ -1.8181818182, -1.5909090909, -1.3636363636, -1.1363636364, -0.9090909091, -0.6818181818,
+ -0.4545454545, -0.2272727273, 0.0000000000, 0.2272727273, 0.4545454545, 0.6818181818,
+ 0.9090909091, 1.1363636364, 1.3636363636, 1.5909090909, 1.8181818182, 2.0454545455,
+ 2.2727272727, 2.5000000000, 2.7272727273, 2.9545454545, 3.1818181818, 3.4090909091,
+ 3.6363636364, 3.8636363636, 4.0909090909, 4.3181818182, 4.5454545455, 4.7727272727,
+ 5.0000000000, 5.2272727273, 5.4545454545, 5.6818181818, 5.9090909091, 6.1363636364,
+ 6.3636363636, 6.5909090909, 6.8181818182, 7.0454545455, 7.2727272727, 7.5000000000,
+ 7.7272727273, 7.9545454545, 8.1818181818, 8.4090909091, 8.6363636364, 8.8636363636,
+ 9.0909090909, 9.3181818182, 9.5454545455, 9.7727272727, 10.0000000000},
+ {0.0000453979, 0.0000569815, 0.0000715205, 0.0000897689, 0.0001126729, 0.0001414198,
+ 0.0001774998, 0.0002227827, 0.0002796147, 0.0003509396, 0.0004404502, 0.0005527786,
+ 0.0006937345, 0.0008706021, 0.0010925128, 0.0013709094, 0.0017201256, 0.0021581065,
+ 0.0027073042, 0.0033957870, 0.0042586071, 0.0053394826, 0.0066928509, 0.0083863576,
+ 0.0105038445, 0.0131488902, 0.0164489307, 0.0205599431, 0.0256715863, 0.0320125562,
+ 0.0398556989, 0.0495221198, 0.0613831074, 0.0758581800, 0.0934070047, 0.1145124805,
+ 0.1396521834, 0.1692560327, 0.2036499335, 0.2429886272, 0.2871859014, 0.3358556241,
+ 0.3882805886, 0.4434251301, 0.5000000000, 0.5565748699, 0.6117194114, 0.6641443759,
+ 0.7128140986, 0.7570113728, 0.7963500665, 0.8307439673, 0.8603478166, 0.8854875195,
+ 0.9065929953, 0.9241418200, 0.9386168926, 0.9504778802, 0.9601443011, 0.9679874438,
+ 0.9743284137, 0.9794400569, 0.9835510693, 0.9868511098, 0.9894961555, 0.9916136424,
+ 0.9933071491, 0.9946605174, 0.9957413929, 0.9966042130, 0.9972926958, 0.9978418935,
+ 0.9982798744, 0.9986290906, 0.9989074872, 0.9991293979, 0.9993062655, 0.9994472214,
+ 0.9995595498, 0.9996490604, 0.9997203853, 0.9997772173, 0.9998225002, 0.9998585802,
+ 0.9998873271, 0.9999102311, 0.9999284795, 0.9999430185, 0.9999546021});
+}
+
+TEST(LogisticTest, IvalidInputOutputType_NEG)
+{
+ Shape input_shape = {1};
+ std::vector<float> input_data{10};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 256, 0);
+
+ Logistic kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(LogisticTest, IvalidQuantParam_NEG)
+{
+ Shape input_shape = {2};
+ std::vector<float> input_data{-10, 10};
+ std::pair<float, int32_t> input_quant_param = quantizationParams<uint8_t>(-10, 10);
+ Tensor input_tensor = makeInputTensor<DataType::U8>(input_shape, input_quant_param.first,
+ input_quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 255, 0);
+
+ Logistic kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/MaxPool2D.cpp b/compiler/luci-interpreter/src/kernels/MaxPool2D.cpp
new file mode 100644
index 000000000..123e6e1a2
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/MaxPool2D.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/MaxPool2D.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/integer_ops/pooling.h>
+#include <tensorflow/lite/kernels/internal/reference/pooling.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+MaxPool2D::MaxPool2D(const Tensor *input, Tensor *output, const Pool2DParams &params)
+ : KernelWithParams<Pool2DParams>({input}, {output}, params)
+{
+}
+
+void MaxPool2D::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ assert(input()->shape().num_dims() == 4);
+ const Shape &input_shape = input()->shape();
+ const int32_t batches = input_shape.dim(0);
+ const int32_t input_height = input_shape.dim(1);
+ const int32_t input_width = input_shape.dim(2);
+ const int32_t depth = input_shape.dim(3);
+
+ const int32_t output_height = computeOutputSize(_params.padding, input_height,
+ _params.filter_height, _params.stride_height);
+ const int32_t output_width =
+ computeOutputSize(_params.padding, input_width, _params.filter_width, _params.stride_width);
+
+ _padding_height =
+ computePadding(_params.stride_height, 1, input_height, _params.filter_height, output_height);
+ _padding_width =
+ computePadding(_params.stride_width, 1, input_width, _params.filter_width, output_width);
+
+ output()->resize({batches, output_height, output_width, depth});
+ if (input()->element_type() == DataType::U8)
+ {
+ LUCI_INTERPRETER_CHECK(std::abs(output()->scale() - input()->scale()) <= 1.0e-6);
+ LUCI_INTERPRETER_CHECK(output()->zero_point() == input()->zero_point());
+ }
+ else if (input()->element_type() == DataType::S16)
+ {
+ LUCI_INTERPRETER_CHECK(std::abs(output()->scale() - input()->scale()) <= 1.0e-6);
+ LUCI_INTERPRETER_CHECK(input()->zero_point() == 0 && output()->zero_point() == 0);
+ }
+}
+
+void MaxPool2D::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ case DataType::S16:
+ evalSInt16();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void MaxPool2D::evalFloat() const
+{
+ float activation_min{};
+ float activation_max{};
+ calculateActivationRange(_params.activation, &activation_min, &activation_max);
+
+ tflite::PoolParams params{};
+ params.padding_values.height = _padding_height;
+ params.padding_values.width = _padding_width;
+ params.stride_height = _params.stride_height;
+ params.stride_width = _params.stride_width;
+ params.filter_height = _params.filter_height;
+ params.filter_width = _params.filter_width;
+ params.float_activation_min = activation_min;
+ params.float_activation_max = activation_max;
+
+ tflite::reference_ops::MaxPool(params, getTensorShape(input()), getTensorData<float>(input()),
+ getTensorShape(output()), getTensorData<float>(output()));
+}
+
+void MaxPool2D::evalQuantized() const
+{
+ int32_t activation_min{};
+ int32_t activation_max{};
+ calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max);
+
+ tflite::PoolParams params{};
+ params.padding_values.height = _padding_height;
+ params.padding_values.width = _padding_width;
+ params.stride_height = _params.stride_height;
+ params.stride_width = _params.stride_width;
+ params.filter_height = _params.filter_height;
+ params.filter_width = _params.filter_width;
+ params.quantized_activation_min = activation_min;
+ params.quantized_activation_max = activation_max;
+
+ tflite::reference_ops::MaxPool(params, getTensorShape(input()), getTensorData<uint8_t>(input()),
+ getTensorShape(output()), getTensorData<uint8_t>(output()));
+}
+
+void MaxPool2D::evalSInt16() const
+{
+ int32_t activation_min{};
+ int32_t activation_max{};
+ calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max);
+
+ tflite::PoolParams params{};
+ params.padding_values.height = _padding_height;
+ params.padding_values.width = _padding_width;
+ params.stride_height = _params.stride_height;
+ params.stride_width = _params.stride_width;
+ params.filter_height = _params.filter_height;
+ params.filter_width = _params.filter_width;
+ params.quantized_activation_min = activation_min;
+ params.quantized_activation_max = activation_max;
+
+ tflite::reference_integer_ops::MaxPool(
+ params, getTensorShape(input()), getTensorData<int16_t>(input()), //
+ getTensorShape(output()), getTensorData<int16_t>(output()));
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/MaxPool2D.h b/compiler/luci-interpreter/src/kernels/MaxPool2D.h
new file mode 100644
index 000000000..bb7666305
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/MaxPool2D.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_MAXPOOL2D_H
+#define LUCI_INTERPRETER_KERNELS_MAXPOOL2D_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class MaxPool2D : public KernelWithParams<Pool2DParams>
+{
+public:
+ MaxPool2D(const Tensor *input, Tensor *output, const Pool2DParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+ void evalSInt16() const;
+
+private:
+ int32_t _padding_height{};
+ int32_t _padding_width{};
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_MAXPOOL2D_H
diff --git a/compiler/luci-interpreter/src/kernels/MaxPool2D.test.cpp b/compiler/luci-interpreter/src/kernels/MaxPool2D.test.cpp
new file mode 100644
index 000000000..1d7fe06c4
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/MaxPool2D.test.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/MaxPool2D.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(MaxPool2DTest, Float)
+{
+ Shape input_shape{1, 3, 5, 1};
+ std::vector<float> input_data{
+ 1, -1, 0, -2, 2, //
+ -7, -6, -5, -4, -3, //
+ 5, 4, 3, 6, 7, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ 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();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{
+ 1, 2, //
+ 5, 6, //
+ };
+ std::initializer_list<int32_t> ref_output_shape{1, 2, 2, 1};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+}
+
+TEST(MaxPool2DTest, Uint8)
+{
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-15.9375, 15.9375);
+ std::vector<float> input_data{
+ 0, -6, 12, 4, //
+ -3, -2, 10, 7, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first,
+ quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
+
+ 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();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{0.0, 6.0};
+ std::initializer_list<int32_t> 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(MaxPool2DTest, SInt16)
+{
+ Shape input_shape{1, 3, 5, 1};
+ std::vector<int32_t> ref_output_shape{1, 2, 2, 1};
+ std::vector<float> input_data{
+ 1, -1, 0, -2, 2, //
+ -7, -6, -5, -4, -3, //
+ 5, 4, 3, 6, 7, //
+ };
+ std::vector<float> ref_output_data{
+ 1, 2, //
+ 5, 6, //
+ };
+
+ Tensor input_tensor = makeInputTensor<DataType::S16>(input_shape, 0.2, 0, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 0.2, 0);
+
+ Pool2DParams params{};
+ params.padding = Padding::VALID;
+ params.filter_height = 2;
+ params.filter_width = 3;
+ params.stride_height = 1;
+ params.stride_width = 2;
+ params.activation = Activation::RELU6;
+
+ MaxPool2D kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Maximum.cpp b/compiler/luci-interpreter/src/kernels/Maximum.cpp
new file mode 100644
index 000000000..c522b0706
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Maximum.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Maximum.h"
+
+#include "kernels/Utils.h"
+
+#include "kernels/BinaryOpCommon.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Maximum::Maximum(const Tensor *input1, const Tensor *input2, Tensor *output)
+ : Kernel({input1, input2}, {output})
+{
+}
+
+void Maximum::configure()
+{
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type())
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type())
+ output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape()));
+}
+
+void Maximum::execute() const
+{
+ switch (input1()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalMaximum<float>();
+ break;
+ case DataType::U8:
+ evalMaximum<uint8_t>();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+template <typename T> inline void Maximum::evalMaximum() const
+{
+ BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData<T>(input1()),
+ getTensorShape(input2()), getTensorData<T>(input2()),
+ getTensorShape(output()), getTensorData<T>(output()),
+ [](T x, T y) { return std::max(x, y); });
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Maximum.h b/compiler/luci-interpreter/src/kernels/Maximum.h
new file mode 100644
index 000000000..3c99e69c7
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Maximum.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_MAXIMUM_H
+#define LUCI_INTERPRETER_KERNELS_MAXIMUM_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Maximum : public Kernel
+{
+public:
+ Maximum(const Tensor *input1, const Tensor *input2, Tensor *output);
+
+ const Tensor *input1() const { return _inputs[0]; }
+ const Tensor *input2() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ template <typename T> inline void evalMaximum() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_MAXIMUM_H
diff --git a/compiler/luci-interpreter/src/kernels/Maximum.test.cpp b/compiler/luci-interpreter/src/kernels/Maximum.test.cpp
new file mode 100644
index 000000000..2ddaeaf04
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Maximum.test.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Maximum.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(MaximumTest, Float)
+{
+ Shape input_shape{3, 1, 2};
+ std::vector<float> input_data1{1.0, 0.0, -1.0, 11.0, -2.0, -1.44};
+ std::vector<float> input_data2{-1.0, 0.0, 1.0, 12.0, -3.0, -1.43};
+ Tensor input_tensor1 = makeInputTensor<DataType::FLOAT32>(input_shape, input_data1);
+ Tensor input_tensor2 = makeInputTensor<DataType::FLOAT32>(input_shape, input_data2);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Maximum kernel(&input_tensor1, &input_tensor2, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{1.0, 0.0, 1.0, 12.0, -2.0, -1.43};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(MaximumTest, Uint8)
+{
+ Shape input_shape{3, 1, 2};
+ std::vector<uint8_t> input_data1{1, 0, 2, 11, 2, 23};
+ std::vector<uint8_t> input_data2{0, 0, 1, 12, 255, 1};
+ Tensor input_tensor1 = makeInputTensor<DataType::U8>(input_shape, input_data1);
+ Tensor input_tensor2 = makeInputTensor<DataType::U8>(input_shape, input_data2);
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ Maximum kernel(&input_tensor1, &input_tensor2, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<int32_t> ref_output_shape{2, 4};
+ EXPECT_THAT(extractTensorData<uint8_t>(output_tensor),
+ ::testing::ElementsAreArray({1, 0, 2, 12, 255, 23}));
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Mean.cpp b/compiler/luci-interpreter/src/kernels/Mean.cpp
new file mode 100644
index 000000000..f20cf7d89
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Mean.cpp
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Mean.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+static void resolveAxes(const int *axes_data, int num_axes, tflite::MeanParams *params)
+{
+ params->axis_count = num_axes;
+ for (int i = 0; i < num_axes; ++i)
+ {
+ params->axis[i] = static_cast<int16>(axes_data[i]);
+ }
+ for (int i = num_axes; i < 4; ++i)
+ {
+ params->axis[i] = 1;
+ }
+}
+
+// Returns the number of axes that will be reduced. Removes duplicates.
+static int getAxisReductionCount(const int *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 int *axes_data, int num_axes,
+ bool keep_dims)
+{
+ int input_num_dims = input_shape.num_dims();
+ if (input_num_dims == 0)
+ {
+ return Shape(0);
+ }
+
+ if (keep_dims)
+ {
+ Shape output_shape(input_num_dims);
+ for (int idx = 0; idx < input_num_dims; ++idx)
+ {
+ bool is_axis = false;
+ for (int axis_idx = 0; axis_idx < num_axes; ++axis_idx)
+ {
+ if (axes_data[axis_idx] == idx || axes_data[axis_idx] + input_num_dims == idx)
+ {
+ is_axis = true;
+ break;
+ }
+ }
+ if (is_axis)
+ {
+ output_shape.dim(idx) = 1;
+ }
+ else
+ {
+ output_shape.dim(idx) = input_shape.dim(idx);
+ }
+ }
+ return output_shape;
+ }
+ else
+ {
+ int num_reduce_axes = getAxisReductionCount(axes_data, num_axes, input_num_dims);
+ Shape output_shape(input_num_dims - num_reduce_axes);
+ int num_skip_axes = 0;
+ for (int idx = 0; idx < input_num_dims; ++idx)
+ {
+ bool is_axis = false;
+ for (int axis_idx = 0; axis_idx < num_axes; ++axis_idx)
+ {
+ if (axes_data[axis_idx] == idx || axes_data[axis_idx] + input_num_dims == idx)
+ {
+ ++num_skip_axes;
+ is_axis = true;
+ break;
+ }
+ }
+ if (!is_axis)
+ {
+ output_shape.dim(idx - num_skip_axes) = input_shape.dim(idx);
+ }
+ }
+ return output_shape;
+ }
+}
+
+Mean::Mean(const Tensor *input, const Tensor *axes, Tensor *output, const ReducerParams &params)
+ : KernelWithParams<ReducerParams>({input, axes}, {output}, params)
+{
+}
+
+void Mean::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ LUCI_INTERPRETER_CHECK(axes()->element_type() == DataType::S32);
+ if (input()->element_type() == DataType::S16)
+ {
+ LUCI_INTERPRETER_CHECK(input()->zero_point() == 0 && output()->zero_point() == 0);
+ }
+
+ const Shape &input_shape = input()->shape();
+ int input_num_dims = input_shape.num_dims();
+
+ const auto *axes_data = getTensorData<int32_t>(axes());
+ int num_axes = axes()->shape().num_elements();
+ assert(num_axes <= 4);
+
+ Shape output_shape = getOutputShape(input_shape, axes_data, num_axes, _params.keep_dims);
+ output()->resize(output_shape);
+
+ tflite::MeanParams params{};
+ resolveAxes(axes_data, num_axes, &params);
+ const bool need_temporaries =
+ !(_params.keep_dims && input_num_dims == 4 && params.axis_count == 2 &&
+ ((params.axis[0] == 1 && params.axis[1] == 2) ||
+ (params.axis[0] == 2 && params.axis[1] == 1)));
+ if (need_temporaries)
+ {
+ _temp_index =
+ std::make_unique<Tensor>(DataType::S32, Shape(input_num_dims), AffineQuantization{}, "");
+ _resolved_axes =
+ std::make_unique<Tensor>(DataType::S32, Shape(num_axes), AffineQuantization{}, "");
+ _temp_sum = std::make_unique<Tensor>(input()->element_type(), output()->shape(),
+ AffineQuantization{}, "");
+ }
+}
+
+void Mean::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ case DataType::S16:
+ evalQuantizedS16();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+ if (!!_temp_index)
+ _temp_index->deallocate();
+ if (!!_resolved_axes)
+ _resolved_axes->deallocate();
+ if (!!_temp_sum)
+ _temp_sum->deallocate();
+}
+
+void Mean::evalFloat() const
+{
+ const Shape &input_shape = input()->shape();
+ int input_num_dims = input_shape.num_dims();
+ const auto *axes_data = getTensorData<int32_t>(axes());
+ int num_axes = axes()->shape().num_elements();
+
+ tflite::MeanParams params{};
+ resolveAxes(axes_data, num_axes, &params);
+
+ // Defer to specialized implementation for 4D Mean across axes 1 & 2.
+ if (_params.keep_dims && input_num_dims == 4 && params.axis_count == 2 &&
+ ((params.axis[0] == 1 && params.axis[1] == 2) ||
+ (params.axis[0] == 2 && params.axis[1] == 1)))
+ {
+ tflite::reference_ops::Mean(params, getTensorShape(input()), getTensorData<float>(input()),
+ getTensorShape(output()), getTensorData<float>(output()));
+ }
+ else
+ {
+ tflite::reference_ops::Mean(
+ getTensorData<float>(input()), getTensorShape(input()).DimsData(),
+ input()->shape().num_dims(), getTensorData<float>(output()),
+ getTensorShape(output()).DimsData(), output()->shape().num_dims(), axes_data, num_axes,
+ _params.keep_dims, getTensorData<int>(_temp_index.get()),
+ getTensorData<int>(_resolved_axes.get()), getTensorData<float>(_temp_sum.get()));
+ }
+}
+
+void Mean::evalQuantized() const
+{
+ const Shape &input_shape = input()->shape();
+ int input_num_dims = input_shape.num_dims();
+ const auto *axes_data = getTensorData<int32_t>(axes());
+ int num_axes = axes()->shape().num_elements();
+
+ tflite::MeanParams params{};
+ resolveAxes(axes_data, num_axes, &params);
+
+ // Defer to specialized implementation for 4D Mean across axes 1 & 2.
+ if (_params.keep_dims && input_num_dims == 4 && params.axis_count == 2 &&
+ ((params.axis[0] == 1 && params.axis[1] == 2) ||
+ (params.axis[0] == 2 && params.axis[1] == 1)))
+ {
+ tflite::reference_ops::Mean(params, getTensorShape(input()), getTensorData<uint8_t>(input()),
+ input()->zero_point(), input()->scale(), getTensorShape(output()),
+ getTensorData<uint8_t>(output()), output()->zero_point(),
+ output()->scale());
+ }
+ else if (input()->zero_point() == output()->zero_point() && input()->scale() == output()->scale())
+ {
+ tflite::reference_ops::Mean(
+ getTensorData<uint8_t>(input()), getTensorShape(input()).DimsData(),
+ input()->shape().num_dims(), getTensorData<uint8_t>(output()),
+ getTensorShape(output()).DimsData(), output()->shape().num_dims(), axes_data, num_axes,
+ _params.keep_dims, getTensorData<int>(_temp_index.get()),
+ getTensorData<int>(_resolved_axes.get()), getTensorData<int>(_temp_sum.get()));
+ }
+ else
+ {
+ tflite::reference_ops::QuantizedMeanOrSum<>(
+ getTensorData<uint8_t>(input()), input()->zero_point(), input()->scale(),
+ getTensorShape(input()).DimsData(), input()->shape().num_dims(),
+ getTensorData<uint8_t>(output()), output()->zero_point(), output()->scale(),
+ getTensorShape(output()).DimsData(), output()->shape().num_dims(), axes_data, num_axes,
+ _params.keep_dims, getTensorData<int>(_temp_index.get()),
+ getTensorData<int>(_resolved_axes.get()), getTensorData<int>(_temp_sum.get()),
+ /*compute_sum=*/false);
+ }
+}
+
+void Mean::evalQuantizedS16() const
+{
+ const auto *input_data = getTensorData<int16_t>(input());
+ auto *output_data = getTensorData<int16_t>(output());
+
+ const Shape &input_shape = input()->shape();
+ const Shape &output_shape = output()->shape();
+
+ const auto *axes_data = getTensorData<int32_t>(axes());
+ const int num_axes = axes()->shape().num_elements();
+
+ constexpr int32_t output_min = -std::numeric_limits<int16_t>::max();
+ constexpr int32_t output_max = std::numeric_limits<int16_t>::max();
+
+ // Defer to specialized implementation for 4D Mean across axes 1 & 2.
+ if (_params.keep_dims && input_shape.num_dims() == 4 && num_axes == 2 &&
+ ((axes_data[0] == 1 && axes_data[1] == 2) || (axes_data[0] == 2 && axes_data[1] == 1)))
+ {
+ const int32_t batches = input_shape.dim(0);
+ const int32_t input_height = input_shape.dim(1);
+ const int32_t input_width = input_shape.dim(2);
+ const int32_t depth = input_shape.dim(3);
+ assert(output_shape.num_dims() == 4);
+ assert(output_shape.dim(0) == batches);
+ assert(output_shape.dim(1) == 1);
+ assert(output_shape.dim(2) == 1);
+ assert(output_shape.dim(3) == depth);
+
+ const double real_multiplier =
+ static_cast<double>(input()->scale()) / static_cast<double>(output()->scale());
+
+ int32_t output_multiplier{};
+ int output_shift{};
+ quantizeMultiplier(real_multiplier, &output_multiplier, &output_shift);
+
+ const int32_t num_elements_in_axes = input_height * input_width;
+
+ for (int32_t batch = 0; batch < batches; ++batch)
+ {
+ for (int32_t c = 0; c < depth; ++c)
+ {
+ int32_t acc = 0;
+ for (int32_t in_y = 0; in_y < input_height; ++in_y)
+ {
+ for (int32_t in_x = 0; in_x < input_width; ++in_x)
+ {
+ acc += input_data[calcOffset(input_shape, batch, in_y, in_x, c)];
+ }
+ }
+ int32_t scaled_acc =
+ tflite::MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift);
+ // Divide by the number of elements rounding to the nearest integer.
+ scaled_acc = scaled_acc > 0
+ ? (scaled_acc + num_elements_in_axes / 2) / num_elements_in_axes
+ : (scaled_acc - num_elements_in_axes / 2) / num_elements_in_axes;
+
+ scaled_acc = std::max(scaled_acc, output_min);
+ scaled_acc = std::min(scaled_acc, output_max);
+
+ output_data[calcOffset(output_shape, batch, 0, 0, c)] = scaled_acc;
+ }
+ }
+ }
+ else
+ {
+ throw std::runtime_error("Unsupported configuration.");
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Mean.h b/compiler/luci-interpreter/src/kernels/Mean.h
new file mode 100644
index 000000000..1cc046894
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Mean.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_MEAN_H
+#define LUCI_INTERPRETER_KERNELS_MEAN_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+#include <memory>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Mean : public KernelWithParams<ReducerParams>
+{
+public:
+ Mean(const Tensor *input, const Tensor *axes, Tensor *output, const ReducerParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ const Tensor *axes() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+ void evalQuantizedS16() const;
+
+private:
+ std::unique_ptr<Tensor> _temp_index;
+ std::unique_ptr<Tensor> _resolved_axes;
+ std::unique_ptr<Tensor> _temp_sum;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_MEAN_H
diff --git a/compiler/luci-interpreter/src/kernels/Mean.test.cpp b/compiler/luci-interpreter/src/kernels/Mean.test.cpp
new file mode 100644
index 000000000..e81d2ad5f
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Mean.test.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Mean.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(MeanTest, FloatKeepDims)
+{
+ std::vector<float> input_data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0,
+ 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
+ 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0};
+
+ std::vector<int32_t> axis_data{0, 2};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({4, 3, 2}, input_data);
+ Tensor axis_tensor = makeInputTensor<DataType::S32>({2}, axis_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ReducerParams params{};
+ params.keep_dims = true;
+
+ Mean kernel(&input_tensor, &axis_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{10.5, 12.5, 14.5};
+ std::initializer_list<int32_t> ref_output_shape{1, 3, 1};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+}
+
+TEST(MeanTest, FloatKeepDims4DMean)
+{
+ std::vector<float> input_data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0,
+ 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
+ 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0};
+
+ std::vector<int32_t> axis_data{1, 2};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 3, 2}, input_data);
+ Tensor axis_tensor = makeInputTensor<DataType::S32>({2}, axis_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ReducerParams params{};
+ params.keep_dims = true;
+
+ Mean kernel(&input_tensor, &axis_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{6, 7, 18, 19};
+ std::initializer_list<int32_t> ref_output_shape{2, 1, 1, 2};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+}
+
+TEST(MeanTest, FloatNotKeepDims)
+{
+ std::vector<float> input_data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0,
+ 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
+ 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0};
+
+ std::vector<int32_t> axis_data{1, 0, -3, -3};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({4, 3, 2}, input_data);
+ Tensor axis_tensor = makeInputTensor<DataType::S32>({4}, axis_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ReducerParams params{};
+ params.keep_dims = false;
+
+ Mean kernel(&input_tensor, &axis_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{12, 13};
+ std::initializer_list<int32_t> ref_output_shape{2};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+}
+
+TEST(MeanTest, Uint8KeepDims)
+{
+ float kQuantizedTolerance = getTolerance(-1.0, 1.0, 255);
+ std::vector<float> input_data = {0.4, 0.2, 0.3, 0.4, 0.5, 0.6};
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-1.0f, 1.0f);
+
+ std::vector<int32_t> axis_data{1};
+ Tensor input_tensor =
+ makeInputTensor<DataType::U8>({3, 2}, quant_param.first, quant_param.second, input_data);
+ Tensor axis_tensor = makeInputTensor<DataType::S32>({1}, axis_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
+
+ ReducerParams params{};
+ params.keep_dims = true;
+
+ Mean kernel(&input_tensor, &axis_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{0.3, 0.35, 0.55};
+ std::initializer_list<int32_t> ref_output_shape{3, 1};
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(ref_output_data, kQuantizedTolerance));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+}
+
+TEST(MeanTest, Uint8NotKeepDims)
+{
+ float kQuantizedTolerance = getTolerance(-1.0, 1.0, 255);
+ std::vector<float> input_data = {0.4, 0.2, 0.3, 0.4, 0.5, 0.6};
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-1.0f, 1.0f);
+
+ std::vector<int32_t> axis_data{1};
+ Tensor input_tensor =
+ makeInputTensor<DataType::U8>({1, 3, 2}, quant_param.first, quant_param.second, input_data);
+ Tensor axis_tensor = makeInputTensor<DataType::S32>({1}, axis_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
+
+ ReducerParams params{};
+ params.keep_dims = false;
+
+ Mean kernel(&input_tensor, &axis_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{0.4, 0.4};
+ std::initializer_list<int32_t> ref_output_shape{1, 2};
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(ref_output_data, kQuantizedTolerance));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+}
+
+TEST(MeanTest, SInt16KeepDims4D)
+{
+ std::vector<float> input_data = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0,
+ 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
+ 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0};
+ std::vector<int32_t> axes_data{1, 2};
+ std::vector<float> ref_output_data{6, 7, 18, 19};
+
+ Tensor input_tensor = makeInputTensor<DataType::S16>({2, 2, 3, 2}, 0.25, 0, input_data);
+ Tensor axes_tensor = makeInputTensor<DataType::S32>({2}, axes_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 0.2, 0);
+
+ ReducerParams params{};
+ params.keep_dims = true;
+
+ Mean kernel(&input_tensor, &axes_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 1, 1, 2}));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Minimum.cpp b/compiler/luci-interpreter/src/kernels/Minimum.cpp
new file mode 100644
index 000000000..5eb13455e
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Minimum.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Minimum.h"
+
+#include "kernels/Utils.h"
+
+#include "kernels/BinaryOpCommon.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Minimum::Minimum(const Tensor *input1, const Tensor *input2, Tensor *output)
+ : Kernel({input1, input2}, {output})
+{
+}
+
+void Minimum::configure()
+{
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type())
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type())
+ output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape()));
+}
+
+void Minimum::execute() const
+{
+ switch (input1()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalMinimum<float>();
+ break;
+ case DataType::U8:
+ evalMinimum<uint8_t>();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+template <typename T> inline void Minimum::evalMinimum() const
+{
+ BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData<T>(input1()),
+ getTensorShape(input2()), getTensorData<T>(input2()),
+ getTensorShape(output()), getTensorData<T>(output()),
+ [](T x, T y) { return std::min(x, y); });
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Minimum.h b/compiler/luci-interpreter/src/kernels/Minimum.h
new file mode 100644
index 000000000..5ff4035b4
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Minimum.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_MINIMUM_H
+#define LUCI_INTERPRETER_KERNELS_MINIMUM_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Minimum : public Kernel
+{
+public:
+ Minimum(const Tensor *input1, const Tensor *input2, Tensor *output);
+
+ const Tensor *input1() const { return _inputs[0]; }
+ const Tensor *input2() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ template <typename T> inline void evalMinimum() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_MINIMUM_H
diff --git a/compiler/luci-interpreter/src/kernels/Minimum.test.cpp b/compiler/luci-interpreter/src/kernels/Minimum.test.cpp
new file mode 100644
index 000000000..b6420dd9b
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Minimum.test.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Minimum.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(MinimumTest, Float)
+{
+ Shape input_shape{3, 1, 2};
+ std::vector<float> input_data1{1.0, 0.0, -1.0, 11.0, -2.0, -1.44};
+ std::vector<float> input_data2{-1.0, 0.0, 1.0, 12.0, -3.0, -1.43};
+ Tensor input_tensor1 = makeInputTensor<DataType::FLOAT32>(input_shape, input_data1);
+ Tensor input_tensor2 = makeInputTensor<DataType::FLOAT32>(input_shape, input_data2);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Minimum kernel(&input_tensor1, &input_tensor2, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{-1.0, 0.0, -1.0, 11.0, -3.0, -1.44};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(MinimumTest, Uint8)
+{
+ Shape input_shape{3, 1, 2};
+ std::vector<uint8_t> input_data1{1, 0, 2, 11, 2, 23};
+ std::vector<uint8_t> input_data2{0, 0, 1, 12, 255, 1};
+ Tensor input_tensor1 = makeInputTensor<DataType::U8>(input_shape, input_data1);
+ Tensor input_tensor2 = makeInputTensor<DataType::U8>(input_shape, input_data2);
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ Minimum kernel(&input_tensor1, &input_tensor2, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<int32_t> ref_output_shape{2, 4};
+ EXPECT_THAT(extractTensorData<uint8_t>(output_tensor),
+ ::testing::ElementsAreArray({0, 0, 1, 11, 2, 1}));
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Mul.cpp b/compiler/luci-interpreter/src/kernels/Mul.cpp
new file mode 100644
index 000000000..513d147a3
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Mul.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Mul.h"
+
+#include "kernels/BinaryOpCommon.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Mul::Mul(const Tensor *input1, const Tensor *input2, Tensor *output, const MulParams &params)
+ : KernelWithParams<MulParams>({input1, input2}, {output}, params)
+{
+}
+
+void Mul::configure()
+{
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type());
+ LUCI_INTERPRETER_CHECK(output()->element_type() == input1()->element_type());
+ if (input1()->element_type() == DataType::S16)
+ {
+ LUCI_INTERPRETER_CHECK(input1()->zero_point() == 0 && input2()->zero_point() == 0 &&
+ output()->zero_point() == 0);
+ }
+
+ output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape()));
+}
+
+void Mul::execute() const
+{
+ switch (input1()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::S16:
+ evalQuantizedS16();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Mul::evalFloat() const
+{
+ float activation_min{};
+ float activation_max{};
+ calculateActivationRange(_params.activation, &activation_min, &activation_max);
+
+ tflite::ArithmeticParams params{};
+ params.float_activation_min = activation_min;
+ params.float_activation_max = activation_max;
+
+ const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes(
+ getTensorShape(input1()), getTensorShape(input2()), &params);
+
+ if (need_broadcast)
+ {
+ tflite::optimized_ops::BroadcastMul4DSlow(
+ params, getTensorShape(input1()), getTensorData<float>(input1()), getTensorShape(input2()),
+ getTensorData<float>(input2()), getTensorShape(output()), getTensorData<float>(output()));
+ }
+ else
+ {
+ tflite::optimized_ops::Mul(params, getTensorShape(input1()), getTensorData<float>(input1()),
+ getTensorShape(input2()), getTensorData<float>(input2()),
+ getTensorShape(output()), getTensorData<float>(output()));
+ }
+}
+
+void Mul::evalQuantizedS16() const
+{
+ const auto input1_scale = static_cast<double>(input1()->scale());
+ const auto input2_scale = static_cast<double>(input2()->scale());
+ const auto output_scale = static_cast<double>(output()->scale());
+
+ 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<int32_t>(input1_val) * static_cast<int32_t>(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<int16_t>(output);
+ };
+
+ BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData<int16_t>(input1()),
+ getTensorShape(input2()), getTensorData<int16_t>(input2()),
+ getTensorShape(output()), getTensorData<int16_t>(output()), fn);
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Mul.h b/compiler/luci-interpreter/src/kernels/Mul.h
new file mode 100644
index 000000000..2ccf60f3a
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Mul.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_MUL_H
+#define LUCI_INTERPRETER_KERNELS_MUL_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+#include <cstdint>
+#include <vector>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Mul : public KernelWithParams<MulParams>
+{
+public:
+ Mul(const Tensor *input1, const Tensor *input2, Tensor *output, const MulParams &params);
+
+ const Tensor *input1() const { return _inputs[0]; }
+ const Tensor *input2() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantizedS16() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_MUL_H
diff --git a/compiler/luci-interpreter/src/kernels/Mul.test.cpp b/compiler/luci-interpreter/src/kernels/Mul.test.cpp
new file mode 100644
index 000000000..1409b3fae
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Mul.test.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Mul.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(MulTest, Float)
+{
+ Shape base_shape = {2, 3, 1, 2};
+ std::vector<Shape> test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}};
+ std::vector<std::vector<float>> 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<float> input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f,
+ 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f};
+ std::vector<float> input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f};
+ for (size_t i = 0; i < test_shapes.size(); ++i)
+ {
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>(base_shape, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>(test_shapes[i], input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ MulParams params{};
+ params.activation = Activation::RELU;
+
+ Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f))
+ << "With shape number " << i;
+ }
+ // Re-run with exchanged inputs.
+ for (size_t i = 0; i < test_shapes.size(); ++i)
+ {
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>(test_shapes[i], input2_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>(base_shape, input1_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ MulParams params{};
+ params.activation = Activation::RELU;
+
+ Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f))
+ << "With shape number " << i;
+ }
+}
+
+TEST(MulTest, SInt16)
+{
+ Shape base_shape = {2, 3, 1, 2};
+ std::vector<Shape> test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}};
+ std::vector<std::vector<int32_t>> ref_output_shapes{
+ {2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}};
+
+ std::vector<float> input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f,
+ 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f};
+ std::vector<float> input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f};
+ std::vector<std::vector<float>> ref_outputs = {
+ {0.00f, 0.69f, 0.12f, 1.15f, 0.00f, 2.07f, 0.18f, 0.15f, 0.00f, 0.25f, 0.90f, 0.45f,
+ 0.16f, 0.00f, 0.00f, 0.00f, 0.80f, 0.00f, 0.24f, 0.84f, 0.00f, 1.40f, 1.20f, 2.52f,
+ 0.00f, 0.00f, 0.64f, 0.00f, 0.00f, 0.00f, 0.14f, 0.00f, 0.00f, 0.00f, 0.70f, 0.00f},
+ {0.00f, 0.69f, 0.00f, 0.25f, 0.80f, 0.00f, 0.24f, 0.84f, 0.64f, 0.00f, 0.70f, 0.00f},
+ {0.00f, 0.46f, 0.00f, 0.69f, 0.12f, 0.00f, 0.18f, 0.10f, 0.27f, 0.15f, 0.00f, 0.00f,
+ 0.16f, 0.00f, 0.24f, 0.00f, 0.00f, 0.44f, 0.60f, 1.40f, 1.20f, 2.80f, 1.08f, 2.52f,
+ 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.35f, 0.00f, 0.70f, 0.00f, 0.63f, 0.00f},
+ {0.00f, 0.46f, 0.27f, 0.15f, 0.00f, 0.44f, 0.60f, 1.40f, 0.00f, 0.00f, 0.63f, 0.00f}};
+ for (size_t i = 0; i < test_shapes.size(); ++i)
+ {
+ Tensor input1_tensor = makeInputTensor<DataType::S16>(base_shape, 3.0 / 32767, 0, input1_data);
+ Tensor input2_tensor =
+ makeInputTensor<DataType::S16>(test_shapes[i], 1.0 / 32767, 0, input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 4.0 / 32767, 0);
+ const float tolerance = output_tensor.scale() * 2;
+
+ MulParams params{};
+ params.activation = Activation::RELU;
+
+ Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor),
+ ::testing::ElementsAreArray(ref_output_shapes[i]))
+ << "With shape number " << i;
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance))
+ << "With shape number " << i;
+ }
+ // Re-run with exchanged inputs and different scales.
+ for (size_t i = 0; i < test_shapes.size(); ++i)
+ {
+ Tensor input1_tensor =
+ makeInputTensor<DataType::S16>(test_shapes[i], 2.0 / 32767, 0, input2_data);
+ Tensor input2_tensor = makeInputTensor<DataType::S16>(base_shape, 4.0 / 32767, 0, input1_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 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();
+ 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;
+ }
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/NotEqual.cpp b/compiler/luci-interpreter/src/kernels/NotEqual.cpp
new file mode 100644
index 000000000..cd2f6c2c1
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/NotEqual.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/NotEqual.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/comparisons.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+NotEqual::NotEqual(const Tensor *x, const Tensor *y, Tensor *output) : Kernel({x, y}, {output}) {}
+
+void NotEqual::configure()
+{
+ LUCI_INTERPRETER_CHECK(x()->element_type() == y()->element_type());
+ LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::BOOL);
+
+ if (x()->element_type() == DataType::U8)
+ {
+ quantizeMultiplierSmallerThanOneExp(x()->scale(), &_x_multiplier, &_x_shift);
+ quantizeMultiplierSmallerThanOneExp(y()->scale(), &_y_multiplier, &_y_shift);
+ }
+ output()->resize(calculateShapeForBroadcast(x()->shape(), y()->shape()));
+}
+
+void NotEqual::execute() const
+{
+ switch (x()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void NotEqual::evalFloat() const
+{
+ const auto x_data = getTensorData<float>(x());
+ const auto y_data = getTensorData<float>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowNotEqual(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::NotEqual(op_params, getTensorShape(x()), x_data, getTensorShape(y()),
+ y_data, getTensorShape(output()), output_data);
+ }
+}
+
+void NotEqual::evalQuantized() const
+{
+ const auto x_data = getTensorData<uint8_t>(x());
+ const auto y_data = getTensorData<uint8_t>(y());
+ auto output_data = getTensorData<bool>(output());
+
+ tflite::ComparisonParams op_params;
+ op_params.left_shift = 8;
+ op_params.input1_offset = -x()->zero_point(); // Note the '-'
+ op_params.input1_shift = _x_shift;
+ op_params.input1_multiplier = _x_multiplier;
+ op_params.input2_offset = -y()->zero_point(); // Note the '-'
+ op_params.input2_shift = _y_shift;
+ op_params.input2_multiplier = _y_multiplier;
+ op_params.is_broadcast = x()->shape() != y()->shape();
+
+ if (op_params.is_broadcast)
+ {
+ tflite::reference_ops::Broadcast4DSlowNotEqualWithScaling(
+ op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+ else
+ {
+ tflite::reference_ops::NotEqualWithScaling(op_params, getTensorShape(x()), x_data,
+ getTensorShape(y()), y_data,
+ getTensorShape(output()), output_data);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/NotEqual.h b/compiler/luci-interpreter/src/kernels/NotEqual.h
new file mode 100644
index 000000000..d729c6c14
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/NotEqual.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_NOT_EQUAL_H
+#define LUCI_INTERPRETER_KERNELS_NOT_EQUAL_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class NotEqual : public Kernel
+{
+public:
+ NotEqual(const Tensor *x, const Tensor *y, Tensor *output);
+
+ const Tensor *x() const { return _inputs[0]; }
+ const Tensor *y() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+
+private:
+ int32_t _x_multiplier = 0;
+ int32_t _x_shift = 0;
+ int32_t _y_multiplier = 0;
+ int32_t _y_shift = 0;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_NOT_EQUAL_H
diff --git a/compiler/luci-interpreter/src/kernels/NotEqual.test.cpp b/compiler/luci-interpreter/src/kernels/NotEqual.test.cpp
new file mode 100644
index 000000000..8c8712371
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/NotEqual.test.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/NotEqual.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(NotEqualTest, FloatSimple)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ -1, 0, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ true, false, true, // Row 1
+ true, false, true, // Row 2
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ NotEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3}));
+}
+
+TEST(NotEqualTest, FloatBroardcast)
+{
+ std::vector<float> x_data{
+ 0.5, 0.7, 0.9, // Row 1
+ 1, 0, -1, // Row 2
+ -1, 0, 1, // Row 3
+ 0.9, 0.7, 0.5, // Row 4
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.7, 0.5, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ true, false, true, // Row 1
+ true, true, true, // Row 2
+ true, true, true, // Row 3
+ false, false, false, // Row 4
+ };
+
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({4, 3}, x_data);
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1, 3}, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ NotEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({4, 3}));
+}
+
+// Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+const float F_MIN = -128.0 / 128.0;
+const float F_MAX = 127.0 / 128.0;
+
+TEST(NotEqualTest, Uint8Quantized)
+{
+ std::vector<float> x_data{
+ 0.5, 0.5, 0.7, 0.9, // Row 1
+ 1, 0, 0.05, -1, // Row 2
+ };
+
+ std::vector<float> y_data{
+ 0.9, 0.5, 0.55, 0.5, // Row 1
+ -1, 0, 0.05, 1, // Row 2
+ };
+
+ std::vector<bool> ref_output_data{
+ true, false, true, true, // Row 1
+ true, false, false, true, // Row 2
+ };
+
+ std::pair<float, int32_t> x_quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first,
+ x_quant_param.second, x_data);
+
+ std::pair<float, int32_t> y_quant_param = quantizationParams<uint8_t>(F_MIN * 2, F_MAX * 2);
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first,
+ y_quant_param.second, y_data);
+
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ NotEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(NotEqualTest, Uint8QuantizedBroadcast)
+{
+ std::vector<float> x_data{
+ 0.4, -0.8, 0.7, 0.3, // Row 1
+ -0.5, 0.1, 0, 0.5, // Row 2
+ 1, 0, 0.05, -1, // Row 3
+ -1, 0.05, 0, 1, // Row 4
+ };
+
+ std::vector<float> y_data{
+ -1, 0.05, 0, 1, // Row 1
+ };
+
+ std::vector<bool> ref_output_data{
+ true, true, true, true, // Row 1
+ true, true, false, true, // Row 2
+ true, true, true, true, // Row 3
+ false, false, false, false, // Row 4
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX);
+ Tensor x_tensor =
+ makeInputTensor<DataType::U8>({1, 4, 4, 1}, quant_param.first, quant_param.second, x_data);
+ Tensor y_tensor =
+ makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data);
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ NotEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 4, 4, 1}));
+ EXPECT_THAT(extractTensorData<bool>(output_tensor), ::testing::ElementsAreArray(ref_output_data));
+}
+
+TEST(NotEqualTest, Input_Type_Mismatch_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::U8>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ NotEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(NotEqualTest, Input_Output_Type_NEG)
+{
+ Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor y_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ NotEqual kernel(&x_tensor, &y_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Pad.cpp b/compiler/luci-interpreter/src/kernels/Pad.cpp
new file mode 100644
index 000000000..bdf3a2a95
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Pad.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Pad.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Pad::Pad(const Tensor *input, const Tensor *paddings, Tensor *output)
+ : Kernel({input, paddings}, {output})
+{
+}
+
+void Pad::configure()
+{
+ const Shape &input_shape = input()->shape();
+ const int num_dims = input_shape.num_dims();
+
+ if (num_dims > 4)
+ throw std::runtime_error("Unsupported number of dimensions.");
+
+ assert(output()->element_type() == input()->element_type());
+ assert(paddings()->element_type() == DataType::S32);
+ // Paddings shape should be [N, 2].
+ assert(paddings()->shape().num_dims() == 2);
+ assert(paddings()->shape().dim(0) == num_dims);
+ assert(paddings()->shape().dim(1) == 2);
+
+ Shape output_shape(num_dims);
+ const auto *paddings_data = getTensorData<int32_t>(paddings());
+ for (int i = 0; i < num_dims; ++i)
+ {
+ const int32_t padding_before = paddings_data[i * 2];
+ const int32_t padding_after = paddings_data[i * 2 + 1];
+ assert(padding_before >= 0 && padding_after >= 0);
+ output_shape.dim(i) = input_shape.dim(i) + padding_before + padding_after;
+ }
+
+ output()->resize(output_shape);
+}
+
+void Pad::execute() const
+{
+ const int num_dims = input()->shape().num_dims();
+
+ tflite::PadParams params{};
+ params.left_padding_count = num_dims;
+ params.right_padding_count = num_dims;
+
+ const auto *paddings_data = getTensorData<int32_t>(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<float>(input()),
+ &pad_value, getTensorShape(output()),
+ getTensorData<float>(output()));
+ break;
+ }
+ case DataType::U8:
+ {
+ assert(output()->zero_point() >= std::numeric_limits<uint8_t>::min());
+ assert(output()->zero_point() <= std::numeric_limits<uint8_t>::max());
+ const auto pad_value = static_cast<uint8_t>(output()->zero_point());
+ tflite::reference_ops::Pad(params, getTensorShape(input()), getTensorData<uint8_t>(input()),
+ &pad_value, getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ break;
+ }
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Pad.h b/compiler/luci-interpreter/src/kernels/Pad.h
new file mode 100644
index 000000000..e05b47f29
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Pad.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_PAD_H
+#define LUCI_INTERPRETER_KERNELS_PAD_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Pad : public Kernel
+{
+public:
+ Pad(const Tensor *input, const Tensor *paddings, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ const Tensor *paddings() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_PAD_H
diff --git a/compiler/luci-interpreter/src/kernels/Pad.test.cpp b/compiler/luci-interpreter/src/kernels/Pad.test.cpp
new file mode 100644
index 000000000..4bee07629
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Pad.test.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Pad.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+float GetTolerance(float min, float max) { return (max - min) / 255.0; }
+
+TEST(Pad, Uint8)
+{
+ float kQuantizedTolerance = GetTolerance(-1.0, 1.0);
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-1.0f, 1.0f);
+ std::vector<float> input_data{-0.8, 0.2, 0.9, 0.7, 0.1, -0.3};
+ std::vector<int32_t> paddings_data{0, 0, 0, 2, 1, 3, 0, 0};
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 3, 1}, quant_param.first,
+ quant_param.second, input_data);
+ Tensor paddings_tensor = makeInputTensor<DataType::S32>({4, 2}, paddings_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
+
+ Pad kernel(&input_tensor, &paddings_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{0, -0.8, 0.2, 0.9, 0, 0, 0, 0, 0.7, 0.1, -0.3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(ref_output_data, kQuantizedTolerance));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 4, 7, 1}));
+}
+
+TEST(Pad, Float)
+{
+ std::vector<float> input_data{1, 2, 3, 4, 5, 6};
+ std::vector<int32_t> paddings_data{1, 0, 0, 2, 0, 3, 0, 0};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1, 2, 3, 1}, input_data);
+ Tensor paddings_tensor = makeInputTensor<DataType::S32>({4, 2}, paddings_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Pad kernel(&input_tensor, &paddings_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> 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<int32_t> ref_output_shape{2, 4, 6, 1};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Pow.cpp b/compiler/luci-interpreter/src/kernels/Pow.cpp
new file mode 100644
index 000000000..a0c092d33
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Pow.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Pow.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Pow::Pow(const Tensor *input1, const Tensor *input2, Tensor *output)
+ : Kernel({input1, input2}, {output})
+{
+}
+
+void Pow::configure()
+{
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type());
+ LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type());
+
+ output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape()));
+}
+
+void Pow::execute() const
+{
+ switch (input1()->element_type())
+ {
+ case DataType::FLOAT32:
+ eval<float>();
+ break;
+ case DataType::S32:
+ eval<int32_t>();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+template <typename T> void Pow::eval() const
+{
+ tflite::ArithmeticParams params{};
+
+ const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes(
+ getTensorShape(input1()), getTensorShape(input2()), &params);
+
+ if (need_broadcast)
+ {
+ tflite::reference_ops::BroadcastPow4DSlow(getTensorShape(input1()), getTensorData<T>(input1()),
+ getTensorShape(input2()), getTensorData<T>(input2()),
+ getTensorShape(output()), getTensorData<T>(output()));
+ }
+ else
+ {
+ tflite::reference_ops::Pow(getTensorShape(input1()), getTensorData<T>(input1()),
+ getTensorShape(input2()), getTensorData<T>(input2()),
+ getTensorShape(output()), getTensorData<T>(output()));
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Pow.h b/compiler/luci-interpreter/src/kernels/Pow.h
new file mode 100644
index 000000000..8ff865e40
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Pow.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_POW_H
+#define LUCI_INTERPRETER_KERNELS_POW_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Pow : public Kernel
+{
+public:
+ Pow(const Tensor *input1, const Tensor *input2, Tensor *output);
+
+ const Tensor *input1() const { return _inputs[0]; }
+ const Tensor *input2() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ template <typename T> void eval() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_POW_H
diff --git a/compiler/luci-interpreter/src/kernels/Pow.test.cpp b/compiler/luci-interpreter/src/kernels/Pow.test.cpp
new file mode 100644
index 000000000..a414440c9
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Pow.test.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Pow.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(PowTest, SimplePow)
+{
+ std::initializer_list<int32_t> base_shape = {1, 1, 3, 2};
+
+ std::vector<float> input1_data{0.3f, 2.3f, 0.9f, 0.5f, 0.8f, 1.1f};
+ std::vector<float> input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f};
+ std::vector<float> test_outputs{0.786f, 1.2838f, 1.043f, 0.7071f, 0.8f, 1.08956f};
+
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>(base_shape, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>(base_shape, input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Pow kernel(&input1_tensor, &input2_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs, 0.0001f));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(base_shape));
+}
+
+TEST(PowTest, FloatBroadcastPow)
+{
+ std::initializer_list<int32_t> input1_shape = {1, 3};
+ std::initializer_list<int32_t> input2_shape = {3, 1};
+
+ std::vector<float> input1_data{0.3f, 2.3f, 0.9f};
+ std::vector<float> input2_data{0.2f, 0.3f, 0.4f};
+ std::vector<float> test_outputs{0.786f, 1.18126f, 0.9791f, 0.6968f, 1.28386f,
+ 0.96888f, 0.6178f, 1.3953f, 0.9587f};
+
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>(input1_shape, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>(input2_shape, input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Pow kernel(&input1_tensor, &input2_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs, 0.0001f));
+}
+
+TEST(PowTest, IntPow)
+{
+ std::initializer_list<int32_t> base_shape = {1, 3};
+
+ std::vector<int32_t> input_data{2, 3, 4};
+ std::vector<int32_t> test_outputs{4, 27, 256};
+
+ Tensor input1_tensor = makeInputTensor<DataType::S32>(base_shape, input_data);
+ Tensor input2_tensor = makeInputTensor<DataType::S32>(base_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S32);
+
+ Pow kernel(&input1_tensor, &input2_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<int32_t>(output_tensor), ::testing::ElementsAreArray(test_outputs));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(base_shape));
+}
+
+TEST(PowTest, Input_Output_Type_NEG)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.0f});
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.0f});
+ Tensor output_tensor = makeOutputTensor(DataType::BOOL);
+
+ Pow kernel(&input1_tensor, &input2_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(PowTest, Input_Type_Mismatch_NEG)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.0f});
+ Tensor input2_tensor = makeInputTensor<DataType::S32>({1}, {4});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Pow kernel(&input1_tensor, &input2_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(PowTest, Invalid_Input_Type_NEG)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::S64>({1}, {1});
+ Tensor input2_tensor = makeInputTensor<DataType::S64>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::S64);
+
+ Pow kernel(&input1_tensor, &input2_tensor, &output_tensor);
+ kernel.configure();
+ EXPECT_ANY_THROW(kernel.execute());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Prelu.cpp b/compiler/luci-interpreter/src/kernels/Prelu.cpp
new file mode 100644
index 000000000..e658d87b5
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Prelu.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Prelu.h"
+
+#include "kernels/BinaryOpCommon.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Prelu::Prelu(const Tensor *input, const Tensor *alpha, Tensor *output)
+ : Kernel({input, alpha}, {output})
+{
+}
+
+void Prelu::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ LUCI_INTERPRETER_CHECK(alpha()->element_type() == output()->element_type());
+
+ if (input()->element_type() == DataType::U8 || input()->element_type() == DataType::S16)
+ {
+ if (input()->element_type() == DataType::S16)
+ {
+ LUCI_INTERPRETER_CHECK(input()->zero_point() == 0 && alpha()->zero_point() == 0 &&
+ output()->zero_point() == 0);
+ }
+ double alpha_multiplier = input()->scale() * alpha()->scale() / output()->scale();
+ quantizeMultiplier(alpha_multiplier, &_output_multiplier_alpha, &_output_shift_alpha);
+ double identity_multiplier = input()->scale() / output()->scale();
+ quantizeMultiplier(identity_multiplier, &_output_multiplier_identity, &_output_shift_identity);
+ }
+ output()->resize(calculateShapeForBroadcast(input()->shape(), alpha()->shape()));
+}
+
+void Prelu::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ case DataType::S16:
+ evalQuantizedS16();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Prelu::evalFloat() const
+{
+ const auto input_data = getTensorData<float>(input());
+ const auto alpha_data = getTensorData<float>(alpha());
+ const auto size = getTensorShape(input()).FlatSize();
+ auto output_data = getTensorData<float>(output());
+
+ auto PreluFunc = [](float input, float alpha) { return input >= 0.0 ? input : input * alpha; };
+
+ if (input()->shape() != alpha()->shape())
+ {
+ tflite::reference_ops::BroadcastBinaryFunction4DSlow<float, float, float>(
+ getTensorShape(input()), getTensorData<float>(input()), getTensorShape(alpha()),
+ getTensorData<float>(alpha()), getTensorShape(output()), getTensorData<float>(output()),
+ PreluFunc);
+ }
+ else
+ {
+ for (auto i = decltype(size){0}; i < size; ++i)
+ {
+ if (input_data[i] >= 0)
+ output_data[i] = input_data[i];
+ else
+ output_data[i] = input_data[i] * alpha_data[i];
+ }
+ }
+}
+
+void Prelu::evalQuantized() const
+{
+ tflite::PreluParams op_params{};
+
+ op_params.input_offset = -input()->zero_point(); // Note the '-'.
+ op_params.alpha_offset = -alpha()->zero_point(); // Note the '-'.
+ op_params.output_offset = output()->zero_point();
+ op_params.output_shift_1 = _output_shift_identity;
+ op_params.output_multiplier_1 = _output_multiplier_identity;
+ op_params.output_shift_2 = _output_shift_alpha;
+ op_params.output_multiplier_2 = _output_multiplier_alpha;
+
+ if (input()->shape() != alpha()->shape())
+ {
+ tflite::reference_ops::BroadcastPrelu4DSlow(
+ op_params, getTensorShape(input()), getTensorData<uint8_t>(input()),
+ getTensorShape(alpha()), getTensorData<uint8_t>(alpha()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ }
+ else
+ {
+ tflite::reference_ops::Prelu<uint8_t>(op_params, getTensorShape(input()),
+ getTensorData<uint8_t>(input()), getTensorShape(alpha()),
+ getTensorData<uint8_t>(alpha()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ }
+}
+
+void Prelu::evalQuantizedS16() const
+{
+ constexpr int32_t quantized_min = std::numeric_limits<int16_t>::min();
+ constexpr int32_t quantized_max = std::numeric_limits<int16_t>::max();
+
+ auto fn = [this, quantized_min, quantized_max](int16_t input_val, int16_t alpha_val) {
+ const int32_t output_val =
+ input_val >= 0
+ ? tflite::MultiplyByQuantizedMultiplier(input_val, _output_multiplier_identity,
+ _output_shift_identity)
+ : tflite::MultiplyByQuantizedMultiplier(input_val * alpha_val, _output_multiplier_alpha,
+ _output_shift_alpha);
+ const int32_t clamped_output = std::min(quantized_max, std::max(quantized_min, output_val));
+ return static_cast<int16_t>(clamped_output);
+ };
+
+ BinaryOpBroadcastSlow(getTensorShape(input()), getTensorData<int16_t>(input()),
+ getTensorShape(alpha()), getTensorData<int16_t>(alpha()),
+ getTensorShape(output()), getTensorData<int16_t>(output()), fn);
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Prelu.h b/compiler/luci-interpreter/src/kernels/Prelu.h
new file mode 100644
index 000000000..c7911a63f
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Prelu.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_PRELU_H
+#define LUCI_INTERPRETER_KERNELS_PRELU_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Prelu : public Kernel
+{
+public:
+ Prelu(const Tensor *input, const Tensor *alpha, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ const Tensor *alpha() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+ void evalQuantizedS16() const;
+
+private:
+ int32_t _output_multiplier_alpha = 0;
+ int32_t _output_shift_alpha = 0;
+ int32_t _output_multiplier_identity = 0;
+ int32_t _output_shift_identity = 0;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_PRELU_H
diff --git a/compiler/luci-interpreter/src/kernels/Prelu.test.cpp b/compiler/luci-interpreter/src/kernels/Prelu.test.cpp
new file mode 100644
index 000000000..30702c826
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Prelu.test.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Prelu.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T>
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> alpha_shape,
+ std::initializer_list<int32_t> output_shape, std::initializer_list<T> input_data,
+ std::initializer_list<T> alpha_data, std::initializer_list<T> output_data)
+{
+ constexpr DataType element_type = getElementType<T>();
+ Tensor input_tensor = makeInputTensor<element_type>(input_shape, input_data);
+ Tensor alpha_tensor = makeInputTensor<element_type>(alpha_shape, alpha_data);
+ Tensor output_tensor = makeOutputTensor(element_type);
+
+ Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor);
+
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<T>(output_tensor), ::testing::ElementsAreArray(output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+TEST(PreluTest, FloatSimple)
+{
+ Check<float>(/*input_shape=*/{2, 3}, /*alpha_shape=*/{2, 3},
+ /*output_shape=*/{2, 3},
+ /*input_data=*/
+ {
+ 0.0f, 1.0f, 3.0f, // Row 1
+ 1.0f, -1.0f, -2.0f, // Row 2
+ },
+ /*alpha_data=*/
+ {
+ 0.0f, 0.5f, 0.1f, // Row 1
+ 0.0f, 0.5f, 0.1f, // Row 2
+ },
+ /*output_data=*/
+ {
+ 0.0f, 1.0f, 3.0f, // Row 1
+ 1.0f, -0.5f, -0.2f, // Row 2
+ });
+
+ SUCCEED();
+}
+
+TEST(PreluTest, FloatBroadcast)
+{
+ Check<float>(/*input_shape=*/{1, 2, 2, 3}, /*alpha_shape=*/{1, 1, 3},
+ /*output_shape=*/{1, 2, 2, 3},
+ /*input_data=*/
+ {
+ 0.0f, 0.0f, 0.0f, // Row 1, Column 1
+ 1.0f, 1.0f, 1.0f, // Row 1, Column 2
+ -1.0f, -1.0f, -1.0f, // Row 2, Column 1
+ -2.0f, -2.0f, -2.0f, // Row 2, Column 2
+ },
+ /*alpha_data=*/
+ {0.0f, 1.0f, 2.0f},
+ /*output_data=*/
+ {
+ 0.0f, 0.0f, 0.0f, // Row 1, Column 1
+ 1.0f, 1.0f, 1.0f, // Row 1, Column 2
+ 0.0f, -1.0f, -2.0f, // Row 2, Column 1
+ 0.0f, -2.0f, -4.0f, // Row 2, Column 2
+ });
+
+ SUCCEED();
+}
+
+float GetTolerance(float min, float max) { return (max - min) / 255.0; }
+
+TEST(PreluTest, Uint8Simple)
+{
+ std::vector<float> input_data{-0.8f, 0.2f, 0.9f, 0.7f, 0.1f, -0.4f};
+ std::vector<float> alpha_data{0.5f, 0.5f, 0.5f, 0.25f, 1.0f, 0.25f};
+ std::vector<float> ref_output_data{-0.4f, 0.2f, 0.9f, 0.7f, 0.1f, -0.1f};
+
+ float kQuantizedTolerance = GetTolerance(-1.0, 1.0);
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-1.0f, 1.0f);
+
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 3, 1}, quant_param.first,
+ quant_param.second, input_data);
+ Tensor alpha_tensor = makeInputTensor<DataType::U8>({1, 2, 3, 1}, quant_param.first,
+ quant_param.second, alpha_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
+
+ Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(ref_output_data, kQuantizedTolerance));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 3, 1}));
+
+ SUCCEED();
+}
+
+TEST(PreluTest, Uint8Broadcast)
+{
+ std::vector<float> input_data{
+ 0.0f, 0.0f, 0.0f, // Row 1, Column 1
+ 0.5f, 0.5f, 0.5f, // Row 1, Column 2
+ -1.0f, -1.0f, -1.0f, // Row 2, Column 1
+ -0.25f, -0.25f, -0.25f, // Row 2, Column 2
+ };
+ std::vector<float> alpha_data{0.0f, 0.5f, -0.5f};
+ std::vector<float> ref_output_data{
+ 0.0f, 0.0f, 0.0f, // Row 1, Column 1
+ 0.5f, 0.5f, 0.5f, // Row 1, Column 2
+ 0.0f, -0.5f, 0.5f, // Row 2, Column 1
+ 0.0f, -0.125f, 0.125f // Row 2, Column 2
+ };
+ std::vector<float> ref_quant_output_data{
+ 128, 128, 128, // Row 1, Column 1
+ 192, 192, 192, // Row 1, Column 2
+ 128, 64, 192, // Row 2, Column 1
+ 128, 112, 144 // Row 2, Column 2
+ };
+ float kQuantizedTolerance = 2 * (1. / 256);
+ const float kMin = -1;
+ const float kMax = 127.f / 128.f;
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(kMin, kMax);
+
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 2, 3}, quant_param.first,
+ quant_param.second, input_data);
+ Tensor alpha_tensor =
+ makeInputTensor<DataType::U8>({1, 1, 3}, quant_param.first, quant_param.second, alpha_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
+
+ Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(ref_output_data, kQuantizedTolerance));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 2, 3}));
+ EXPECT_THAT(extractTensorData<uint8_t>(output_tensor),
+ ::testing::ElementsAreArray(ref_quant_output_data));
+}
+
+TEST(PreluTest, SInt16Simple)
+{
+ std::vector<float> input_data{-0.8f, 0.2f, 0.9f, 0.7f, 0.1f, -0.4f};
+ std::vector<float> alpha_data{0.5f, 0.5f, 0.5f, 0.25f, 1.0f, 0.25f};
+ std::vector<float> ref_output_data{-0.4f, 0.2f, 0.9f, 0.7f, 0.1f, -0.1f};
+
+ Tensor input_tensor = makeInputTensor<DataType::S16>({1, 2, 3, 1}, 0.1, 0, input_data);
+ Tensor alpha_tensor = makeInputTensor<DataType::S16>({1, 2, 3, 1}, 0.1, 0, alpha_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 0.1, 0);
+
+ Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 3, 1}));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(PreluTest, SInt16Broadcast)
+{
+ std::vector<float> input_data{
+ 0.0f, 0.0f, 0.0f, // Row 1, Column 1
+ 0.5f, 0.5f, 0.5f, // Row 1, Column 2
+ -1.0f, -1.0f, -1.0f, // Row 2, Column 1
+ -0.25f, -0.25f, -0.25f, // Row 2, Column 2
+ };
+ std::vector<float> alpha_data{0.0f, 0.5f, -0.5f};
+ std::vector<float> ref_output_data{
+ 0.0f, 0.0f, 0.0f, // Row 1, Column 1
+ 0.5f, 0.5f, 0.5f, // Row 1, Column 2
+ 0.0f, -0.5f, 0.5f, // Row 2, Column 1
+ 0.0f, -0.125f, 0.125f // Row 2, Column 2
+ };
+
+ Tensor input_tensor = makeInputTensor<DataType::S16>({1, 2, 2, 3}, 0.01, 0, input_data);
+ Tensor alpha_tensor = makeInputTensor<DataType::S16>({1, 1, 3}, 0.1, 0, alpha_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 0.001, 0);
+
+ Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 2, 3}));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(PreluTest, Input_Output_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor alpha_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(PreluTest, Input_Alpha_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor alpha_tensor = makeInputTensor<DataType::U8>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(PreluTest, Invalid_Input_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::S64>({1}, {1});
+ Tensor alpha_tensor = makeInputTensor<DataType::S64>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::S64);
+
+ Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor);
+ kernel.configure();
+ EXPECT_ANY_THROW(kernel.execute());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Relu.cpp b/compiler/luci-interpreter/src/kernels/Relu.cpp
new file mode 100644
index 000000000..a2e02d708
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Relu.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Relu.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Relu::Relu(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {}
+
+void Relu::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ if (input()->element_type() == DataType::S16)
+ {
+ LUCI_INTERPRETER_CHECK(input()->zero_point() == 0 && output()->zero_point() == 0);
+ }
+
+ if (input()->element_type() == DataType::U8 || input()->element_type() == DataType::S16)
+ {
+ double multiplier = input()->scale() / output()->scale();
+ quantizeMultiplier(multiplier, &_output_multiplier, &_output_shift);
+ }
+ output()->resize(input()->shape());
+}
+
+void Relu::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ case DataType::S16:
+ evalQuantizedS16();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Relu::evalFloat() const
+{
+ const auto input_data = getTensorData<float>(input());
+ const auto input_shape = getTensorShape(input());
+ auto output_data = getTensorData<float>(output());
+ auto output_shape = getTensorShape(output());
+
+ tflite::optimized_ops::Relu(input_shape, input_data, output_shape, output_data);
+}
+
+void Relu::evalQuantized() const
+{
+ tflite::ReluParams params;
+ params.input_offset = input()->zero_point();
+ params.output_offset = output()->zero_point();
+ params.output_multiplier = _output_multiplier;
+ params.output_shift = _output_shift;
+
+ params.quantized_activation_min =
+ std::max(static_cast<int32_t>(std::numeric_limits<uint8_t>::min()), params.output_offset);
+ params.quantized_activation_max = static_cast<int32_t>(std::numeric_limits<uint8_t>::max());
+
+ tflite::optimized_ops::ReluX(params, getTensorShape(input()), getTensorData<uint8_t>(input()),
+ getTensorShape(output()), getTensorData<uint8_t>(output()));
+}
+
+void Relu::evalQuantizedS16() const
+{
+ const auto *input_data = getTensorData<int16_t>(input());
+ auto *output_data = getTensorData<int16_t>(output());
+
+ constexpr int32_t output_min = 0;
+ constexpr int32_t output_max = std::numeric_limits<int16_t>::max();
+
+ const int32_t num_elements = input()->shape().num_elements();
+
+ for (int32_t i = 0; i < num_elements; ++i)
+ {
+ const int32_t input_val = input_data[i];
+ int32_t output_val =
+ tflite::MultiplyByQuantizedMultiplier(input_val, _output_multiplier, _output_shift);
+ output_val = std::max(output_val, output_min);
+ output_val = std::min(output_val, output_max);
+ output_data[i] = static_cast<int16_t>(output_val);
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Relu.h b/compiler/luci-interpreter/src/kernels/Relu.h
new file mode 100644
index 000000000..b813f0cdf
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Relu.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_RELU_H
+#define LUCI_INTERPRETER_KERNELS_RELU_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Relu : public Kernel
+{
+public:
+ Relu(const Tensor *input, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+ void evalQuantizedS16() const;
+
+private:
+ int32_t _output_multiplier{0};
+ int32_t _output_shift{0};
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_RELU_H
diff --git a/compiler/luci-interpreter/src/kernels/Relu.test.cpp b/compiler/luci-interpreter/src/kernels/Relu.test.cpp
new file mode 100644
index 000000000..cabefa733
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Relu.test.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Relu.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(ReluTest, FloatSimple)
+{
+ std::vector<float> input_data{
+ 0.0f, 1.0f, 3.0f, // Row 1
+ 1.0f, -1.0f, -2.0f, // Row 2
+ };
+
+ std::vector<float> ref_output_data{
+ 0.0f, 1.0f, 3.0f, // Row 1
+ 1.0f, 0.0f, 0.0f, // Row 2
+ };
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Relu kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor),
+ ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3}));
+}
+
+TEST(ReluTest, Uint8Quantized)
+{
+ std::vector<float> input_data{
+ 0, -6, 2, 4, //
+ 3, -2, 7, 1, //
+ };
+ // Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+ const float f_min = (-128.0 / 128.0) * 8;
+ const float f_max = (127.0 / 128.0) * 8;
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(f_min, f_max);
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first,
+ quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
+
+ Relu kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<uint8_t>(output_tensor),
+ ::testing::ElementsAreArray({128, 128, 160, 192, 176, 128, 240, 144}));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear({0, 0, 2, 4, 3, 0, 7, 1}));
+}
+
+TEST(ReluTest, Uint8Requantized)
+{
+ std::vector<float> input_data{
+ 0, -6, 2, 4, //
+ 3, -2, 7, 1, //
+ };
+
+ // Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+ const float in_min = (-128.0 / 128.0) * 8;
+ const float in_max = (127.0 / 128.0) * 8;
+ const float out_min = (0.0 / 256.0) * 8;
+ const float out_max = (255.0 / 256.0) * 8;
+
+ std::pair<float, int32_t> quant_input = quantizationParams<uint8_t>(in_min, in_max);
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_input.first,
+ quant_input.second, input_data);
+
+ std::pair<float, int32_t> quant_output = quantizationParams<uint8_t>(out_min, out_max);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_output.first, quant_output.second);
+
+ Relu kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<uint8_t>(output_tensor),
+ ::testing::ElementsAreArray({0, 0, 64, 128, 96, 0, 224, 32}));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear({0, 0, 2, 4, 3, 0, 7, 1}));
+}
+
+TEST(ReluTest, SInt16)
+{
+ std::vector<float> input_data{
+ 0, -6, 2, 4, //
+ 3, -2, 7, 1, //
+ };
+ std::vector<float> ref_output_data{
+ 0, 0, 2, 4, //
+ 3, 0, 7, 1, //
+ };
+
+ Tensor input_tensor = makeInputTensor<DataType::S16>({1, 2, 4, 1}, 0.5, 0, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 0.25, 0);
+
+ Relu kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(ReluTest, Input_Output_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ Relu kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ReluTest, Invalid_Input_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::S64>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::S64);
+
+ Relu kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ EXPECT_ANY_THROW(kernel.execute());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Relu6.cpp b/compiler/luci-interpreter/src/kernels/Relu6.cpp
new file mode 100644
index 000000000..1046ef27b
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Relu6.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Relu6.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Relu6::Relu6(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {}
+
+void Relu6::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+
+ if (input()->element_type() == DataType::U8)
+ {
+ double multiplier = input()->scale() / output()->scale();
+ quantizeMultiplier(multiplier, &_output_multiplier, &_output_shift);
+ }
+ output()->resize(input()->shape());
+}
+
+void Relu6::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Relu6::evalFloat() const
+{
+ const auto input_data = getTensorData<float>(input());
+ const auto input_shape = getTensorShape(input());
+ auto output_data = getTensorData<float>(output());
+ auto output_shape = getTensorShape(output());
+
+ tflite::optimized_ops::Relu6(input_shape, input_data, output_shape, output_data);
+}
+
+void Relu6::evalQuantized() const
+{
+ tflite::ReluParams params;
+ params.input_offset = input()->zero_point();
+ params.output_offset = output()->zero_point();
+ params.output_multiplier = _output_multiplier;
+ params.output_shift = _output_shift;
+
+ params.quantized_activation_min =
+ std::max(static_cast<int32_t>(std::numeric_limits<uint8_t>::min()), params.output_offset);
+ params.quantized_activation_max =
+ std::min(static_cast<int32_t>(std::numeric_limits<uint8_t>::max()),
+ params.output_offset + static_cast<int32>(roundf(6.f / output()->scale())));
+
+ tflite::optimized_ops::ReluX(params, getTensorShape(input()), getTensorData<uint8_t>(input()),
+ getTensorShape(output()), getTensorData<uint8_t>(output()));
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Relu6.h b/compiler/luci-interpreter/src/kernels/Relu6.h
new file mode 100644
index 000000000..f5030b588
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Relu6.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_RELU6_H
+#define LUCI_INTERPRETER_KERNELS_RELU6_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Relu6 : public Kernel
+{
+public:
+ Relu6(const Tensor *input, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+
+private:
+ int32_t _output_multiplier{0};
+ int32_t _output_shift{0};
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_RELU6_H
diff --git a/compiler/luci-interpreter/src/kernels/Relu6.test.cpp b/compiler/luci-interpreter/src/kernels/Relu6.test.cpp
new file mode 100644
index 000000000..a7f104d85
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Relu6.test.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Relu6.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(Relu6Test, FloatSimple)
+{
+ std::vector<float> input_data{
+ 0.0f, 1.0f, 3.0f, // Row 1
+ 7.0f, -1.0f, -2.0f, // Row 2
+ };
+
+ std::vector<float> ref_output_data{
+ 0.0f, 1.0f, 3.0f, // Row 1
+ 6.0f, 0.0f, 0.0f, // Row 2
+ };
+
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Relu6 kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor),
+ ::testing::ElementsAreArray(ref_output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({2, 3}));
+}
+
+TEST(Relu6Test, Uint8Quantized)
+{
+ // Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+ const float f_min = (-128.0 / 128.0) * 10;
+ const float f_max = (127.0 / 128.0) * 10;
+ const float tolerance = (f_max - f_min) / 255.0;
+
+ std::vector<float> input_data{
+ 0, -6, 2, 8, //
+ -2, 3, 7, 1, //
+ };
+
+ std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(f_min, f_max);
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first,
+ quant_param.second, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second);
+
+ Relu6 kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<uint8_t>(output_tensor),
+ ::testing::ElementsAreArray({128, 128, 154, 205, 128, 166, 205, 141}));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear({0, 0, 2, 6, 0, 3, 6, 1}, tolerance));
+}
+
+TEST(Relu6Test, Uint8Requantized)
+{
+ // Choose min / max in such a way that there are exactly 256 units to avoid rounding errors.
+ const float in_min = (-128.0 / 128.0) * 10;
+ const float in_max = (127.0 / 128.0) * 10;
+ const float out_min = (0.0 / 256.0) * 0;
+ const float out_max = (255.0 / 256.0) * 6;
+ const float tolerance = (in_max - in_min) / 255.0;
+
+ std::vector<float> input_data{
+ 0, -6, 2, 8, //
+ -2, 3, 7, 1, //
+ };
+
+ std::pair<float, int32_t> quant_input = quantizationParams<uint8_t>(in_min, in_max);
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_input.first,
+ quant_input.second, input_data);
+
+ std::pair<float, int32_t> quant_output = quantizationParams<uint8_t>(out_min, out_max);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_output.first, quant_output.second);
+
+ Relu6 kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 4, 1}));
+ EXPECT_THAT(extractTensorData<uint8_t>(output_tensor),
+ ::testing::ElementsAreArray({0, 0, 87, 255, 0, 127, 255, 43}));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear({0, 0, 2, 6, 0, 3, 6, 1}, tolerance));
+}
+
+TEST(Relu6Test, Input_Output_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ Relu6 kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(Relu6Test, Invalid_Input_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::S64>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::S64);
+
+ Relu6 kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ EXPECT_ANY_THROW(kernel.execute());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Reshape.cpp b/compiler/luci-interpreter/src/kernels/Reshape.cpp
new file mode 100644
index 000000000..d88b5392a
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Reshape.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Reshape.h"
+
+#include <cassert>
+#include <cstring>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+static Shape extractShapeFromTensor(const Tensor *tensor)
+{
+ assert(tensor->element_type() == DataType::S32);
+ Shape shape(tensor->shape().num_elements());
+ const auto *shape_data = tensor->data<int32_t>();
+ for (int i = 0; i < tensor->shape().num_elements(); ++i)
+ {
+ shape.dim(i) = shape_data[i];
+ }
+ return shape;
+}
+
+static void resolveUnknownDimension(const Shape &input_shape, Shape *output_shape)
+{
+ const int32_t num_input_elements = input_shape.num_elements();
+ int32_t num_output_elements = 1;
+ int unknown_dim_index = -1;
+ for (int i = 0; i < output_shape->num_dims(); ++i)
+ {
+ const int32_t value = output_shape->dim(i);
+ if (value == -1)
+ {
+ assert(unknown_dim_index == -1);
+ unknown_dim_index = i;
+ }
+ else
+ {
+ num_output_elements *= value;
+ }
+ }
+ if (unknown_dim_index != -1)
+ {
+ output_shape->dim(unknown_dim_index) = num_input_elements / num_output_elements;
+ num_output_elements *= output_shape->dim(unknown_dim_index);
+ }
+ assert(num_output_elements == num_input_elements);
+}
+
+Reshape::Reshape(const Tensor *input, const Tensor *shape, Tensor *output)
+ : Kernel({input, shape}, {output})
+{
+}
+
+void Reshape::configure()
+{
+ Shape output_shape = extractShapeFromTensor(shape());
+ resolveUnknownDimension(input()->shape(), &output_shape);
+ output()->resize(output_shape);
+}
+
+void Reshape::execute() const
+{
+ const auto *input_data = input()->data<void>();
+ auto *output_data = output()->data<void>();
+
+ const size_t element_size = getDataTypeSize(input()->element_type());
+ const int32_t num_elements = input()->shape().num_elements();
+ std::memcpy(output_data, input_data, num_elements * element_size);
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Reshape.h b/compiler/luci-interpreter/src/kernels/Reshape.h
new file mode 100644
index 000000000..99b947f77
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Reshape.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_RESHAPE_H
+#define LUCI_INTERPRETER_KERNELS_RESHAPE_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Reshape : public Kernel
+{
+public:
+ Reshape(const Tensor *input, const Tensor *shape, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ const Tensor *shape() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_RESHAPE_H
diff --git a/compiler/luci-interpreter/src/kernels/Reshape.test.cpp b/compiler/luci-interpreter/src/kernels/Reshape.test.cpp
new file mode 100644
index 000000000..38159380f
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Reshape.test.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Reshape.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+// TODO Test types other than FLOAT32.
+
+TEST(ReshapeTest, Regular)
+{
+ Shape input_shape{1, 2, 2, 3};
+ std::vector<float> input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+ Shape shape_shape{2};
+ std::vector<int32_t> shape_data{3, 4};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor shape_tensor = makeInputTensor<DataType::S32>(shape_shape, shape_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Reshape kernel(&input_tensor, &shape_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(input_data));
+}
+
+TEST(ReshapeTest, UnknownDimension)
+{
+ Shape input_shape{2, 1, 2, 3};
+ std::vector<float> input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+ Shape shape_shape{3};
+ std::vector<int32_t> shape_data{2, -1, 2};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor shape_tensor = makeInputTensor<DataType::S32>(shape_shape, shape_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Reshape kernel(&input_tensor, &shape_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(input_data));
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/ResizeBilinear.cpp b/compiler/luci-interpreter/src/kernels/ResizeBilinear.cpp
new file mode 100644
index 000000000..9385855cf
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/ResizeBilinear.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/ResizeBilinear.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+ResizeBilinear::ResizeBilinear(const Tensor *input, const Tensor *size, Tensor *output,
+ const ResizeBilinearParams &params)
+ : KernelWithParams<ResizeBilinearParams>({input, size}, {output}, params)
+{
+}
+
+void ResizeBilinear::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->shape().num_dims() == 4);
+ LUCI_INTERPRETER_CHECK(size()->shape().num_dims() == 1);
+ LUCI_INTERPRETER_CHECK(size()->element_type() == DataType::S32);
+ if (params().half_pixel_centers && params().align_corners)
+ throw std::runtime_error("If half_pixel_centers is True, align_corners must be False.");
+ LUCI_INTERPRETER_CHECK(size()->shape().dim(0) == 2);
+ Shape output_shape(4);
+ output_shape.dim(0) = input()->shape().dim(0);
+ output_shape.dim(1) = getTensorData<int32_t>(size())[0];
+ output_shape.dim(2) = getTensorData<int32_t>(size())[1];
+ output_shape.dim(3) = input()->shape().dim(3);
+ output()->resize(output_shape);
+}
+
+void ResizeBilinear::execute() const
+{
+ tflite::ResizeBilinearParams op_params{};
+ op_params.align_corners = params().align_corners;
+ op_params.half_pixel_centers = params().half_pixel_centers;
+ switch (output()->element_type())
+ {
+ case DataType::FLOAT32:
+ tflite::optimized_ops::ResizeBilinear(
+ op_params, getTensorShape(input()), getTensorData<float>(input()), getTensorShape(size()),
+ getTensorData<int32_t>(size()), getTensorShape(output()), getTensorData<float>(output()));
+ break;
+ case DataType::U8:
+ tflite::optimized_ops::ResizeBilinear(
+ op_params, getTensorShape(input()), getTensorData<uint8_t>(input()),
+ getTensorShape(size()), getTensorData<int32_t>(size()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/ResizeBilinear.h b/compiler/luci-interpreter/src/kernels/ResizeBilinear.h
new file mode 100644
index 000000000..b7bdc2ab7
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/ResizeBilinear.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_RESIZEBILINEAR_H
+#define LUCI_INTERPRETER_KERNELS_RESIZEBILINEAR_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class ResizeBilinear : public KernelWithParams<ResizeBilinearParams>
+{
+public:
+ ResizeBilinear(const Tensor *input, const Tensor *shape, Tensor *output,
+ const ResizeBilinearParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ const Tensor *size() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_RESIZEBILINEAR_H
diff --git a/compiler/luci-interpreter/src/kernels/ResizeBilinear.test.cpp b/compiler/luci-interpreter/src/kernels/ResizeBilinear.test.cpp
new file mode 100644
index 000000000..51c1359da
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/ResizeBilinear.test.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/ResizeBilinear.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T>
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> size_shape,
+ std::initializer_list<int32_t> output_shape, std::initializer_list<float> input_data,
+ std::initializer_list<int32_t> size_data, std::initializer_list<float> output_data,
+ bool align_corners, bool half_pixel_centers)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor size_tensor = makeInputTensor<DataType::S32>(size_shape, size_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ResizeBilinearParams params{};
+ params.align_corners = align_corners;
+ params.half_pixel_centers = half_pixel_centers;
+
+ ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+ EXPECT_THAT(extractTensorData<T>(output_tensor), FloatArrayNear(output_data));
+}
+
+template <>
+void Check<uint8_t>(std::initializer_list<int32_t> input_shape,
+ std::initializer_list<int32_t> size_shape,
+ std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data,
+ std::initializer_list<int32_t> size_data,
+ std::initializer_list<float> output_data, bool align_corners,
+ bool half_pixel_centers)
+{
+ // On TFlite example use Uint8 value it self, so this means quant param scale 1.0f and zero
+ // point 0.
+ Tensor input_tensor = makeInputTensor<DataType::U8>(input_shape, 1.0, 0, input_data);
+ Tensor size_tensor = makeInputTensor<DataType::S32>(size_shape, size_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 1.0, 0);
+
+ ResizeBilinearParams params{};
+ params.align_corners = align_corners;
+ params.half_pixel_centers = half_pixel_centers;
+
+ ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data, output_tensor.scale()));
+}
+
+template <typename T> class ResizeBilinearTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(ResizeBilinearTest, DataTypes);
+
+TYPED_TEST(ResizeBilinearTest, SimpleTest)
+{
+ Check<TypeParam>({2, 2, 2, 1}, {2}, {2, 3, 3, 1},
+ {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ },
+ {3, 3},
+ {
+ 3, 5, 6, //
+ 7, 9, 10, //
+ 9, 11, 12, //
+ 4, 8, 10, //
+ 8, 12, 14, //
+ 10, 14, 16, //
+ },
+ false, false);
+ SUCCEED();
+}
+
+TEST(ResizeBilinearTest, HalfPixelCenterFloatTest)
+{
+ Check<float>({2, 2, 2, 1}, {2}, {2, 3, 3, 1},
+ {
+ 1, 2, //
+ 3, 4, //
+ 1, 2, //
+ 3, 4 //
+ },
+ {3, 3},
+ {
+ 1, 1.5, 2, //
+ 2, 2.5, 3, //
+ 3, 3.5, 4, //
+ 1, 1.5, 2, //
+ 2, 2.5, 3, //
+ 3, 3.5, 4, //
+ },
+ false, true);
+ SUCCEED();
+}
+
+TEST(ResizeBilinearTest, HalfPixelCenterUint8Test)
+{
+ Check<uint8_t>({2, 2, 2, 1}, {2}, {2, 3, 3, 1},
+ {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 12, 16 //
+ },
+ {3, 3},
+ {
+ 2, 4, 6, //
+ 6, 7, 9, //
+ 9, 10, 12, //
+ 4, 7, 10, //
+ 8, 10, 13, //
+ 12, 14, 16, //
+ },
+ false, true);
+ SUCCEED();
+}
+
+TEST(ResizeBilinearTest, InputShapeInvalid_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2}, {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ });
+ Tensor size_tensor = makeInputTensor<DataType::S32>({2}, {3, 3});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ResizeBilinearParams params{};
+ params.align_corners = false;
+ params.half_pixel_centers = false;
+
+ ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ResizeBilinearTest, SizeShapeInvalid_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2, 1}, {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ });
+ Tensor size_tensor = makeInputTensor<DataType::S32>({2, 1}, {3, 3});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ResizeBilinearParams params{};
+ params.align_corners = false;
+ params.half_pixel_centers = false;
+
+ ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ResizeBilinearTest, SizeDimInvalid_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2, 1}, {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ });
+ Tensor size_tensor = makeInputTensor<DataType::S32>({3}, {3, 3, 1});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ResizeBilinearParams params{};
+ params.align_corners = false;
+ params.half_pixel_centers = false;
+
+ ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ResizeBilinearTest, InvalidParams_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2, 1}, {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ });
+ Tensor size_tensor = makeInputTensor<DataType::S32>({2}, {3, 3});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ResizeBilinearParams params{};
+ params.align_corners = true;
+ params.half_pixel_centers = true;
+
+ ResizeBilinear kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.cpp b/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.cpp
new file mode 100644
index 000000000..e4ad8f742
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/ResizeNearestNeighbor.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+ResizeNearestNeighbor::ResizeNearestNeighbor(const Tensor *input, const Tensor *size,
+ Tensor *output,
+ const ResizeNearestNeighborParams &params)
+ : KernelWithParams<ResizeNearestNeighborParams>({input, size}, {output}, params)
+{
+}
+
+void ResizeNearestNeighbor::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->shape().num_dims() == 4);
+ LUCI_INTERPRETER_CHECK(size()->shape().num_dims() == 1);
+ LUCI_INTERPRETER_CHECK(size()->element_type() == DataType::S32);
+ LUCI_INTERPRETER_CHECK(size()->shape().dim(0) == 2);
+ Shape output_shape(4);
+ output_shape.dim(0) = input()->shape().dim(0);
+ output_shape.dim(1) = getTensorData<int32_t>(size())[0];
+ output_shape.dim(2) = getTensorData<int32_t>(size())[1];
+ output_shape.dim(3) = input()->shape().dim(3);
+ output()->resize(output_shape);
+}
+
+void ResizeNearestNeighbor::execute() const
+{
+ tflite::ResizeNearestNeighborParams op_params{};
+ op_params.align_corners = params().align_corners;
+ op_params.half_pixel_centers = params().half_pixel_centers;
+ switch (output()->element_type())
+ {
+ case DataType::FLOAT32:
+ tflite::reference_ops::ResizeNearestNeighbor(
+ op_params, getTensorShape(input()), getTensorData<int32_t>(input()),
+ getTensorShape(size()), getTensorData<int32_t>(size()), getTensorShape(output()),
+ getTensorData<int32_t>(output()));
+ break;
+ case DataType::U8:
+ tflite::optimized_ops::ResizeNearestNeighbor(
+ op_params, getTensorShape(input()), getTensorData<uint8_t>(input()),
+ getTensorShape(size()), getTensorData<int32_t>(size()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.h b/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.h
new file mode 100644
index 000000000..137d031cf
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_RESIZENEARESTNEIGHBOR_H
+#define LUCI_INTERPRETER_KERNELS_RESIZENEARESTNEIGHBOR_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class ResizeNearestNeighbor : public KernelWithParams<ResizeNearestNeighborParams>
+{
+public:
+ ResizeNearestNeighbor(const Tensor *input, const Tensor *shape, Tensor *output,
+ const ResizeNearestNeighborParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ const Tensor *size() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_RESIZENEARESTNEIGHBOR_H
diff --git a/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.test.cpp b/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.test.cpp
new file mode 100644
index 000000000..9a804cca7
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.test.cpp
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/ResizeNearestNeighbor.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T>
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> size_shape,
+ std::initializer_list<int32_t> output_shape, std::initializer_list<float> input_data,
+ std::initializer_list<int32_t> size_data, std::initializer_list<float> output_data,
+ bool align_corners, bool half_pixel_centers)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor size_tensor = makeInputTensor<DataType::S32>(size_shape, size_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ResizeNearestNeighborParams params{};
+ params.align_corners = align_corners;
+ params.half_pixel_centers = half_pixel_centers;
+
+ ResizeNearestNeighbor kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+ EXPECT_THAT(extractTensorData<T>(output_tensor), FloatArrayNear(output_data));
+}
+
+template <>
+void Check<uint8_t>(std::initializer_list<int32_t> input_shape,
+ std::initializer_list<int32_t> size_shape,
+ std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data,
+ std::initializer_list<int32_t> size_data,
+ std::initializer_list<float> output_data, bool align_corners,
+ bool half_pixel_centers)
+{
+ std::pair<float, int32_t> quant_param =
+ quantizationParams<uint8_t>(std::min(input_data) < 0 ? std::min(input_data) : 0.f,
+ std::max(input_data) > 0 ? std::max(input_data) : 0.f);
+ Tensor input_tensor =
+ makeInputTensor<DataType::U8>(input_shape, quant_param.first, quant_param.second, input_data);
+ Tensor size_tensor = makeInputTensor<DataType::S32>(size_shape, size_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.first);
+
+ ResizeNearestNeighborParams params{};
+ params.align_corners = align_corners;
+ params.half_pixel_centers = half_pixel_centers;
+
+ ResizeNearestNeighbor kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data, output_tensor.scale()));
+}
+
+template <typename T> class ResizeNearestNeighborTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(ResizeNearestNeighborTest, DataTypes);
+
+TYPED_TEST(ResizeNearestNeighborTest, SimpleTest)
+{
+ Check<TypeParam>({2, 2, 2, 1}, {2}, {2, 3, 3, 1},
+ {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ },
+ {3, 3},
+ {
+ 3, 3, 6, //
+ 3, 3, 6, //
+ 9, 9, 12, //
+ 4, 4, 10, //
+ 4, 4, 10, //
+ 10, 10, 16, //
+ },
+ false, false);
+}
+
+TYPED_TEST(ResizeNearestNeighborTest, AlignCenterTest)
+{
+ Check<TypeParam>({2, 2, 2, 1}, {2}, {2, 3, 3, 1},
+ {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ },
+ {3, 3},
+ {
+ 3, 6, 6, //
+ 9, 12, 12, //
+ 9, 12, 12, //
+ 4, 10, 10, //
+ 10, 16, 16, //
+ 10, 16, 16, //
+ },
+ true, false);
+}
+
+TYPED_TEST(ResizeNearestNeighborTest, HalfPixelCenterTest)
+{
+ Check<TypeParam>({2, 2, 2, 1}, {2}, {2, 3, 3, 1},
+ {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ },
+ {3, 3},
+ {
+ 3, 6, 6, //
+ 9, 12, 12, //
+ 9, 12, 12, //
+ 4, 10, 10, //
+ 10, 16, 16, //
+ 10, 16, 16, //
+ },
+ false, true);
+}
+
+TEST(ResizeNearestNeighborTest, InputShapeInvalid_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2}, {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ });
+ Tensor size_tensor = makeInputTensor<DataType::S32>({2}, {3, 3});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ResizeNearestNeighborParams params{};
+ params.align_corners = false;
+ params.half_pixel_centers = false;
+
+ ResizeNearestNeighbor kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ResizeNearestNeighborTest, SizeShapeInvalid_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2, 1}, {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ });
+ Tensor size_tensor = makeInputTensor<DataType::S32>({2, 1}, {3, 3});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ResizeNearestNeighborParams params{};
+ params.align_corners = false;
+ params.half_pixel_centers = false;
+
+ ResizeNearestNeighbor kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(ResizeNearestNeighborTest, SizeDimInvalid_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2, 1}, {
+ 3, 6, //
+ 9, 12, //
+ 4, 10, //
+ 10, 16 //
+ });
+ Tensor size_tensor = makeInputTensor<DataType::S32>({3}, {3, 3, 1});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ ResizeNearestNeighborParams params{};
+ params.align_corners = false;
+ params.half_pixel_centers = false;
+
+ ResizeNearestNeighbor kernel(&input_tensor, &size_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Reverse.cpp b/compiler/luci-interpreter/src/kernels/Reverse.cpp
new file mode 100644
index 000000000..a46308412
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Reverse.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Reverse.h"
+#include "kernels/Utils.h"
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Reverse::Reverse(const Tensor *input, const Tensor *axes, Tensor *output)
+ : Kernel({input, axes}, {output})
+{
+}
+
+void Reverse::configure()
+{
+ assert(axes()->shape().num_dims() == 1);
+ assert(input()->shape().num_dims() >= axes()->shape().num_elements());
+ if (input()->element_type() != DataType::S32 && input()->element_type() != DataType::FLOAT32 &&
+ input()->element_type() != DataType::U8 && input()->element_type() != DataType::S16 &&
+ input()->element_type() != DataType::S64)
+ {
+ throw std::runtime_error("Unsupported input type.");
+ }
+ if (axes()->element_type() != DataType::S32)
+ {
+ throw std::runtime_error("Unsupported axes type.");
+ }
+ if (axes()->shape().num_elements() > 1)
+ {
+ throw std::runtime_error("Current implementation does not support more than 1 axis.");
+ }
+ int axis_value = getTensorData<int32_t>(axes())[0];
+ if (axis_value < 0 || axis_value >= input()->shape().num_dims())
+ {
+ throw std::runtime_error("Invalid axes value");
+ }
+ assert(input()->element_type() == output()->element_type());
+
+ output()->resize(input()->shape());
+}
+
+void Reverse::execute() const
+{
+ int axis_value = getTensorData<int32_t>(axes())[0];
+ switch (output()->element_type())
+ {
+ case DataType::FLOAT32:
+ tflite::reference_ops::Reverse<float>(axis_value, getTensorShape(input()),
+ getTensorData<float>(input()), getTensorShape(output()),
+ getTensorData<float>(output()));
+ break;
+ case DataType::U8:
+ tflite::reference_ops::Reverse<uint8_t>(
+ axis_value, getTensorShape(input()), getTensorData<uint8_t>(input()),
+ getTensorShape(output()), getTensorData<uint8_t>(output()));
+ break;
+ default:
+ throw std::runtime_error("Unsupported output type");
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Reverse.h b/compiler/luci-interpreter/src/kernels/Reverse.h
new file mode 100644
index 000000000..3489dae28
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Reverse.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_REVERSE_H
+#define LUCI_INTERPRETER_KERNELS_REVERSE_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Reverse : public Kernel
+{
+public:
+ Reverse(const Tensor *input, const Tensor *axes, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ const Tensor *axes() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_REVERSE_H
diff --git a/compiler/luci-interpreter/src/kernels/Reverse.test.cpp b/compiler/luci-interpreter/src/kernels/Reverse.test.cpp
new file mode 100644
index 000000000..5475a8bd3
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Reverse.test.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Reverse.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T> class ReverseTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(ReverseTest, DataTypes);
+
+TYPED_TEST(ReverseTest, MultiDimensions)
+{
+ // TypeParam
+ std::vector<TypeParam> input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
+ Shape input_shape{4, 3, 2};
+ std::vector<int32_t> axis_data{1};
+ Shape axis_shape{1};
+
+ std::vector<TypeParam> output_data{5, 6, 3, 4, 1, 2, 11, 12, 9, 10, 7, 8,
+ 17, 18, 15, 16, 13, 14, 23, 24, 21, 22, 19, 20};
+ std::vector<int32_t> output_shape{4, 3, 2};
+
+ Tensor input_tensor = makeInputTensor<getElementType<TypeParam>()>(input_shape, input_data);
+ Tensor axis_tensor = makeInputTensor<DataType::S32>(axis_shape, axis_data);
+
+ Tensor output_tensor = makeOutputTensor(getElementType<TypeParam>());
+
+ Reverse kernel = Reverse(&input_tensor, &axis_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<TypeParam>(output_tensor),
+ ::testing::ElementsAreArray(output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Rsqrt.cpp b/compiler/luci-interpreter/src/kernels/Rsqrt.cpp
new file mode 100644
index 000000000..6dd92dc98
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Rsqrt.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Rsqrt.h"
+#include "kernels/Utils.h"
+
+#include <stdexcept>
+#include <cmath>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Rsqrt::Rsqrt(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {}
+
+void Rsqrt::configure()
+{
+ if (input()->element_type() != output()->element_type())
+ {
+ throw std::runtime_error("Input/output tensor data type mismatch.");
+ }
+ output()->resize(input()->shape());
+}
+
+void Rsqrt::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Rsqrt::evalFloat() const
+{
+ auto in = getTensorData<float>(input());
+ auto out = getTensorData<float>(output());
+ auto size = getTensorShape(input()).FlatSize();
+ for (auto i = in; i != in + size; ++i)
+ {
+ *out = 1.f / std::sqrt(*i);
+ ++out;
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Rsqrt.h b/compiler/luci-interpreter/src/kernels/Rsqrt.h
new file mode 100644
index 000000000..adc5bcfa2
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Rsqrt.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_RSQRT_H
+#define LUCI_INTERPRETER_KERNELS_RSQRT_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Rsqrt : public Kernel
+{
+public:
+ Rsqrt(const Tensor *input, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_RSQRT_H
diff --git a/compiler/luci-interpreter/src/kernels/Rsqrt.test.cpp b/compiler/luci-interpreter/src/kernels/Rsqrt.test.cpp
new file mode 100644
index 000000000..d33b800be
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Rsqrt.test.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Rsqrt.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data, std::initializer_list<float> output_data)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Rsqrt kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+TEST(RsqrtTest, SimpleRsqrt)
+{
+ Check(
+ /*input_shape=*/{1, 2, 4, 1}, /*output_shape=*/{1, 2, 4, 1},
+ /*input_data=*/
+ {
+ 5, 4, 8, 2, //
+ 6, 7.5, 9, 0.3, //
+ },
+ /*output_data=*/
+ {
+ 0.44721360, 0.5, 0.35355339, 0.70710678, //
+ 0.40824829, 0.36514837, 0.33333333, 1.8257419, //
+ });
+}
+
+TEST(RsqrtTest, Input_Output_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::S32);
+
+ Rsqrt kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(RsqrtTest, Invalid_Input_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::S64>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::S64);
+
+ Rsqrt kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ EXPECT_ANY_THROW(kernel.execute());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Slice.cpp b/compiler/luci-interpreter/src/kernels/Slice.cpp
new file mode 100644
index 000000000..c4bc3c57c
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Slice.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Slice.h"
+#include "Utils.h"
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+#include <cassert>
+#include <cstring>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+const int max_dim = 4;
+
+Slice::Slice(const Tensor *input, const Tensor *begin, const Tensor *size, Tensor *output)
+ : Kernel({input, begin, size}, {output})
+{
+}
+
+template <typename T>
+Shape calculateOutputShape(const Tensor *input, const Tensor *begin, const Tensor *size)
+{
+ Shape output_shape = Shape(input->shape().num_dims());
+ for (int idx = 0; idx < input->shape().num_dims(); idx++)
+ {
+ T size_value = getTensorData<T>(size)[idx];
+ if (size_value < 0)
+ {
+ if (size_value != -1)
+ {
+ throw std::runtime_error("Invalid size.");
+ }
+ size_value = input->shape().dim(idx) - getTensorData<T>(begin)[idx];
+ }
+ else
+ {
+ if (input->shape().dim(idx) < getTensorData<T>(begin)[idx] + size_value)
+ {
+ throw std::runtime_error("Invalid begin and size.");
+ }
+ }
+ output_shape.dim(idx) = static_cast<int>(size_value);
+ }
+ return output_shape;
+}
+
+template <typename T>
+void getBeginAndSizeVectors(int dimensions, const Tensor *begin, const Tensor *size,
+ std::vector<int> *begins, std::vector<int> *sizes)
+{
+ for (int idx = dimensions - 1; idx >= 0; --idx)
+ {
+ begins->push_back(getTensorData<T>(begin)[idx]);
+ sizes->push_back(getTensorData<T>(size)[idx]);
+ }
+}
+
+void Slice::configure()
+{
+ assert(input()->element_type() == output()->element_type());
+ assert(begin()->element_type() == DataType::S32 || begin()->element_type() == DataType::S64);
+ assert(size()->element_type() == DataType::S32 || size()->element_type() == DataType::S64);
+ assert(begin()->shape().num_dims() == 1);
+ assert(size()->shape().num_dims() == 1);
+ assert(input()->shape().num_dims() <= max_dim);
+
+ if (begin()->element_type() == DataType::S32)
+ {
+ output()->resize(calculateOutputShape<int32_t>(input(), begin(), size()));
+ }
+ else if (begin()->element_type() == DataType::S64)
+ {
+ output()->resize(calculateOutputShape<int64_t>(input(), begin(), size()));
+ }
+ else
+ {
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Slice::execute() const
+{
+ std::vector<int> begins;
+ begins.reserve(max_dim);
+ std::vector<int> sizes;
+ sizes.reserve(max_dim);
+ if (begin()->element_type() == DataType::S32)
+ {
+ getBeginAndSizeVectors<int32_t>(input()->shape().num_dims(), begin(), size(), &begins, &sizes);
+ }
+ else if (begin()->element_type() == DataType::S64)
+ {
+ getBeginAndSizeVectors<int64_t>(input()->shape().num_dims(), begin(), size(), &begins, &sizes);
+ }
+ else
+ {
+ throw std::runtime_error("Unsupported begin type.");
+ }
+ for (int i = input()->shape().num_dims(); i < max_dim; ++i)
+ {
+ begins.push_back(0);
+ sizes.push_back(1);
+ }
+
+ assert(begins.size() == 4);
+ assert(sizes.size() == 4);
+ tflite::SliceParams op_params{};
+ op_params.begin_count = 4;
+ op_params.size_count = 4;
+ for (int i = 0; i < 4; i++)
+ {
+ op_params.begin[i] = begins[3 - i];
+ op_params.size[i] = sizes[3 - i];
+ }
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ tflite::optimized_ops::Slice(op_params, getTensorShape(input()),
+ getTensorData<float>(input()), getTensorShape(output()),
+ getTensorData<float>(output()));
+ break;
+ case DataType::U8:
+ tflite::optimized_ops::Slice(op_params, getTensorShape(input()),
+ getTensorData<uint8_t>(input()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ break;
+ default:
+ throw std::runtime_error("Unsupported input type.");
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Slice.h b/compiler/luci-interpreter/src/kernels/Slice.h
new file mode 100644
index 000000000..23c359608
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Slice.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_SLICE_H
+#define LUCI_INTERPRETER_KERNELS_SLICE_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Slice : public Kernel
+{
+public:
+ Slice(const Tensor *input, const Tensor *begin, const Tensor *size, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ const Tensor *begin() const { return _inputs[1]; }
+ const Tensor *size() const { return _inputs[2]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_SLICE_H
diff --git a/compiler/luci-interpreter/src/kernels/Slice.test.cpp b/compiler/luci-interpreter/src/kernels/Slice.test.cpp
new file mode 100644
index 000000000..a360a29cc
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Slice.test.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Slice.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T> class SliceTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(SliceTest, DataTypes);
+
+TYPED_TEST(SliceTest, SimpleTest)
+{
+ std::vector<TypeParam> 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<int32_t> begin_data{1, 0, 0, 0};
+ Shape begin_shape{4};
+ std::vector<int32_t> size_data{2, 1, -1, 1};
+ Shape size_shape{4};
+ std::vector<TypeParam> output_data{3, 3, 3, 5, 5, 5};
+ std::vector<int32_t> output_shape{2, 1, 3, 1};
+
+ Tensor input_tensor = makeInputTensor<getElementType<TypeParam>()>(input_shape, input_data);
+ Tensor begin_tensor = makeInputTensor<DataType::S32>(begin_shape, begin_data);
+ Tensor size_tensor = makeInputTensor<DataType::S32>(size_shape, size_data);
+
+ Tensor output_tensor = makeOutputTensor(getElementType<TypeParam>());
+
+ Slice kernel(&input_tensor, &begin_tensor, &size_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<TypeParam>(output_tensor),
+ ::testing::ElementsAreArray(output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Softmax.cpp b/compiler/luci-interpreter/src/kernels/Softmax.cpp
new file mode 100644
index 000000000..642c0ad75
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Softmax.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Softmax.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/softmax.h>
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Softmax::Softmax(const Tensor *input, Tensor *output, const SoftmaxParams &params)
+ : KernelWithParams<SoftmaxParams>({input}, {output}, params)
+{
+}
+
+void Softmax::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ LUCI_INTERPRETER_CHECK(input()->shape().num_dims() >= 1);
+ if (input()->element_type() == DataType::U8 || input()->element_type() == DataType::S8)
+ {
+ LUCI_INTERPRETER_CHECK(output()->zero_point() == 0);
+ tflite::SoftmaxParams op_params{};
+ op_params.table = _table;
+ tflite::optimized_ops::PopulateSoftmaxLookupTable(&op_params, input()->scale(), params().beta);
+ }
+ output()->resize(input()->shape());
+}
+
+void Softmax::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::S8:
+ evalQuantized<int8_t>();
+ break;
+ case DataType::U8:
+ evalQuantized<uint8_t>();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Softmax::evalFloat() const
+{
+ tflite::SoftmaxParams op_params{};
+ op_params.beta = params().beta;
+
+ tflite::reference_ops::Softmax(op_params, getTensorShape(input()), getTensorData<float>(input()),
+ getTensorShape(output()), getTensorData<float>(output()));
+}
+
+template <typename T> void Softmax::evalQuantized() const
+{
+ tflite::SoftmaxParams op_params{};
+ op_params.table = const_cast<float *>(_table);
+ op_params.zero_point = output()->zero_point();
+ op_params.scale = output()->scale();
+
+ tflite::optimized_ops::Softmax(op_params, getTensorShape(input()), getTensorData<T>(input()),
+ getTensorShape(output()), getTensorData<T>(output()));
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Softmax.h b/compiler/luci-interpreter/src/kernels/Softmax.h
new file mode 100644
index 000000000..1f281df1c
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Softmax.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_SOFTMAX_H
+#define LUCI_INTERPRETER_KERNELS_SOFTMAX_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Softmax : public KernelWithParams<SoftmaxParams>
+{
+public:
+ Softmax(const Tensor *input, Tensor *output, const SoftmaxParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ template <typename T> void evalQuantized() const;
+
+ float _table[256];
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_SOFTMAX_H
diff --git a/compiler/luci-interpreter/src/kernels/Softmax.test.cpp b/compiler/luci-interpreter/src/kernels/Softmax.test.cpp
new file mode 100644
index 000000000..d3d8209a5
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Softmax.test.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Softmax.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T>
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data, std::initializer_list<float> output_data)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ SoftmaxParams params{};
+ params.beta = 0.1;
+
+ Softmax kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<T>(output_tensor), FloatArrayNear(output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), output_shape);
+}
+
+template <>
+void Check<uint8_t>(std::initializer_list<int32_t> input_shape,
+ std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data,
+ std::initializer_list<float> output_data)
+{
+ std::pair<float, int32_t> input_quant_param =
+ quantizationParams<uint8_t>(std::min<float>(std::min<float>(input_data), 0.f),
+ std::max<float>(std::max<float>(input_data), 0.f));
+ std::pair<float, int32_t> output_quant_param =
+ quantizationParams<uint8_t>(std::min<float>(std::min<float>(output_data), 0.f),
+ std::max<float>(std::max<float>(output_data), 0.f));
+ Tensor input_tensor = makeInputTensor<DataType::U8>(input_shape, input_quant_param.first,
+ input_quant_param.second, input_data);
+ Tensor output_tensor =
+ makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second);
+
+ SoftmaxParams params{};
+ params.beta = 0.1;
+
+ Softmax kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data, output_tensor.scale()));
+}
+
+template <typename T> class SoftmaxTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(SoftmaxTest, DataTypes);
+
+TYPED_TEST(SoftmaxTest, Simple)
+{
+ Check<TypeParam>({2, 1, 2, 3}, {2, 1, 2, 3},
+ {
+ 5, -9, 8, //
+ -7, 2, -4, //
+ 1, -2, 9, //
+ 3, -6, -1, //
+ },
+ {
+ 0.38514, 0.09497, 0.51989, //
+ 0.20792, 0.51141, 0.28067, //
+ 0.25212, 0.18678, 0.56110, //
+ 0.48149, 0.19576, 0.32275, //
+ });
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/SpaceToDepth.cpp b/compiler/luci-interpreter/src/kernels/SpaceToDepth.cpp
new file mode 100644
index 000000000..6a5bd7cf8
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/SpaceToDepth.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SpaceToDepth.h"
+#include "Utils.h"
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+SpaceToDepth::SpaceToDepth(const Tensor *input, Tensor *output, const SpaceToDepthParams &params)
+ : KernelWithParams<SpaceToDepthParams>({input}, {output}, params)
+{
+}
+
+void SpaceToDepth::configure()
+{
+ assert(input()->shape().num_dims() == 4);
+ assert(output()->element_type() == DataType::FLOAT32 ||
+ output()->element_type() == DataType::U8 || output()->element_type() == DataType::S8 ||
+ output()->element_type() == DataType::S32 || output()->element_type() == DataType::S64);
+ assert(input()->element_type() == output()->element_type());
+
+ const int block_size = params().block_size;
+ const int32_t input_height = input()->shape().dim(1);
+ const int32_t input_width = input()->shape().dim(2);
+ int32_t output_height = input_height / block_size;
+ int32_t output_width = input_width / block_size;
+
+ assert(input_height == output_height * block_size);
+ assert(input_width == output_width * block_size);
+
+ Shape output_shape(4);
+ output_shape.dim(0) = input()->shape().dim(0);
+ output_shape.dim(1) = output_height;
+ output_shape.dim(2) = output_width;
+ output_shape.dim(3) = input()->shape().dim(3) * block_size * block_size;
+
+ output()->resize(output_shape);
+}
+
+void SpaceToDepth::execute() const
+{
+ tflite::SpaceToDepthParams op_params{};
+ op_params.block_size = params().block_size;
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ tflite::optimized_ops::SpaceToDepth(op_params, getTensorShape(input()),
+ getTensorData<float>(input()), getTensorShape(output()),
+ getTensorData<float>(output()));
+ break;
+ case DataType::U8:
+ tflite::optimized_ops::SpaceToDepth(op_params, getTensorShape(input()),
+ getTensorData<uint8_t>(input()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/SpaceToDepth.h b/compiler/luci-interpreter/src/kernels/SpaceToDepth.h
new file mode 100644
index 000000000..e66316b11
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/SpaceToDepth.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_SPACETODEPTH_H
+#define LUCI_INTERPRETER_KERNELS_SPACETODEPTH_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+#include <vector>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class SpaceToDepth : public KernelWithParams<SpaceToDepthParams>
+{
+public:
+ SpaceToDepth(const Tensor *input, Tensor *output, const SpaceToDepthParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_SPACETODEPTH_H
diff --git a/compiler/luci-interpreter/src/kernels/SpaceToDepth.test.cpp b/compiler/luci-interpreter/src/kernels/SpaceToDepth.test.cpp
new file mode 100644
index 000000000..77b6655dc
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/SpaceToDepth.test.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/SpaceToDepth.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T> class SpaceToDepthTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(SpaceToDepthTest, DataTypes);
+
+TYPED_TEST(SpaceToDepthTest, SimpleCase)
+{
+ constexpr DataType element_type = getElementType<TypeParam>();
+ std::vector<TypeParam> input_data{1, 5, 6, 7, 2, 3, 4, 8};
+ Shape input_shape{1, 2, 2, 2};
+ Tensor input_tensor = makeInputTensor<element_type>(input_shape, input_data);
+ std::vector<TypeParam> output_data{1, 5, 6, 7, 2, 3, 4, 8};
+ std::vector<int32_t> output_shape{1, 1, 1, 8};
+ Tensor output_tensor = makeOutputTensor(element_type);
+
+ SpaceToDepthParams params{};
+ params.block_size = 2;
+
+ SpaceToDepth kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<TypeParam>(output_tensor),
+ ::testing::ElementsAreArray(output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Split.cpp b/compiler/luci-interpreter/src/kernels/Split.cpp
new file mode 100644
index 000000000..325b1c22f
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Split.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Split.h"
+
+#include "Utils.h"
+
+#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Split::Split(const Tensor *axis, const Tensor *input, std::vector<Tensor *> outputs)
+ : Kernel({axis, input}, std::move(outputs))
+{
+}
+
+void Split::configure()
+{
+ assert(axis()->shape().num_elements() == 1);
+ _axis_value = getTensorData<int32_t>(axis())[0];
+ if (_axis_value < 0)
+ _axis_value += input()->shape().num_dims();
+ assert(_axis_value >= 0 && _axis_value < input()->shape().num_dims());
+
+ const int32_t input_size = input()->shape().dim(_axis_value);
+ assert(input_size % _outputs.size() == 0);
+ const int32_t slice_size = input_size / _outputs.size();
+
+ Shape output_shape = input()->shape();
+ output_shape.dim(_axis_value) = slice_size;
+ for (Tensor *output : _outputs)
+ {
+ output->resize(output_shape);
+ }
+}
+
+void Split::execute() const
+{
+ tflite::SplitParams params{};
+ params.num_split = _outputs.size();
+ params.axis = _axis_value;
+
+#define TF_LITE_SPLIT(scalar) \
+ { \
+ VectorOfTensors<scalar, false> all_outputs(_outputs); \
+ tflite::optimized_ops::Split(params, getTensorShape(input()), getTensorData<scalar>(input()), \
+ all_outputs.shapes(), all_outputs.data()); \
+ }
+
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ TF_LITE_SPLIT(float);
+ break;
+ case DataType::U8:
+ TF_LITE_SPLIT(uint8_t);
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+#undef TF_LITE_SPLIT
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Split.h b/compiler/luci-interpreter/src/kernels/Split.h
new file mode 100644
index 000000000..9542b1e56
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Split.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_SPLIT_H
+#define LUCI_INTERPRETER_KERNELS_SPLIT_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Split : public Kernel
+{
+public:
+ Split(const Tensor *axis, const Tensor *input, std::vector<Tensor *> outputs);
+
+ const Tensor *axis() const { return _inputs[0]; }
+ const Tensor *input() const { return _inputs[1]; }
+ Tensor *output(int index) const { return _outputs[index]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ int32_t _axis_value{};
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_SPLIT_H
diff --git a/compiler/luci-interpreter/src/kernels/Split.test.cpp b/compiler/luci-interpreter/src/kernels/Split.test.cpp
new file mode 100644
index 000000000..2147d15c1
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Split.test.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Split.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T>
+void Check(int axis, int num_splits, std::initializer_list<int32_t> input_shape,
+ std::initializer_list<int32_t> output_shape, std::initializer_list<T> input_data,
+ std::vector<std::vector<T>> output_data)
+{
+ constexpr DataType element_type = getElementType<T>();
+ Tensor axis_tensor = makeInputTensor<DataType::S32>({}, {axis});
+ Tensor input_tensor = makeInputTensor<element_type>(input_shape, input_data);
+
+ std::vector<Tensor> output_tensors;
+ output_tensors.reserve(num_splits);
+ for (int i = 0; i < num_splits; ++i)
+ {
+ output_tensors.emplace_back(makeOutputTensor(element_type));
+ }
+
+ std::vector<Tensor *> output_tensor_ptrs(num_splits);
+ for (int i = 0; i < num_splits; ++i)
+ {
+ output_tensor_ptrs[i] = &output_tensors[i];
+ }
+
+ Split kernel(&axis_tensor, &input_tensor, std::move(output_tensor_ptrs));
+ kernel.configure();
+ kernel.execute();
+
+ for (int i = 0; i < num_splits; ++i)
+ {
+ EXPECT_THAT(extractTensorData<T>(output_tensors[i]),
+ ::testing::ElementsAreArray(output_data[i]));
+ }
+}
+
+template <typename T> class SplitTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(SplitTest, DataTypes);
+
+TYPED_TEST(SplitTest, FourDimensional)
+{
+ Check<TypeParam>(/*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<TypeParam>(
+ /*axis=*/1, /*num_splits=*/2, {2, 2, 2, 2}, {2, 1, 2, 2},
+ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, {
+ {1, 2, 3, 4, 9, 10, 11, 12}, //
+ {5, 6, 7, 8, 13, 14, 15, 16}, //
+ });
+ Check<TypeParam>(
+ /*axis=*/2, /*num_splits=*/2, {2, 2, 2, 2}, {2, 2, 1, 2},
+ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, {
+ {1, 2, 5, 6, 9, 10, 13, 14}, //
+ {3, 4, 7, 8, 11, 12, 15, 16}, //
+ });
+ Check<TypeParam>(
+ /*axis=*/3, /*num_splits=*/2, {2, 2, 2, 2}, {2, 2, 2, 1},
+ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, {
+ {1, 3, 5, 7, 9, 11, 13, 15}, //
+ {2, 4, 6, 8, 10, 12, 14, 16}, //
+ });
+}
+
+TYPED_TEST(SplitTest, OneDimensional)
+{
+ Check<TypeParam>(
+ /*axis=*/0, /*num_splits=*/8, {8}, {1}, {1, 2, 3, 4, 5, 6, 7, 8},
+ {{1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}});
+}
+
+TYPED_TEST(SplitTest, NegativeAxis)
+{
+ Check<TypeParam>(
+ /*axis=*/-4, /*num_splits=*/2, {2, 2, 2, 2}, {1, 2, 2, 2},
+ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, {
+ {1, 2, 3, 4, 5, 6, 7, 8}, //
+ {9, 10, 11, 12, 13, 14, 15, 16},
+ });
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Sqrt.cpp b/compiler/luci-interpreter/src/kernels/Sqrt.cpp
new file mode 100644
index 000000000..46e9fc9ad
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Sqrt.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Sqrt.h"
+#include "kernels/Utils.h"
+
+#include <stdexcept>
+#include <cmath>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Sqrt::Sqrt(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {}
+
+void Sqrt::configure()
+{
+ if (input()->element_type() != output()->element_type())
+ {
+ throw std::runtime_error("Input/output tensor data type mismatch.");
+ }
+ output()->resize(input()->shape());
+}
+
+void Sqrt::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Sqrt::evalFloat() const
+{
+ auto in = getTensorData<float>(input());
+ auto out = getTensorData<float>(output());
+ auto size = getTensorShape(input()).FlatSize();
+ for (auto i = in; i != in + size; ++i)
+ {
+ *out = std::sqrt(*i);
+ ++out;
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Sqrt.h b/compiler/luci-interpreter/src/kernels/Sqrt.h
new file mode 100644
index 000000000..4034655ed
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Sqrt.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_SQRT_H
+#define LUCI_INTERPRETER_KERNELS_SQRT_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Sqrt : public Kernel
+{
+public:
+ Sqrt(const Tensor *input, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_SQRT_H
diff --git a/compiler/luci-interpreter/src/kernels/Sqrt.test.cpp b/compiler/luci-interpreter/src/kernels/Sqrt.test.cpp
new file mode 100644
index 000000000..504db4493
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Sqrt.test.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Sqrt.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> output_shape,
+ std::initializer_list<float> input_data, std::initializer_list<float> output_data)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Sqrt kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+TEST(SqrtTest, SimpleSqrt)
+{
+ Check(
+ /*input_shape=*/{1, 2, 4, 1}, /*output_shape=*/{1, 2, 4, 1},
+ /*input_data=*/
+ {
+ 0, 8, 2, 4, //
+ 3, 7, 10, 0.3, //
+ },
+ /*output_data=*/
+ {
+ 0.0, 2.8284271, 1.4142136, 2, //
+ 1.7320508, 2.6457513, 3.1622777, 0.54772256, //
+ });
+}
+
+TEST(SqrtTest, Input_Output_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor output_tensor = makeOutputTensor(DataType::S32);
+
+ Sqrt kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(AddTest, Invalid_Input_Type_NEG)
+{
+ Tensor input_tensor = makeInputTensor<DataType::S64>({1}, {1});
+ Tensor output_tensor = makeOutputTensor(DataType::S64);
+
+ Sqrt kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ EXPECT_ANY_THROW(kernel.execute());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Squeeze.cpp b/compiler/luci-interpreter/src/kernels/Squeeze.cpp
new file mode 100644
index 000000000..ce43ef789
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Squeeze.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Squeeze.h"
+
+#include "kernels/Utils.h"
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Squeeze::Squeeze(const Tensor *input, Tensor *output, const SqueezeParams &params)
+ : KernelWithParams<SqueezeParams>({input}, {output}, params)
+{
+}
+
+void Squeeze::configure()
+{
+ int input_num_dims = input()->shape().num_dims();
+ int num_squeeze_dims = params().squeeze_dims.size();
+ assert(input_num_dims <= 8);
+ bool should_squeeze[8] = {false};
+ int num_squeezed_dims = 0;
+ if (num_squeeze_dims == 0)
+ {
+ for (int idx = 0; idx < input_num_dims; ++idx)
+ {
+ if (input()->shape().dim(idx) == 1)
+ {
+ should_squeeze[idx] = true;
+ ++num_squeezed_dims;
+ }
+ }
+ }
+ else
+ {
+ for (int idx = 0; idx < num_squeeze_dims; ++idx)
+ {
+ int current = params().squeeze_dims[idx] < 0 ? params().squeeze_dims[idx] + input_num_dims
+ : params().squeeze_dims[idx];
+ assert(current >= 0 && current < input_num_dims && input()->shape().dim(current) == 1);
+ if (!should_squeeze[current])
+ ++num_squeezed_dims;
+ should_squeeze[current] = true;
+ }
+ }
+ Shape output_shape(input_num_dims - num_squeezed_dims);
+ for (int in_idx = 0, out_idx = 0; in_idx < input_num_dims; ++in_idx)
+ {
+ if (!should_squeeze[in_idx])
+ {
+ output_shape.dim(out_idx++) = input()->shape().dim(in_idx);
+ }
+ }
+ output()->resize(output_shape);
+}
+
+void Squeeze::execute() const
+{
+ assert(input()->shape().num_elements() == output()->shape().num_elements());
+
+ const auto *input_data = input()->data<void>();
+ auto *output_data = output()->data<void>();
+ std::memcpy(output_data, input_data,
+ getDataTypeSize(input()->element_type()) * input()->shape().num_elements());
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Squeeze.h b/compiler/luci-interpreter/src/kernels/Squeeze.h
new file mode 100644
index 000000000..687af5158
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Squeeze.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_SQUEEZE_H
+#define LUCI_INTERPRETER_KERNELS_SQUEEZE_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Squeeze : public KernelWithParams<SqueezeParams>
+{
+public:
+ Squeeze(const Tensor *input, Tensor *output, const SqueezeParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_SQUEEZE_H
diff --git a/compiler/luci-interpreter/src/kernels/Squeeze.test.cpp b/compiler/luci-interpreter/src/kernels/Squeeze.test.cpp
new file mode 100644
index 000000000..ff9fb09d2
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Squeeze.test.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Squeeze.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T>
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> output_shape,
+ std::initializer_list<T> input_data, std::initializer_list<T> output_data,
+ std::initializer_list<int32_t> squeeze_dims)
+{
+ constexpr DataType element_type = getElementType<T>();
+ Tensor input_tensor = makeInputTensor<element_type>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(element_type);
+
+ SqueezeParams params{};
+ params.squeeze_dims = squeeze_dims;
+
+ Squeeze kernel(&input_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<T>(output_tensor), ::testing::ElementsAreArray(output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+template <typename T> class SqueezeTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(SqueezeTest, DataTypes);
+
+TYPED_TEST(SqueezeTest, TotalTest)
+{
+ Check<TypeParam>(
+ /*input_shape=*/{1, 24, 1}, /*output_shape=*/{24},
+ /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24},
+ /*output_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24},
+ {-1, 0});
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/StridedSlice.cpp b/compiler/luci-interpreter/src/kernels/StridedSlice.cpp
new file mode 100644
index 000000000..679485439
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/StridedSlice.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/StridedSlice.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+StridedSlice::StridedSlice(const Tensor *input, const Tensor *begin, const Tensor *end,
+ const Tensor *strides, Tensor *output, const StridedSliceParams &params)
+ : KernelWithParams<StridedSliceParams>({input, begin, end, strides}, {output}, params)
+{
+}
+
+void StridedSlice::configure()
+{
+ assert(begin()->shape().num_dims() == 1);
+ assert(end()->shape().num_dims() == 1);
+ assert(strides()->shape().num_dims() == 1);
+ assert(input()->element_type() == output()->element_type());
+ assert(begin()->element_type() == DataType::S32);
+ assert(end()->element_type() == DataType::S32);
+ assert(strides()->element_type() == DataType::S32);
+ assert(input()->shape().num_dims() <= 4);
+ if (params().ellipsis_mask != 0)
+ {
+ throw std::runtime_error("ellipsis_mask is not implemented yet.");
+ }
+ if (params().new_axis_mask != 0)
+ {
+ throw std::runtime_error("new_axis_mask is not implemented yet.");
+ }
+ if (input()->element_type() == DataType::U8)
+ {
+ assert(input()->scale() == output()->scale());
+ assert(input()->zero_point() == output()->zero_point());
+ }
+ tflite::StridedSliceParams op_params{};
+ op_params.start_indices_count = input()->shape().num_dims();
+ op_params.stop_indices_count = input()->shape().num_dims();
+ op_params.strides_count = input()->shape().num_dims();
+
+ for (int i = 0; i < input()->shape().num_dims(); i++)
+ {
+ op_params.start_indices[i] = getTensorData<int32_t>(begin())[i];
+ op_params.stop_indices[i] = getTensorData<int32_t>(end())[i];
+ op_params.strides[i] = getTensorData<int32_t>(strides())[i];
+ }
+ op_params.begin_mask = params().begin_mask;
+ op_params.ellipsis_mask = 0;
+ op_params.end_mask = params().end_mask;
+ op_params.new_axis_mask = 0;
+ op_params.shrink_axis_mask = params().shrink_axis_mask;
+ std::vector<int32_t> 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<int32_t>(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<float>(stride));
+ dim_shape = dim_shape < 0 ? 0 : dim_shape;
+ if (!shrink_axis)
+ {
+ output_shape_vector.push_back(dim_shape);
+ }
+ }
+ Shape output_shape = Shape(output_shape_vector.size());
+ for (size_t i = 0; i < output_shape_vector.size(); i++)
+ {
+ output_shape.dim(i) = output_shape_vector[output_shape_vector.size() - i - 1];
+ }
+ output()->resize(output_shape);
+}
+
+void StridedSlice::execute() const
+{
+ tflite::StridedSliceParams op_params{};
+ op_params.start_indices_count = input()->shape().num_dims();
+ op_params.stop_indices_count = input()->shape().num_dims();
+ op_params.strides_count = input()->shape().num_dims();
+
+ for (int i = 0; i < input()->shape().num_dims(); i++)
+ {
+ op_params.start_indices[i] = getTensorData<int32_t>(begin())[i];
+ op_params.stop_indices[i] = getTensorData<int32_t>(end())[i];
+ op_params.strides[i] = getTensorData<int32_t>(strides())[i];
+ }
+ op_params.begin_mask = params().begin_mask;
+ op_params.ellipsis_mask = 0;
+ op_params.end_mask = params().end_mask;
+ op_params.new_axis_mask = 0;
+ op_params.shrink_axis_mask = params().shrink_axis_mask;
+
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ tflite::reference_ops::StridedSlice(op_params, getTensorShape(input()),
+ getTensorData<float>(input()), getTensorShape(output()),
+ getTensorData<float>(output()));
+ break;
+ case DataType::U8:
+ tflite::reference_ops::StridedSlice(op_params, getTensorShape(input()),
+ getTensorData<uint8_t>(input()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/StridedSlice.h b/compiler/luci-interpreter/src/kernels/StridedSlice.h
new file mode 100644
index 000000000..fc96893a7
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/StridedSlice.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_STRIDEDSLICE_H
+#define LUCI_INTERPRETER_KERNELS_STRIDEDSLICE_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class StridedSlice : public KernelWithParams<StridedSliceParams>
+{
+public:
+ StridedSlice(const Tensor *input, const Tensor *begin, const Tensor *end, const Tensor *strides,
+ Tensor *output, const StridedSliceParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ const Tensor *begin() const { return _inputs[1]; }
+ const Tensor *end() const { return _inputs[2]; }
+ const Tensor *strides() const { return _inputs[3]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_STRIDEDSLICE_H
diff --git a/compiler/luci-interpreter/src/kernels/StridedSlice.test.cpp b/compiler/luci-interpreter/src/kernels/StridedSlice.test.cpp
new file mode 100644
index 000000000..66dffcaf2
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/StridedSlice.test.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/StridedSlice.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(StridedSliceTest, Float)
+{
+ Shape input_shape{2, 3, 2};
+ std::vector<float> input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+ Shape begin_shape{3};
+ std::vector<int32_t> begin_data{0, 0, 0};
+ Shape end_shape{3};
+ std::vector<int32_t> end_data{1, 3, 2};
+ Shape strides_shape{3};
+ std::vector<int32_t> strides_data{1, 1, 1};
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor begin_tensor = makeInputTensor<DataType::S32>(begin_shape, begin_data);
+ Tensor end_tensor = makeInputTensor<DataType::S32>(end_shape, end_data);
+ Tensor strides_tensor = makeInputTensor<DataType::S32>(strides_shape, strides_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ 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();
+ kernel.execute();
+
+ std::vector<int32_t> output_shape{3, 2};
+ std::vector<float> output_data{1, 2, 3, 4, 5, 6};
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+TEST(StridedSliceTest, Uint8)
+{
+ Shape input_shape{2, 3, 2};
+ std::vector<float> input_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+ Shape begin_shape{3};
+ std::vector<int32_t> begin_data{0, 0, 0};
+ Shape end_shape{3};
+ std::vector<int32_t> end_data{1, 3, 2};
+ Shape strides_shape{3};
+ std::vector<int32_t> strides_data{1, 1, 1};
+ Tensor input_tensor = makeInputTensor<DataType::U8>(input_shape, 1.0f, 0, input_data);
+ Tensor begin_tensor = makeInputTensor<DataType::S32>(begin_shape, begin_data);
+ Tensor end_tensor = makeInputTensor<DataType::S32>(end_shape, end_data);
+ Tensor strides_tensor = makeInputTensor<DataType::S32>(strides_shape, strides_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, 1.0f, 0);
+
+ 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();
+ kernel.execute();
+
+ std::vector<int32_t> output_shape{3, 2};
+ std::vector<float> output_data{1, 2, 3, 4, 5, 6};
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(output_data));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape));
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Sub.cpp b/compiler/luci-interpreter/src/kernels/Sub.cpp
new file mode 100644
index 000000000..dd9c1102f
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Sub.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Sub.h"
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/optimized/legacy_optimized_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Sub::Sub(const Tensor *input1, const Tensor *input2, Tensor *output, const SubParams &params)
+ : KernelWithParams<SubParams>({input1, input2}, {output}, params)
+{
+}
+
+void Sub::configure()
+{
+ LUCI_INTERPRETER_CHECK(!(input1()->element_type() != input2()->element_type()))
+ output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape()));
+}
+
+void Sub::execute() const
+{
+ switch (input1()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Sub::evalFloat() const
+{
+ float activation_min{};
+ float activation_max{};
+ calculateActivationRange(_params.activation, &activation_min, &activation_max);
+
+ tflite::ArithmeticParams params{};
+ params.float_activation_min = activation_min;
+ params.float_activation_max = activation_max;
+
+ const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes(
+ getTensorShape(input1()), getTensorShape(input2()), &params);
+
+ if (need_broadcast)
+ {
+ tflite::reference_ops::BroadcastSubSlow(
+ params, getTensorShape(input1()), getTensorData<float>(input1()), getTensorShape(input2()),
+ getTensorData<float>(input2()), getTensorShape(output()), getTensorData<float>(output()));
+ }
+ else
+ {
+ tflite::optimized_ops::Sub(params, getTensorShape(input1()), getTensorData<float>(input1()),
+ getTensorShape(input2()), getTensorData<float>(input2()),
+ getTensorShape(output()), getTensorData<float>(output()));
+ }
+}
+
+void Sub::evalQuantized() const
+{
+ const auto input1_scale = static_cast<double>(input1()->scale());
+ const auto input2_scale = static_cast<double>(input2()->scale());
+ const auto output_scale = static_cast<double>(output()->scale());
+
+ const int left_shift = 20;
+ const double twice_max_input_scale = 2 * std::max(input1_scale, input2_scale);
+ const double real_input1_multiplier = input1_scale / twice_max_input_scale;
+ const double real_input2_multiplier = input2_scale / twice_max_input_scale;
+ const double real_output_multiplier = twice_max_input_scale / ((1 << left_shift) * output_scale);
+
+ int32_t input1_multiplier{}, input2_multiplier{}, output_multiplier{};
+ int input1_shift{}, input2_shift{}, output_shift{};
+ quantizeMultiplierSmallerThanOneExp(real_input1_multiplier, &input1_multiplier, &input1_shift);
+ quantizeMultiplierSmallerThanOneExp(real_input2_multiplier, &input2_multiplier, &input2_shift);
+ quantizeMultiplierSmallerThanOneExp(real_output_multiplier, &output_multiplier, &output_shift);
+
+ int32_t activation_min{};
+ int32_t activation_max{};
+ calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max);
+
+ tflite::ArithmeticParams params{};
+ params.left_shift = left_shift;
+ // The kernel expects inputs' zero points to be negated.
+ params.input1_offset = -input1()->zero_point(); // Note the '-'.
+ params.input1_multiplier = input1_multiplier;
+ params.input1_shift = input1_shift;
+ params.input2_offset = -input2()->zero_point(); // Note the '-'.
+ params.input2_multiplier = input2_multiplier;
+ params.input2_shift = input2_shift;
+ params.output_offset = output()->zero_point();
+ params.output_multiplier = output_multiplier;
+ params.output_shift = output_shift;
+ params.quantized_activation_min = activation_min;
+ params.quantized_activation_max = activation_max;
+
+ const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes(
+ getTensorShape(input1()), getTensorShape(input2()), &params);
+
+ if (need_broadcast)
+ {
+ tflite::reference_ops::BroadcastSubSlow(
+ params, getTensorShape(input1()), getTensorData<uint8_t>(input1()),
+ getTensorShape(input2()), getTensorData<uint8_t>(input2()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ }
+ else
+ {
+ tflite::reference_ops::Sub(params, getTensorShape(input1()), getTensorData<uint8_t>(input1()),
+ getTensorShape(input2()), getTensorData<uint8_t>(input2()),
+ getTensorShape(output()), getTensorData<uint8_t>(output()));
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Sub.h b/compiler/luci-interpreter/src/kernels/Sub.h
new file mode 100644
index 000000000..d7940b5c6
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Sub.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_SUB_H
+#define LUCI_INTERPRETER_KERNELS_SUB_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Sub : public KernelWithParams<SubParams>
+{
+public:
+ Sub(const Tensor *input1, const Tensor *input2, Tensor *output, const SubParams &params);
+
+ const Tensor *input1() const { return _inputs[0]; }
+ const Tensor *input2() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_SUB_H
diff --git a/compiler/luci-interpreter/src/kernels/Sub.test.cpp b/compiler/luci-interpreter/src/kernels/Sub.test.cpp
new file mode 100644
index 000000000..9f77fe7e0
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Sub.test.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Sub.h"
+#include "kernels/TestUtils.h"
+
+#include <algorithm>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+using std::pair;
+using std::vector;
+using std::transform;
+using std::initializer_list;
+
+// for quantized Add, the error shouldn't exceed step
+float GetTolerance(float min, float max)
+{
+ float kQuantizedStep = (max - min) / 255.0;
+ return kQuantizedStep;
+}
+
+TEST(SubTest, Uint8)
+{
+ Shape base_shape = {2, 3, 1, 2};
+ vector<float> base_data = {-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f,
+ 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f};
+ vector<Shape> test_shapes = {{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}};
+ vector<float> test_data = {0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f};
+ vector<vector<int32_t>> output_shapes = {{2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}};
+ vector<vector<float>> output_data = {
+ {-0.5f, 2.0f, 0.1f, 1.8f, -1.3f, 1.4f, 0.7f, 0.2f, 1.3f, 0.0f, -0.1f, -0.4f,
+ 0.6f, -1.4f, 1.2f, -1.6f, -0.2f, -2.0f, 1.0f, 2.5f, 1.6f, 2.3f, 0.2f, 1.9f,
+ -1.8f, -0.3f, -1.2f, -0.5f, -2.6f, -0.9f, 0.5f, -2.5f, 1.1f, -2.7f, -0.3f, -3.0f},
+ {-0.5f, 2.0f, 1.3f, 0.0f, -0.2f, -2.0f, 1.0f, 2.5f, -1.2f, -0.5f, -0.3f, -3.0f},
+ {-0.5f, 2.1f, -0.6f, 2.0f, 0.1f, 2.7f, 0.7f, 0.3f, 0.6f, 0.2f, 1.3f, 0.9f,
+ 0.6f, -1.3f, 0.5f, -1.4f, 1.2f, -0.7f, 0.7f, 2.3f, 0.2f, 1.8f, 0.3f, 1.9f,
+ -2.1f, -0.5f, -2.6f, -1.0f, -2.5f, -0.9f, 0.2f, -2.7f, -0.3f, -3.0f, -0.2f, -3.0f},
+ {-0.5f, 2.1f, 0.6f, 0.2f, 1.2f, -0.7f, 0.7f, 2.3f, -2.6f, -1.0f, -0.2f, -3.0f}};
+
+ float kQuantizedTolerance = GetTolerance(-3.f, 3.f);
+ pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-3.f, 3.f);
+ for (size_t i = 0; i < output_data.size(); ++i)
+ {
+ Tensor input1_tensor =
+ makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, base_data);
+ Tensor input2_tensor = makeInputTensor<DataType::U8>(test_shapes[i], quant_param.first,
+ quant_param.second, test_data);
+ Tensor output_tensor =
+ makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second);
+
+ SubParams params{};
+ params.activation = Activation::NONE;
+
+ Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data[i], kQuantizedTolerance));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i]));
+ }
+
+ // Inversion step for output_data, because subtract is not commutative operation
+ auto multiply = [](auto &i) {
+ transform(i.begin(), i.end(), i.begin(), [](auto &value) { return value * -1.0f; });
+ };
+ for_each(output_data.begin(), output_data.end(), multiply);
+
+ // Re-run with exchanged inputs.
+ for (size_t i = 0; i < output_data.size(); ++i)
+ {
+ Tensor input1_tensor = makeInputTensor<DataType::U8>(test_shapes[i], quant_param.first,
+ quant_param.second, test_data);
+ Tensor input2_tensor =
+ makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, base_data);
+ Tensor output_tensor =
+ makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second);
+
+ SubParams params{};
+ params.activation = Activation::NONE;
+
+ Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(dequantizeTensorData(output_tensor),
+ FloatArrayNear(output_data[i], kQuantizedTolerance));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i]));
+ }
+}
+
+TEST(SubTest, Float)
+{
+ Shape base_shape = {2, 3, 1, 2};
+ vector<Shape> test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}};
+ vector<vector<int32_t>> output_shapes{{2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}};
+ vector<vector<float>> test_outputs = {
+ {0.0f, 2.0f, 0.1f, 1.8f, 0.0f, 1.4f, 0.7f, 0.2f, 1.3f, 0.0f, 0.0f, 0.0f,
+ 0.6f, 0.0f, 1.2f, 0.0f, 0.0f, 0.0f, 1.0f, 2.5f, 1.6f, 2.3f, 0.2f, 1.9f,
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 1.1f, 0.0f, 0.0f, 0.0f},
+ {0.0f, 2.0f, 1.3f, 0.0f, 0.0f, 0.0f, 1.0f, 2.5f, 0.0f, 0.0f, 0.0f, 0.0f},
+ {0.0f, 2.1f, 0.0f, 2.0f, 0.1f, 2.7f, 0.7f, 0.3f, 0.6f, 0.2f, 1.3f, 0.9f,
+ 0.6f, 0.0f, 0.5f, 0.0f, 1.2f, 0.0f, 0.7f, 2.3f, 0.2f, 1.8f, 0.3f, 1.9f,
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
+ {0.0f, 2.1f, 0.6f, 0.2f, 1.2f, 0.0f, 0.7f, 2.3f, 0.0f, 0.0f, 0.0f, 0.0f}};
+
+ vector<float> input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f,
+ 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f};
+ vector<float> input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f};
+ for (size_t i = 0; i < test_shapes.size(); ++i)
+ {
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>(base_shape, input1_data);
+ Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>(test_shapes[i], input2_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ SubParams params{};
+ params.activation = Activation::RELU;
+
+ Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f))
+ << "With shape number " << i;
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i]));
+ }
+}
+
+TEST(SubTest, Input_Output_Type_NEG)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f});
+ Tensor input2_tensor = makeInputTensor<DataType::S32>({1}, {2});
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ SubParams params{};
+ params.activation = Activation::RELU;
+
+ Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+TEST(SubTest, Invalid_Input_Type_NEG)
+{
+ Tensor input1_tensor = makeInputTensor<DataType::S64>({1}, {1});
+ Tensor input2_tensor = makeInputTensor<DataType::S64>({1}, {2});
+ Tensor output_tensor = makeOutputTensor(DataType::S64);
+
+ SubParams params{};
+ params.activation = Activation::RELU;
+
+ Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
+ kernel.configure();
+ EXPECT_ANY_THROW(kernel.execute());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Tanh.cpp b/compiler/luci-interpreter/src/kernels/Tanh.cpp
new file mode 100644
index 000000000..1c3d1281d
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Tanh.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Tanh.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+Tanh::Tanh(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {}
+
+void Tanh::configure()
+{
+ LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
+ if (input()->element_type() == DataType::U8)
+ {
+ populateLookupTable();
+ }
+ output()->resize(input()->shape());
+}
+
+void Tanh::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+void Tanh::evalFloat() const
+{
+ tflite::reference_ops::Tanh(getTensorShape(input()), getTensorData<float>(input()),
+ getTensorShape(output()), getTensorData<float>(output()));
+}
+
+void Tanh::evalQuantized() const
+{
+ const int size = tflite::MatchingFlatSize(getTensorShape(input()), getTensorShape(output()));
+ uint8_t *output_data = getTensorData<uint8_t>(output());
+ const uint8_t *input_data = getTensorData<uint8_t>(input());
+ for (int i = 0; i < size; ++i)
+ {
+ output_data[i] = getTableValue(input_data[i]);
+ }
+}
+
+void Tanh::populateLookupTable()
+{
+ const auto input_scale = static_cast<double>(input()->scale());
+ const auto input_zero_point = static_cast<int32_t>(input()->zero_point());
+ const auto output_scale = static_cast<double>(output()->scale());
+ const auto output_zero_point = static_cast<int32_t>(output()->zero_point());
+ const float inverse_scale = 1 / output_scale;
+ int32_t maxval = std::numeric_limits<uint8_t>::max();
+ int32_t minval = std::numeric_limits<uint8_t>::min();
+ for (int32_t val = minval; val <= maxval; ++val)
+ {
+ const float dequantized = input_scale * (val - input_zero_point);
+ const float transformed = std::tanh(dequantized);
+ const float rescaled = std::round(transformed * inverse_scale);
+ const int32_t quantized = static_cast<int32_t>(rescaled + output_zero_point);
+ setTableValue(static_cast<uint8_t>(std::max(std::min(maxval, quantized), minval)),
+ static_cast<uint8_t>(val));
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Tanh.h b/compiler/luci-interpreter/src/kernels/Tanh.h
new file mode 100644
index 000000000..8017c9638
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Tanh.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_TANH_H
+#define LUCI_INTERPRETER_KERNELS_TANH_H
+
+#include "core/Kernel.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Tanh : public Kernel
+{
+public:
+ Tanh(const Tensor *input, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+ void populateLookupTable();
+ void setTableValue(uint8_t value, uint8_t idx) { _table[idx] = value; };
+ uint8_t getTableValue(uint8_t idx) const { return _table[idx]; };
+
+private:
+ uint8_t _table[256]{};
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_TANH_H
diff --git a/compiler/luci-interpreter/src/kernels/Tanh.test.cpp b/compiler/luci-interpreter/src/kernels/Tanh.test.cpp
new file mode 100644
index 000000000..17b50f259
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Tanh.test.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Tanh.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+TEST(TanhTest, Float)
+{
+ Shape input_shape{1, 2, 4, 1};
+ std::vector<float> input_data{
+ 0, -6, 2, 4, //
+ 3, -2, 10, 1, //
+ };
+ Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Tanh kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> ref_output_data{
+ 0, -0.9999877, 0.9640275, 0.999329, //
+ 0.99505475, -0.9640275, 1, 0.7615941, //
+ };
+ EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(TanhTest, Uint8)
+{
+ float kMin = -1;
+ float kMax = 127.f / 128.f;
+ float kTanhTolerance = 2 * (1. / 256);
+ std::pair<float, int32_t> input_quant_param = quantizationParams<uint8_t>(8 * kMin, 8 * kMax);
+ std::pair<float, int32_t> output_quant_param = quantizationParams<uint8_t>(kMin, kMax);
+ std::vector<float> 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<DataType::U8>({2, 6, 4, 1}, input_quant_param.first,
+ input_quant_param.second, input_data);
+ Tensor output_tensor =
+ makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second);
+
+ Tanh kernel(&input_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ std::vector<float> 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<int32_t> ref_output_shape{2, 6, 4, 1};
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data, kTanhTolerance));
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
+}
+
+TEST(TanhTest, InputTypeInvalid_NEG)
+{
+ std::vector<int64_t> 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<DataType::S64>({2, 6, 4, 1}, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
+
+ Tanh kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.execute());
+}
+
+TEST(TanhTest, InputOutputMismatch_NEG)
+{
+ std::vector<float> 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<DataType::FLOAT32>({2, 6, 4, 1}, input_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8);
+
+ Tanh kernel(&input_tensor, &output_tensor);
+ EXPECT_ANY_THROW(kernel.configure());
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/TestUtils.cpp b/compiler/luci-interpreter/src/kernels/TestUtils.cpp
new file mode 100644
index 000000000..c3c0b5a7d
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/TestUtils.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/TestUtils.h"
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace testing
+{
+
+using ::testing::FloatNear;
+using ::testing::Matcher;
+
+Tensor makeOutputTensor(DataType element_type) { return Tensor(element_type, {}, {}, ""); }
+
+Tensor makeOutputTensor(DataType element_type, float scale, int32_t zero_point)
+{
+ return Tensor(element_type, {}, {{scale}, {zero_point}}, "");
+}
+
+std::vector<float> dequantizeTensorData(const Tensor &tensor)
+{
+ if (tensor.element_type() == DataType::U8)
+ {
+ std::vector<uint8_t> data = extractTensorData<uint8_t>(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<int16_t> data = extractTensorData<int16_t>(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<float> 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<float> part_dequantized_data =
+ dequantize(data.data() + offset, inner_dims_size, scale, 0);
+ dequantized_data.insert(dequantized_data.end(), part_dequantized_data.begin(),
+ part_dequantized_data.end());
+ }
+ return dequantized_data;
+ }
+ else
+ {
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+Matcher<std::vector<float>> FloatArrayNear(const std::vector<float> &values, float max_abs_error)
+{
+ std::vector<Matcher<float>> matchers;
+ matchers.reserve(values.size());
+ for (const float v : values)
+ {
+ matchers.emplace_back(FloatNear(v, max_abs_error));
+ }
+ return ElementsAreArray(matchers);
+}
+
+std::vector<int32_t> extractTensorShape(const Tensor &tensor)
+{
+ std::vector<int32_t> result;
+ int dims = tensor.shape().num_dims();
+ for (int i = 0; i < dims; i++)
+ {
+ result.push_back(tensor.shape().dim(i));
+ }
+ return result;
+}
+
+} // namespace testing
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/TestUtils.h b/compiler/luci-interpreter/src/kernels/TestUtils.h
new file mode 100644
index 000000000..1f17e39e1
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/TestUtils.h
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_TESTUTILS_H
+#define LUCI_INTERPRETER_KERNELS_TESTUTILS_H
+
+#include "luci_interpreter/core/Tensor.h"
+
+#include <type_traits>
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace testing
+{
+
+template <typename T>
+std::vector<T> quantize(const float *data, size_t num_elements, float scale, int32_t zero_point);
+
+template <DataType DT>
+Tensor makeInputTensor(const Shape &shape, const std::vector<typename DataTypeImpl<DT>::Type> &data)
+{
+ Tensor tensor(DT, shape, {}, "");
+ tensor.writeData(data.data(), data.size() * sizeof(typename DataTypeImpl<DT>::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
+ * @return created tensor
+ */
+template <DataType DT>
+Tensor makeInputTensor(const Shape &shape, float scale, int32_t zero_point,
+ const std::vector<float> &data)
+{
+ using NativeT = typename DataTypeImpl<DT>::Type;
+ Tensor tensor(DT, shape, {{scale}, {zero_point}}, "");
+ std::vector<NativeT> quantized_data =
+ quantize<NativeT>(data.data(), data.size(), scale, zero_point);
+ 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
+ * @return created tensor
+ */
+template <DataType DT>
+Tensor makeInputTensor(const Shape &shape, const std::vector<float> &scales,
+ const std::vector<int32_t> &zero_points, int quantized_dimension,
+ const std::vector<float> &data)
+{
+ using NativeT = typename DataTypeImpl<DT>::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<NativeT> 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<NativeT> part_quantized_data =
+ quantize<NativeT>(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());
+ 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<int32_t> extractTensorShape(const Tensor &tensor);
+
+// Returns the corresponding DataType given the type T.
+template <typename T> constexpr DataType getElementType()
+{
+ if (std::is_same<T, float>::value)
+ return DataType::FLOAT32;
+ if (std::is_same<T, uint8_t>::value)
+ return DataType::U8;
+ if (std::is_same<T, int32_t>::value)
+ return DataType::S32;
+ if (std::is_same<T, int64_t>::value)
+ return DataType::S64;
+ return DataType::Unknown;
+}
+
+template <typename T> std::vector<T> extractTensorData(const Tensor &tensor)
+{
+ const auto *data_ptr = tensor.data<T>();
+ return std::vector<T>(data_ptr, data_ptr + tensor.shape().num_elements());
+}
+
+std::vector<float> dequantizeTensorData(const Tensor &tensor);
+
+// Array version of `::testing::FloatNear` matcher.
+::testing::Matcher<std::vector<float>> FloatArrayNear(const std::vector<float> &values,
+ float max_abs_error = 1.0e-5f);
+
+template <typename T>
+std::vector<T> quantize(const float *data, size_t num_elements, float scale, int32_t zero_point)
+{
+ static_assert(std::is_integral<T>::value, "Integral type expected.");
+
+ float q_min{}, q_max{};
+ if (std::is_signed<T>::value)
+ {
+ // For now, assume that signed type implies signed symmetric quantization.
+ assert(zero_point == 0);
+ q_min = -std::numeric_limits<T>::max();
+ q_max = std::numeric_limits<T>::max();
+ }
+ else
+ {
+ q_min = 0;
+ q_max = std::numeric_limits<T>::max();
+ }
+
+ std::vector<T> q;
+ for (size_t i = 0; i < num_elements; ++i)
+ {
+ const auto &f = data[i];
+ q.push_back(static_cast<T>(
+ std::max<float>(q_min, std::min<float>(q_max, std::round(zero_point + (f / scale))))));
+ }
+ return q;
+}
+
+template <typename T>
+std::vector<float> dequantize(const T *data, size_t num_elements, float scale, int32_t zero_point)
+{
+ static_assert(std::is_integral<T>::value, "Integral type expected.");
+ std::vector<float> 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 <typename T> std::pair<float, int32_t> quantizationParams(float f_min, float f_max)
+{
+ static_assert(std::is_integral<T>::value, "Integral type expected.");
+ int32_t zero_point = 0;
+ float scale = 0;
+ const T qmin = std::numeric_limits<T>::lowest();
+ const T qmax = std::numeric_limits<T>::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<T>(std::round(zero_point_double));
+ }
+
+ // The zero point should always be in the range of quantized value,
+ // // [qmin, qmax].
+ assert(qmax >= nudged_zero_point);
+ assert(qmin <= nudged_zero_point);
+ zero_point = nudged_zero_point;
+ // finally, return the values
+ return {scale, zero_point};
+}
+
+inline float getTolerance(float min, float max, int quantize_steps)
+{
+ return ((max - min) / quantize_steps);
+}
+
+} // namespace testing
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_TESTUTILS_H
diff --git a/compiler/luci-interpreter/src/kernels/Transpose.cpp b/compiler/luci-interpreter/src/kernels/Transpose.cpp
new file mode 100644
index 000000000..8265d9937
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Transpose.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Transpose.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Transpose::Transpose(const Tensor *input, const Tensor *perm, Tensor *output)
+ : Kernel({input, perm}, {output})
+{
+}
+
+void Transpose::configure()
+{
+ // Transpose op only supports 1D-4D input arrays.
+ int dims = input()->shape().num_dims();
+ const int *perm_data = getTensorData<int32_t>(perm());
+
+ assert(input()->shape().num_dims() <= 4);
+ assert(input()->element_type() == output()->element_type());
+
+ assert(perm()->shape().num_dims() == 1);
+ assert(perm()->shape().dim(0) == dims);
+
+ Shape output_shape(dims);
+ for (int i = 0; i < dims; i++)
+ {
+ assert(perm_data[i] < dims && perm_data[i] >= 0);
+ output_shape.dim(i) = input()->shape().dim(perm_data[i]);
+ }
+
+ output()->resize(output_shape);
+}
+
+void Transpose::execute() const
+{
+ tflite::TransposeParams params{};
+ const int *perm_data = getTensorData<int32_t>(perm());
+ const int size = perm()->shape().dim(0);
+ params.perm_count = size;
+ for (int i = 0; i < size; i++)
+ params.perm[i] = perm_data[i];
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ tflite::reference_ops::Transpose(params, getTensorShape(input()),
+ getTensorData<float>(input()), getTensorShape(output()),
+ getTensorData<float>(output()));
+ break;
+ case DataType::U8:
+ tflite::reference_ops::Transpose(params, getTensorShape(input()),
+ getTensorData<uint8_t>(input()), getTensorShape(output()),
+ getTensorData<uint8_t>(output()));
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Transpose.h b/compiler/luci-interpreter/src/kernels/Transpose.h
new file mode 100644
index 000000000..d6f89c352
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Transpose.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_TRANSPOSE_H
+#define LUCI_INTERPRETER_KERNELS_TRANSPOSE_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Transpose : public Kernel
+{
+public:
+ Transpose(const Tensor *input, const Tensor *perm, Tensor *output);
+
+ const Tensor *input() const { return _inputs[0]; }
+ const Tensor *perm() const { return _inputs[1]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_TRANSPOSE_H
diff --git a/compiler/luci-interpreter/src/kernels/Transpose.test.cpp b/compiler/luci-interpreter/src/kernels/Transpose.test.cpp
new file mode 100644
index 000000000..1c99223a8
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Transpose.test.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Transpose.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T>
+void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> perm_shape,
+ std::initializer_list<int32_t> output_shape, std::initializer_list<T> input_data,
+ std::initializer_list<int32_t> perm_data, std::initializer_list<T> output_data)
+{
+ constexpr DataType element_type = getElementType<T>();
+ Tensor input_tensor = makeInputTensor<element_type>(input_shape, input_data);
+ Tensor perm_tensor = makeInputTensor<DataType::S32>(perm_shape, perm_data);
+ Tensor output_tensor = makeOutputTensor(element_type);
+
+ Transpose kernel(&input_tensor, &perm_tensor, &output_tensor);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorData<T>(output_tensor), ::testing::ElementsAreArray(output_data));
+}
+
+template <typename T> class TransposeTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(TransposeTest, DataTypes);
+
+TYPED_TEST(TransposeTest, Small3D)
+{
+ Check<TypeParam>(/*input_shape=*/{2, 3, 4}, /*perm_shape=*/{3}, /*output_shape=*/{4, 2, 3},
+ /*input_data=*/{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23},
+ /*perm_data=*/{2, 0, 1},
+ /*output_data=*/{0, 4, 8, 12, 16, 20, 1, 5, 9, 13, 17, 21,
+ 2, 6, 10, 14, 18, 22, 3, 7, 11, 15, 19, 23});
+}
+
+TYPED_TEST(TransposeTest, Large4D)
+{
+ Check<TypeParam>(
+ /*input_shape=*/{2, 3, 4, 5}, /*perm_shape=*/{4}, /*output_shape=*/{4, 2, 3, 5},
+ /*input_data=*/{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119},
+ /*perm_data=*/{2, 0, 1, 3},
+ /*output_data=*/{0, 1, 2, 3, 4, 20, 21, 22, 23, 24, 40, 41, 42, 43, 44,
+ 60, 61, 62, 63, 64, 80, 81, 82, 83, 84, 100, 101, 102, 103, 104,
+ 5, 6, 7, 8, 9, 25, 26, 27, 28, 29, 45, 46, 47, 48, 49,
+ 65, 66, 67, 68, 69, 85, 86, 87, 88, 89, 105, 106, 107, 108, 109,
+ 10, 11, 12, 13, 14, 30, 31, 32, 33, 34, 50, 51, 52, 53, 54,
+ 70, 71, 72, 73, 74, 90, 91, 92, 93, 94, 110, 111, 112, 113, 114,
+ 15, 16, 17, 18, 19, 35, 36, 37, 38, 39, 55, 56, 57, 58, 59,
+ 75, 76, 77, 78, 79, 95, 96, 97, 98, 99, 115, 116, 117, 118, 119});
+}
+
+TYPED_TEST(TransposeTest, Large2D)
+{
+ Check<TypeParam>(
+ /*input_shape=*/{10, 12}, /*perm_shape=*/{2}, /*output_shape=*/{12, 10},
+ /*input_data=*/{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119},
+ /*perm_data=*/{1, 0},
+ /*output_data=*/{
+ 0, 12, 24, 36, 48, 60, 72, 84, 96, 108, 1, 13, 25, 37, 49, 61, 73, 85, 97, 109,
+ 2, 14, 26, 38, 50, 62, 74, 86, 98, 110, 3, 15, 27, 39, 51, 63, 75, 87, 99, 111,
+ 4, 16, 28, 40, 52, 64, 76, 88, 100, 112, 5, 17, 29, 41, 53, 65, 77, 89, 101, 113,
+ 6, 18, 30, 42, 54, 66, 78, 90, 102, 114, 7, 19, 31, 43, 55, 67, 79, 91, 103, 115,
+ 8, 20, 32, 44, 56, 68, 80, 92, 104, 116, 9, 21, 33, 45, 57, 69, 81, 93, 105, 117,
+ 10, 22, 34, 46, 58, 70, 82, 94, 106, 118, 11, 23, 35, 47, 59, 71, 83, 95, 107, 119});
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/TransposeConv.cpp b/compiler/luci-interpreter/src/kernels/TransposeConv.cpp
new file mode 100644
index 000000000..b0ee905dc
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/TransposeConv.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/TransposeConv.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+TransposeConv::TransposeConv(const Tensor *output_shape, const Tensor *filter, const Tensor *input,
+ const Tensor *bias, Tensor *output, const TransposeConvParams &params)
+ : KernelWithParams<TransposeConvParams>({output_shape, filter, input, bias}, {output}, params)
+{
+}
+
+TransposeConv::~TransposeConv()
+{
+ // Define destructor here, to delete vector of qunatized multipliers properly
+}
+
+void TransposeConv::configure()
+{
+ assert(output_shape()->shape().num_dims() == 1);
+ assert(input()->shape().num_dims() == 4);
+ assert(filter()->shape().num_dims() == 4);
+ assert(input()->element_type() == DataType::FLOAT32 || input()->element_type() == DataType::U8 ||
+ input()->element_type() == DataType::S16);
+ assert(input()->element_type() == output()->element_type());
+ assert(input()->shape().dim(3) == filter()->shape().dim(3));
+
+ const int num_dims = output_shape()->shape().dim(0);
+ Shape out_shape(num_dims);
+ const auto *shape_data = getTensorData<int32_t>(output_shape());
+ for (int i = 0; i < num_dims; i++)
+ out_shape.dim(i) = shape_data[i];
+ output()->resize(out_shape);
+
+ const int32_t filter_height = filter()->shape().dim(1);
+ const int32_t filter_width = filter()->shape().dim(2);
+ const int32_t output_height = out_shape.dim(1);
+ const int32_t output_width = out_shape.dim(2);
+
+ const int32_t unused_output_height =
+ computeOutputSize(params().padding, output_height, filter_height, params().stride_height, 1);
+ const int32_t unused_output_width =
+ computeOutputSize(params().padding, output_width, filter_width, params().stride_width, 1);
+
+ _padding_height =
+ computePadding(params().stride_height, 1, output_height, filter_height, unused_output_height);
+ _padding_width =
+ computePadding(params().stride_width, 1, output_width, filter_width, unused_output_width);
+
+ if (input()->element_type() == DataType::U8 || input()->element_type() == DataType::S16)
+ {
+ DataType scratch_data_type =
+ input()->element_type() == DataType::S16 ? DataType::S64 : DataType::S32;
+ _scratch_tensor =
+ std::make_unique<Tensor>(scratch_data_type, output()->shape(), AffineQuantization{}, "");
+ const std::vector<double> real_multipliers =
+ getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale());
+
+ _quant_multipliers = quantizeMultipliers(real_multipliers);
+ }
+}
+
+void TransposeConv::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ evalFloat();
+ break;
+ case DataType::U8:
+ evalQuantized();
+ break;
+ case DataType::S16:
+ evalQuantizedS16();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+ if (!!_scratch_tensor)
+ _scratch_tensor->deallocate();
+}
+
+void TransposeConv::evalFloat() const
+{
+ tflite::ConvParams op_params{};
+ op_params.padding_type = tflite::PaddingType::kSame;
+ op_params.padding_values.height = _padding_height;
+ op_params.padding_values.width = _padding_width;
+ op_params.stride_height = params().stride_height;
+ op_params.stride_width = params().stride_width;
+ tflite::reference_ops::TransposeConv(op_params, //
+ getTensorShape(input()), getTensorData<float>(input()), //
+ getTensorShape(filter()), getTensorData<float>(filter()), //
+ getTensorShape(bias()), getTensorData<float>(bias()), //
+ getTensorShape(output()), getTensorData<float>(output()), //
+ tflite::RuntimeShape(), nullptr);
+}
+
+void TransposeConv::evalQuantized() const
+{
+ tflite::ConvParams op_params{};
+ op_params.padding_type = tflite::PaddingType::kSame;
+ op_params.padding_values.height = _padding_height;
+ op_params.padding_values.width = _padding_width;
+ op_params.stride_height = params().stride_height;
+ op_params.stride_width = params().stride_width;
+ // The kernel expects input and filter zero points to be negated.
+ op_params.input_offset = -input()->zero_point(); // Note the '-'.
+ op_params.weights_offset = -filter()->zero_point(); // Note the '-'.
+ op_params.output_offset = output()->zero_point();
+ op_params.output_multiplier = _quant_multipliers[0].multiplier;
+ op_params.output_shift = _quant_multipliers[0].shift;
+ op_params.quantized_activation_min = std::numeric_limits<uint8_t>::min();
+ op_params.quantized_activation_max = std::numeric_limits<uint8_t>::max();
+
+ tflite::reference_ops::TransposeConv(op_params, //
+ getTensorShape(input()), getTensorData<uint8>(input()), //
+ getTensorShape(filter()), getTensorData<uint8>(filter()), //
+ getTensorShape(bias()), getTensorData<int32_t>(bias()), //
+ getTensorShape(output()), getTensorData<uint8>(output()), //
+ tflite::RuntimeShape(), nullptr, //
+ getTensorData<int32_t>(_scratch_tensor.get()));
+}
+
+void TransposeConv::evalQuantizedS16() const
+{
+ const auto *input_data = getTensorData<int16_t>(input());
+ const auto *filter_data = getTensorData<int16_t>(filter());
+ const auto *bias_data = getTensorData<int64_t>(bias());
+ auto *output_data = getTensorData<int16_t>(output());
+ auto *scratch_data = getTensorData<int64_t>(_scratch_tensor.get());
+
+ const Shape &input_shape = input()->shape();
+ const Shape &filter_shape = filter()->shape();
+ const Shape &output_shape = output()->shape();
+
+ const int32_t batches = input_shape.dim(0);
+ const int32_t input_height = input_shape.dim(1);
+ const int32_t input_width = input_shape.dim(2);
+ const int32_t input_depth = input_shape.dim(3);
+ const int32_t output_depth = filter_shape.dim(0);
+ const int32_t filter_height = filter_shape.dim(1);
+ const int32_t filter_width = filter_shape.dim(2);
+ const int32_t output_height = output_shape.dim(1);
+ const int32_t output_width = output_shape.dim(2);
+
+ const int32_t stride_height = _params.stride_height;
+ const int32_t stride_width = _params.stride_width;
+
+ int32_t activation_min{};
+ int32_t activation_max{};
+ calculateActivationRangeQuantized(Activation::NONE, output(), &activation_min, &activation_max);
+
+ std::memset(scratch_data, 0, _scratch_tensor->shape().num_elements() * sizeof(int64_t));
+
+ BroadcastableWrapper<ChannelQuantMultipliers> output_multipliers(_quant_multipliers);
+ for (int32_t batch = 0; batch < batches; ++batch)
+ {
+ for (int32_t in_y = 0; in_y < input_height; ++in_y)
+ {
+ for (int32_t in_x = 0; in_x < input_width; ++in_x)
+ {
+ for (int32_t in_c = 0; in_c < input_depth; ++in_c)
+ {
+ const int32_t out_y_origin = in_y * stride_height - _padding_height;
+ const int32_t out_x_origin = in_x * stride_width - _padding_width;
+ for (int32_t filter_y = 0; filter_y < filter_height; ++filter_y)
+ {
+ for (int32_t filter_x = 0; filter_x < filter_width; ++filter_x)
+ {
+ const int32_t out_x = out_x_origin + filter_x;
+ const int32_t out_y = out_y_origin + filter_y;
+ if ((out_y >= 0 && out_y < output_height) && (out_x >= 0 && out_x < output_width))
+ {
+ for (int32_t out_c = 0; out_c < output_depth; ++out_c)
+ {
+ const int16_t input_val =
+ input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)];
+ const int16_t filter_val =
+ filter_data[calcOffset(filter_shape, out_c, filter_y, filter_x, in_c)];
+ scratch_data[calcOffset(output_shape, batch, out_y, out_x, out_c)] +=
+ static_cast<int64_t>(input_val) * static_cast<int64_t>(filter_val);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ for (int32_t out_y = 0; out_y < output_height; ++out_y)
+ {
+ for (int32_t out_x = 0; out_x < output_width; ++out_x)
+ {
+ for (int32_t out_c = 0; out_c < output_depth; ++out_c)
+ {
+ int64_t acc = scratch_data[calcOffset(output_shape, batch, out_y, out_x, out_c)];
+ if (bias_data)
+ {
+ acc += bias_data[out_c];
+ }
+ int32_t scaled_acc = tflite::MultiplyByQuantizedMultiplier(
+ acc, output_multipliers[out_c].multiplier, output_multipliers[out_c].shift);
+
+ scaled_acc = std::max(scaled_acc, activation_min);
+ scaled_acc = std::min(scaled_acc, activation_max);
+
+ output_data[calcOffset(output_shape, batch, out_y, out_x, out_c)] = scaled_acc;
+ }
+ }
+ }
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/TransposeConv.h b/compiler/luci-interpreter/src/kernels/TransposeConv.h
new file mode 100644
index 000000000..f51e16976
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/TransposeConv.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_TRANSPOSECONV_H
+#define LUCI_INTERPRETER_KERNELS_TRANSPOSECONV_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class ChannelQuantMultipliers;
+
+class TransposeConv : public KernelWithParams<TransposeConvParams>
+{
+public:
+ TransposeConv(const Tensor *output_shape, const Tensor *filter, const Tensor *input,
+ const Tensor *bias, Tensor *output, const TransposeConvParams &params);
+
+ ~TransposeConv();
+
+ const Tensor *output_shape() const { return _inputs[0]; }
+ const Tensor *filter() const { return _inputs[1]; }
+ const Tensor *input() const { return _inputs[2]; }
+ const Tensor *bias() const { return _inputs[3]; }
+ Tensor *output() const { return _outputs[0]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ void evalFloat() const;
+ void evalQuantized() const;
+ void evalQuantizedS16() const;
+
+private:
+ std::unique_ptr<Tensor> _scratch_tensor;
+
+ int32_t _padding_height{};
+ int32_t _padding_width{};
+ // The scaling factor from input to output (aka the 'real multiplier') can
+ // be represented as a fixed point multiplier plus a left shift.
+ std::vector<ChannelQuantMultipliers> _quant_multipliers;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_TRANSPOSECONV_H
diff --git a/compiler/luci-interpreter/src/kernels/TransposeConv.test.cpp b/compiler/luci-interpreter/src/kernels/TransposeConv.test.cpp
new file mode 100644
index 000000000..8564de01d
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/TransposeConv.test.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/TransposeConv.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T, typename B>
+void Check(std::initializer_list<int32_t> output_shape_shape,
+ std::initializer_list<int32_t> weight_shape, std::initializer_list<int32_t> input_shape,
+ std::initializer_list<int32_t> bias_shape, std::initializer_list<int32_t> output_shape,
+ std::initializer_list<int32_t> output_shape_data, std::initializer_list<T> weight_data,
+ std::initializer_list<T> input_data, std::initializer_list<B> bias_data,
+ std::initializer_list<T> output_data, luci::Padding padding, int32_t stride_height,
+ int32_t stride_width)
+{
+ constexpr DataType element_type = getElementType<T>();
+ Tensor output_shape_tensor =
+ makeInputTensor<DataType::S32>(output_shape_shape, output_shape_data);
+ Tensor weight_tensor = makeInputTensor<element_type>(weight_shape, weight_data);
+ Tensor input_data_tensor = makeInputTensor<element_type>(input_shape, input_data);
+ Tensor output_tensor = makeOutputTensor(element_type);
+
+ TransposeConvParams params{};
+ params.padding = padding;
+ params.stride_height = stride_height;
+ params.stride_width = stride_width;
+
+ if (bias_data.size() != 0)
+ {
+ Tensor bias_tensor = makeInputTensor<getElementType<B>()>(bias_shape, bias_data);
+ TransposeConv kernel(&output_shape_tensor, &weight_tensor, &input_data_tensor, &bias_tensor,
+ &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+ }
+ else
+ {
+ TransposeConv kernel(&output_shape_tensor, &weight_tensor, &input_data_tensor, nullptr,
+ &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+ }
+ EXPECT_THAT(extractTensorData<T>(output_tensor), ::testing::ElementsAreArray(output_data));
+}
+
+TEST(TransposeConvTest, FloatSimple)
+{
+ Check<float, float>(
+ /*output_shape_shape=*/{4}, /*weight_shape=*/{1, 3, 3, 1}, /*input_shape=*/{1, 4, 4, 1},
+ /*bias_shape=*/{}, /*output_shape=*/{1, 4, 4, 1}, /*output_shape_data=*/{1, 4, 4, 1},
+ /*weight_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9},
+ /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
+ /*bias_data=*/{},
+ /*output_data=*/{29, 62, 83, 75, 99, 192, 237, 198, 207, 372, 417, 330, 263, 446, 485, 365},
+ /*params.padding=*/luci::Padding::SAME, /*stride_height=*/1, /*stride_width=*/1);
+
+ SUCCEED();
+}
+
+TEST(TransposeConvTest, FloatTwoFiltersTest)
+{
+ Check<float, float>(
+ /*output_shape_shape=*/{4}, /*weight_shape=*/{1, 3, 3, 2}, /*input_shape=*/{1, 4, 4, 2},
+ /*bias_shape=*/{}, /*output_shape=*/{1, 4, 4, 1}, /*output_shape_data=*/{1, 4, 4, 1},
+ /*weight_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18},
+ /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32},
+ /*bias_data=*/{},
+ /*output_data=*/
+ {184, 412, 568, 528, 678, 1347, 1689, 1434, 1494, 2715, 3057, 2442, 1968, 3352, 3652, 2760},
+ /*params.padding=*/luci::Padding::SAME, /*stride_height=*/1, /*stride_width=*/1);
+
+ SUCCEED();
+}
+
+TEST(TransposeConvTest, SimpleBiasTest)
+{
+ Check<float, float>(
+ /*output_shape_shape=*/{4}, /*weight_shape=*/{2, 3, 3, 1},
+ /*input_shape=*/{1, 2, 2, 1},
+ /*bias_shape=*/{2}, /*output_shape=*/{1, 4, 4, 1}, /*output_shape_data=*/{1, 5, 5, 2},
+ /*weight_data=*/{1, 3, 5, 7, 9, 11, 13, 15, 17, 2, 4, 6, 8, 10, 12, 14, 16, 18},
+ /*input_data=*/{1, 2, 3, 4},
+ /*bias_data=*/{3, 4},
+ /*output_data=*/{4, 6, 6, 8, 10, 14, 9, 12, 13, 16, 10, 12, 12, 14, 28, 32, 21,
+ 24, 25, 28, 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, 24, 28, 30, 34,
+ 64, 72, 39, 44, 47, 52, 42, 46, 48, 52, 106, 114, 63, 68, 71, 76},
+ /*params.padding=*/luci::Padding::VALID, /*stride_height=*/2, /*stride_width=*/2);
+
+ SUCCEED();
+}
+
+TEST(TransposeConvTest, UInt8)
+{
+ std::vector<float> input_data{1, 2, 3, 4};
+ std::vector<float> filter_data{1, 3, 5, 7, 9, 11, 13, 15, 17, 2, 4, 6, 8, 10, 12, 14, 16, 18};
+ std::vector<float> bias_data{3, 4};
+ std::vector<int32_t> output_shape_data{1, 5, 5, 2};
+ std::vector<float> ref_output_data{
+ 4, 6, 6, 8, 10, 14, 9, 12, 13, 16, //
+ 10, 12, 12, 14, 28, 32, 21, 24, 25, 28, //
+ 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, //
+ 24, 28, 30, 34, 64, 72, 39, 44, 47, 52, //
+ 42, 46, 48, 52, 106, 114, 63, 68, 71, 76, //
+ };
+
+ // Choose quantization parameters carefully.
+ auto input_quant = quantizationParams<uint8_t>(-8.0, 7.9375); // s = 1 / 16, zp = 128
+ auto filter_quant = quantizationParams<uint8_t>(-24.0, 39.75); // s = 1 / 4, zp = 96
+ auto output_quant = quantizationParams<uint8_t>(-64.0, 191.0); // s = 1, zp = 64
+
+ Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 2, 1}, input_quant.first,
+ input_quant.second, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::U8>({2, 3, 3, 1}, filter_quant.first,
+ filter_quant.second, filter_data);
+ Tensor bias_tensor =
+ makeInputTensor<DataType::S32>({2}, input_quant.first * filter_quant.first, 0, bias_data);
+ Tensor output_shape_tensor = makeInputTensor<DataType::S32>({4}, output_shape_data);
+ Tensor output_tensor = makeOutputTensor(DataType::U8, output_quant.first, output_quant.second);
+
+ TransposeConvParams params{};
+ params.padding = Padding::VALID;
+ params.stride_height = 2;
+ params.stride_width = 2;
+
+ TransposeConv kernel(&output_shape_tensor, &filter_tensor, &input_tensor, &bias_tensor,
+ &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape_data));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(TransposeConvTest, SInt16)
+{
+ std::vector<float> input_data{1, 2, 3, 4};
+ std::vector<float> filter_data{1, 3, 5, 7, 9, 11, 13, 15, 17, 2, 4, 6, 8, 10, 12, 14, 16, 18};
+ std::vector<float> bias_data{3, 4};
+ std::vector<int32_t> output_shape_data{1, 5, 5, 2};
+ std::vector<float> ref_output_data{
+ 4, 6, 6, 8, 10, 14, 9, 12, 13, 16, //
+ 10, 12, 12, 14, 28, 32, 21, 24, 25, 28, //
+ 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, //
+ 24, 28, 30, 34, 64, 72, 39, 44, 47, 52, //
+ 42, 46, 48, 52, 106, 114, 63, 68, 71, 76, //
+ };
+
+ Tensor input_tensor = makeInputTensor<DataType::S16>({1, 2, 2, 1}, 0.25, 0, input_data);
+ Tensor filter_tensor = makeInputTensor<DataType::S16>({2, 3, 3, 1}, 0.2, 0, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::S64>({2}, 0.25 * 0.2, 0, bias_data);
+ Tensor output_shape_tensor = makeInputTensor<DataType::S32>({4}, output_shape_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, 0.5, 0);
+
+ TransposeConvParams params{};
+ params.padding = Padding::VALID;
+ params.stride_height = 2;
+ params.stride_width = 2;
+
+ TransposeConv kernel(&output_shape_tensor, &filter_tensor, &input_tensor, &bias_tensor,
+ &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape_data));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+TEST(TransposeConvTest, SInt16_CWQ_weights)
+{
+ const int output_channels = 2;
+ const Shape input_shape{1, 2, 2, 1};
+ const Shape filter_shape{output_channels, 3, 3, 1};
+ const Shape bias_shape{output_channels};
+ std::vector<int32_t> output_shape_data{1, 5, 5, output_channels};
+
+ std::vector<float> input_data{1, 2, 3, 4};
+ std::vector<float> filter_data{1, 3, 5, 7, 9, 11, 13, 15, 17, 2, 4, 6, 8, 10, 12, 14, 16, 18};
+ std::vector<float> bias_data{3, 4};
+
+ std::vector<float> ref_output_data{
+ 4, 6, 6, 8, 10, 14, 9, 12, 13, 16, //
+ 10, 12, 12, 14, 28, 32, 21, 24, 25, 28, //
+ 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, //
+ 24, 28, 30, 34, 64, 72, 39, 44, 47, 52, //
+ 42, 46, 48, 52, 106, 114, 63, 68, 71, 76, //
+ };
+
+ const float input_scale = 0.25;
+ const float output_scale = 0.5;
+ const std::vector<float> filter_scales{0.2f, 0.5f};
+ std::vector<float> bias_scales{filter_scales[0] * input_scale, filter_scales[1] * input_scale};
+ const std::vector<int32_t> zerop(2, 0);
+
+ Tensor input_tensor = makeInputTensor<DataType::S16>(input_shape, input_scale, 0, input_data);
+ Tensor filter_tensor =
+ makeInputTensor<DataType::S16>(filter_shape, filter_scales, zerop, 0, filter_data);
+ Tensor bias_tensor = makeInputTensor<DataType::S64>(bias_shape, bias_scales, zerop, 0, bias_data);
+ Tensor output_shape_tensor = makeInputTensor<DataType::S32>({4}, output_shape_data);
+ Tensor output_tensor = makeOutputTensor(DataType::S16, output_scale, 0);
+
+ TransposeConvParams params{};
+ params.padding = Padding::VALID;
+ params.stride_height = 2;
+ params.stride_width = 2;
+
+ TransposeConv kernel(&output_shape_tensor, &filter_tensor, &input_tensor, &bias_tensor,
+ &output_tensor, params);
+ kernel.configure();
+ kernel.execute();
+
+ EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape_data));
+ EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Unpack.cpp b/compiler/luci-interpreter/src/kernels/Unpack.cpp
new file mode 100644
index 000000000..834b79926
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Unpack.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Unpack.h"
+
+#include "kernels/Utils.h"
+
+#include <tensorflow/lite/kernels/internal/reference/reference_ops.h>
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+namespace kernels
+{
+
+Unpack::Unpack(const Tensor *input, std::vector<Tensor *> outputs, const UnpackParams &params)
+ : KernelWithParams<UnpackParams>({input}, std::move(outputs), params)
+{
+}
+
+void Unpack::configure()
+{
+ const Shape &input_shape = input()->shape();
+
+ int axis = _params.axis;
+ if (axis < 0)
+ axis += input()->shape().num_dims();
+ assert(axis >= 0 && axis < input_shape.num_dims());
+
+ Shape output_shape(input_shape.num_dims() - 1);
+ int out_index = 0;
+ for (int in_index = 0; in_index < input_shape.num_dims(); ++in_index)
+ {
+ if (in_index != axis)
+ output_shape.dim(out_index++) = input_shape.dim(in_index);
+ }
+
+ for (Tensor *output : _outputs)
+ {
+ assert(output->element_type() == input()->element_type());
+ output->resize(output_shape);
+ }
+}
+
+template <typename T> void Unpack::executeImpl() const
+{
+ tflite::UnpackParams params{};
+ params.axis = _params.axis;
+ params.num_split = _outputs.size();
+ VectorOfTensors<T, false> all_outputs(_outputs);
+ tflite::reference_ops::Unpack<T>(params, getTensorShape(input()), getTensorData<T>(input()),
+ **all_outputs.shapes(), all_outputs.data());
+}
+
+void Unpack::execute() const
+{
+ switch (input()->element_type())
+ {
+ case DataType::FLOAT32:
+ return executeImpl<float>();
+ case DataType::U8:
+ return executeImpl<uint8_t>();
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Unpack.h b/compiler/luci-interpreter/src/kernels/Unpack.h
new file mode 100644
index 000000000..f4a44ecad
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Unpack.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_UNPACK_H
+#define LUCI_INTERPRETER_KERNELS_UNPACK_H
+
+#include "core/Kernel.h"
+#include "core/KernelParams.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+class Unpack : public KernelWithParams<UnpackParams>
+{
+public:
+ Unpack(const Tensor *input, std::vector<Tensor *> outputs, const UnpackParams &params);
+
+ const Tensor *input() const { return _inputs[0]; }
+ Tensor *output(int index) const { return _outputs[index]; }
+
+ void configure() override;
+ void execute() const override;
+
+private:
+ template <typename T> void executeImpl() const;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_UNPACK_H
diff --git a/compiler/luci-interpreter/src/kernels/Unpack.test.cpp b/compiler/luci-interpreter/src/kernels/Unpack.test.cpp
new file mode 100644
index 000000000..f70c5847a
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Unpack.test.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Unpack.h"
+#include "kernels/TestUtils.h"
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+namespace
+{
+
+using namespace testing;
+
+template <typename T>
+void Check(int axis, Shape input_shape, std::initializer_list<T> input_data,
+ const std::vector<std::initializer_list<int32_t>> &exp_output_shape,
+ std::vector<std::initializer_list<T>> exp_output_data)
+{
+ constexpr DataType element_type = getElementType<T>();
+ const int num_outputs = input_shape.dim(axis < 0 ? axis + input_shape.num_dims() : axis);
+
+ Tensor input_tensor = makeInputTensor<element_type>(input_shape, input_data);
+ std::vector<Tensor> output_tensors;
+ output_tensors.reserve(num_outputs);
+ for (int i = 0; i < num_outputs; ++i)
+ {
+ output_tensors.push_back(makeOutputTensor(element_type));
+ }
+
+ std::vector<Tensor *> output_tensor_ptrs(num_outputs);
+ for (int i = 0; i < num_outputs; ++i)
+ {
+ output_tensor_ptrs[i] = &output_tensors[i];
+ }
+
+ UnpackParams params{};
+ params.axis = axis;
+
+ Unpack kernel(&input_tensor, std::move(output_tensor_ptrs), params);
+ kernel.configure();
+ kernel.execute();
+
+ for (int i = 0; i < num_outputs; ++i)
+ {
+ EXPECT_THAT(extractTensorData<T>(output_tensors[i]),
+ ::testing::ElementsAreArray(exp_output_data[i]));
+ }
+}
+
+template <typename T> class UnpackTest : public ::testing::Test
+{
+};
+
+using DataTypes = ::testing::Types<float, uint8_t>;
+TYPED_TEST_CASE(UnpackTest, DataTypes);
+
+TYPED_TEST(UnpackTest, ThreeOutputs)
+{
+ Check<TypeParam>(/*axis=*/0, /*input_shape=*/{3, 2},
+ /*input_data=*/{1, 2, 3, 4, 5, 6},
+ /*exp_output_shape=*/{{2}, {2}, {2}},
+ /*exp_output_data=*/{{1, 2}, {3, 4}, {5, 6}});
+}
+
+TYPED_TEST(UnpackTest, ThreeOutputsAxisOne)
+{
+ Check<TypeParam>(/*axis=*/1, /*input_shape=*/{3, 2},
+ /*input_data=*/{1, 2, 3, 4, 5, 6},
+ /*exp_output_shape=*/{{3}, {3}},
+ /*exp_output_data=*/{{1, 3, 5}, {2, 4, 6}});
+}
+
+TYPED_TEST(UnpackTest, ThreeOutputsNegativeAxisOne)
+{
+ Check<TypeParam>(/*axis=*/-1, /*input_shape=*/{3, 2},
+ /*input_data=*/{1, 2, 3, 4, 5, 6},
+ /*exp_output_shape=*/{{3}, {3}},
+ /*exp_output_data=*/{{1, 3, 5}, {2, 4, 6}});
+}
+
+TYPED_TEST(UnpackTest, ThreeOutputsNegativeAxisTwo)
+{
+ Check<TypeParam>(/*axis=*/-2, /*input_shape=*/{3, 2},
+ /*input_data=*/{1, 2, 3, 4, 5, 6},
+ /*exp_output_shape=*/{{2}, {2}, {2}},
+ /*exp_output_data=*/{{1, 2}, {3, 4}, {5, 6}});
+}
+
+TYPED_TEST(UnpackTest, OneOutput)
+{
+ Check<TypeParam>(/*axis=*/0, /*input_shape=*/{1, 6},
+ /*input_data=*/{1, 2, 3, 4, 5, 6},
+ /*exp_output_shape=*/{{6}},
+ /*exp_output_data=*/{{1, 2, 3, 4, 5, 6}});
+}
+
+TYPED_TEST(UnpackTest, ThreeDimensionsTwoOutputs)
+{
+ Check<TypeParam>(/*axis=*/2, /*input_shape=*/{2, 2, 2},
+ /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8},
+ /*exp_output_shape=*/{{2, 2}, {2, 2}},
+ /*exp_output_data=*/{{1, 3, 5, 7}, {2, 4, 6, 8}});
+}
+
+TYPED_TEST(UnpackTest, FiveDimensionsTwoOutputs)
+{
+ Check<TypeParam>(
+ /*axis=*/2, /*input_shape=*/{2, 2, 2, 2, 1},
+ /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
+ /*exp_output_shape=*/{{2, 2, 2, 1}, {2, 2, 2, 1}},
+ /*exp_output_data=*/
+ {{1, 2, 5, 6, 9, 10, 13, 14}, {3, 4, 7, 8, 11, 12, 15, 16}});
+}
+
+TYPED_TEST(UnpackTest, VectorToScalar)
+{
+ Check<TypeParam>(/*axis=*/0, /*input_shape=*/{5},
+ /*input_data=*/{1, 2, 3, 4, 5},
+ /*exp_output_shape=*/{{}, {}, {}, {}, {}},
+ /*exp_output_data=*/{{1}, {2}, {3}, {4}, {5}});
+}
+
+} // namespace
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Utils.cpp b/compiler/luci-interpreter/src/kernels/Utils.cpp
new file mode 100644
index 000000000..52e76a81c
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Utils.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kernels/Utils.h"
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+void calculateActivationRange(Activation activation, float *activation_min, float *activation_max)
+{
+ switch (activation)
+ {
+ case Activation::NONE:
+ *activation_min = std::numeric_limits<float>::lowest();
+ *activation_max = std::numeric_limits<float>::max();
+ break;
+ case Activation::RELU:
+ *activation_min = 0;
+ *activation_max = std::numeric_limits<float>::max();
+ break;
+ case Activation::RELU_N1_TO_1:
+ *activation_min = -1;
+ *activation_max = 1;
+ break;
+ case Activation::RELU6:
+ *activation_min = 0;
+ *activation_max = 6;
+ break;
+ default:
+ throw std::runtime_error("Unsupported activation.");
+ }
+}
+
+static void calculateActivationRangeQuantizedImpl(Activation activation, int32_t qmin, int32_t qmax,
+ const Tensor *output, int32_t *activation_min,
+ int32_t *activation_max)
+{
+ const float scale = output->scale();
+ const int32_t zero_point = output->zero_point();
+
+ auto quantize = [scale, zero_point](float x) {
+ return zero_point + static_cast<int32_t>(std::round(x / scale));
+ };
+
+ switch (activation)
+ {
+ case Activation::NONE:
+ *activation_min = qmin;
+ *activation_max = qmax;
+ break;
+ case Activation::RELU:
+ *activation_min = std::max(qmin, quantize(0.0f));
+ *activation_max = qmax;
+ break;
+ case Activation::RELU_N1_TO_1:
+ *activation_min = std::max(qmin, quantize(-1.0f));
+ *activation_max = std::min(qmax, quantize(1.0f));
+ break;
+ case Activation::RELU6:
+ *activation_min = std::max(qmin, quantize(0.0f));
+ *activation_max = std::min(qmax, quantize(6.0f));
+ break;
+ default:
+ throw std::runtime_error("Unsupported activation.");
+ }
+}
+
+void calculateActivationRangeQuantized(Activation activation, const Tensor *output,
+ int32_t *activation_min, int32_t *activation_max)
+{
+ // For now, assume that signed type implies signed symmetric quantization.
+ int32_t qmin{};
+ int32_t qmax{};
+ switch (output->element_type())
+ {
+ case DataType::U8:
+ qmin = 0;
+ qmax = std::numeric_limits<uint8_t>::max();
+ break;
+ case DataType::S8:
+ assert(output->zero_point() == 0);
+ qmin = -std::numeric_limits<int8_t>::max();
+ qmax = std::numeric_limits<int8_t>::max();
+ break;
+ case DataType::S16:
+ assert(output->zero_point() == 0);
+ qmin = -std::numeric_limits<int16_t>::max();
+ qmax = std::numeric_limits<int16_t>::max();
+ break;
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+
+ calculateActivationRangeQuantizedImpl(activation, qmin, qmax, output, activation_min,
+ activation_max);
+}
+
+void quantizeMultiplier(double double_multiplier, int32_t *quantized_multiplier, int *shift)
+{
+ if (double_multiplier == 0.0)
+ {
+ *quantized_multiplier = 0;
+ *shift = 0;
+ return;
+ }
+
+ const double q = std::frexp(double_multiplier, shift);
+ auto q_fixed = static_cast<int64_t>(std::round(q * (INT64_C(1) << 31)));
+
+ if (q_fixed == (INT64_C(1) << 31))
+ {
+ q_fixed /= 2;
+ ++*shift;
+ }
+ assert(q_fixed <= std::numeric_limits<int32_t>::max());
+ // A shift amount smaller than -31 would cause all bits to be shifted out
+ // and thus all results would be zero. We implement that instead with
+ // q_fixed==0, so as to avoid hitting issues with right-shift
+ // operations with shift amounts greater than 31. Note that this happens
+ // roughly when abs(double_multiplier) < 2^-31 and the present handling means
+ // that we're effectively flushing tiny double_multiplier's to zero.
+ // We could conceivably handle values in the range (roughly) [32, 63]
+ // as 'denormals' i.e. (shift==0, q_fixed < 2^30). In that point of view
+ // the present handling is just doing 'flush denormals to zero'. We could
+ // reconsider and actually generate nonzero denormals if a need arises.
+ if (*shift < -31)
+ {
+ *shift = 0;
+ q_fixed = 0;
+ }
+ *quantized_multiplier = static_cast<int32_t>(q_fixed);
+}
+
+void quantizeMultiplierSmallerThanOneExp(double double_multiplier, int32_t *quantized_multiplier,
+ int *left_shift)
+{
+ assert(double_multiplier < 1.0);
+ assert(double_multiplier > 0.0);
+ int shift;
+ quantizeMultiplier(double_multiplier, quantized_multiplier, &shift);
+ assert(shift <= 0);
+ *left_shift = shift;
+}
+
+Shape calculateShapeForBroadcast(const Shape &input1_shape, const Shape &input2_shape)
+{
+ const int num_input1_dims = input1_shape.num_dims();
+ const int num_input2_dims = input2_shape.num_dims();
+ const int num_out_dims = std::max(num_input1_dims, num_input2_dims);
+ Shape output_shape(num_out_dims);
+
+ for (int i = 0; i < num_out_dims; ++i)
+ {
+ const int32_t input1_dim = i < num_input1_dims ? input1_shape.dim(num_input1_dims - i - 1) : 1;
+ const int32_t input2_dim = i < num_input2_dims ? input2_shape.dim(num_input2_dims - i - 1) : 1;
+ assert(input1_dim == input2_dim || input1_dim == 1 || input2_dim == 1);
+ output_shape.dim(num_out_dims - i - 1) = std::max(input1_dim, input2_dim);
+ }
+
+ return output_shape;
+}
+
+} // namespace kernels
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/kernels/Utils.h b/compiler/luci-interpreter/src/kernels/Utils.h
new file mode 100644
index 000000000..4b5e72917
--- /dev/null
+++ b/compiler/luci-interpreter/src/kernels/Utils.h
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_KERNELS_UTILS_H
+#define LUCI_INTERPRETER_KERNELS_UTILS_H
+
+#include "core/KernelParams.h"
+#include "luci_interpreter/core/Tensor.h"
+
+#include <tensorflow/lite/kernels/internal/types.h>
+
+#include <cassert>
+#include <cstdint>
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+namespace kernels
+{
+
+#define LUCI_INTERPRETER_CHECK(cond) \
+ if (!(cond)) \
+ throw std::runtime_error(std::string(__FILE__) + ":" + std::to_string(__LINE__) + +"(" + \
+ std::string(#cond) + ") was not true.");
+
+inline int32_t computePadding(int32_t stride, int32_t dilation_rate, int32_t in_size,
+ int32_t filter_size, int32_t out_size)
+{
+ const int32_t effective_filter_size = (filter_size - 1) * dilation_rate + 1;
+ const int32_t padding = ((out_size - 1) * stride + effective_filter_size - in_size) / 2;
+ return padding > 0 ? padding : 0;
+}
+
+inline int32_t computePaddingWithOffset(int32_t stride, int32_t dilation_rate, int32_t in_size,
+ int32_t filter_size, int32_t out_size, int32_t *offset)
+{
+ int32_t effective_filter_size = (filter_size - 1) * dilation_rate + 1;
+ int32_t total_padding = ((out_size - 1) * stride + effective_filter_size - in_size);
+ total_padding = total_padding > 0 ? total_padding : 0;
+ *offset = total_padding % 2;
+ return total_padding / 2;
+}
+
+inline int32_t computeOutputSize(Padding padding, int32_t image_size, int32_t filter_size,
+ int32_t stride, int32_t dilation_rate = 1)
+{
+ const int32_t effective_filter_size = (filter_size - 1) * dilation_rate + 1;
+ switch (padding)
+ {
+ case Padding::SAME:
+ return (image_size + stride - 1) / stride;
+ case Padding::VALID:
+ return (image_size + stride - effective_filter_size) / stride;
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
+inline int32_t calcOffset(const Shape &shape, int32_t d0, int32_t d1, int32_t d2, int32_t d3)
+{
+ return ((d0 * shape.dim(1) + d1) * shape.dim(2) + d2) * shape.dim(3) + d3;
+}
+
+void calculateActivationRange(Activation activation, float *activation_min, float *activation_max);
+
+void calculateActivationRangeQuantized(Activation activation, const Tensor *output,
+ 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.
+//
+// Handles an arbitrary positive multiplier. The 'shift' output-value is
+// basically the 'floating-point exponent' of the multiplier:
+// Negative for a right-shift (when the multiplier is <1), positive for a
+// left-shift (when the multiplier is >1)
+void quantizeMultiplier(double double_multiplier, int32_t *quantized_multiplier, int *shift);
+
+// Decompose a double multiplier into a Q0.31 int32 representation of its
+// significand, and shift representation of NEGATIVE its exponent ---
+// this is intended as a RIGHT-shift.
+//
+// Restricted to the case where the multiplier < 1 (and non-negative).
+void quantizeMultiplierSmallerThanOneExp(double double_multiplier, int32_t *quantized_multiplier,
+ int *left_shift);
+
+Shape calculateShapeForBroadcast(const Shape &input1_shape, const Shape &input2_shape);
+
+inline double getQuantizedConvolutionMultipler(float input_scale, float filter_scale,
+ float output_scale)
+{
+ const double input_product_scale = static_cast<double>(input_scale * filter_scale);
+ LUCI_INTERPRETER_CHECK(input_product_scale >= 0);
+ return input_product_scale / static_cast<double>(output_scale);
+}
+
+inline std::vector<double> getQuantizedConvolutionMultiplers(float input_scale,
+ const std::vector<float> &filter_scale,
+ float output_scale)
+{
+ std::vector<double> effective_output_scales;
+ size_t n = filter_scale.size();
+ effective_output_scales.reserve(n);
+ for (size_t i = 0; i < n; ++i)
+ {
+ effective_output_scales.push_back(
+ getQuantizedConvolutionMultipler(input_scale, filter_scale[i], output_scale));
+ }
+ return effective_output_scales;
+}
+
+struct ChannelQuantMultipliers
+{
+ int shift;
+ int32_t multiplier;
+ ChannelQuantMultipliers() = default;
+};
+
+inline std::vector<ChannelQuantMultipliers>
+quantizeMultipliers(const std::vector<double> &effective_scale)
+{
+ size_t n = effective_scale.size();
+ std::vector<ChannelQuantMultipliers> params(n);
+ for (size_t i = 0; i < n; ++i)
+ {
+ quantizeMultiplier(effective_scale[i], &params[i].multiplier, &params[i].shift);
+ }
+ return params;
+}
+
+// Helper wrapper to hide broadcast logic
+template <typename T> class BroadcastableWrapper
+{
+public:
+ BroadcastableWrapper(const std::vector<T> &v) : _v(v), _stride(v.size() == 1 ? 0 : 1) {}
+
+ T operator[](int idx) { return _v[idx * _stride]; }
+private:
+ const std::vector<T> &_v;
+ int _stride;
+};
+
+inline tflite::RuntimeShape getTensorShape(const Tensor *tensor)
+{
+ if (tensor == nullptr)
+ return tflite::RuntimeShape();
+
+ const Shape &shape = tensor->shape();
+ tflite::RuntimeShape runtime_shape(shape.num_dims());
+ for (int i = 0; i < shape.num_dims(); ++i)
+ {
+ runtime_shape.SetDim(i, shape.dim(i));
+ }
+ return runtime_shape;
+}
+
+template <typename T> const T *getTensorData(const Tensor *tensor)
+{
+ return tensor != nullptr ? tensor->data<T>() : nullptr;
+}
+
+template <typename T> T *getTensorData(Tensor *tensor)
+{
+ return tensor != nullptr ? tensor->data<T>() : nullptr;
+}
+
+// A list of tensors in a format that can be used by kernels like split and
+// concatenation.
+template <typename T, bool is_const> class VectorOfTensors
+{
+public:
+ using ElementT = typename std::conditional<is_const, const T, T>::type;
+ using TensorT = typename std::conditional<is_const, const Tensor, Tensor>::type;
+
+ // Build with the tensors in 'tensor_list'.
+ explicit VectorOfTensors(const std::vector<TensorT *> &tensor_list)
+ {
+ const int num_tensors = tensor_list.size();
+
+ all_data_.reserve(num_tensors);
+ all_shape_.reserve(num_tensors);
+ all_shape_ptr_.reserve(num_tensors);
+
+ for (TensorT *tensor : tensor_list)
+ {
+ all_data_.push_back(getTensorData<T>(tensor));
+ all_shape_.push_back(getTensorShape(tensor));
+ }
+
+ // Taking the pointer from inside a std::vector is only OK if the vector is
+ // never modified, so we populate all_shape in the previous loop and then we
+ // are free to grab iterators here.
+ for (tflite::RuntimeShape &shape : all_shape_)
+ {
+ all_shape_ptr_.push_back(&shape);
+ }
+ }
+ // Return a pointer to the data pointers of all tensors in the list. For
+ // example:
+ // float* const* f = v.data();
+ // f[0][1] is the second element of the first tensor.
+ ElementT *const *data() const { return all_data_.data(); }
+
+ // Return a pointer the shape pointers of all tensors in the list. For
+ // example:
+ // const RuntimeShape* const* d = v.dims();
+ // dims[1] are the dimensions of the second tensor in the list.
+ const tflite::RuntimeShape *const *shapes() const { return all_shape_ptr_.data(); }
+
+private:
+ std::vector<ElementT *> all_data_;
+ std::vector<tflite::RuntimeShape> all_shape_;
+ std::vector<tflite::RuntimeShape *> all_shape_ptr_;
+};
+
+// A list of quantized tensors in a format that can be used by kernels like
+// split and concatenation.
+template <bool is_const> class VectorOfQuantizedTensors : public VectorOfTensors<uint8_t, is_const>
+{
+public:
+ using typename VectorOfTensors<uint8_t, is_const>::TensorT;
+
+ // Build with the tensors in 'tensor_list'.
+ explicit VectorOfQuantizedTensors(const std::vector<TensorT *> &tensor_list)
+ : VectorOfTensors<uint8_t, is_const>(tensor_list)
+ {
+ for (TensorT *tensor : tensor_list)
+ {
+ zero_point_.push_back(tensor->zero_point());
+ scale_.push_back(tensor->scale());
+ }
+ }
+
+ const float *scale() const { return scale_.data(); }
+ const int32_t *zero_point() const { return zero_point_.data(); }
+
+private:
+ std::vector<int32_t> zero_point_;
+ std::vector<float> scale_;
+};
+
+} // namespace kernels
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_KERNELS_UTILS_H
diff --git a/compiler/luci-interpreter/src/loader/CMakeLists.txt b/compiler/luci-interpreter/src/loader/CMakeLists.txt
new file mode 100644
index 000000000..d99485d06
--- /dev/null
+++ b/compiler/luci-interpreter/src/loader/CMakeLists.txt
@@ -0,0 +1,22 @@
+nnas_find_package(GTest REQUIRED)
+
+set(SOURCES
+ GraphLoader.h
+ GraphLoader.cpp
+ KernelBuilder.h
+ KernelBuilder.cpp
+ ModuleLoader.h
+ ModuleLoader.cpp
+ RuntimeToIR.h)
+
+add_library(luci_interpreter_loader STATIC ${SOURCES})
+set_target_properties(luci_interpreter_loader PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(luci_interpreter_loader PUBLIC "${LUCI_INTERPRETER_SOURCE_DIR}")
+target_link_libraries(luci_interpreter_loader
+ PUBLIC luci_lang luci_interpreter_core
+ PRIVATE luci_interpreter_kernels nncc_common)
+
+set(TEST_SOURCES KernelBuilder.test.cpp)
+
+GTest_AddTest(luci_interpreter_loader_test ${TEST_SOURCES})
+target_link_libraries(luci_interpreter_loader_test luci_interpreter_loader)
diff --git a/compiler/luci-interpreter/src/loader/GraphLoader.cpp b/compiler/luci-interpreter/src/loader/GraphLoader.cpp
new file mode 100644
index 000000000..c52d99e6f
--- /dev/null
+++ b/compiler/luci-interpreter/src/loader/GraphLoader.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "loader/GraphLoader.h"
+
+#include "loader/KernelBuilder.h"
+
+#include <loco/IR/Algorithm.h>
+
+namespace luci_interpreter
+{
+namespace
+{
+
+template <typename NodeT> Shape getNodeShape(const NodeT *node)
+{
+ Shape shape(node->rank());
+ for (uint32_t i = 0; i < node->rank(); ++i)
+ {
+ shape.dim(i) = node->dim(i).value();
+ }
+ return shape;
+}
+
+template <DataType DT> const void *getNodeDataImpl(const luci::CircleConst *node, size_t *data_size)
+{
+ const size_t element_size = getDataTypeSize(DT);
+ const int32_t num_elements = node->size<DT>();
+
+ *data_size = num_elements * element_size;
+ if (*data_size > 0)
+ {
+ // FIXME There is no good way to get the pointer to the data currently.
+ return &node->at<DT>(0);
+ }
+ return nullptr;
+}
+
+const void *getNodeData(const luci::CircleConst *node, size_t *data_size)
+{
+ switch (node->dtype())
+ {
+ case DataType::U8:
+ return getNodeDataImpl<DataType::U8>(node, data_size);
+ case DataType::FLOAT32:
+ return getNodeDataImpl<DataType::FLOAT32>(node, data_size);
+ case DataType::S32:
+ return getNodeDataImpl<DataType::S32>(node, data_size);
+ default:
+ throw std::runtime_error("Unsupported type.");
+ }
+}
+
+bool isExecutableNode(const luci::CircleNode *node)
+{
+ switch (node->opcode())
+ {
+ // These nodes denote inputs / outputs of a graph.
+ case luci::CircleOpcode::CIRCLECONST:
+ case luci::CircleOpcode::CIRCLEINPUT:
+ case luci::CircleOpcode::CIRCLEOUTPUT:
+ case luci::CircleOpcode::CIRCLEOUTPUTEXCLUDE:
+ // The following nodes denote outputs of multiple-output nodes.
+ case luci::CircleOpcode::CIRCLEIFOUT:
+ case luci::CircleOpcode::CIRCLESPLITOUT:
+ case luci::CircleOpcode::CIRCLEUNPACKOUT:
+ return false;
+ default:
+ return true;
+ }
+}
+
+bool isTensorProducingNode(const luci::CircleNode *node)
+{
+ switch (node->opcode())
+ {
+ // Output nodes do not produce tensors.
+ case luci::CircleOpcode::CIRCLEOUTPUT:
+ // The following nodes are multiple-output nodes. They do not produce tensors, the tensors
+ // are produced by the corresponding *Out nodes instead.
+ case luci::CircleOpcode::IF:
+ case luci::CircleOpcode::SPLIT:
+ case luci::CircleOpcode::UNPACK:
+ return false;
+ default:
+ return true;
+ }
+}
+
+} // namespace
+
+GraphLoader::GraphLoader(
+ const loco::Graph *graph, RuntimeGraph *runtime_graph, RuntimeToIR &runtime_to_ir,
+ const std::unordered_map<const loco::Graph *, RuntimeGraph *> &graph_to_runtime_graph,
+ std::unordered_map<const loco::Node *, Tensor *> &node_to_tensor)
+ : _graph(graph), _runtime_graph(runtime_graph), _runtime_to_ir(runtime_to_ir),
+ _graph_to_runtime_graph(graph_to_runtime_graph), _node_to_tensor(node_to_tensor)
+{
+}
+
+void GraphLoader::loadTensors()
+{
+ for (uint32_t i = 0; i < _graph->nodes()->size(); ++i)
+ {
+ const auto *node = loco::must_cast<const luci::CircleNode *>(_graph->nodes()->at(i));
+
+ if (!isTensorProducingNode(node))
+ continue;
+
+ // Only Input and Const nodes have shapes. Shapes of intermediate tensors will be inferred.
+ Shape shape{};
+ if (const auto *input_node = dynamic_cast<const luci::CircleInput *>(node))
+ {
+ shape = getNodeShape(input_node);
+ }
+ else if (const auto *const_node = dynamic_cast<const luci::CircleConst *>(node))
+ {
+ shape = getNodeShape(const_node);
+ }
+
+ AffineQuantization quantization;
+ if (node->quantparam() != nullptr)
+ {
+ const luci::CircleQuantParam *params = node->quantparam();
+ assert(params->scale.size() == params->zerop.size());
+ quantization.scale.assign(params->scale.cbegin(), params->scale.cend());
+ quantization.zero_point.assign(params->zerop.cbegin(), params->zerop.cend());
+ quantization.quantized_dimension = params->quantized_dimension;
+ }
+
+ auto tensor = std::make_unique<Tensor>(node->dtype(), std::move(shape), std::move(quantization),
+ node->name());
+
+ if (const auto *const_node = dynamic_cast<const luci::CircleConst *>(node))
+ {
+ size_t data_size{};
+ const void *const_data = getNodeData(const_node, &data_size);
+ if (const_data != nullptr)
+ tensor->writeData(const_data, data_size);
+ }
+
+ _node_to_tensor.emplace(node, tensor.get());
+ _runtime_to_ir.tensor_to_node.emplace(tensor.get(), node);
+
+ _runtime_graph->addTensor(std::move(tensor));
+ }
+}
+
+void GraphLoader::initInputOutputTensors() const
+{
+ auto input_nodes = loco::input_nodes(_graph);
+ std::vector<Tensor *> input_tensors(input_nodes.size());
+ for (size_t i = 0; i < input_nodes.size(); ++i)
+ {
+ input_tensors[i] = _node_to_tensor.at(input_nodes[i]);
+ }
+ _runtime_graph->setInputTensors(input_tensors);
+
+ auto output_nodes = loco::output_nodes(const_cast<loco::Graph *>(_graph));
+ std::vector<Tensor *> output_tensors(output_nodes.size());
+ for (size_t i = 0; i < output_nodes.size(); ++i)
+ {
+ const auto *node = loco::must_cast<const luci::CircleOutput *>(output_nodes[i]);
+ output_tensors[i] = _node_to_tensor.at(node->from());
+ }
+ _runtime_graph->setOutputTensors(output_tensors);
+}
+
+void GraphLoader::loadOperators()
+{
+ KernelBuilder kernel_builder(_graph_to_runtime_graph, _node_to_tensor);
+
+ // Create kernels for executable nodes. This has to be done in execution order.
+ for (const loco::Node *loco_node :
+ loco::postorder_traversal(loco::output_nodes(const_cast<loco::Graph *>(_graph))))
+ {
+ const auto *node = loco::must_cast<const luci::CircleNode *>(loco_node);
+
+ if (isExecutableNode(node))
+ {
+ std::unique_ptr<Kernel> kernel = node->accept(&kernel_builder);
+ _runtime_to_ir.kernel_to_node.emplace(kernel.get(), node);
+ _runtime_graph->addKernel(std::move(kernel));
+ }
+ }
+}
+
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/loader/GraphLoader.h b/compiler/luci-interpreter/src/loader/GraphLoader.h
new file mode 100644
index 000000000..89c5bcad7
--- /dev/null
+++ b/compiler/luci-interpreter/src/loader/GraphLoader.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_LOADER_GRAPHLOADER_H
+#define LUCI_INTERPRETER_LOADER_GRAPHLOADER_H
+
+#include "core/RuntimeGraph.h"
+#include "loader/RuntimeToIR.h"
+
+#include <loco/IR/Graph.h>
+
+#include <unordered_map>
+
+namespace luci_interpreter
+{
+
+class GraphLoader
+{
+public:
+ GraphLoader(const loco::Graph *graph, RuntimeGraph *runtime_graph, RuntimeToIR &runtime_to_ir,
+ const std::unordered_map<const loco::Graph *, RuntimeGraph *> &graph_to_runtime_graph,
+ std::unordered_map<const loco::Node *, Tensor *> &node_to_tensor);
+
+ void loadTensors();
+ void initInputOutputTensors() const;
+ void loadOperators();
+
+private:
+ const loco::Graph *_graph;
+ RuntimeGraph *_runtime_graph;
+ RuntimeToIR &_runtime_to_ir;
+
+ const std::unordered_map<const loco::Graph *, RuntimeGraph *> &_graph_to_runtime_graph;
+ std::unordered_map<const loco::Node *, Tensor *> &_node_to_tensor;
+};
+
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_LOADER_GRAPHLOADER_H
diff --git a/compiler/luci-interpreter/src/loader/KernelBuilder.cpp b/compiler/luci-interpreter/src/loader/KernelBuilder.cpp
new file mode 100644
index 000000000..7b723e88a
--- /dev/null
+++ b/compiler/luci-interpreter/src/loader/KernelBuilder.cpp
@@ -0,0 +1,906 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "loader/KernelBuilder.h"
+
+#include "kernels/Add.h"
+#include "kernels/ArgMax.h"
+#include "kernels/AveragePool2D.h"
+#include "kernels/Concatenation.h"
+#include "kernels/Conv2D.h"
+#include "kernels/DepthToSpace.h"
+#include "kernels/DepthwiseConv2D.h"
+#include "kernels/Div.h"
+#include "kernels/Elu.h"
+#include "kernels/Exp.h"
+#include "kernels/Floor.h"
+#include "kernels/FloorDiv.h"
+#include "kernels/Equal.h"
+#include "kernels/FullyConnected.h"
+#include "kernels/Greater.h"
+#include "kernels/GreaterEqual.h"
+#include "kernels/If.h"
+#include "kernels/InstanceNorm.h"
+#include "kernels/L2Normalize.h"
+#include "kernels/L2Pool2D.h"
+#include "kernels/LeakyRelu.h"
+#include "kernels/Less.h"
+#include "kernels/LessEqual.h"
+#include "kernels/LocalResponseNormalization.h"
+#include "kernels/LogicalAnd.h"
+#include "kernels/LogicalNot.h"
+#include "kernels/LogicalOr.h"
+#include "kernels/Logistic.h"
+#include "kernels/LogSoftmax.h"
+#include "kernels/Maximum.h"
+#include "kernels/MaxPool2D.h"
+#include "kernels/Mean.h"
+#include "kernels/Minimum.h"
+#include "kernels/Mul.h"
+#include "kernels/NotEqual.h"
+#include "kernels/Pad.h"
+#include "kernels/Pow.h"
+#include "kernels/Prelu.h"
+#include "kernels/Relu.h"
+#include "kernels/Relu6.h"
+#include "kernels/Reshape.h"
+#include "kernels/ResizeBilinear.h"
+#include "kernels/ResizeNearestNeighbor.h"
+#include "kernels/Reverse.h"
+#include "kernels/Rsqrt.h"
+#include "kernels/Slice.h"
+#include "kernels/Softmax.h"
+#include "kernels/SpaceToDepth.h"
+#include "kernels/Split.h"
+#include "kernels/StridedSlice.h"
+#include "kernels/Sqrt.h"
+#include "kernels/Sub.h"
+#include "kernels/Squeeze.h"
+#include "kernels/Tanh.h"
+#include "kernels/Unpack.h"
+#include "kernels/Transpose.h"
+#include "kernels/TransposeConv.h"
+
+#include <stdexcept>
+
+namespace luci_interpreter
+{
+
+template <typename CircleNodeOut>
+static std::vector<const loco::Node *> collectOutputNodes(const luci::CircleNode *node)
+{
+ std::vector<const CircleNodeOut *> output_nodes;
+ for (const loco::Node *loco_node : loco::succs(node))
+ {
+ output_nodes.push_back(loco::must_cast<const CircleNodeOut *>(loco_node));
+ }
+ std::sort(output_nodes.begin(), output_nodes.end(),
+ [](const CircleNodeOut *node1, const CircleNodeOut *node2) {
+ return node1->index() < node2->index();
+ });
+ return {output_nodes.cbegin(), output_nodes.cend()};
+}
+
+const Tensor *KernelBuilder::getInputTensor(const loco::Node *node) const
+{
+ const Tensor *tensor = _node_to_tensor.at(node);
+ assert(tensor != nullptr);
+ return tensor;
+}
+
+const Tensor *KernelBuilder::getOptionalInputTensor(const loco::Node *node) const
+{
+ if (dynamic_cast<const luci::CircleOutputExclude *>(node))
+ {
+ return nullptr;
+ }
+ return getInputTensor(node);
+}
+
+Tensor *KernelBuilder::getOutputTensor(const loco::Node *node) const
+{
+ Tensor *tensor = _node_to_tensor.at(node);
+ assert(tensor != nullptr);
+ return tensor;
+}
+
+std::vector<Tensor *>
+KernelBuilder::getOutputTensors(const std::vector<const loco::Node *> &nodes) const
+{
+ std::vector<Tensor *> tensors;
+ tensors.reserve(nodes.size());
+ for (const loco::Node *node : nodes)
+ tensors.push_back(getOutputTensor(node));
+ return tensors;
+}
+
+RuntimeGraph *KernelBuilder::getRuntimeGraph(const loco::Graph *graph) const
+{
+ RuntimeGraph *runtime_graph = _graph_to_runtime_graph.at(graph);
+ assert(runtime_graph != nullptr);
+ return runtime_graph;
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleAdd *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input1 = getInputTensor(node->x());
+ const Tensor *input2 = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ AddParams params{};
+ params.activation = node->fusedActivationFunction();
+
+ return std::make_unique<kernels::Add>(input1, input2, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleArgMax *node)
+{
+ assert(node->arity() == 2);
+ const Tensor *input = getInputTensor(node->input());
+ const Tensor *axis = getInputTensor(node->dimension());
+ Tensor *output = getOutputTensor(node);
+
+ ArgMaxParams params{};
+ params.output_type = node->output_type();
+
+ return std::make_unique<kernels::ArgMax>(input, axis, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleAveragePool2D *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->value());
+ Tensor *output = getOutputTensor(node);
+
+ Pool2DParams params{};
+ params.padding = node->padding();
+ params.filter_height = node->filter()->h();
+ params.filter_width = node->filter()->w();
+ params.stride_height = node->stride()->h();
+ params.stride_width = node->stride()->w();
+ params.activation = node->fusedActivationFunction();
+
+ return std::make_unique<kernels::AveragePool2D>(input, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleConcatenation *node)
+{
+ std::vector<const Tensor *> inputs(node->numValues());
+ for (uint32_t i = 0; i < node->numValues(); ++i)
+ {
+ inputs[i] = getInputTensor(node->values(i));
+ }
+ Tensor *output = getOutputTensor(node);
+
+ ConcatenationParams params{};
+ params.axis = node->axis();
+
+ return std::make_unique<kernels::Concatenation>(std::move(inputs), output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleConst *)
+{
+ throw std::runtime_error("Const node cannot be executed.");
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleConv2D *node)
+{
+ assert(node->arity() == 3);
+
+ const Tensor *input = getInputTensor(node->input());
+ const Tensor *filter = getInputTensor(node->filter());
+ const Tensor *bias = getInputTensor(node->bias());
+ Tensor *output = getOutputTensor(node);
+
+ Conv2DParams params{};
+ params.padding = node->padding();
+ params.stride_height = node->stride()->h();
+ params.stride_width = node->stride()->w();
+ params.dilation_height_factor = node->dilation()->h();
+ params.dilation_width_factor = node->dilation()->w();
+ params.activation = node->fusedActivationFunction();
+
+ return std::make_unique<kernels::Conv2D>(input, filter, bias, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleDepthToSpace *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->input());
+ Tensor *output = getOutputTensor(node);
+
+ DepthToSpaceParams params{};
+ params.block_size = node->block_size();
+
+ return std::make_unique<kernels::DepthToSpace>(input, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleDepthwiseConv2D *node)
+{
+ assert(node->arity() == 3);
+
+ const Tensor *input = getInputTensor(node->input());
+ const Tensor *filter = getInputTensor(node->filter());
+ const Tensor *bias = getInputTensor(node->bias());
+ Tensor *output = getOutputTensor(node);
+
+ DepthwiseConv2DParams params{};
+ params.padding = node->padding();
+ params.depth_multiplier = node->depthMultiplier();
+ params.stride_height = node->stride()->h();
+ params.stride_width = node->stride()->w();
+ params.dilation_height_factor = node->dilation()->h();
+ params.dilation_width_factor = node->dilation()->w();
+ params.activation = node->fusedActivationFunction();
+
+ return std::make_unique<kernels::DepthwiseConv2D>(input, filter, bias, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleDiv *node)
+{
+ assert(node->arity() == 2);
+ const Tensor *input1 = getInputTensor(node->x());
+ const Tensor *input2 = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ DivParams params{};
+ params.activation = node->fusedActivationFunction();
+
+ return std::make_unique<kernels::Div>(input1, input2, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleElu *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->features());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Elu>(input, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleExp *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->x());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Exp>(input, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleFloor *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->x());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Floor>(input, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleFloorDiv *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *x = getInputTensor(node->x());
+ const Tensor *y = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::FloorDiv>(x, y, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleEqual *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *x = getInputTensor(node->x());
+ const Tensor *y = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Equal>(x, y, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleFullyConnected *node)
+{
+ assert(node->arity() == 3);
+
+ const Tensor *input = getInputTensor(node->input());
+ const Tensor *weights = getInputTensor(node->weights());
+ const Tensor *bias = getOptionalInputTensor(node->bias());
+ Tensor *output = getOutputTensor(node);
+
+ FullyConnectedParams params{};
+ params.activation = node->fusedActivationFunction();
+
+ return std::make_unique<kernels::FullyConnected>(input, weights, bias, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleGreater *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *x = getInputTensor(node->x());
+ const Tensor *y = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Greater>(x, y, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleGreaterEqual *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *x = getInputTensor(node->x());
+ const Tensor *y = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::GreaterEqual>(x, y, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleIf *node)
+{
+ auto output_nodes = collectOutputNodes<luci::CircleIfOut>(node);
+ assert(node->arity() == 1 + node->input_count());
+ assert(output_nodes.size() == static_cast<size_t>(node->output_count()));
+
+ const Tensor *cond = getInputTensor(node->cond());
+ std::vector<const Tensor *> inputs(node->input_count());
+ for (uint32_t i = 0; i < node->input_count(); ++i)
+ {
+ inputs[i] = getInputTensor(node->input(i));
+ }
+ std::vector<Tensor *> outputs = getOutputTensors(output_nodes);
+
+ RuntimeGraph *then_graph = getRuntimeGraph(node->then_graph());
+ RuntimeGraph *else_graph = getRuntimeGraph(node->else_graph());
+
+ return std::make_unique<kernels::If>(cond, std::move(inputs), std::move(outputs), then_graph,
+ else_graph);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleInstanceNorm *node)
+{
+ assert(node->arity() == 3);
+
+ const Tensor *input = getInputTensor(node->input());
+ const Tensor *gamma = getInputTensor(node->gamma());
+ const Tensor *beta = getInputTensor(node->beta());
+
+ Tensor *output = getOutputTensor(node);
+
+ InstanceNormParams params{};
+ params.epsilon = node->epsilon();
+ params.activation = node->fusedActivationFunction();
+
+ return std::make_unique<kernels::InstanceNorm>(input, gamma, beta, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleInput *)
+{
+ throw std::runtime_error("Input node cannot be executed.");
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleL2Normalize *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->x());
+ Tensor *output = getOutputTensor(node);
+
+ L2NormParams params{};
+ params.activation = node->fusedActivationFunction();
+
+ return std::make_unique<kernels::L2Normalize>(input, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleL2Pool2D *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->value());
+ Tensor *output = getOutputTensor(node);
+
+ Pool2DParams params{};
+ params.padding = node->padding();
+ params.filter_height = node->filter()->h();
+ params.filter_width = node->filter()->w();
+ params.stride_height = node->stride()->h();
+ params.stride_width = node->stride()->w();
+ params.activation = node->fusedActivationFunction();
+
+ return std::make_unique<kernels::L2Pool2D>(input, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLeakyRelu *node)
+{
+ assert(node->arity() == 1);
+ const Tensor *input = getInputTensor(node->features());
+ Tensor *output = getOutputTensor(node);
+
+ LeakyReluParams params{};
+ params.alpha = node->alpha();
+
+ return std::make_unique<kernels::LeakyRelu>(input, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLess *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *x = getInputTensor(node->x());
+ const Tensor *y = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Less>(x, y, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLessEqual *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *x = getInputTensor(node->x());
+ const Tensor *y = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::LessEqual>(x, y, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLocalResponseNormalization *node)
+{
+ assert(node->arity() == 1);
+ const Tensor *input = getInputTensor(node->input());
+ Tensor *output = getOutputTensor(node);
+
+ LocalResponseNormalizationParams params{};
+ params.radius = node->radius();
+ params.bias = node->bias();
+ params.alpha = node->alpha();
+ params.beta = node->beta();
+
+ return std::make_unique<kernels::LocalResponseNormalization>(input, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLogicalAnd *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input1 = getInputTensor(node->x());
+ const Tensor *input2 = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::LogicalAnd>(input1, input2, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLogicalNot *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->x());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::LogicalNot>(input, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLogicalOr *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input1 = getInputTensor(node->x());
+ const Tensor *input2 = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::LogicalOr>(input1, input2, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLogistic *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->x());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Logistic>(input, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleLogSoftmax *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->logits());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::LogSoftmax>(input, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMaximum *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input1 = getInputTensor(node->x());
+ const Tensor *input2 = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Maximum>(input1, input2, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMaxPool2D *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->value());
+ Tensor *output = getOutputTensor(node);
+
+ Pool2DParams params{};
+ params.padding = node->padding();
+ params.filter_height = node->filter()->h();
+ params.filter_width = node->filter()->w();
+ params.stride_height = node->stride()->h();
+ params.stride_width = node->stride()->w();
+ params.activation = node->fusedActivationFunction();
+
+ return std::make_unique<kernels::MaxPool2D>(input, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMean *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input = getInputTensor(node->input());
+ const Tensor *axes = getInputTensor(node->reduction_indices());
+ Tensor *output = getOutputTensor(node);
+
+ ReducerParams params{};
+ params.keep_dims = node->keep_dims();
+
+ return std::make_unique<kernels::Mean>(input, axes, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMinimum *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input1 = getInputTensor(node->x());
+ const Tensor *input2 = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Minimum>(input1, input2, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMul *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input1 = getInputTensor(node->x());
+ const Tensor *input2 = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ MulParams params{};
+ params.activation = node->fusedActivationFunction();
+
+ return std::make_unique<kernels::Mul>(input1, input2, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleNotEqual *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *x = getInputTensor(node->x());
+ const Tensor *y = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::NotEqual>(x, y, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleOutput *)
+{
+ throw std::runtime_error("Output node cannot be executed.");
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CirclePad *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input = getInputTensor(node->input());
+ const Tensor *paddings = getInputTensor(node->paddings());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Pad>(input, paddings, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CirclePow *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input1 = getInputTensor(node->x());
+ const Tensor *input2 = getInputTensor(node->y());
+
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Pow>(input1, input2, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CirclePRelu *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input = getInputTensor(node->input());
+ const Tensor *alpha = getInputTensor(node->alpha());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Prelu>(input, alpha, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleRelu *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->features());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Relu>(input, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleRelu6 *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->features());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Relu6>(input, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleReshape *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input = getInputTensor(node->tensor());
+ const Tensor *shape = getInputTensor(node->shape());
+ Tensor *output = getOutputTensor(node);
+
+ // NOTE 'newShape' attribute is ignored.
+ return std::make_unique<kernels::Reshape>(input, shape, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleResizeBilinear *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input = getInputTensor(node->input());
+ const Tensor *size = getInputTensor(node->size());
+ Tensor *output = getOutputTensor(node);
+
+ ResizeBilinearParams params{};
+ params.align_corners = node->align_corners();
+ params.half_pixel_centers = node->half_pixel_centers();
+
+ return std::make_unique<kernels::ResizeBilinear>(input, size, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleResizeNearestNeighbor *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input = getInputTensor(node->input());
+ const Tensor *size = getInputTensor(node->size());
+ Tensor *output = getOutputTensor(node);
+
+ ResizeNearestNeighborParams params{};
+ params.align_corners = node->align_corners();
+ // TODO update half_pixel_centers after CircleResizeNearestNeighbor updated
+ // Current CircleResizeNearestNeighbor don't have half_pixel_centers.
+ // default value on current is false.
+ // it need to be updated when CircleResizeNearestNeighbor updated.
+ params.half_pixel_centers = false;
+
+ return std::make_unique<kernels::ResizeNearestNeighbor>(input, size, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleReverseV2 *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input = getInputTensor(node->tensor());
+ const Tensor *axes = getInputTensor(node->axis());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Reverse>(input, axes, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleRsqrt *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->x());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Rsqrt>(input, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSub *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input1 = getInputTensor(node->x());
+ const Tensor *input2 = getInputTensor(node->y());
+ Tensor *output = getOutputTensor(node);
+
+ SubParams params{};
+ params.activation = node->fusedActivationFunction();
+
+ return std::make_unique<kernels::Sub>(input1, input2, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSlice *node)
+{
+ assert(node->arity() == 3);
+
+ const Tensor *input = getInputTensor(node->input());
+ const Tensor *begin = getInputTensor(node->begin());
+ const Tensor *size = getInputTensor(node->size());
+
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Slice>(input, begin, size, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSoftmax *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->logits());
+ Tensor *output = getOutputTensor(node);
+
+ SoftmaxParams params{};
+ params.beta = node->beta();
+
+ return std::make_unique<kernels::Softmax>(input, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSpaceToDepth *node)
+{
+ assert(node->arity() == 1);
+ const Tensor *input = getInputTensor(node->input());
+
+ Tensor *output = getOutputTensor(node);
+
+ SpaceToDepthParams params{};
+ params.block_size = node->block_size();
+
+ return std::make_unique<kernels::SpaceToDepth>(input, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSplit *node)
+{
+ auto output_nodes = collectOutputNodes<luci::CircleSplitOut>(node);
+ assert(node->arity() == 2);
+ assert(output_nodes.size() == static_cast<size_t>(node->num_split()));
+
+ const Tensor *axis = getInputTensor(node->split_dim());
+ const Tensor *input = getInputTensor(node->input());
+ std::vector<Tensor *> outputs = getOutputTensors(output_nodes);
+
+ // NOTE 'num_splits' attribute is ignored.
+ return std::make_unique<kernels::Split>(axis, input, std::move(outputs));
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSqrt *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->x());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Sqrt>(input, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSqueeze *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->input());
+ Tensor *output = getOutputTensor(node);
+
+ SqueezeParams params{};
+ params.squeeze_dims = node->squeeze_dims();
+
+ return std::make_unique<kernels::Squeeze>(input, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleStridedSlice *node)
+{
+ assert(node->arity() == 4);
+
+ const Tensor *input = getInputTensor(node->input());
+ const Tensor *begin = getInputTensor(node->begin());
+ const Tensor *end = getInputTensor(node->end());
+ const Tensor *strides = getInputTensor(node->strides());
+
+ Tensor *output = getOutputTensor(node);
+
+ StridedSliceParams params{};
+ params.begin_mask = node->begin_mask();
+ params.ellipsis_mask = node->ellipsis_mask();
+ params.end_mask = node->end_mask();
+ params.new_axis_mask = node->new_axis_mask();
+ params.shrink_axis_mask = node->shrink_axis_mask();
+
+ return std::make_unique<kernels::StridedSlice>(input, begin, end, strides, output, params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleTanh *node)
+{
+ assert(node->arity() == 1);
+
+ const Tensor *input = getInputTensor(node->x());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Tanh>(input, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleTranspose *node)
+{
+ assert(node->arity() == 2);
+
+ const Tensor *input = getInputTensor(node->a());
+ const Tensor *perm = getInputTensor(node->perm());
+ Tensor *output = getOutputTensor(node);
+
+ return std::make_unique<kernels::Transpose>(input, perm, output);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleTransposeConv *node)
+{
+ assert(node->arity() == 4);
+
+ const Tensor *input_sizes = getInputTensor(node->inputSizes());
+ const Tensor *filter = getInputTensor(node->filter());
+ const Tensor *out_backprop = getInputTensor(node->outBackprop());
+ const Tensor *bias = getOptionalInputTensor(node->bias());
+
+ Tensor *output = getOutputTensor(node);
+
+ TransposeConvParams params{};
+ params.padding = node->padding();
+ params.stride_height = node->stride()->h();
+ params.stride_width = node->stride()->w();
+
+ return std::make_unique<kernels::TransposeConv>(input_sizes, filter, out_backprop, bias, output,
+ params);
+}
+
+std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleUnpack *node)
+{
+ auto output_nodes = collectOutputNodes<luci::CircleUnpackOut>(node);
+ assert(node->arity() == 1);
+ assert(output_nodes.size() == static_cast<size_t>(node->num()));
+
+ const Tensor *input = getInputTensor(node->value());
+ std::vector<Tensor *> outputs = getOutputTensors(output_nodes);
+
+ UnpackParams params{};
+ params.axis = node->axis();
+
+ // NOTE 'num' attribute is ignored.
+ return std::make_unique<kernels::Unpack>(input, std::move(outputs), params);
+}
+
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/loader/KernelBuilder.h b/compiler/luci-interpreter/src/loader/KernelBuilder.h
new file mode 100644
index 000000000..1546ba01b
--- /dev/null
+++ b/compiler/luci-interpreter/src/loader/KernelBuilder.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_LOADER_KERNELBUILDER_H
+#define LUCI_INTERPRETER_LOADER_KERNELBUILDER_H
+
+#include "core/Kernel.h"
+#include "core/RuntimeGraph.h"
+
+#include <luci/IR/CircleNodeVisitor.h>
+
+#include <memory>
+#include <vector>
+#include <unordered_map>
+
+namespace luci_interpreter
+{
+
+class KernelBuilder : public luci::CircleNodeVisitor<std::unique_ptr<Kernel>>
+{
+public:
+ KernelBuilder(
+ const std::unordered_map<const loco::Graph *, RuntimeGraph *> &graph_to_runtime_graph,
+ const std::unordered_map<const loco::Node *, Tensor *> &node_to_tensor)
+ : _graph_to_runtime_graph(graph_to_runtime_graph), _node_to_tensor(node_to_tensor)
+ {
+ }
+
+ std::unique_ptr<Kernel> visit(const luci::CircleAdd *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleArgMax *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleAveragePool2D *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleConcatenation *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleConv2D *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleConst *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleDepthToSpace *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleDepthwiseConv2D *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleDiv *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleElu *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleExp *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleFloor *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleFloorDiv *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleEqual *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleFullyConnected *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleGreater *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleGreaterEqual *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleIf *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleInstanceNorm *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleL2Normalize *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleL2Pool2D *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleLeakyRelu *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleLess *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleLessEqual *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleLocalResponseNormalization *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleLogicalAnd *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleLogicalNot *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleLogicalOr *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleLogistic *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleLogSoftmax *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleInput *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleMaximum *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleMaxPool2D *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleMean *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleMinimum *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleMul *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleNotEqual *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleOutput *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CirclePad *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CirclePow *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CirclePRelu *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleRelu *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleRelu6 *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleReshape *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleResizeBilinear *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleResizeNearestNeighbor *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleReverseV2 *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleRsqrt *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleSub *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleSlice *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleSoftmax *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleSpaceToDepth *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleSplit *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleStridedSlice *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleSqrt *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleSqueeze *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleTanh *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleTranspose *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleTransposeConv *node) override;
+ std::unique_ptr<Kernel> visit(const luci::CircleUnpack *node) override;
+
+private:
+ const Tensor *getInputTensor(const loco::Node *node) const;
+
+ const Tensor *getOptionalInputTensor(const loco::Node *node) const;
+
+ Tensor *getOutputTensor(const loco::Node *node) const;
+
+ std::vector<Tensor *> getOutputTensors(const std::vector<const loco::Node *> &nodes) const;
+
+ RuntimeGraph *getRuntimeGraph(const loco::Graph *graph) const;
+
+private:
+ const std::unordered_map<const loco::Graph *, RuntimeGraph *> &_graph_to_runtime_graph;
+ const std::unordered_map<const loco::Node *, Tensor *> &_node_to_tensor;
+};
+
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_LOADER_KERNELBUILDER_H
diff --git a/compiler/luci-interpreter/src/loader/KernelBuilder.test.cpp b/compiler/luci-interpreter/src/loader/KernelBuilder.test.cpp
new file mode 100644
index 000000000..c49a05a49
--- /dev/null
+++ b/compiler/luci-interpreter/src/loader/KernelBuilder.test.cpp
@@ -0,0 +1,1227 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "loader/GraphLoader.h"
+#include "loader/KernelBuilder.h"
+
+#include <kernels/Add.h>
+#include <kernels/ArgMax.h>
+#include <kernels/AveragePool2D.h>
+#include <kernels/Concatenation.h>
+#include <kernels/Conv2D.h>
+#include <kernels/DepthToSpace.h>
+#include <kernels/DepthwiseConv2D.h>
+#include <kernels/Div.h>
+#include <kernels/Elu.h>
+#include <kernels/Exp.h>
+#include <kernels/Floor.h>
+#include <kernels/FloorDiv.h>
+#include <kernels/Equal.h>
+#include <kernels/FullyConnected.h>
+#include <kernels/Greater.h>
+#include <kernels/GreaterEqual.h>
+#include <kernels/InstanceNorm.h>
+#include <kernels/L2Normalize.h>
+#include <kernels/L2Pool2D.h>
+#include <kernels/LeakyRelu.h>
+#include <kernels/Less.h>
+#include <kernels/LessEqual.h>
+#include <kernels/LocalResponseNormalization.h>
+#include <kernels/LogicalAnd.h>
+#include <kernels/LogicalNot.h>
+#include <kernels/LogicalOr.h>
+#include <kernels/Logistic.h>
+#include <kernels/LogSoftmax.h>
+#include <kernels/Maximum.h>
+#include <kernels/MaxPool2D.h>
+#include <kernels/Mean.h>
+#include <kernels/Minimum.h>
+#include <kernels/Mul.h>
+#include <kernels/NotEqual.h>
+#include <kernels/Pad.h>
+#include <kernels/Pow.h>
+#include <kernels/Prelu.h>
+#include <kernels/Relu.h>
+#include <kernels/Relu6.h>
+#include <kernels/Reshape.h>
+#include <kernels/ResizeBilinear.h>
+#include <kernels/ResizeNearestNeighbor.h>
+#include <kernels/Reverse.h>
+#include <kernels/Rsqrt.h>
+#include <kernels/Slice.h>
+#include <kernels/Softmax.h>
+#include <kernels/SpaceToDepth.h>
+#include <kernels/Split.h>
+#include <kernels/Sqrt.h>
+#include <kernels/Sub.h>
+#include <kernels/Squeeze.h>
+#include <kernels/StridedSlice.h>
+#include <kernels/Tanh.h>
+#include <kernels/Transpose.h>
+#include <kernels/TransposeConv.h>
+#include <kernels/Unpack.h>
+
+#include <gmock/gmock.h>
+
+namespace luci_interpreter
+{
+namespace
+{
+
+using namespace testing;
+
+class KernelBuilderTest : public Test
+{
+protected:
+ luci::CircleInput *createInputNode() { return createNode<luci::CircleInput>(); }
+
+ template <typename NodeT, typename... Args> NodeT *createNode(Args &&... args)
+ {
+ auto *node = _graph.nodes()->create<NodeT>(std::forward<Args>(args)...);
+ // The actual type does not matter for the purpose of the tests.
+ // NOTE The type is meaningless for nodes with multiple outputs (corresponding *Out nodes carry
+ // actual output types).
+ node->dtype(loco::DataType::FLOAT32);
+ return node;
+ }
+
+ template <typename NodeOutT> NodeOutT *createNodeOut(loco::Node *node, int index)
+ {
+ auto *node_out = createNode<NodeOutT>();
+ node_out->input(node);
+ node_out->index(index);
+ return node_out;
+ }
+
+ template <typename KernelT> std::unique_ptr<KernelT> buildKernel(const luci::CircleNode *op)
+ {
+ std::unordered_map<const loco::Graph *, RuntimeGraph *> graph_to_runtime_graph;
+
+ RuntimeGraph runtime_graph(nullptr);
+ RuntimeToIR runtime_to_ir;
+ GraphLoader graph_loader(&_graph, &runtime_graph, runtime_to_ir, graph_to_runtime_graph,
+ _node_to_tensor);
+ graph_loader.loadTensors();
+
+ KernelBuilder kernel_builder(graph_to_runtime_graph, _node_to_tensor);
+
+ auto kernel = op->accept(&kernel_builder);
+ return std::unique_ptr<KernelT>(dynamic_cast<KernelT *>(kernel.release()));
+ }
+
+ void checkTensor(const Tensor *tensor, const loco::Node *node)
+ {
+ EXPECT_THAT(tensor, Eq(_node_to_tensor.at(node)));
+ }
+
+private:
+ loco::Graph _graph;
+ std::unordered_map<const loco::Node *, Tensor *> _node_to_tensor;
+};
+
+TEST_F(KernelBuilderTest, Add)
+{
+ auto *input1 = createInputNode();
+ auto *input2 = createInputNode();
+
+ auto *op = createNode<luci::CircleAdd>();
+ op->x(input1);
+ op->y(input2);
+
+ op->fusedActivationFunction(luci::FusedActFunc::RELU);
+
+ auto kernel = buildKernel<kernels::Add>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input1(), input1);
+ checkTensor(kernel->input2(), input2);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction()));
+}
+
+TEST_F(KernelBuilderTest, ArgMax)
+{
+ auto *input = createInputNode();
+ auto *axis = createInputNode();
+
+ auto *op = createNode<luci::CircleArgMax>();
+ op->input(input);
+ op->dimension(axis);
+
+ op->output_type(loco::DataType::FLOAT32);
+
+ auto kernel = buildKernel<kernels::ArgMax>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->axis(), axis);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().output_type, Eq(op->output_type()));
+}
+
+TEST_F(KernelBuilderTest, AveragePool2D)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleAveragePool2D>();
+ op->value(input);
+
+ op->padding(luci::Padding::SAME);
+ op->filter()->h(11);
+ op->filter()->w(13);
+ op->stride()->h(17);
+ op->stride()->w(19);
+ op->fusedActivationFunction(luci::FusedActFunc::RELU);
+
+ auto kernel = buildKernel<kernels::AveragePool2D>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().padding, Eq(op->padding()));
+ EXPECT_THAT(kernel->params().filter_height, Eq(op->filter()->h()));
+ EXPECT_THAT(kernel->params().filter_width, Eq(op->filter()->w()));
+ EXPECT_THAT(kernel->params().stride_height, Eq(op->stride()->h()));
+ EXPECT_THAT(kernel->params().stride_width, Eq(op->stride()->w()));
+ EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction()));
+}
+
+TEST_F(KernelBuilderTest, Concatenation)
+{
+ auto *input1 = createInputNode();
+ auto *input2 = createInputNode();
+
+ auto *op = createNode<luci::CircleConcatenation>(2);
+ op->values(0, input1);
+ op->values(1, input2);
+ op->axis(11);
+
+ auto kernel = buildKernel<kernels::Concatenation>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(0), input1);
+ checkTensor(kernel->input(1), input2);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().axis, Eq(op->axis()));
+}
+
+TEST_F(KernelBuilderTest, Conv2D)
+{
+ auto *input = createInputNode();
+ auto *filter = createInputNode();
+ auto *bias = createInputNode();
+
+ auto *op = createNode<luci::CircleConv2D>();
+ op->input(input);
+ op->filter(filter);
+ op->bias(bias);
+
+ op->padding(luci::Padding::SAME);
+ op->stride()->h(11);
+ op->stride()->w(13);
+ op->dilation()->h(17);
+ op->dilation()->w(19);
+ op->fusedActivationFunction(luci::FusedActFunc::RELU);
+
+ auto kernel = buildKernel<kernels::Conv2D>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->filter(), filter);
+ checkTensor(kernel->bias(), bias);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().padding, Eq(op->padding()));
+ EXPECT_THAT(kernel->params().stride_height, Eq(op->stride()->h()));
+ EXPECT_THAT(kernel->params().stride_width, Eq(op->stride()->w()));
+ EXPECT_THAT(kernel->params().dilation_height_factor, Eq(op->dilation()->h()));
+ EXPECT_THAT(kernel->params().dilation_width_factor, Eq(op->dilation()->w()));
+ EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction()));
+}
+
+TEST_F(KernelBuilderTest, DepthToSpace)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleDepthToSpace>();
+ op->input(input);
+
+ op->block_size(11);
+
+ auto kernel = buildKernel<kernels::DepthToSpace>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().block_size, Eq(op->block_size()));
+}
+
+TEST_F(KernelBuilderTest, DepthwiseConv2D)
+{
+ auto *input = createInputNode();
+ auto *filter = createInputNode();
+ auto *bias = createInputNode();
+
+ auto *op = createNode<luci::CircleDepthwiseConv2D>();
+ op->input(input);
+ op->filter(filter);
+ op->bias(bias);
+
+ op->padding(luci::Padding::SAME);
+ op->depthMultiplier(11);
+ op->stride()->h(13);
+ op->stride()->w(17);
+ op->dilation()->h(19);
+ op->dilation()->w(23);
+ op->fusedActivationFunction(luci::FusedActFunc::RELU);
+
+ auto kernel = buildKernel<kernels::DepthwiseConv2D>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->filter(), filter);
+ checkTensor(kernel->bias(), bias);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().padding, Eq(op->padding()));
+ EXPECT_THAT(kernel->params().depth_multiplier, Eq(op->depthMultiplier()));
+ EXPECT_THAT(kernel->params().stride_height, Eq(op->stride()->h()));
+ EXPECT_THAT(kernel->params().stride_width, Eq(op->stride()->w()));
+ EXPECT_THAT(kernel->params().dilation_height_factor, Eq(op->dilation()->h()));
+ EXPECT_THAT(kernel->params().dilation_width_factor, Eq(op->dilation()->w()));
+ EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction()));
+}
+
+TEST_F(KernelBuilderTest, Div)
+{
+ auto *input1 = createInputNode();
+ auto *input2 = createInputNode();
+
+ auto *op = createNode<luci::CircleDiv>();
+ op->x(input1);
+ op->y(input2);
+
+ op->fusedActivationFunction(luci::FusedActFunc::RELU);
+
+ auto kernel = buildKernel<kernels::Div>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input1(), input1);
+ checkTensor(kernel->input2(), input2);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction()));
+}
+
+TEST_F(KernelBuilderTest, Elu)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleElu>();
+ op->features(input);
+
+ auto kernel = buildKernel<kernels::Elu>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Exp)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleExp>();
+ op->x(input);
+
+ auto kernel = buildKernel<kernels::Exp>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Floor)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleFloor>();
+ op->x(input);
+
+ auto kernel = buildKernel<kernels::Floor>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, FloorDiv)
+{
+ auto *x = createInputNode();
+ auto *y = createInputNode();
+
+ auto *op = createNode<luci::CircleFloorDiv>();
+ op->x(x);
+ op->y(y);
+
+ auto kernel = buildKernel<kernels::FloorDiv>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->x(), x);
+ checkTensor(kernel->y(), y);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Equal)
+{
+ auto *x_input = createInputNode();
+ auto *y_input = createInputNode();
+
+ auto *op = createNode<luci::CircleEqual>();
+ op->x(x_input);
+ op->y(y_input);
+
+ auto kernel = buildKernel<kernels::Equal>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->x(), x_input);
+ checkTensor(kernel->y(), y_input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, FullyConnected)
+{
+ auto *input = createInputNode();
+ auto *weights = createInputNode();
+ auto *bias = createInputNode();
+
+ auto *op = createNode<luci::CircleFullyConnected>();
+ op->input(input);
+ op->weights(weights);
+ op->bias(bias);
+
+ op->fusedActivationFunction(luci::FusedActFunc::RELU);
+
+ auto kernel = buildKernel<kernels::FullyConnected>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->weights(), weights);
+ checkTensor(kernel->bias(), bias);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction()));
+}
+
+TEST_F(KernelBuilderTest, Greater)
+{
+ auto *x_input = createInputNode();
+ auto *y_input = createInputNode();
+
+ auto *op = createNode<luci::CircleGreater>();
+ op->x(x_input);
+ op->y(y_input);
+
+ auto kernel = buildKernel<kernels::Greater>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->x(), x_input);
+ checkTensor(kernel->y(), y_input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, GreaterEqual)
+{
+ auto *x_input = createInputNode();
+ auto *y_input = createInputNode();
+
+ auto *op = createNode<luci::CircleGreaterEqual>();
+ op->x(x_input);
+ op->y(y_input);
+
+ auto kernel = buildKernel<kernels::GreaterEqual>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->x(), x_input);
+ checkTensor(kernel->y(), y_input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, InstanceNorm)
+{
+ auto *input = createInputNode();
+ auto *gamma = createInputNode();
+ auto *beta = createInputNode();
+
+ auto *op = createNode<luci::CircleInstanceNorm>();
+ op->input(input);
+ op->gamma(gamma);
+ op->beta(beta);
+
+ op->epsilon(1e-05);
+ op->fusedActivationFunction(luci::FusedActFunc::RELU);
+
+ auto kernel = buildKernel<kernels::InstanceNorm>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->gamma(), gamma);
+ checkTensor(kernel->beta(), beta);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().epsilon, Eq(op->epsilon()));
+ EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction()));
+}
+
+TEST_F(KernelBuilderTest, L2Normalize)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleL2Normalize>();
+ op->x(input);
+
+ op->fusedActivationFunction(luci::FusedActFunc::RELU);
+
+ auto kernel = buildKernel<kernels::L2Normalize>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction()));
+}
+
+TEST_F(KernelBuilderTest, L2Pool2D)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleL2Pool2D>();
+ op->value(input);
+
+ op->padding(luci::Padding::SAME);
+ op->filter()->h(11);
+ op->filter()->w(13);
+ op->stride()->h(17);
+ op->stride()->w(19);
+ op->fusedActivationFunction(luci::FusedActFunc::RELU);
+
+ auto kernel = buildKernel<kernels::L2Pool2D>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().padding, Eq(op->padding()));
+ EXPECT_THAT(kernel->params().filter_height, Eq(op->filter()->h()));
+ EXPECT_THAT(kernel->params().filter_width, Eq(op->filter()->w()));
+ EXPECT_THAT(kernel->params().stride_height, Eq(op->stride()->h()));
+ EXPECT_THAT(kernel->params().stride_width, Eq(op->stride()->w()));
+ EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction()));
+}
+
+TEST_F(KernelBuilderTest, LeakyRelu)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleLeakyRelu>();
+ op->features(input);
+
+ op->alpha(11.0f);
+
+ auto kernel = buildKernel<kernels::LeakyRelu>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().alpha, Eq(op->alpha()));
+}
+
+TEST_F(KernelBuilderTest, Less)
+{
+ auto *x_input = createInputNode();
+ auto *y_input = createInputNode();
+
+ auto *op = createNode<luci::CircleLess>();
+ op->x(x_input);
+ op->y(y_input);
+
+ auto kernel = buildKernel<kernels::Less>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->x(), x_input);
+ checkTensor(kernel->y(), y_input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, LessEqual)
+{
+ auto *x_input = createInputNode();
+ auto *y_input = createInputNode();
+
+ auto *op = createNode<luci::CircleLessEqual>();
+ op->x(x_input);
+ op->y(y_input);
+
+ auto kernel = buildKernel<kernels::LessEqual>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->x(), x_input);
+ checkTensor(kernel->y(), y_input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, LocalResponseNormalization)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleLocalResponseNormalization>();
+ op->input(input);
+
+ op->radius(11);
+ op->bias(13.0f);
+ op->alpha(15.0f);
+ op->beta(17.0f);
+
+ auto kernel = buildKernel<kernels::LocalResponseNormalization>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().radius, Eq(op->radius()));
+ EXPECT_THAT(kernel->params().bias, Eq(op->bias()));
+ EXPECT_THAT(kernel->params().alpha, Eq(op->alpha()));
+ EXPECT_THAT(kernel->params().beta, Eq(op->beta()));
+}
+
+TEST_F(KernelBuilderTest, LogicalAnd)
+{
+ auto *input1 = createInputNode();
+ auto *input2 = createInputNode();
+
+ auto *op = createNode<luci::CircleLogicalAnd>();
+ op->x(input1);
+ op->y(input2);
+
+ auto kernel = buildKernel<kernels::LogicalAnd>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input1(), input1);
+ checkTensor(kernel->input2(), input2);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, LogicalNot)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleLogicalNot>();
+ op->x(input);
+
+ auto kernel = buildKernel<kernels::LogicalNot>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, LogicalOr)
+{
+ auto *input1 = createInputNode();
+ auto *input2 = createInputNode();
+
+ auto *op = createNode<luci::CircleLogicalOr>();
+ op->x(input1);
+ op->y(input2);
+
+ auto kernel = buildKernel<kernels::LogicalOr>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input1(), input1);
+ checkTensor(kernel->input2(), input2);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Logistic)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleLogistic>();
+ op->x(input);
+
+ auto kernel = buildKernel<kernels::Logistic>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, LogSoftmax)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleLogSoftmax>();
+ op->logits(input);
+
+ auto kernel = buildKernel<kernels::LogSoftmax>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Maximum)
+{
+ auto *input1 = createInputNode();
+ auto *input2 = createInputNode();
+
+ auto *op = createNode<luci::CircleMaximum>();
+ op->x(input1);
+ op->y(input2);
+
+ auto kernel = buildKernel<kernels::Maximum>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input1(), input1);
+ checkTensor(kernel->input2(), input2);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, MaxPool2D)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleMaxPool2D>();
+ op->value(input);
+
+ op->padding(luci::Padding::SAME);
+ op->filter()->h(11);
+ op->filter()->w(13);
+ op->stride()->h(17);
+ op->stride()->w(19);
+ op->fusedActivationFunction(luci::FusedActFunc::RELU);
+
+ auto kernel = buildKernel<kernels::MaxPool2D>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().padding, Eq(op->padding()));
+ EXPECT_THAT(kernel->params().filter_height, Eq(op->filter()->h()));
+ EXPECT_THAT(kernel->params().filter_width, Eq(op->filter()->w()));
+ EXPECT_THAT(kernel->params().stride_height, Eq(op->stride()->h()));
+ EXPECT_THAT(kernel->params().stride_width, Eq(op->stride()->w()));
+ EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction()));
+}
+
+TEST_F(KernelBuilderTest, Mean)
+{
+ auto *input = createInputNode();
+ auto *axes = createInputNode();
+
+ auto *op = createNode<luci::CircleMean>();
+ op->input(input);
+ op->reduction_indices(axes);
+
+ op->keep_dims(true);
+
+ auto kernel = buildKernel<kernels::Mean>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->axes(), axes);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().keep_dims, Eq(op->keep_dims()));
+}
+
+TEST_F(KernelBuilderTest, Minimum)
+{
+ auto *input1 = createInputNode();
+ auto *input2 = createInputNode();
+
+ auto *op = createNode<luci::CircleMinimum>();
+ op->x(input1);
+ op->y(input2);
+
+ auto kernel = buildKernel<kernels::Minimum>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input1(), input1);
+ checkTensor(kernel->input2(), input2);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Mul)
+{
+ auto *input1 = createInputNode();
+ auto *input2 = createInputNode();
+
+ auto *op = createNode<luci::CircleMul>();
+ op->x(input1);
+ op->y(input2);
+
+ op->fusedActivationFunction(luci::FusedActFunc::RELU);
+
+ auto kernel = buildKernel<kernels::Mul>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input1(), input1);
+ checkTensor(kernel->input2(), input2);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction()));
+}
+
+TEST_F(KernelBuilderTest, NotEqual)
+{
+ auto *x_input = createInputNode();
+ auto *y_input = createInputNode();
+
+ auto *op = createNode<luci::CircleNotEqual>();
+ op->x(x_input);
+ op->y(y_input);
+
+ auto kernel = buildKernel<kernels::NotEqual>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->x(), x_input);
+ checkTensor(kernel->y(), y_input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Pad)
+{
+ auto *input = createInputNode();
+ auto *paddings = createInputNode();
+
+ auto *op = createNode<luci::CirclePad>();
+ op->input(input);
+ op->paddings(paddings);
+
+ auto kernel = buildKernel<kernels::Pad>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->paddings(), paddings);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Pow)
+{
+ auto *input1 = createInputNode();
+ auto *input2 = createInputNode();
+
+ auto *op = createNode<luci::CirclePow>();
+ op->x(input1);
+ op->y(input2);
+
+ auto kernel = buildKernel<kernels::Pow>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input1(), input1);
+ checkTensor(kernel->input2(), input2);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Prelu)
+{
+ auto *input = createInputNode();
+ auto *alpha = createInputNode();
+
+ auto *op = createNode<luci::CirclePRelu>();
+ op->input(input);
+ op->alpha(alpha);
+
+ auto kernel = buildKernel<kernels::Prelu>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->alpha(), alpha);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Relu)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleRelu>();
+ op->features(input);
+
+ auto kernel = buildKernel<kernels::Relu>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Relu6)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleRelu6>();
+ op->features(input);
+
+ auto kernel = buildKernel<kernels::Relu6>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Reshape)
+{
+ auto *input = createInputNode();
+ auto *shape = createInputNode();
+
+ auto *op = createNode<luci::CircleReshape>();
+ op->tensor(input);
+ op->shape(shape);
+
+ auto kernel = buildKernel<kernels::Reshape>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->shape(), shape);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, ResizeBilinear)
+{
+ auto *input = createInputNode();
+ auto *size = createInputNode();
+
+ auto *op = createNode<luci::CircleResizeBilinear>();
+ op->input(input);
+ op->size(size);
+ op->align_corners(true);
+ op->half_pixel_centers(true);
+
+ auto kernel = buildKernel<kernels::ResizeBilinear>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->size(), size);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().align_corners, Eq(op->align_corners()));
+ EXPECT_THAT(kernel->params().half_pixel_centers, Eq(op->half_pixel_centers()));
+}
+
+TEST_F(KernelBuilderTest, ResizeNearestNeighbor)
+{
+ auto *input = createInputNode();
+ auto *size = createInputNode();
+
+ auto *op = createNode<luci::CircleResizeNearestNeighbor>();
+ op->input(input);
+ op->size(size);
+ op->align_corners(true);
+
+ auto kernel = buildKernel<kernels::ResizeNearestNeighbor>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->size(), size);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().align_corners, Eq(op->align_corners()));
+ // TODO currently half_pixel_centers are not implemented on CircleResizeNearestNeighbor
+ // after adding, need to be updated.
+}
+
+TEST_F(KernelBuilderTest, ReverseV2)
+{
+ auto *input = createInputNode();
+ auto *axes = createInputNode();
+
+ auto *op = createNode<luci::CircleReverseV2>();
+ op->tensor(input);
+ op->axis(axes);
+
+ auto kernel = buildKernel<kernels::Reverse>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->axes(), axes);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Rsqrt)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleRsqrt>();
+ op->x(input);
+
+ auto kernel = buildKernel<kernels::Rsqrt>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Slice)
+{
+ auto *input = createInputNode();
+ auto *begin = createInputNode();
+ auto *size = createInputNode();
+
+ auto *op = createNode<luci::CircleSlice>();
+ op->input(input);
+ op->begin(begin);
+ op->size(size);
+
+ auto kernel = buildKernel<kernels::Slice>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->begin(), begin);
+ checkTensor(kernel->size(), size);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Softmax)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleSoftmax>();
+ op->logits(input);
+
+ op->beta(11.0f);
+
+ auto kernel = buildKernel<kernels::Softmax>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().beta, Eq(op->beta()));
+}
+
+TEST_F(KernelBuilderTest, SpaceToDepth)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleSpaceToDepth>();
+ op->input(input);
+
+ op->block_size(11);
+
+ auto kernel = buildKernel<kernels::SpaceToDepth>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().block_size, op->block_size());
+}
+
+TEST_F(KernelBuilderTest, Split)
+{
+ auto *axis = createInputNode();
+ auto *input = createInputNode();
+ auto *op = createNode<luci::CircleSplit>();
+ auto *output1 = createNodeOut<luci::CircleSplitOut>(op, 0);
+ auto *output2 = createNodeOut<luci::CircleSplitOut>(op, 1);
+
+ op->split_dim(axis);
+ op->input(input);
+
+ op->num_split(2);
+
+ auto kernel = buildKernel<kernels::Split>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->axis(), axis);
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(0), output1);
+ checkTensor(kernel->output(1), output2);
+}
+
+TEST_F(KernelBuilderTest, Sqrt)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleSqrt>();
+ op->x(input);
+
+ auto kernel = buildKernel<kernels::Sqrt>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Sub)
+{
+ auto *input1 = createInputNode();
+ auto *input2 = createInputNode();
+
+ auto *op = createNode<luci::CircleSub>();
+ op->x(input1);
+ op->y(input2);
+
+ op->fusedActivationFunction(luci::FusedActFunc::RELU);
+
+ auto kernel = buildKernel<kernels::Sub>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input1(), input1);
+ checkTensor(kernel->input2(), input2);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction()));
+}
+
+TEST_F(KernelBuilderTest, Squeeze)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleSqueeze>();
+ op->input(input);
+
+ op->squeeze_dims({11, 13});
+
+ auto kernel = buildKernel<kernels::Squeeze>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().squeeze_dims, ElementsAreArray(op->squeeze_dims()));
+}
+
+TEST_F(KernelBuilderTest, StridedSlice)
+{
+ auto *input = createInputNode();
+ auto *begin = createInputNode();
+ auto *end = createInputNode();
+ auto *strides = createInputNode();
+
+ auto *op = createNode<luci::CircleStridedSlice>();
+ op->input(input);
+ op->begin(begin);
+ op->end(end);
+ op->strides(strides);
+
+ op->begin_mask(11);
+ op->ellipsis_mask(13);
+ op->end_mask(17);
+ op->new_axis_mask(19);
+ op->shrink_axis_mask(23);
+
+ auto kernel = buildKernel<kernels::StridedSlice>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->begin(), begin);
+ checkTensor(kernel->end(), end);
+ checkTensor(kernel->strides(), strides);
+ checkTensor(kernel->output(), op);
+ EXPECT_THAT(kernel->params().begin_mask, Eq(op->begin_mask()));
+ EXPECT_THAT(kernel->params().ellipsis_mask, Eq(op->ellipsis_mask()));
+ EXPECT_THAT(kernel->params().end_mask, Eq(op->end_mask()));
+ EXPECT_THAT(kernel->params().new_axis_mask, Eq(op->new_axis_mask()));
+ EXPECT_THAT(kernel->params().shrink_axis_mask, Eq(op->shrink_axis_mask()));
+}
+
+TEST_F(KernelBuilderTest, Tanh)
+{
+ auto *input = createInputNode();
+
+ auto *op = createNode<luci::CircleTanh>();
+ op->x(input);
+
+ auto kernel = buildKernel<kernels::Tanh>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, Transpose)
+{
+ auto *input = createInputNode();
+ auto *perm = createInputNode();
+
+ auto *op = createNode<luci::CircleTranspose>();
+ op->a(input);
+ op->perm(perm);
+
+ auto kernel = buildKernel<kernels::Transpose>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->perm(), perm);
+ checkTensor(kernel->output(), op);
+}
+
+TEST_F(KernelBuilderTest, TransposeConv)
+{
+ auto *output_shape = createInputNode();
+ auto *filter = createInputNode();
+ auto *input = createInputNode();
+ auto *bias = createInputNode();
+
+ auto *op = createNode<luci::CircleTransposeConv>();
+ op->inputSizes(output_shape);
+ op->filter(filter);
+ op->outBackprop(input);
+ op->bias(bias);
+
+ op->padding(luci::Padding::SAME);
+ op->stride()->h(11);
+ op->stride()->w(13);
+
+ auto kernel = buildKernel<kernels::TransposeConv>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->output_shape(), output_shape);
+ checkTensor(kernel->filter(), filter);
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(), op);
+ checkTensor(kernel->bias(), bias);
+ EXPECT_THAT(kernel->params().padding, Eq(op->padding()));
+ EXPECT_THAT(kernel->params().stride_height, Eq(op->stride()->h()));
+ EXPECT_THAT(kernel->params().stride_width, Eq(op->stride()->w()));
+}
+
+TEST_F(KernelBuilderTest, Unpack)
+{
+ auto *input = createInputNode();
+ auto *op = createNode<luci::CircleUnpack>();
+ auto *output1 = createNodeOut<luci::CircleUnpackOut>(op, 0);
+ auto *output2 = createNodeOut<luci::CircleUnpackOut>(op, 1);
+
+ op->value(input);
+
+ op->num(2);
+ op->axis(11);
+
+ auto kernel = buildKernel<kernels::Unpack>(op);
+ ASSERT_THAT(kernel, NotNull());
+
+ checkTensor(kernel->input(), input);
+ checkTensor(kernel->output(0), output1);
+ checkTensor(kernel->output(1), output2);
+ EXPECT_THAT(kernel->params().axis, Eq(op->axis()));
+}
+
+TEST_F(KernelBuilderTest, NonExisting1_NEG)
+{
+ auto *op = createNode<luci::CircleConst>();
+ ASSERT_ANY_THROW(buildKernel<Kernel>(op));
+}
+
+TEST_F(KernelBuilderTest, NonExisting2_NEG)
+{
+ auto *op = createNode<luci::CircleInput>();
+ ASSERT_ANY_THROW(buildKernel<Kernel>(op));
+}
+
+TEST_F(KernelBuilderTest, NonExisting3_NEG)
+{
+ auto *op = createNode<luci::CircleOutput>();
+ ASSERT_ANY_THROW(buildKernel<Kernel>(op));
+}
+
+} // namespace
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/loader/ModuleLoader.cpp b/compiler/luci-interpreter/src/loader/ModuleLoader.cpp
new file mode 100644
index 000000000..b9a2ae0a9
--- /dev/null
+++ b/compiler/luci-interpreter/src/loader/ModuleLoader.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ModuleLoader.h"
+
+#include "GraphLoader.h"
+
+namespace luci_interpreter
+{
+
+ModuleLoader::ModuleLoader(const luci::Module *module, RuntimeModule *runtime_module,
+ RuntimeToIR &runtime_to_ir,
+ std::unordered_map<const loco::Node *, Tensor *> &node_to_tensor)
+ : _module(module), _runtime_module(runtime_module), _runtime_to_ir(runtime_to_ir),
+ _node_to_tensor(node_to_tensor)
+{
+}
+
+void ModuleLoader::load()
+{
+ // Runtime graphs have to be created in advance, because they will be needed during the loading
+ // process for control flow nodes.
+ for (size_t i = 0; i < _module->size(); ++i)
+ {
+ _graph_to_runtime_graph.emplace(_module->graph(i), _runtime_module->addGraph());
+ }
+ for (size_t i = 0; i < _module->size(); ++i)
+ {
+ const loco::Graph *graph = _module->graph(i);
+ RuntimeGraph *runtime_graph = _graph_to_runtime_graph.at(graph);
+ GraphLoader loader(graph, runtime_graph, _runtime_to_ir, _graph_to_runtime_graph,
+ _node_to_tensor);
+ loader.loadTensors();
+ loader.initInputOutputTensors();
+ loader.loadOperators();
+ }
+}
+
+} // namespace luci_interpreter
diff --git a/compiler/luci-interpreter/src/loader/ModuleLoader.h b/compiler/luci-interpreter/src/loader/ModuleLoader.h
new file mode 100644
index 000000000..1af0ed747
--- /dev/null
+++ b/compiler/luci-interpreter/src/loader/ModuleLoader.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_LOADER_MODULELOADER_H
+#define LUCI_INTERPRETER_LOADER_MODULELOADER_H
+
+#include "core/RuntimeModule.h"
+#include "loader/RuntimeToIR.h"
+
+#include <luci/IR/Module.h>
+
+#include <unordered_map>
+
+namespace luci_interpreter
+{
+
+class ModuleLoader
+{
+public:
+ ModuleLoader(const luci::Module *module, RuntimeModule *runtime_module,
+ RuntimeToIR &runtime_to_ir,
+ std::unordered_map<const loco::Node *, Tensor *> &node_to_tensor);
+
+ void load();
+
+private:
+ const luci::Module *_module;
+ RuntimeModule *_runtime_module;
+ RuntimeToIR &_runtime_to_ir;
+ std::unordered_map<const loco::Node *, Tensor *> &_node_to_tensor;
+ std::unordered_map<const loco::Graph *, RuntimeGraph *> _graph_to_runtime_graph;
+};
+
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_LOADER_MODULELOADER_H
diff --git a/compiler/luci-interpreter/src/loader/RuntimeToIR.h b/compiler/luci-interpreter/src/loader/RuntimeToIR.h
new file mode 100644
index 000000000..9ea8b1fa2
--- /dev/null
+++ b/compiler/luci-interpreter/src/loader/RuntimeToIR.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LUCI_INTERPRETER_LOADER_RUNTIMETOIR_H
+#define LUCI_INTERPRETER_LOADER_RUNTIMETOIR_H
+
+#include "luci_interpreter/core/Tensor.h"
+
+#include <luci/IR/CircleNode.h>
+
+#include <unordered_map>
+
+namespace luci_interpreter
+{
+
+// Maps runtime entities back to IR entities. It is used to implement observing functionality.
+struct RuntimeToIR
+{
+ std::unordered_map<const Tensor *, const luci::CircleNode *> tensor_to_node;
+ std::unordered_map<const Kernel *, const luci::CircleNode *> kernel_to_node;
+};
+
+} // namespace luci_interpreter
+
+#endif // LUCI_INTERPRETER_LOADER_RUNTIMETOIR_H
diff --git a/compiler/luci-value-test/CMakeLists.txt b/compiler/luci-value-test/CMakeLists.txt
new file mode 100644
index 000000000..ec7463409
--- /dev/null
+++ b/compiler/luci-value-test/CMakeLists.txt
@@ -0,0 +1,25 @@
+unset(LUCI_VALUE_TESTS)
+
+macro(addeval NAME)
+ list(APPEND LUCI_VALUE_TESTS ${NAME})
+endmacro(addeval)
+
+# Read "test.lst"
+include("test.lst")
+# Read "test.local.lst" if exists
+include("test.local.lst" OPTIONAL)
+
+# Generate dependencies
+add_custom_target(luci_eval_testfiles ALL DEPENDS ${TESTFILES})
+
+add_subdirectory(tester)
+
+get_target_property(ARTIFACTS_BIN_PATH testDataGenerator BINARY_DIR)
+
+add_test(NAME luci_value_test
+ COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/evalverify.sh"
+ "${CMAKE_CURRENT_BINARY_DIR}"
+ "${ARTIFACTS_BIN_PATH}"
+ "${NNCC_OVERLAY_DIR}/venv_2_3_0"
+ ${LUCI_VALUE_TESTS}
+)
diff --git a/compiler/luci-value-test/README.md b/compiler/luci-value-test/README.md
new file mode 100644
index 000000000..90e92834b
--- /dev/null
+++ b/compiler/luci-value-test/README.md
@@ -0,0 +1,15 @@
+# luci-value-test
+
+`luci-value-test` validates luci IR graph model file (.circle)
+
+The test proceeds as follows
+
+Step 1: Generate tflite files and circle files from TFLite recipes (listsed in test.lst).
+"TFLite recipe" -> tflchef -> "tflite file" -> tflite2circle -> "circle file"
+
+Step 2: Run TFLite interpreter and luci-interpreter for the generated tflite and circle, respectively.
+(with the same input tensors filled with random values)
+circle file -> luci-interpreter -------> Execution result 1
+tflite file -> TFLite interpreter -----> Execution result 2
+
+Step 3: Compare the execution result 1 and 2. The result must be the same.
diff --git a/compiler/luci-value-test/evalverify.sh b/compiler/luci-value-test/evalverify.sh
new file mode 100755
index 000000000..12c9a459a
--- /dev/null
+++ b/compiler/luci-value-test/evalverify.sh
@@ -0,0 +1,63 @@
+#!/bin/bash
+
+# This script verifies the basic behavior of luci interpreter
+#
+# HOW TO USE
+#
+# ./evalverify.sh <path/to/bin_dir> <path/to/work_dir> <path/to/venv_dir> <TEST 1> <TEST 2> ...
+# bin_dir : build directory of luci-value-test (ex: build/compiler/luci-value-test)
+# work_dir : artifacts directoy where test materials exist
+# venv_dir : python virtual environment home directory
+
+VERIFY_SOURCE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+VERIFY_SCRIPT_PATH="${VERIFY_SOURCE_PATH}/luci_eval_verifier.py"
+BINDIR="$1"; shift
+WORKDIR="$1"; shift
+VIRTUALENV="$1"; shift
+INTERPRETER_DRIVER_PATH="${BINDIR}/tester/luci_eval_tester"
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+for TESTCASE in "$@"; do
+ TESTED+=("${TESTCASE}")
+
+ TESTCASE_FILE="${WORKDIR}/${TESTCASE}"
+ TEST_RESULT_FILE="${BINDIR}/${TESTCASE}"
+
+ PASSED_TAG="${TEST_RESULT_FILE}.passed"
+ rm -f "${PASSED_TAG}"
+
+ cat > "${TEST_RESULT_FILE}.log" <(
+ exec 2>&1
+ set -ex
+
+ source "${VIRTUALENV}/bin/activate"
+ "${VIRTUALENV}/bin/python" "${VERIFY_SCRIPT_PATH}" \
+ --driver "${INTERPRETER_DRIVER_PATH}" \
+ --model "${TESTCASE_FILE}"
+
+ if [[ $? -eq 0 ]]; then
+ touch "${PASSED_TAG}"
+ fi
+ )
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ PASSED+=("${TESTCASE}")
+ else
+ FAILED+=("${TESTCASE}")
+ fi
+done
+
+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/luci-value-test/luci_eval_verifier.py b/compiler/luci-value-test/luci_eval_verifier.py
new file mode 100755
index 000000000..7a2cebb91
--- /dev/null
+++ b/compiler/luci-value-test/luci_eval_verifier.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python3
+import numpy as np
+import tensorflow as tf
+import subprocess
+import argparse
+import traceback
+
+#
+# This script compares the execution result of luci-interpreter with that of TFLite interpreter
+#
+# Basic usage:
+# eval_verifier.py --driver build/compiler/luci-value-test/tester/luci_eval_tester
+# --model inception_v3
+parser = argparse.ArgumentParser()
+parser.add_argument('--driver', type=str, required=True)
+parser.add_argument('--model', type=str, required=True)
+args = parser.parse_args()
+
+driver = args.driver
+tflite_model = args.model + ".tflite"
+circle_model = args.model + ".circle"
+
+# Build TFLite interpreter.
+interpreter = tf.lite.Interpreter(tflite_model)
+interpreter.allocate_tensors()
+
+# Generate random input data.
+num_inputs = len(interpreter.get_input_details())
+for i in range(num_inputs):
+ input_details = interpreter.get_input_details()[i]
+ if input_details["dtype"] == np.float32:
+ input_data = np.array(
+ np.random.random_sample(input_details["shape"]), input_details["dtype"])
+ elif input_details["dtype"] == np.uint8:
+ input_data = np.array(
+ np.random.randint(0, 256, size=input_details["shape"]),
+ input_details["dtype"])
+ elif input_details["dtype"] == np.bool_:
+ input_data = np.array(
+ np.random.choice(a=[True, False], size=input_details["shape"]),
+ input_details["dtype"])
+ else:
+ raise SystemExit("Unsupported input dtype")
+
+ interpreter.set_tensor(input_details["index"], input_data)
+ input_data.tofile(circle_model + ".input" + str(i))
+
+# Do inference
+interpreter.invoke()
+
+# Execute luci interpreter.
+subprocess.run(
+ [
+ driver, circle_model,
+ str(num_inputs), circle_model + ".input", circle_model + ".output"
+ ],
+ check=True)
+
+# Compare the results.
+for idx in range(len(interpreter.get_output_details())):
+ output_details = interpreter.get_output_details()[idx]
+ output_data = np.fromfile(circle_model + ".output" + str(idx),
+ output_details["dtype"])
+ shape_file = open(circle_model + ".output" + str(idx) + ".shape", 'r')
+ output_shape = [int(i) for i in shape_file.read().split(',')]
+ luci_output_data = np.reshape(output_data, output_shape)
+ try:
+ if output_details["dtype"] == np.uint8:
+ if np.allclose(
+ luci_output_data,
+ interpreter.get_tensor(
+ interpreter.get_output_details()[idx]["index"]),
+ rtol=0,
+ atol=0) == False:
+ raise SystemExit("Execution result of " + tflite_model +
+ " does not match with " + circle_model)
+ elif output_details["dtype"] == np.float32:
+ if np.allclose(
+ luci_output_data,
+ interpreter.get_tensor(
+ interpreter.get_output_details()[idx]["index"]),
+ rtol=1.e-5,
+ atol=1.e-5) == False:
+ raise SystemExit("Execution result of " + tflite_model +
+ " does not match with " + circle_model)
+ elif output_details["dtype"] == np.int64:
+ if np.allclose(
+ luci_output_data,
+ interpreter.get_tensor(
+ interpreter.get_output_details()[idx]["index"]),
+ rtol=0,
+ atol=0) == False:
+ raise SystemExit("Execution result of " + tflite_model +
+ " does not match with " + circle_model)
+ elif output_details["dtype"] == np.int32:
+ if np.allclose(
+ luci_output_data,
+ interpreter.get_tensor(
+ interpreter.get_output_details()[idx]["index"]),
+ rtol=0,
+ atol=0) == False:
+ raise SystemExit("Execution result of " + tflite_model +
+ " does not match with " + circle_model)
+ else:
+ raise SystemExit("Unsupported data type: ", output_details["dtype"])
+ except:
+ print(traceback.format_exc())
+ quit(255)
+
+quit(0)
diff --git a/compiler/luci-value-test/requires.cmake b/compiler/luci-value-test/requires.cmake
new file mode 100644
index 000000000..f8af5f27e
--- /dev/null
+++ b/compiler/luci-value-test/requires.cmake
@@ -0,0 +1,6 @@
+require("common-artifacts")
+require("luci")
+require("luci-interpreter")
+require("safemain")
+require("oops")
+require("loco")
diff --git a/compiler/luci-value-test/test.lst b/compiler/luci-value-test/test.lst
new file mode 100644
index 000000000..0e5231eca
--- /dev/null
+++ b/compiler/luci-value-test/test.lst
@@ -0,0 +1,183 @@
+#addeval(Abs_000)
+addeval(Add_000)
+#addeval(Add_001)
+addeval(Add_U8_000)
+#addeval(AddN_000)
+addeval(ArgMax_000)
+addeval(ArgMax_001)
+addeval(ArgMax_002)
+addeval(ArgMax_003)
+addeval(ArgMax_U8_000)
+addeval(ArgMax_U8_001)
+addeval(ArgMax_U8_002)
+addeval(ArgMax_U8_003)
+#addeval(ArgMin_000)
+#addeval(ArgMin_001)
+#addeval(ArgMin_002)
+#addeval(ArgMin_003)
+#addeval(ArgMin_U8_000)
+#addeval(ArgMin_U8_001)
+#addeval(ArgMin_U8_002)
+#addeval(ArgMin_U8_003)
+addeval(AveragePool2D_000)
+#addeval(BatchMatMul_000)
+#addeval(BatchMatMulV2_000)
+#addeval(BatchMatMulV2_001)
+#addeval(BatchToSpaceND_000)
+#addeval(Cast_000)
+#addeval(Cast_001)
+#addeval(Ceil_000)
+addeval(Concatenation_000)
+addeval(Concatenation_U8_000)
+addeval(Conv2D_000)
+addeval(Conv2D_001)
+addeval(Conv2D_002)
+#addeval(Conv2D_003)
+addeval(Conv2D_U8_000)
+addeval(Conv2D_U8_001)
+#addeval(Cos_000)
+#addeval(DepthToSpace_000)
+addeval(DepthwiseConv2D_000)
+addeval(DepthwiseConv2D_U8_000)
+#addeval(DepthwiseConv2D_U8_001)
+addeval(DepthwiseConv2D_001)
+#addeval(Div_000)
+addeval(ELU_000)
+#addeval(Equal_000)
+#addeval(Exp_000)
+#addeval(ExpandDims_000)
+#addeval(ExpandDims_001)
+#addeval(ExpandDims_002)
+#addeval(ExpandDims_003)
+#addeval(Fill_000)
+#addeval(Fill_001)
+#addeval(Floor_000)
+#addeval(FloorDiv_000)
+#addeval(FloorDiv_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(Greater_000)
+#addeval(GreaterEqual_000)
+addeval(If_000)
+addeval(If_001)
+addeval(L2Normalize_000)
+addeval(L2Pool2D_000)
+#addeval(L2Pool2D_U8_000)
+addeval(LeakyRelu_000)
+#addeval(Less_000)
+#addeval(LessEqual_000)
+addeval(LocalResponseNormalization_000)
+#addeval(Log_000)
+#addeval(LogicalAnd_000)
+#addeval(LogicalNot_000)
+#addeval(LogicalOr_000)
+addeval(Logistic_000)
+#addeval(LogSoftmax_000)
+#addeval(MatMul_000)
+#addeval(MatrixDiag_000)
+#addeval(MatrixSetDiag_000)
+#addeval(Maximum_000)
+addeval(MaxPool2D_000)
+addeval(MaxPool2D_U8_000)
+addeval(Mean_000)
+addeval(Mean_001)
+#addeval(Mean_U8_000)
+#addeval(Minimum_000)
+#addeval(MirrorPad_000)
+addeval(Mul_000)
+#addeval(Mul_U8_000)
+#addeval(Neg_000)
+#addeval(NotEqual_000)
+#addeval(OneHot_000)
+#addeval(OneHot_001)
+#addeval(OneHot_002)
+#addeval(OneHot_003)
+#addeval(Pack_000)
+#addeval(Pack_U8_000)
+addeval(Pad_000)
+addeval(Pad_U8_000)
+#addeval(Pow_000)
+#addeval(PRelu_000)
+#addeval(Range_000)
+#addeval(Rank_000)
+#addeval(ReduceAny_000)
+#addeval(ReduceAny_001)
+#addeval(ReduceAny_002)
+#addeval(ReduceAny_003)
+#addeval(ReduceMax_000)
+#addeval(ReduceMin_000)
+#addeval(ReduceProd_000)
+#addeval(ReduceProd_001)
+#addeval(ReduceProd_002)
+#addeval(ReduceProd_003)
+#addeval(ReLU_000)
+#addeval(ReLU6_000)
+#addeval(ReLUN1To1_000)
+addeval(Reshape_000)
+addeval(Reshape_001)
+addeval(Reshape_002)
+#addeval(Reshape_003)
+addeval(Reshape_U8_000)
+#addeval(ResizeBilinear_000)
+#addeval(ResizeNearestNeighbor_000)
+#addeval(ReverseSequence_000)
+#addeval(ReverseV2_000)
+#addeval(Round_000)
+#addeval(Rsqrt_000)
+#addeval(ScatterNd_000)
+#addeval(SegmentSum_000)
+#addeval(Select_000)
+#addeval(Select_001)
+#addeval(Select_002)
+#addeval(SelectV2_000)
+#addeval(SelectV2_001)
+#addeval(SelectV2_002)
+#addeval(Shape_000)
+#addeval(Sin_000)
+addeval(Slice_000)
+addeval(Softmax_000)
+#addeval(Softmax_U8_000)
+#addeval(SpaceToBatchND_000)
+#addeval(SpaceToBatchND_001)
+#addeval(SpaceToBatchND_002)
+#addeval(SpaceToBatchND_003)
+addeval(SpaceToDepth_000)
+#addeval(SparseToDense_000)
+addeval(Split_000)
+#addeval(SplitV_000)
+#addeval(Sqrt_000)
+#addeval(Square_000)
+#addeval(SquaredDifference_000)
+addeval(Squeeze_000)
+addeval(StridedSlice_000)
+addeval(StridedSlice_001)
+addeval(StridedSlice_002)
+#addeval(Sub_000)
+#addeval(Sub_U8_000)
+#addeval(Sum_000)
+#addeval(Sum_001)
+#addeval(Tanh_000)
+#addeval(Tile_000)
+#addeval(Tile_U8_000)
+#addeval(TopKV2_000)
+#addeval(TopKV2_001)
+addeval(Transpose_000)
+#addeval(TransposeConv_000)
+addeval(Unpack_000)
+addeval(Unpack_001)
+addeval(Unpack_002)
+addeval(Unpack_003)
+#addeval(Where_000)
+#addeval(Where_001)
+#addeval(While_000)
+#addeval(While_001)
+#addeval(While_002)
+#addeval(While_003)
+#addeval(YUV_TO_RGB_U8_000)
+#addeval(ZerosLike_000)
diff --git a/compiler/luci-value-test/tester/CMakeLists.txt b/compiler/luci-value-test/tester/CMakeLists.txt
new file mode 100644
index 000000000..f2a4ff4b6
--- /dev/null
+++ b/compiler/luci-value-test/tester/CMakeLists.txt
@@ -0,0 +1,13 @@
+
+set(SRCS_EVAL_TESTER
+ src/EvalTester.cpp
+ )
+
+add_executable(luci_eval_tester ${SRCS_EVAL_TESTER})
+target_link_libraries(luci_eval_tester PRIVATE oops)
+target_link_libraries(luci_eval_tester PRIVATE loco)
+target_link_libraries(luci_eval_tester PRIVATE luci_import)
+target_link_libraries(luci_eval_tester PRIVATE luci_export)
+target_link_libraries(luci_eval_tester PRIVATE luci_lang)
+target_link_libraries(luci_eval_tester PRIVATE luci_interpreter)
+target_link_libraries(luci_eval_tester PRIVATE safemain)
diff --git a/compiler/luci-value-test/tester/src/EvalTester.cpp b/compiler/luci-value-test/tester/src/EvalTester.cpp
new file mode 100644
index 000000000..b49602e5e
--- /dev/null
+++ b/compiler/luci-value-test/tester/src/EvalTester.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <luci/Importer.h>
+#include <luci_interpreter/Interpreter.h>
+#include <luci/CircleExporter.h>
+#include <luci/CircleFileExpContract.h>
+
+#include <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <map>
+#include <string>
+#include <random>
+
+namespace
+{
+
+void readDataFromFile(const std::string &filename, char *data, size_t data_size)
+{
+ std::ifstream fs(filename, std::ifstream::binary);
+ if (fs.fail())
+ throw std::runtime_error("Cannot open file \"" + filename + "\".\n");
+ if (fs.read(data, data_size).fail())
+ throw std::runtime_error("Failed to read data from file \"" + filename + "\".\n");
+}
+
+void writeDataToFile(const std::string &filename, const char *data, size_t data_size)
+{
+ std::ofstream fs(filename, std::ofstream::binary);
+ if (fs.fail())
+ throw std::runtime_error("Cannot open file \"" + filename + "\".\n");
+ if (fs.write(data, data_size).fail())
+ {
+ throw std::runtime_error("Failed to write data to file \"" + filename + "\".\n");
+ }
+}
+
+std::unique_ptr<luci::Module> importModel(const std::string &filename)
+{
+ std::ifstream fs(filename, std::ifstream::binary);
+ if (fs.fail())
+ {
+ throw std::runtime_error("Cannot open model file \"" + filename + "\".\n");
+ }
+ std::vector<char> model_data((std::istreambuf_iterator<char>(fs)),
+ std::istreambuf_iterator<char>());
+ return luci::Importer().importModule(circle::GetModel(model_data.data()));
+}
+
+template <typename NodeT> size_t getTensorSize(const NodeT *node)
+{
+ uint32_t tensor_size = loco::size(node->dtype());
+ for (uint32_t i = 0; i < node->rank(); ++i)
+ tensor_size *= node->dim(i).value();
+ return tensor_size;
+}
+
+} // namespace
+
+/*
+ * @brief EvalTester main
+ *
+ * Driver for testing luci-inerpreter
+ *
+ */
+int entry(int argc, char **argv)
+{
+ if (argc != 5)
+ {
+ std::cerr
+ << "Usage: " << argv[0]
+ << " <path/to/circle/model> <num_inputs> <path/to/input/prefix> <path/to/output/file>\n";
+ return EXIT_FAILURE;
+ }
+
+ const char *filename = argv[1];
+ const int32_t num_inputs = atoi(argv[2]);
+ const char *input_prefix = argv[3];
+ const char *output_file = argv[4];
+ const std::string intermediate_filename = std::string(filename) + ".inter.circle";
+
+ // Load model from the file
+ std::unique_ptr<luci::Module> initial_module = importModel(filename);
+ if (initial_module == nullptr)
+ {
+ std::cerr << "ERROR: Failed to load '" << filename << "'" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Export to a Circle file
+ luci::CircleExporter exporter;
+
+ luci::CircleFileExpContract contract(initial_module.get(), intermediate_filename);
+
+ if (!exporter.invoke(&contract))
+ {
+ std::cerr << "ERROR: Failed to export '" << intermediate_filename << "'" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Import model again
+ std::unique_ptr<luci::Module> module = importModel(intermediate_filename);
+ if (module == nullptr)
+ {
+ std::cerr << "ERROR: Failed to load '" << intermediate_filename << "'" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Create interpreter.
+ luci_interpreter::Interpreter interpreter(module.get());
+
+ // Set input.
+ // Data for n'th input is read from ${input_prefix}n
+ // (ex: Add.circle.input0, Add.circle.input1 ..)
+ const auto input_nodes = loco::input_nodes(module->graph());
+ assert(num_inputs == input_nodes.size());
+ for (int32_t i = 0; i < num_inputs; i++)
+ {
+ const auto *input_node = loco::must_cast<const luci::CircleInput *>(input_nodes[i]);
+ std::vector<char> input_data(getTensorSize(input_node));
+ readDataFromFile(std::string(input_prefix) + std::to_string(i), input_data.data(),
+ input_data.size());
+ interpreter.writeInputTensor(input_node, input_data.data(), input_data.size());
+ }
+
+ // Do inference.
+ interpreter.interpret();
+
+ // Get output.
+ const auto output_nodes = loco::output_nodes(module->graph());
+ for (int i = 0; i < module->graph()->outputs()->size(); i++)
+ {
+ const auto *output_node = loco::must_cast<const luci::CircleOutput *>(output_nodes[i]);
+ std::vector<char> output_data(getTensorSize(output_node));
+ interpreter.readOutputTensor(output_node, output_data.data(), output_data.size());
+
+ // Output data is written in ${output_file}
+ // (ex: Add.circle.output0)
+ // Output shape is written in ${output_file}.shape
+ // (ex: Add.circle.output0.shape)
+ writeDataToFile(std::string(output_file) + std::to_string(i), output_data.data(),
+ output_data.size());
+ // In case of Tensor output is Scalar value.
+ // The output tensor with rank 0 is treated as a scalar with shape (1)
+ if (output_node->rank() == 0)
+ {
+ writeDataToFile(std::string(output_file) + std::to_string(i) + ".shape", "1", 1);
+ }
+ else
+ {
+ auto shape_str = std::to_string(output_node->dim(0).value());
+ for (int j = 1; j < output_node->rank(); j++)
+ {
+ shape_str += ",";
+ shape_str += std::to_string(output_node->dim(j).value());
+ }
+ writeDataToFile(std::string(output_file) + std::to_string(i) + ".shape", shape_str.c_str(),
+ shape_str.size());
+ }
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/compiler/luci/CMakeLists.txt b/compiler/luci/CMakeLists.txt
new file mode 100644
index 000000000..214a1bbf2
--- /dev/null
+++ b/compiler/luci/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_subdirectory(env)
+add_subdirectory(log)
+add_subdirectory(lang)
+add_subdirectory(service)
+add_subdirectory(pass)
+add_subdirectory(logex)
+add_subdirectory(import)
+add_subdirectory(export)
+add_subdirectory(tester)
+
+add_subdirectory(tests)
diff --git a/compiler/luci/README.md b/compiler/luci/README.md
new file mode 100644
index 000000000..49c833121
--- /dev/null
+++ b/compiler/luci/README.md
@@ -0,0 +1,3 @@
+# luci
+
+_luci_ provides IR for TFLite/Circle and Graph from FlatBuffer.
diff --git a/compiler/luci/env/CMakeLists.txt b/compiler/luci/env/CMakeLists.txt
new file mode 100644
index 000000000..3d8387a47
--- /dev/null
+++ b/compiler/luci/env/CMakeLists.txt
@@ -0,0 +1,18 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(luci_env SHARED ${SOURCES})
+target_include_directories(luci_env PUBLIC include)
+target_link_libraries(luci_env PRIVATE nncc_common)
+install(TARGETS luci_env DESTINATION lib)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(luci_env_test ${TESTS})
+target_include_directories(luci_env_test PRIVATE src)
+target_link_libraries(luci_env_test luci_env)
diff --git a/compiler/luci/env/README.md b/compiler/luci/env/README.md
new file mode 100644
index 000000000..cda007867
--- /dev/null
+++ b/compiler/luci/env/README.md
@@ -0,0 +1,3 @@
+# luci-env
+
+_luci-env_ provides user environment settings that control _luci_
diff --git a/compiler/luci/env/include/luci/UserSettings.h b/compiler/luci/env/include/luci/UserSettings.h
new file mode 100644
index 000000000..bcfd16071
--- /dev/null
+++ b/compiler/luci/env/include/luci/UserSettings.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_USER_SETTINGS__
+#define __LUCI_USER_SETTINGS__
+
+// NOTE Revise the logic if we find a better way not using global status
+
+namespace luci
+{
+
+/**
+ * @brief UserSettings provides user settings by key-value
+ */
+struct UserSettings
+{
+ enum Key
+ {
+ Undefined,
+ MuteWarnings,
+ DisableValidation,
+ };
+
+ static UserSettings *settings();
+
+ virtual void set(const Key key, bool value) = 0;
+ virtual bool get(const Key key) const = 0;
+};
+
+} // namespace luci
+
+#endif // __LUCI_USER_SETTINGS__
diff --git a/compiler/luci/env/src/UserSettings.cpp b/compiler/luci/env/src/UserSettings.cpp
new file mode 100644
index 000000000..27dec762d
--- /dev/null
+++ b/compiler/luci/env/src/UserSettings.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/UserSettings.h"
+
+#include <stdexcept>
+
+namespace luci
+{
+
+class UserSettingsImpl : public UserSettings
+{
+public:
+ void set(const Key key, bool value) override;
+ bool get(const Key key) const override;
+
+private:
+ bool _MuteWarnings{false};
+ bool _DisableValidation{false};
+};
+
+void UserSettingsImpl::set(const Key key, bool value)
+{
+ switch (key)
+ {
+ case Key::MuteWarnings:
+ _MuteWarnings = value;
+ break;
+ case Key::DisableValidation:
+ _DisableValidation = value;
+ break;
+ default:
+ throw std::runtime_error("Invalid key in boolean set");
+ break;
+ }
+}
+
+bool UserSettingsImpl::get(const Key key) const
+{
+ switch (key)
+ {
+ case Key::MuteWarnings:
+ return _MuteWarnings;
+ case Key::DisableValidation:
+ return _DisableValidation;
+ default:
+ throw std::runtime_error("Invalid key in boolean get");
+ break;
+ }
+ return false;
+}
+
+} // namespace luci
+
+namespace luci
+{
+
+UserSettings *UserSettings::settings()
+{
+ static UserSettingsImpl _this;
+ return &_this;
+}
+
+} // namespace luci
diff --git a/compiler/luci/env/src/UserSettings.test.cpp b/compiler/luci/env/src/UserSettings.test.cpp
new file mode 100644
index 000000000..8d9d1875b
--- /dev/null
+++ b/compiler/luci/env/src/UserSettings.test.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/UserSettings.h"
+
+#include <gtest/gtest.h>
+
+TEST(UserSettings, instance)
+{
+ auto settings = luci::UserSettings::settings();
+ ASSERT_NE(nullptr, settings);
+
+ auto s2 = luci::UserSettings::settings();
+ ASSERT_EQ(s2, settings);
+}
+
+TEST(UserSettings, MuteWarnings)
+{
+ auto settings = luci::UserSettings::settings();
+ ASSERT_NE(nullptr, settings);
+
+ settings->set(luci::UserSettings::Key::MuteWarnings, false);
+ ASSERT_FALSE(settings->get(luci::UserSettings::Key::MuteWarnings));
+
+ settings->set(luci::UserSettings::Key::MuteWarnings, true);
+ ASSERT_TRUE(settings->get(luci::UserSettings::Key::MuteWarnings));
+}
+
+TEST(UserSettings, DisableValidation)
+{
+ auto settings = luci::UserSettings::settings();
+ ASSERT_NE(nullptr, settings);
+
+ settings->set(luci::UserSettings::Key::DisableValidation, false);
+ ASSERT_FALSE(settings->get(luci::UserSettings::Key::DisableValidation));
+
+ settings->set(luci::UserSettings::Key::DisableValidation, true);
+ ASSERT_TRUE(settings->get(luci::UserSettings::Key::DisableValidation));
+}
+
+TEST(UserSettings, undefined_set_NEG)
+{
+ auto settings = luci::UserSettings::settings();
+ ASSERT_NE(nullptr, settings);
+
+ ASSERT_THROW(settings->set(luci::UserSettings::Key::Undefined, true), std::exception);
+}
+
+TEST(UserSettings, undefined_get_NEG)
+{
+ auto settings = luci::UserSettings::settings();
+ ASSERT_NE(nullptr, settings);
+
+ ASSERT_THROW(settings->get(luci::UserSettings::Key::Undefined), std::exception);
+}
diff --git a/compiler/luci/export/CMakeLists.txt b/compiler/luci/export/CMakeLists.txt
new file mode 100644
index 000000000..fe4382ecd
--- /dev/null
+++ b/compiler/luci/export/CMakeLists.txt
@@ -0,0 +1,30 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+# TODO enable tests
+#file(GLOB_RECURSE TESTS "src/*.test.cpp")
+#list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(luci_export SHARED ${SOURCES})
+target_include_directories(luci_export PRIVATE src)
+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_circle)
+target_link_libraries(luci_export PRIVATE luci_env)
+target_link_libraries(luci_export PRIVATE luci_log)
+target_link_libraries(luci_export PRIVATE luci_logex)
+target_link_libraries(luci_export PRIVATE nncc_common)
+target_link_libraries(luci_export PRIVATE locop)
+target_link_libraries(luci_export PRIVATE oops)
+install(TARGETS luci_export DESTINATION lib)
+
+#if(NOT ENABLE_TEST)
+# return()
+#endif(NOT ENABLE_TEST)
+#
+#nnas_find_package(GTest REQUIRED)
+#
+#GTest_AddTest(luci_export_test ${TESTS})
+#target_include_directories(luci_export_test PRIVATE src)
+#target_link_libraries(luci_export_test luci_export)
+#target_link_libraries(luci_export_test oops)
diff --git a/compiler/luci/export/README.md b/compiler/luci/export/README.md
new file mode 100644
index 000000000..12b190a2f
--- /dev/null
+++ b/compiler/luci/export/README.md
@@ -0,0 +1,3 @@
+# luci-export
+
+_luci-export_ provides exporting _loco_ graph of Circle IR to Circle model file
diff --git a/compiler/luci/export/include/luci/CircleExporter.h b/compiler/luci/export/include/luci/CircleExporter.h
new file mode 100644
index 000000000..0584c623c
--- /dev/null
+++ b/compiler/luci/export/include/luci/CircleExporter.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_CIRCLEEXPORTER_H__
+#define __LUCI_CIRCLEEXPORTER_H__
+
+#include <luci/IR/Module.h>
+
+#include <loco.h>
+
+#include <memory>
+
+namespace luci
+{
+
+class CircleExporter
+{
+public:
+ // This contract class describes the interaction between a exporter and its client.
+ struct Contract
+ {
+ public:
+ virtual ~Contract() = default;
+
+ public: // Client -> Exporter
+ // Input Graph (to be exported)
+ // Exporter expects a loco graph that consists of Circle nodes
+ virtual loco::Graph *graph(void) const = 0;
+
+ // Input Module (to be exported)
+ // Exporter expects a luci module that consists of loco graphs
+ // TODO make this pure virtual
+ virtual luci::Module *module(void) const;
+
+ public: // Exporter -> Client
+ // Exporter calls store for export data
+ // Notice: Please DO NOT STORE ptr and size when implementing this in Client
+ virtual bool store(const char *ptr, const size_t size) const = 0;
+ };
+
+public:
+ explicit CircleExporter();
+
+public:
+ // invoke(...) returns false on failure.
+ bool invoke(Contract *) const;
+};
+
+} // namespace luci
+
+#endif // __LUCI_CIRCLEEXPORTER_H__
diff --git a/compiler/luci/export/include/luci/CircleFileExpContract.h b/compiler/luci/export/include/luci/CircleFileExpContract.h
new file mode 100644
index 000000000..eeaf2d9bb
--- /dev/null
+++ b/compiler/luci/export/include/luci/CircleFileExpContract.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_CIRCLEFILEEXPCONTRACT_H__
+#define __LUCI_CIRCLEFILEEXPCONTRACT_H__
+
+#include <loco.h>
+#include <luci/CircleExporter.h>
+#include <luci/IR/Module.h>
+#include <oops/InternalExn.h>
+
+#include <string>
+#include <fstream>
+#include <iostream>
+
+namespace luci
+{
+
+struct CircleFileExpContract : public luci::CircleExporter::Contract
+{
+public:
+ CircleFileExpContract(luci::Module *module, const std::string &filename)
+ : _module(module), _filepath(filename)
+ {
+ // NOTHING TO DO
+ }
+ virtual ~CircleFileExpContract() = default;
+
+public:
+ loco::Graph *graph(void) const final { return nullptr; }
+ luci::Module *module(void) const final { return _module; }
+
+public:
+ bool store(const char *ptr, const size_t size) const final
+ {
+ if (!ptr)
+ INTERNAL_EXN("Graph was not serialized by FlatBuffer for some reason");
+
+ std::ofstream fs(_filepath, std::ofstream::binary);
+ fs.write(ptr, size);
+
+ return fs.good();
+ }
+
+private:
+ luci::Module *_module;
+ const std::string _filepath;
+};
+
+} // namespace luci
+
+#endif // __LUCI_CIRCLEFILEEXPCONTRACT_H__
diff --git a/compiler/luci/export/src/Check.h b/compiler/luci/export/src/Check.h
new file mode 100644
index 000000000..e05ec904a
--- /dev/null
+++ b/compiler/luci/export/src/Check.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CHECK_H__
+#define __CHECK_H__
+
+#include <stdexcept>
+#include <cassert>
+#include <iostream>
+
+// TODO Add macro for Release version
+
+#define LUCI_ASSERT(condition, msg) \
+ { \
+ if (!(condition)) \
+ { \
+ std::cerr << "[assert failed] " << (msg) << ". " << std::endl; \
+ assert((condition)); \
+ } \
+ }
+
+#endif // __CHECK_H__
diff --git a/compiler/luci/export/src/CircleExporter.cpp b/compiler/luci/export/src/CircleExporter.cpp
new file mode 100644
index 000000000..125df7802
--- /dev/null
+++ b/compiler/luci/export/src/CircleExporter.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/CircleExporter.h"
+#include "luci/IR/Module.h"
+#include "CircleExporterImpl.h"
+
+#include <oops/InternalExn.h>
+
+#include <fstream>
+#include <memory>
+
+namespace luci
+{
+
+// TODO remove this
+Module *CircleExporter::Contract::module(void) const { return nullptr; }
+
+CircleExporter::CircleExporter()
+{
+ // NOTHING TO DO
+}
+
+bool CircleExporter::invoke(Contract *contract) const
+{
+ auto module = contract->module();
+ if (module != nullptr)
+ {
+ CircleExporterImpl impl(module);
+
+ const char *ptr = impl.getBufferPointer();
+ const size_t size = impl.getBufferSize();
+
+ // we just send one time
+ return contract->store(ptr, size);
+ }
+
+ auto graph = contract->graph();
+ if (graph == nullptr)
+ return false;
+
+ CircleExporterImpl impl(graph);
+
+ const char *ptr = impl.getBufferPointer();
+ const size_t size = impl.getBufferSize();
+
+ // we just send one time
+ return contract->store(ptr, size);
+}
+
+} // namespace luci
diff --git a/compiler/luci/export/src/CircleExporterImpl.cpp b/compiler/luci/export/src/CircleExporterImpl.cpp
new file mode 100644
index 000000000..860cebf6e
--- /dev/null
+++ b/compiler/luci/export/src/CircleExporterImpl.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CircleExporterImpl.h"
+#include "Optimize.h"
+#include "TypeBridge.h"
+#include "CircleTensorExporter.h"
+#include "CircleOperationExporter.h"
+#include "CircleExporterUtils.h"
+
+#include <oops/InternalExn.h>
+#include <mio/circle/schema_generated.h>
+#include <flatbuffers/flatbuffers.h>
+
+#include <cassert>
+#include <unordered_map>
+#include <string>
+#include <stdexcept>
+
+namespace
+{
+
+luci::CircleInput *input_node(loco::Graph *g, const loco::GraphInputIndex &index)
+{
+ for (uint32_t n = 0; n < g->nodes()->size(); ++n)
+ {
+ if (auto input = dynamic_cast<luci::CircleInput *>(g->nodes()->at(n)))
+ {
+ if (input->indexed() && input->index() == index)
+ {
+ return input;
+ }
+ }
+ }
+ return nullptr;
+}
+
+luci::CircleOutput *output_node(loco::Graph *g, const loco::GraphOutputIndex &index)
+{
+ for (uint32_t n = 0; n < g->nodes()->size(); ++n)
+ {
+ if (auto output = dynamic_cast<luci::CircleOutput *>(g->nodes()->at(n)))
+ {
+ if (output->indexed() && output->index() == index)
+ {
+ return output;
+ }
+ }
+ }
+ return nullptr;
+}
+
+void registerGraphInputTensors(loco::Graph *graph, luci::SubGraphContext &ctx)
+{
+ for (uint32_t n = 0; n < graph->inputs()->size(); ++n)
+ {
+ auto node = input_node(graph, n);
+ assert(node != nullptr);
+ ctx._inputs.push_back(luci::get_tensor_index(node));
+ }
+}
+
+void registerGraphOutputTensors(loco::Graph *graph, luci::SubGraphContext &ctx)
+{
+ for (uint32_t n = 0; n < graph->outputs()->size(); ++n)
+ {
+ auto push = output_node(graph, n);
+ assert(push != nullptr);
+ auto node = push->from();
+ assert(node != nullptr);
+
+ // Do not export CircleOutput when it's input is CircleOutputExclude
+ if (dynamic_cast<luci::CircleOutputExclude *>(push->from()) != nullptr)
+ {
+ continue;
+ }
+
+ ctx._outputs.push_back(luci::get_tensor_index(node));
+ }
+}
+
+} // namespace
+
+namespace
+{
+
+using namespace circle;
+using namespace flatbuffers;
+
+Offset<Vector<Offset<OperatorCode>>>
+encodeOperatorCodes(FlatBufferBuilder &builder, std::unordered_map<luci::OpCode, uint32_t> &opcodes)
+{
+ std::vector<Offset<OperatorCode>> operator_codes_vec(opcodes.size());
+ for (auto it : opcodes)
+ {
+ uint32_t idx = it.second;
+ if (it.first.opcode != BuiltinOperator_CUSTOM)
+ {
+ operator_codes_vec[idx] = CreateOperatorCode(builder, it.first.opcode, 0, it.first.version);
+ }
+ else
+ {
+ operator_codes_vec[idx] =
+ CreateOperatorCode(builder, it.first.opcode, builder.CreateString(it.first.custom_code));
+ }
+ }
+
+ return builder.CreateVector(operator_codes_vec);
+}
+
+} // namespace
+
+namespace luci
+{
+
+using namespace circle;
+using namespace flatbuffers;
+
+CircleExporterImpl::CircleExporterImpl(loco::Graph *graph) { exportGraph(graph); }
+CircleExporterImpl::CircleExporterImpl(Module *module) { exportModule(module); }
+
+::flatbuffers::Offset<::circle::SubGraph>
+CircleExporterImpl::exportSubgraph(SerializedGraphData &gd)
+{
+ auto tensors = _builder.CreateVector(gd._tensors);
+ auto inputs = _builder.CreateVector(gd._inputs);
+ auto outputs = _builder.CreateVector(gd._outputs);
+ auto operators = _builder.CreateVector(gd._operators);
+ auto name = _builder.CreateString(gd._name);
+ auto df = gd._data_format;
+ auto subgraph = CreateSubGraph(_builder, tensors, inputs, outputs, operators, name, df);
+ return subgraph;
+}
+
+void CircleExporterImpl::exportGraph(loco::Graph *graph)
+{
+ // do graph optimization
+ optimize(graph);
+
+ // copy shape/dtype inference data to CircleNode
+ copy_shape_dtype(graph);
+
+ _builder.Clear();
+
+ SerializedModelData md;
+ SerializedGraphData gd;
+
+ // This version is taken from comment in fbs
+ constexpr uint32_t version = 0;
+
+ // set Subgraph name
+ gd._name = graph->name();
+
+ // TODO set this value properly
+ gd._data_format = circle::DataFormat::DataFormat_CHANNELS_LAST;
+
+ // prepare model data
+ prepareModelData(_builder, md);
+
+ // parse graph into SerializedModelData structure
+ exportOpDefinedTensors(graph, _builder, md, gd);
+
+ // NOTE Invoke these register functions only after each node is annotated with its tensor_index
+ registerGraphInputTensors(graph, gd);
+ registerGraphOutputTensors(graph, gd);
+
+ exportNodes(graph, _builder, md, gd);
+
+ // encode operator codes
+ auto operator_codes = encodeOperatorCodes(_builder, md._operator_codes);
+
+ // Subgraphs
+ Offset<SubGraph> subgraph = exportSubgraph(gd);
+ auto subgraphs = _builder.CreateVector(std::vector<Offset<SubGraph>>{subgraph});
+
+ // Description
+ std::string description_str = "nnpackage";
+ auto description = _builder.CreateString(description_str);
+
+ // create array of buffers
+ auto buffers = _builder.CreateVector(md._buffers);
+
+ // empty metadata
+ std::vector<int> metadata_buffer_vec;
+ auto metadata_buffer = _builder.CreateVector(metadata_buffer_vec);
+
+ // Model
+ auto model_offset = CreateModel(_builder, version, operator_codes, subgraphs, description,
+ buffers, metadata_buffer);
+ FinishModelBuffer(_builder, model_offset);
+}
+
+void CircleExporterImpl::exportModule(Module *module)
+{
+ assert(module->size() > 0);
+ // do graph optimization
+
+ SerializedModelData md;
+
+ _builder.Clear();
+
+ // prepare model data
+ prepareModelData(_builder, md);
+
+ std::vector<flatbuffers::Offset<circle::SubGraph>> subgraph_vec;
+
+ for (size_t g = 0; g < module->size(); ++g)
+ {
+ auto graph = module->graph(g);
+
+ optimize(graph);
+
+ // copy shape/dtype inference data to CircleNode
+ copy_shape_dtype(graph);
+
+ SerializedGraphData gd;
+
+ // set Subgraph name
+ gd._name = graph->name();
+
+ // TODO set this value properly
+ gd._data_format = circle::DataFormat::DataFormat_CHANNELS_LAST;
+
+ // parse graph into SerializedModelData structure
+ exportOpDefinedTensors(graph, _builder, md, gd);
+
+ // NOTE Invoke these register functions only after each node is annotated with its tensor_index
+ registerGraphInputTensors(graph, gd);
+ registerGraphOutputTensors(graph, gd);
+
+ exportNodes(graph, _builder, md, gd);
+
+ // Subgraphs
+ Offset<SubGraph> subgraph = exportSubgraph(gd);
+ subgraph_vec.push_back(subgraph);
+ }
+
+ auto subgraphs = _builder.CreateVector(std::vector<Offset<SubGraph>>{subgraph_vec});
+
+ // encode operator codes
+ auto operator_codes = encodeOperatorCodes(_builder, md._operator_codes);
+
+ // Description
+ std::string description_str = "nnpackage";
+ auto description = _builder.CreateString(description_str);
+
+ // create array of buffers
+ auto buffers = _builder.CreateVector(md._buffers);
+
+ // empty metadata
+ std::vector<int> metadata_buffer_vec;
+ auto metadata_buffer = _builder.CreateVector(metadata_buffer_vec);
+
+ // This version is taken from comment in fbs
+ constexpr uint32_t version = 0;
+
+ // Model
+ auto model_offset = CreateModel(_builder, version, operator_codes, subgraphs, description,
+ buffers, metadata_buffer);
+ FinishModelBuffer(_builder, model_offset);
+}
+
+const char *CircleExporterImpl::getBufferPointer() const
+{
+ return reinterpret_cast<const char *>(_builder.GetBufferPointer());
+}
+
+size_t CircleExporterImpl::getBufferSize() const { return _builder.GetSize(); }
+
+} // namespace luci
diff --git a/compiler/luci/export/src/CircleExporterImpl.h b/compiler/luci/export/src/CircleExporterImpl.h
new file mode 100644
index 000000000..e5d5b5a00
--- /dev/null
+++ b/compiler/luci/export/src/CircleExporterImpl.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_EXPORTER_IMPL_H__
+#define __CIRCLE_EXPORTER_IMPL_H__
+
+#include "luci/CircleExporter.h"
+#include "luci/IR/Module.h"
+
+#include "SerializedData.h"
+
+#include "SerializedData.h"
+
+#include <mio/circle/schema_generated.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+/**
+ * internal implementation of interface exporter class
+ */
+class CircleExporterImpl
+{
+public:
+ CircleExporterImpl() = delete;
+ ~CircleExporterImpl() = default;
+
+ explicit CircleExporterImpl(loco::Graph *graph);
+ explicit CircleExporterImpl(Module *module);
+
+ /**
+ * @return pointer to buffer with serialized graph
+ */
+ const char *getBufferPointer() const;
+
+ /**
+ * @return size of buffer with serialized graph
+ */
+ size_t getBufferSize() const;
+
+private:
+ /**
+ * @brief create Subgraph using data stored in SerializedGraphData
+ * @param gd information about serializer parts of model
+ * @return offset in buffer corresponding to serialized subgraph
+ */
+ flatbuffers::Offset<circle::SubGraph> exportSubgraph(SerializedGraphData &gd);
+
+ /**
+ * @brief root function that writes graph into internal buffer
+ * @param graph
+ */
+ void exportGraph(loco::Graph *graph);
+
+ /**
+ * @brief root function that writes Module into internal buffer
+ * @param module
+ */
+ void exportModule(Module *module);
+
+private:
+ flatbuffers::FlatBufferBuilder _builder;
+};
+
+} // namespace luci
+
+#endif // __CIRCLE_EXPORTER_IMPL_H__
diff --git a/compiler/luci/export/src/CircleExporterUtils.cpp b/compiler/luci/export/src/CircleExporterUtils.cpp
new file mode 100644
index 000000000..1fdb40e51
--- /dev/null
+++ b/compiler/luci/export/src/CircleExporterUtils.cpp
@@ -0,0 +1,251 @@
+/*
+ * 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 "CircleExporterUtils.h"
+
+#include <oops/InternalExn.h>
+
+#include <cassert>
+#include <memory>
+
+namespace luci
+{
+
+circle::ActivationFunctionType to_circle_actfunc(luci::FusedActFunc func)
+{
+ switch (func)
+ {
+ case luci::FusedActFunc::NONE:
+ return circle::ActivationFunctionType_NONE;
+ case luci::FusedActFunc::RELU:
+ return circle::ActivationFunctionType_RELU;
+ case luci::FusedActFunc::RELU_N1_TO_1:
+ return circle::ActivationFunctionType_RELU_N1_TO_1;
+ case luci::FusedActFunc::RELU6:
+ return circle::ActivationFunctionType_RELU6;
+ case luci::FusedActFunc::TANH:
+ return circle::ActivationFunctionType_TANH;
+ case luci::FusedActFunc::SIGN_BIT:
+ return circle::ActivationFunctionType_SIGN_BIT;
+ default:
+ INTERNAL_EXN_V("trying to convert unsupported luci::FusedActFunc", oops::to_uint32(func));
+ }
+}
+
+circle::TensorType to_circle_tensortype(loco::DataType type)
+{
+ switch (type)
+ {
+ case loco::DataType::U8:
+ return circle::TensorType_UINT8;
+
+ case loco::DataType::S8:
+ return circle::TensorType_INT8;
+ case loco::DataType::S16:
+ return circle::TensorType_INT16;
+ case loco::DataType::S32:
+ return circle::TensorType_INT32;
+ case loco::DataType::S64:
+ return circle::TensorType_INT64;
+
+ case loco::DataType::FLOAT16:
+ return circle::TensorType_FLOAT16;
+ case loco::DataType::FLOAT32:
+ return circle::TensorType_FLOAT32;
+
+ case loco::DataType::BOOL:
+ return circle::TensorType_BOOL;
+
+ default:
+ INTERNAL_EXN_V("failed to convert unsupported loco::DataType", oops::to_uint32(type));
+ }
+}
+
+circle::MirrorPadMode to_circle_mirrorpadmode(luci::MirrorPadMode mode)
+{
+ switch (mode)
+ {
+ case luci::MirrorPadMode::REFLECT:
+ return circle::MirrorPadMode::MirrorPadMode_REFLECT;
+ case luci::MirrorPadMode::SYMMETRIC:
+ return circle::MirrorPadMode::MirrorPadMode_SYMMETRIC;
+ default:
+ INTERNAL_EXN_V("trying to convert unsupported luci::MirrorPadMode", oops::to_uint32(mode));
+ }
+}
+
+circle::DimensionType to_circle_dimensiontype(luci::DimensionType type)
+{
+ switch (type)
+ {
+ case luci::DimensionType::DENSE:
+ return circle::DimensionType_DENSE;
+ case luci::DimensionType::SPARSE_CSR:
+ return circle::DimensionType_SPARSE_CSR;
+ default:
+ INTERNAL_EXN_V("trying to convert unsupported luci::DimensionType", oops::to_uint32(type));
+ }
+}
+
+flatbuffers::Offset<void> to_circle_sparse_index_vector(flatbuffers::FlatBufferBuilder &fb,
+ const SparseIndexVector &sparse_idx_vec)
+{
+ auto type = sparse_idx_vec.type();
+ switch (type)
+ {
+ case luci::SparseIndexVectorType::NONE:
+ return flatbuffers::Offset<void>();
+ case luci::SparseIndexVectorType::I32:
+ {
+ return circle::CreateInt32VectorDirect(fb, sparse_idx_vec.as_int32_vector()).Union();
+ }
+ case luci::SparseIndexVectorType::U16:
+ {
+ return circle::CreateUint16VectorDirect(fb, sparse_idx_vec.as_uint16_vector()).Union();
+ }
+ case luci::SparseIndexVectorType::U8:
+ {
+ return circle::CreateUint8VectorDirect(fb, sparse_idx_vec.as_uint8_vector()).Union();
+ }
+ default:
+ INTERNAL_EXN_V("trying to convert unsupported luci::SparseIndexVectorType",
+ oops::to_uint32(type));
+ }
+}
+
+circle::SparseIndexVector to_circle_sparse_index_vector_type(luci::SparseIndexVectorType type)
+{
+ switch (type)
+ {
+ case luci::SparseIndexVectorType::NONE:
+ return circle::SparseIndexVector_NONE;
+ case luci::SparseIndexVectorType::I32:
+ return circle::SparseIndexVector_Int32Vector;
+ case luci::SparseIndexVectorType::U16:
+ return circle::SparseIndexVector_Uint16Vector;
+ case luci::SparseIndexVectorType::U8:
+ return circle::SparseIndexVector_Uint8Vector;
+ default:
+ INTERNAL_EXN_V("trying to convert unsupported luci::SparseIndexVectorType",
+ oops::to_uint32(type));
+ }
+}
+
+} // namespace luci
+
+namespace luci
+{
+
+uint32_t SerializedModelData::registerBuiltinOpcode(circle::BuiltinOperator builtin_code,
+ const int32_t op_version)
+{
+ assert(op_version > 0);
+
+ auto it = _operator_codes.find(OpCode{builtin_code, "", op_version});
+ if (it != _operator_codes.end())
+ {
+ return it->second;
+ }
+ auto idx = static_cast<uint32_t>(_operator_codes.size());
+ _operator_codes.emplace(OpCode{builtin_code, "", op_version}, idx);
+ return idx;
+}
+
+uint32_t SerializedModelData::registerCustomOpcode(const std::string &custom_code)
+{
+ const circle::BuiltinOperator builtin_code = circle::BuiltinOperator_CUSTOM;
+ auto it = _operator_codes.find(OpCode{builtin_code, custom_code});
+ if (it != _operator_codes.end())
+ {
+ return it->second;
+ }
+ auto idx = static_cast<uint32_t>(_operator_codes.size());
+ _operator_codes.emplace(OpCode{builtin_code, custom_code}, idx);
+ return idx;
+}
+
+circle::Padding getOpPadding(const loco::Padding2D *pad, const loco::Stride<2> *stride,
+ const ShapeDescription &ifm, const ShapeDescription &ofm)
+{
+ // VALID padding
+ if (pad->top() == 0 && pad->bottom() == 0 && pad->left() == 0 && pad->right() == 0)
+ return circle::Padding_VALID;
+
+ // SAME padding
+ //
+ // For same padding, by definition, following equation should hold:
+ // O = floor((I - 1) / S) + 1
+ // where input size I, output size O, stride S
+ //
+ // NOTE input and output 'feature' map are shape of NHWC
+ bool same_padding_criterion_1 =
+ (static_cast<uint32_t>(ofm._dims[1]) == (ifm._dims[1] - 1) / stride->vertical() + 1) &&
+ (static_cast<uint32_t>(ofm._dims[2]) == (ifm._dims[2] - 1) / stride->horizontal() + 1);
+
+ // For same padding, rear padding is same or bigger than front padding by at most 1
+ bool same_padding_criterion_2 =
+ (pad->top() <= pad->bottom()) && (pad->bottom() <= pad->top() + 1) &&
+ (pad->left() <= pad->right()) && (pad->right() <= pad->left() + 1);
+
+ if (same_padding_criterion_1 && same_padding_criterion_2)
+ return circle::Padding_SAME;
+
+ INTERNAL_EXN("Unsupported padding criteria");
+}
+
+circle::Padding getOpPadding(const luci::Padding pad)
+{
+ if (pad == luci::Padding::VALID)
+ return circle::Padding_VALID;
+ if (pad == luci::Padding::SAME)
+ return circle::Padding_SAME;
+
+ INTERNAL_EXN_V("Unsupported luci::Padding", oops::to_uint32(pad));
+}
+
+namespace
+{
+
+class CircleTensorIndexAnnotation final : public loco::NodeAnnotation
+{
+public:
+ CircleTensorIndexAnnotation(const CircleTensorIndex &index) : _index{index}
+ {
+ // DO NOTHING
+ }
+
+public:
+ const CircleTensorIndex &index(void) const { return _index; }
+
+private:
+ CircleTensorIndex _index;
+};
+
+} // namespace
+
+void set_tensor_index(loco::Node *node, const CircleTensorIndex &tensor_id)
+{
+ assert(node->annot<CircleTensorIndexAnnotation>() == nullptr);
+ node->annot(std::make_unique<CircleTensorIndexAnnotation>(tensor_id));
+}
+
+CircleTensorIndex get_tensor_index(loco::Node *node)
+{
+ assert(node->annot<CircleTensorIndexAnnotation>() != nullptr);
+ return node->annot<CircleTensorIndexAnnotation>()->index();
+}
+
+} // namespace luci
diff --git a/compiler/luci/export/src/CircleExporterUtils.h b/compiler/luci/export/src/CircleExporterUtils.h
new file mode 100644
index 000000000..7857213b2
--- /dev/null
+++ b/compiler/luci/export/src/CircleExporterUtils.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_EXPORTER_UTILS_H__
+#define __CIRCLE_EXPORTER_UTILS_H__
+
+#include "SerializedData.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/Service/ShapeDescription.h>
+
+#include <loco.h>
+
+#include <mio/circle/schema_generated.h>
+
+namespace luci
+{
+
+circle::ActivationFunctionType to_circle_actfunc(luci::FusedActFunc func);
+circle::TensorType to_circle_tensortype(loco::DataType type);
+circle::MirrorPadMode to_circle_mirrorpadmode(luci::MirrorPadMode mode);
+circle::DimensionType to_circle_dimensiontype(luci::DimensionType type);
+flatbuffers::Offset<void> to_circle_sparse_index_vector(flatbuffers::FlatBufferBuilder &fb,
+ const SparseIndexVector &sparse_idx_vec);
+circle::SparseIndexVector to_circle_sparse_index_vector_type(luci::SparseIndexVectorType type);
+
+} // namespace luci
+
+namespace luci
+{
+
+circle::Padding getOpPadding(const loco::Padding2D *pad, const loco::Stride<2> *stride,
+ const ShapeDescription &ifm, const ShapeDescription &ofm);
+circle::Padding getOpPadding(const luci::Padding pad);
+
+using CircleTensorIndex = int32_t;
+
+void set_tensor_index(loco::Node *node, const CircleTensorIndex &tensor_id);
+CircleTensorIndex get_tensor_index(loco::Node *node);
+
+} // namespace luci
+
+#endif // __CIRCLE_EXPORTER_UTILS_H__
diff --git a/compiler/luci/export/src/CircleOperationExporter.cpp b/compiler/luci/export/src/CircleOperationExporter.cpp
new file mode 100644
index 000000000..c937109cd
--- /dev/null
+++ b/compiler/luci/export/src/CircleOperationExporter.cpp
@@ -0,0 +1,1464 @@
+/*
+ * 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 "CircleOperationExporter.h"
+#include "CircleExporterUtils.h"
+#include "Check.h"
+
+#include <luci/IR/CircleNode.h>
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/CircleNodeVisitor.h>
+#include <luci/Service/CircleShapeInference.h>
+#include <luci/UserSettings.h>
+#include <luci/Log.h>
+
+#include <loco/IR/CanonicalNodeVisitor.h>
+#include <oops/InternalExn.h>
+
+#include <flatbuffers/flexbuffers.h>
+
+using namespace flatbuffers;
+using namespace circle;
+
+namespace
+{
+
+using namespace luci;
+
+struct ExportContext
+{
+ FlatBufferBuilder &builder;
+ SerializedModelData &md;
+ SerializedGraphData &gd;
+};
+
+/**
+ * @brief Exports CircleMaxPool2D or CircleAveragePool2D
+ *
+ * @note CirclePool2D should be one of CircleMaxPool2D or CircleAveragePool2D
+ */
+template <class CirclePool2D>
+void export_pool_2d(ExportContext &ctx, CirclePool2D *node, circle::BuiltinOperator builtin_op)
+{
+ LUCI_ASSERT(builtin_op == circle::BuiltinOperator_MAX_POOL_2D ||
+ builtin_op == circle::BuiltinOperator_L2_POOL_2D ||
+ builtin_op == circle::BuiltinOperator_AVERAGE_POOL_2D,
+ "Should be L2Pool, MaxPool or AvgPool");
+ LUCI_ASSERT(node->padding() != luci::Padding::UNDEFINED, "Padding is not set");
+
+ uint32_t op_idx = ctx.md.registerBuiltinOpcode(builtin_op, node->op_version());
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->value())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = ctx.builder.CreateVector(inputs_vec);
+ auto outputs = ctx.builder.CreateVector(outputs_vec);
+
+ circle::Padding padding = getOpPadding(node->padding());
+
+ auto options = CreatePool2DOptions(ctx.builder, padding, node->stride()->w(), node->stride()->h(),
+ node->filter()->w(), node->filter()->h(),
+ to_circle_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(ctx.builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_Pool2DOptions, options.Union());
+ ctx.gd._operators.push_back(op_offset);
+}
+
+/**
+ * @brief export simple nodes
+ */
+void export_node(ExportContext &ctx, loco::Node *node, circle::BuiltinOperator bop,
+ circle::BuiltinOptions bot, flatbuffers::Offset<void> options_offset)
+{
+ uint32_t op_idx =
+ ctx.md.registerBuiltinOpcode(bop, loco::must_cast<luci::CircleNode *>(node)->op_version());
+ std::vector<int32_t> inputs_vec;
+ std::vector<int32_t> outputs_vec{get_tensor_index(node)};
+ for (uint32_t i = 0; i < node->arity(); ++i)
+ inputs_vec.push_back(get_tensor_index(node->arg(i)));
+ auto inputs = ctx.builder.CreateVector(inputs_vec);
+ auto outputs = ctx.builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(ctx.builder, op_idx, inputs, outputs, bot, options_offset);
+ ctx.gd._operators.push_back(op_offset);
+}
+
+/**
+ * @brief export simple nodes having void options
+ */
+void export_node(ExportContext &ctx, loco::Node *node, circle::BuiltinOperator bop)
+{
+ uint32_t op_idx =
+ ctx.md.registerBuiltinOpcode(bop, loco::must_cast<luci::CircleNode *>(node)->op_version());
+ std::vector<int32_t> inputs_vec;
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ for (uint32_t i = 0; i < node->arity(); ++i)
+ inputs_vec.push_back(get_tensor_index(node->arg(i)));
+ auto inputs = ctx.builder.CreateVector(inputs_vec);
+ auto outputs = ctx.builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(ctx.builder, op_idx, inputs, outputs);
+ ctx.gd._operators.push_back(op_offset);
+}
+
+void export_node(ExportContext &ctx, luci::CircleAddN *node)
+{
+ uint32_t op_idx = ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_ADD_N, node->op_version());
+ std::vector<int32_t> inputs_vec;
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+
+ for (uint32_t i = 0; i < node->arity(); ++i)
+ inputs_vec.push_back(get_tensor_index(node->inputs(i)));
+
+ auto inputs = ctx.builder.CreateVector(inputs_vec);
+ auto outputs = ctx.builder.CreateVector(outputs_vec);
+ auto options = CreateAddNOptions(ctx.builder);
+ auto op_offset = CreateOperator(ctx.builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_AddNOptions, options.Union());
+ ctx.gd._operators.push_back(op_offset);
+}
+
+void export_node(ExportContext &ctx, luci::CircleCast *node)
+{
+ uint32_t op_idx = ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_CAST, node->op_version());
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = ctx.builder.CreateVector(inputs_vec);
+ auto outputs = ctx.builder.CreateVector(outputs_vec);
+
+ flatbuffers::Offset<Operator> op_offset;
+ if (node->out_data_type() != loco::DataType::Unknown)
+ {
+ auto options = CreateCastOptions(ctx.builder, to_circle_tensortype(node->in_data_type()),
+ to_circle_tensortype(node->out_data_type()));
+ op_offset = CreateOperator(ctx.builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_CastOptions, options.Union());
+ }
+ else
+ {
+ op_offset = CreateOperator(ctx.builder, op_idx, inputs, outputs);
+ }
+ ctx.gd._operators.push_back(op_offset);
+}
+
+void export_node(ExportContext &ctx, luci::CircleConcatenation *node)
+{
+ uint32_t op_idx =
+ ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_CONCATENATION, node->op_version());
+ std::vector<int32_t> inputs_vec;
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+
+ for (uint32_t i = 0; i < node->numValues(); ++i)
+ inputs_vec.push_back(get_tensor_index(node->values(i)));
+
+ auto inputs = ctx.builder.CreateVector(inputs_vec);
+ auto outputs = ctx.builder.CreateVector(outputs_vec);
+ auto options = CreateConcatenationOptions(ctx.builder, node->axis(),
+ to_circle_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(ctx.builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_ConcatenationOptions, options.Union());
+ ctx.gd._operators.push_back(op_offset);
+}
+
+void export_node(ExportContext &ctx, luci::CircleCustom *node)
+{
+ auto custom_outputs = loco::succs(node);
+
+ uint32_t op_idx = ctx.md.registerCustomOpcode(node->custom_code());
+ std::vector<int32_t> inputs_vec;
+ std::vector<int32_t> outputs_vec;
+
+ for (uint32_t index = 0; index < node->numInputs(); index++)
+ {
+ inputs_vec.push_back(get_tensor_index(node->inputs(index)));
+ }
+ for (uint32_t index = 0; index < custom_outputs.size(); index++)
+ {
+ // store in order of index
+ bool found = false;
+ for (auto out : custom_outputs)
+ {
+ auto custom_out = loco::must_cast<luci::CircleCustomOut *>(out);
+ if (custom_out->index() == static_cast<int32_t>(index))
+ {
+ outputs_vec.push_back(get_tensor_index(custom_out));
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ INTERNAL_EXN("Invalid Custom output");
+ }
+ }
+
+ auto inputs = ctx.builder.CreateVector(inputs_vec);
+ auto outputs = ctx.builder.CreateVector(outputs_vec);
+ flatbuffers::Offset<flatbuffers::Vector<uint8_t>> circle_custom_options;
+ std::vector<uint8_t> custom_options_vec{node->custom_options().begin(),
+ node->custom_options().end()};
+ circle_custom_options = ctx.builder.CreateVector(custom_options_vec);
+ auto op_offset = CreateOperator(ctx.builder, op_idx, inputs, outputs, circle::BuiltinOptions_NONE,
+ flatbuffers::Offset<void>(), circle_custom_options);
+ ctx.gd._operators.push_back(op_offset);
+}
+
+void export_node(ExportContext &ctx, luci::CircleIf *node)
+{
+ auto if_outs = loco::succs(node);
+ assert(if_outs.size() == node->output_count());
+
+ uint32_t op_idx = ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_IF, node->op_version());
+ std::vector<int32_t> inputs_vec;
+ std::vector<int32_t> outputs_vec;
+
+ inputs_vec.push_back(get_tensor_index(node->cond()));
+ for (uint32_t idx = 0; idx < node->input_count(); ++idx)
+ inputs_vec.push_back(get_tensor_index(node->input(idx)));
+
+ for (uint32_t idx = 0; idx < node->output_count(); ++idx)
+ {
+ // store in order of index
+ bool found = false;
+ for (auto out : if_outs)
+ {
+ auto if_out = loco::must_cast<luci::CircleIfOut *>(out);
+ if (if_out->index() == static_cast<int32_t>(idx))
+ {
+ outputs_vec.push_back(get_tensor_index(if_out));
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ INTERNAL_EXN("Invalid CircleIf output");
+ }
+ }
+
+ auto inputs = ctx.builder.CreateVector(inputs_vec);
+ auto outputs = ctx.builder.CreateVector(outputs_vec);
+ auto options = CreateIfOptions(ctx.builder, node->then_branch(), node->else_branch());
+ auto op_offset = CreateOperator(ctx.builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_IfOptions, options.Union());
+ ctx.gd._operators.push_back(op_offset);
+}
+
+void export_node(ExportContext &ctx, luci::CircleNonMaxSuppressionV4 *node)
+{
+ auto nms_outs = loco::succs(node);
+ assert(nms_outs.size() == 2);
+
+ uint32_t op_idx = ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_NON_MAX_SUPPRESSION_V4,
+ node->op_version());
+ std::vector<int32_t> inputs_vec{
+ get_tensor_index(node->boxes()), get_tensor_index(node->scores()),
+ get_tensor_index(node->max_output_size()), get_tensor_index(node->iou_threshold()),
+ get_tensor_index(node->score_threshold()),
+ };
+ std::vector<int32_t> outputs_vec;
+
+ for (uint32_t idx = 0; idx < nms_outs.size(); ++idx)
+ {
+ // store in order of index
+ bool found = false;
+ for (auto out : nms_outs)
+ {
+ auto nms_out = loco::must_cast<luci::CircleNonMaxSuppressionV4Out *>(out);
+ if (nms_out->index() == static_cast<int32_t>(idx))
+ {
+ outputs_vec.push_back(get_tensor_index(nms_out));
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ INTERNAL_EXN("Invalid NonMaxSuppressionV4 output");
+ }
+ }
+
+ auto inputs = ctx.builder.CreateVector(inputs_vec);
+ auto outputs = ctx.builder.CreateVector(outputs_vec);
+ auto options = CreateNonMaxSuppressionV4Options(ctx.builder);
+ auto op_offset =
+ CreateOperator(ctx.builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_NonMaxSuppressionV4Options, options.Union());
+ ctx.gd._operators.push_back(op_offset);
+}
+
+void export_node(ExportContext &ctx, luci::CircleNonMaxSuppressionV5 *node)
+{
+ auto nms_outs = loco::succs(node);
+ assert(nms_outs.size() == 3);
+
+ uint32_t op_idx = ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_NON_MAX_SUPPRESSION_V5,
+ node->op_version());
+ std::vector<int32_t> inputs_vec{
+ get_tensor_index(node->boxes()), get_tensor_index(node->scores()),
+ get_tensor_index(node->max_output_size()), get_tensor_index(node->iou_threshold()),
+ get_tensor_index(node->score_threshold()), get_tensor_index(node->soft_nms_sigma()),
+ };
+ std::vector<int32_t> outputs_vec;
+
+ for (uint32_t idx = 0; idx < nms_outs.size(); ++idx)
+ {
+ // store in order of index
+ bool found = false;
+ for (auto out : nms_outs)
+ {
+ auto nms_out = loco::must_cast<luci::CircleNonMaxSuppressionV5Out *>(out);
+ if (nms_out->index() == static_cast<int32_t>(idx))
+ {
+ outputs_vec.push_back(get_tensor_index(nms_out));
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ INTERNAL_EXN("Invalid NonMaxSuppressionV5 output");
+ }
+ }
+
+ auto inputs = ctx.builder.CreateVector(inputs_vec);
+ auto outputs = ctx.builder.CreateVector(outputs_vec);
+ auto options = CreateNonMaxSuppressionV5Options(ctx.builder);
+ auto op_offset =
+ CreateOperator(ctx.builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_NonMaxSuppressionV5Options, options.Union());
+ ctx.gd._operators.push_back(op_offset);
+}
+
+void export_node(ExportContext &ctx, luci::CircleReverseV2 *node)
+{
+ uint32_t op_idx =
+ ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_REVERSE_V2, node->op_version());
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->tensor()), get_tensor_index(node->axis())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = ctx.builder.CreateVector(inputs_vec);
+ auto outputs = ctx.builder.CreateVector(outputs_vec);
+ auto options = CreateReverseV2Options(ctx.builder);
+ auto op_offset = CreateOperator(ctx.builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_ReverseSequenceOptions, options.Union());
+ ctx.gd._operators.push_back(op_offset);
+}
+
+void export_node(ExportContext &ctx, luci::CircleSplit *node)
+{
+ auto split_outs = loco::succs(node);
+ assert(int32_t(split_outs.size()) == node->num_split());
+
+ uint32_t op_idx = ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_SPLIT, node->op_version());
+ // NOTE BuiltinOperator_SPLIT input is placed at second position
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->split_dim()),
+ get_tensor_index(node->input())};
+ std::vector<int32_t> outputs_vec;
+
+ for (int32_t index = 0; index < node->num_split(); index++)
+ {
+ // store in order of index
+ bool found = false;
+ for (auto out : split_outs)
+ {
+ auto split_out = loco::must_cast<luci::CircleSplitOut *>(out);
+ if (split_out->index() == index)
+ {
+ outputs_vec.push_back(get_tensor_index(split_out));
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ INTERNAL_EXN("Invalid Split output");
+ }
+ }
+
+ auto inputs = ctx.builder.CreateVector(inputs_vec);
+ auto outputs = ctx.builder.CreateVector(outputs_vec);
+ auto options = CreateSplitOptions(ctx.builder, node->num_split());
+ auto op_offset = CreateOperator(ctx.builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_SplitOptions, options.Union());
+ ctx.gd._operators.push_back(op_offset);
+}
+
+void export_node(ExportContext &ctx, luci::CircleSplitV *node)
+{
+ auto split_outs = loco::succs(node);
+ assert(int32_t(split_outs.size()) == node->num_split());
+
+ uint32_t op_idx =
+ ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_SPLIT_V, node->op_version());
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()),
+ get_tensor_index(node->size_splits()),
+ get_tensor_index(node->split_dim())};
+ std::vector<int32_t> outputs_vec;
+
+ for (int32_t index = 0; index < node->num_split(); index++)
+ {
+ // store in order of index
+ bool found = false;
+ for (auto out : split_outs)
+ {
+ auto split_out = loco::must_cast<luci::CircleSplitVOut *>(out);
+ if (split_out->index() == index)
+ {
+ outputs_vec.push_back(get_tensor_index(split_out));
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ INTERNAL_EXN("Invalid SplitV output");
+ }
+ }
+
+ auto inputs = ctx.builder.CreateVector(inputs_vec);
+ auto outputs = ctx.builder.CreateVector(outputs_vec);
+ auto options = CreateSplitVOptions(ctx.builder, node->num_split());
+ auto op_offset = CreateOperator(ctx.builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_SplitVOptions, options.Union());
+ ctx.gd._operators.push_back(op_offset);
+}
+
+void export_node(ExportContext &ctx, luci::CircleTopKV2 *node)
+{
+ auto topkv2_outs = loco::succs(node);
+ int outs_count = int32_t(topkv2_outs.size());
+ assert(outs_count == 2);
+
+ uint32_t op_idx =
+ ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_TOPK_V2, node->op_version());
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->k())};
+ std::vector<int32_t> outputs_vec;
+
+ for (int32_t index = 0; index < outs_count; index++)
+ {
+ // store in order of index
+ bool found = false;
+ for (auto out : topkv2_outs)
+ {
+ auto topkv2_out = loco::must_cast<luci::CircleTopKV2Out *>(out);
+ if (topkv2_out->index() == index)
+ {
+ outputs_vec.push_back(get_tensor_index(topkv2_out));
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ INTERNAL_EXN("Invalid TopKV2 output");
+ }
+ }
+
+ auto inputs = ctx.builder.CreateVector(inputs_vec);
+ auto outputs = ctx.builder.CreateVector(outputs_vec);
+ auto options = CreateTopKV2Options(ctx.builder);
+ auto op_offset = CreateOperator(ctx.builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_TopKV2Options, options.Union());
+ ctx.gd._operators.push_back(op_offset);
+}
+
+void export_node(ExportContext &ctx, luci::CircleUnique *node)
+{
+ auto unique_outs = loco::succs(node);
+ assert(int32_t(unique_outs.size()) == 2);
+ uint32_t op_idx =
+ ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_UNIQUE, node->op_version());
+
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
+ std::vector<int32_t> outputs_vec;
+
+ for (int32_t index = 0; index < 2; index++)
+ {
+ // store in order of index
+ bool found = false;
+ for (auto out : unique_outs)
+ {
+ auto unique_out = loco::must_cast<luci::CircleUniqueOut *>(out);
+ if (unique_out->index() == index)
+ {
+ outputs_vec.push_back(get_tensor_index(unique_out));
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ INTERNAL_EXN("Invalid Unique output");
+ }
+ }
+
+ auto inputs = ctx.builder.CreateVector(inputs_vec);
+ auto outputs = ctx.builder.CreateVector(outputs_vec);
+ auto options = CreateUniqueOptions(ctx.builder, to_circle_tensortype(node->idx_out_type()));
+ auto op_offset = CreateOperator(ctx.builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_UniqueOptions, options.Union());
+ ctx.gd._operators.push_back(op_offset);
+}
+
+void export_node(ExportContext &ctx, luci::CircleUnpack *node)
+{
+ LOGGER(l);
+ auto settings = luci::UserSettings::settings();
+
+ auto unpack_outs = loco::succs(node);
+ // NOTE real models may not use all of the outputs
+ if (static_cast<int32_t>(unpack_outs.size()) != node->num())
+ {
+ if (settings->get(luci::UserSettings::Key::DisableValidation))
+ {
+ WARN(l) << "Warning: export Unpack(" << node->name() << ") 'num' not same as outputs";
+ }
+ else
+ assert(false);
+ }
+
+ uint32_t op_idx =
+ ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_UNPACK, node->op_version());
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->value())};
+ std::vector<int32_t> outputs_vec;
+
+ for (int32_t index = 0; index < node->num(); index++)
+ {
+ // store in order of index
+ bool found = false;
+ for (auto out : unpack_outs)
+ {
+ auto unpack_out = loco::must_cast<luci::CircleUnpackOut *>(out);
+ if (unpack_out->index() == index)
+ {
+ outputs_vec.push_back(get_tensor_index(unpack_out));
+ found = true;
+ break;
+ }
+ }
+ // NOTE real models may not use all of the outputs
+ if (!found)
+ {
+ if (settings->get(luci::UserSettings::Key::DisableValidation))
+ {
+ WARN(l) << "Warning: export Unpack(" << node->name() << ") output " << index << " not used";
+ }
+ else
+ assert(false);
+ }
+ }
+
+ auto inputs = ctx.builder.CreateVector(inputs_vec);
+ auto outputs = ctx.builder.CreateVector(outputs_vec);
+ auto options = CreateUnpackOptions(ctx.builder, node->num(), node->axis());
+ auto op_offset = CreateOperator(ctx.builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_UnpackOptions, options.Union());
+ ctx.gd._operators.push_back(op_offset);
+}
+
+void export_node(ExportContext &ctx, luci::CircleWhile *node)
+{
+ auto while_outs = loco::succs(node);
+ assert(while_outs.size() == node->output_count());
+
+ uint32_t op_idx = ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_WHILE, node->op_version());
+ std::vector<int32_t> inputs_vec;
+ std::vector<int32_t> outputs_vec;
+
+ for (uint32_t idx = 0; idx < node->input_count(); ++idx)
+ inputs_vec.push_back(get_tensor_index(node->input(idx)));
+
+ for (uint32_t idx = 0; idx < node->output_count(); ++idx)
+ {
+ // store in order of index
+ bool found = false;
+ for (auto out : while_outs)
+ {
+ auto while_out = loco::must_cast<luci::CircleWhileOut *>(out);
+ if (while_out->index() == static_cast<int32_t>(idx))
+ {
+ outputs_vec.push_back(get_tensor_index(while_out));
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ INTERNAL_EXN("Invalid CircleWhile output");
+ }
+ }
+
+ auto inputs = ctx.builder.CreateVector(inputs_vec);
+ auto outputs = ctx.builder.CreateVector(outputs_vec);
+ auto options = CreateWhileOptions(ctx.builder, node->cond_branch(), node->body_branch());
+ auto op_offset = CreateOperator(ctx.builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_WhileOptions, options.Union());
+ ctx.gd._operators.push_back(op_offset);
+}
+
+class OperationExporter final : public luci::CircleNodeMutableVisitor<void>,
+ public loco::CanonicalNodeMutableVisitor<void>
+{
+public:
+ OperationExporter(ExportContext &ctx) : _ctx{ctx}
+ {
+ // DO NOTHING
+ }
+
+public:
+ void visit(luci::CircleAbs *) final;
+ void visit(luci::CircleAdd *) final;
+ void visit(luci::CircleAddN *) final;
+ void visit(luci::CircleArgMax *) final;
+ void visit(luci::CircleArgMin *) final;
+ void visit(luci::CircleAveragePool2D *) final;
+ void visit(luci::CircleBatchMatMul *) final;
+ void visit(luci::CircleBatchToSpaceND *) final;
+ void visit(luci::CircleCast *) final;
+ void visit(luci::CircleCeil *) final;
+ void visit(luci::CircleConcatenation *) final;
+ void visit(luci::CircleConst *) final{/* skip, everything is done in exportOpDefinedTensors */};
+ void visit(luci::CircleConv2D *) final;
+ void visit(luci::CircleCos *) final;
+ void visit(luci::CircleCustom *) final;
+ void visit(luci::CircleDepthToSpace *) final;
+ void visit(luci::CircleDepthwiseConv2D *) final;
+ void visit(luci::CircleDequantize *) final;
+ void visit(luci::CircleDiv *) final;
+ void visit(luci::CircleElu *) final;
+ void visit(luci::CircleEqual *) final;
+ void visit(luci::CircleExp *) final;
+ void visit(luci::CircleExpandDims *) final;
+ void visit(luci::CircleFill *) final;
+ void visit(luci::CircleFloor *) final;
+ void visit(luci::CircleFloorDiv *) final;
+ void visit(luci::CircleFloorMod *) final;
+ void visit(luci::CircleFullyConnected *) final;
+ void visit(luci::CircleGather *) final;
+ void visit(luci::CircleGatherNd *) final;
+ void visit(luci::CircleGreater *) final;
+ void visit(luci::CircleGreaterEqual *) final;
+ void visit(luci::CircleIf *) final;
+ void visit(luci::CircleL2Normalize *) final;
+ void visit(luci::CircleL2Pool2D *) final;
+ void visit(luci::CircleLeakyRelu *) final;
+ void visit(luci::CircleLess *) final;
+ void visit(luci::CircleLessEqual *) final;
+ void visit(luci::CircleLocalResponseNormalization *) final;
+ void visit(luci::CircleLog *) final;
+ void visit(luci::CircleLogicalAnd *) final;
+ void visit(luci::CircleLogicalNot *) final;
+ void visit(luci::CircleLogicalOr *) final;
+ void visit(luci::CircleLogistic *) final;
+ void visit(luci::CircleLogSoftmax *) final;
+ void visit(luci::CircleMatrixDiag *) final;
+ void visit(luci::CircleMatrixSetDiag *) final;
+ void visit(luci::CircleMaximum *) final;
+ void visit(luci::CircleMaxPool2D *) final;
+ void visit(luci::CircleMean *) final;
+ void visit(luci::CircleMinimum *) final;
+ void visit(luci::CircleMirrorPad *) final;
+ void visit(luci::CircleMul *) final;
+ void visit(luci::CircleNeg *) final;
+ void visit(luci::CircleNonMaxSuppressionV4 *) final;
+ void visit(luci::CircleNonMaxSuppressionV5 *) final;
+ void visit(luci::CircleNotEqual *) final;
+ void visit(luci::CircleOneHot *) final;
+ void visit(luci::CirclePack *) final;
+ void visit(luci::CirclePad *) final;
+ void visit(luci::CirclePadV2 *) final;
+ void visit(luci::CirclePow *) final;
+ void visit(luci::CirclePRelu *) final;
+ void visit(luci::CircleRange *) final;
+ void visit(luci::CircleRank *) final;
+ void visit(luci::CircleReduceAny *) final;
+ void visit(luci::CircleReduceMax *) final;
+ void visit(luci::CircleReduceMin *) final;
+ void visit(luci::CircleReduceProd *) final;
+ void visit(luci::CircleRelu *) final;
+ void visit(luci::CircleRelu6 *) final;
+ void visit(luci::CircleReluN1To1 *) final;
+ void visit(luci::CircleReshape *) final;
+ void visit(luci::CircleResizeBilinear *) final;
+ void visit(luci::CircleResizeNearestNeighbor *) final;
+ void visit(luci::CircleReverseSequence *) final;
+ void visit(luci::CircleReverseV2 *) final;
+ void visit(luci::CircleRound *) final;
+ void visit(luci::CircleRsqrt *) final;
+ void visit(luci::CircleScatterNd *) final;
+ void visit(luci::CircleSegmentSum *) final;
+ void visit(luci::CircleSelect *) final;
+ void visit(luci::CircleSelectV2 *) final;
+ void visit(luci::CircleShape *) final;
+ void visit(luci::CircleSin *) final;
+ void visit(luci::CircleSlice *) final;
+ void visit(luci::CircleSoftmax *) final;
+ void visit(luci::CircleSpaceToBatchND *) final;
+ void visit(luci::CircleSpaceToDepth *) final;
+ void visit(luci::CircleSparseToDense *) final;
+ void visit(luci::CircleSplit *) final;
+ void visit(luci::CircleSplitV *) final;
+ void visit(luci::CircleSqrt *) final;
+ void visit(luci::CircleSquare *) final;
+ void visit(luci::CircleSquaredDifference *) final;
+ void visit(luci::CircleSqueeze *) final;
+ void visit(luci::CircleStridedSlice *) final;
+ void visit(luci::CircleSub *) final;
+ void visit(luci::CircleSum *) final;
+ void visit(luci::CircleTanh *) final;
+ void visit(luci::CircleTile *) final;
+ void visit(luci::CircleTopKV2 *) final;
+ void visit(luci::CircleTranspose *) final;
+ void visit(luci::CircleTransposeConv *) final;
+ void visit(luci::CircleUnidirectionalSequenceLSTM *) final;
+ void visit(luci::CircleUnique *) final;
+ void visit(luci::CircleUnpack *) final;
+ void visit(luci::CircleWhere *) final;
+ void visit(luci::CircleWhile *) final;
+ void visit(luci::CircleZerosLike *) final;
+ // Circle only
+ void visit(luci::CircleBCQFullyConnected *) final;
+ void visit(luci::CircleBCQGather *) final;
+ void visit(luci::CircleInstanceNorm *) final;
+ // Virtual
+ void visit(luci::CircleInput *) final {}
+ void visit(luci::CircleOutput *) final {}
+ void visit(luci::CircleOutputDummy *) final {}
+ void visit(luci::CircleOutputExclude *) final {}
+ // Virtual for multiple-outputs
+ void visit(luci::CircleCustomOut *) final {}
+ void visit(luci::CircleIfOut *) final {}
+ void visit(luci::CircleNonMaxSuppressionV4Out *) final {}
+ void visit(luci::CircleNonMaxSuppressionV5Out *) final {}
+ void visit(luci::CircleSplitOut *) final {}
+ void visit(luci::CircleSplitVOut *) final {}
+ void visit(luci::CircleTopKV2Out *) final {}
+ void visit(luci::CircleUniqueOut *) final {}
+ void visit(luci::CircleUnpackOut *) final {}
+ void visit(luci::CircleWhileOut *) final {}
+
+private:
+ /**
+ * @brief export simple nodes
+ */
+ void export_simple(loco::Node *node, circle::BuiltinOperator bop, circle::BuiltinOptions bot,
+ flatbuffers::Offset<void> options_offset);
+
+ /**
+ * @brief export simple nodes having void options
+ */
+ void export_simple(loco::Node *node, circle::BuiltinOperator bop);
+
+private:
+ ExportContext &_ctx;
+};
+
+void OperationExporter::export_simple(loco::Node *node, circle::BuiltinOperator bop,
+ circle::BuiltinOptions bot,
+ flatbuffers::Offset<void> options_offset)
+{
+ export_node(_ctx, node, bop, bot, options_offset);
+}
+
+void OperationExporter::export_simple(loco::Node *node, circle::BuiltinOperator bop)
+{
+ export_node(_ctx, node, bop);
+}
+
+void OperationExporter::visit(luci::CircleAbs *node)
+{
+ export_simple(node, circle::BuiltinOperator_ABS, circle::BuiltinOptions_AbsOptions,
+ CreateAbsOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleAdd *node)
+{
+ export_simple(
+ node, circle::BuiltinOperator_ADD, circle::BuiltinOptions_AddOptions,
+ CreateAddOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
+}
+
+void OperationExporter::visit(luci::CircleAddN *node) { export_node(_ctx, node); }
+
+void OperationExporter::visit(luci::CircleArgMax *node)
+{
+ export_simple(
+ node, circle::BuiltinOperator_ARG_MAX, circle::BuiltinOptions_ArgMaxOptions,
+ CreateArgMaxOptions(_ctx.builder, to_circle_tensortype(node->output_type())).Union());
+}
+
+void OperationExporter::visit(luci::CircleArgMin *node)
+{
+ export_simple(
+ node, circle::BuiltinOperator_ARG_MIN, circle::BuiltinOptions_ArgMinOptions,
+ CreateArgMinOptions(_ctx.builder, to_circle_tensortype(node->output_type())).Union());
+}
+
+void OperationExporter::visit(luci::CircleAveragePool2D *node)
+{
+ export_pool_2d<luci::CircleAveragePool2D>(_ctx, node, circle::BuiltinOperator_AVERAGE_POOL_2D);
+}
+
+void OperationExporter::visit(luci::CircleBatchMatMul *node)
+{
+ export_simple(node, circle::BuiltinOperator_BATCH_MATMUL,
+ circle::BuiltinOptions_BatchMatMulOptions,
+ CreateBatchMatMulOptions(_ctx.builder, node->adj_x(), node->adj_y()).Union());
+}
+
+void OperationExporter::visit(luci::CircleCast *node) { export_node(_ctx, node); }
+
+void OperationExporter::visit(luci::CircleCeil *node)
+{
+ export_simple(node, circle::BuiltinOperator_CEIL);
+}
+
+void OperationExporter::visit(luci::CircleConcatenation *node) { export_node(_ctx, node); }
+
+void OperationExporter::visit(luci::CircleBatchToSpaceND *node)
+{
+ export_simple(node, circle::BuiltinOperator_BATCH_TO_SPACE_ND,
+ circle::BuiltinOptions_BatchToSpaceNDOptions,
+ CreateBatchToSpaceNDOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleConv2D *node)
+{
+ export_simple(node, circle::BuiltinOperator_CONV_2D, circle::BuiltinOptions_Conv2DOptions,
+ CreateConv2DOptions(_ctx.builder, getOpPadding(node->padding()),
+ node->stride()->w(), node->stride()->h(),
+ to_circle_actfunc(node->fusedActivationFunction()),
+ node->dilation()->w(), node->dilation()->h())
+ .Union());
+}
+
+void OperationExporter::visit(luci::CircleCos *node)
+{
+ export_simple(node, circle::BuiltinOperator_COS, circle::BuiltinOptions_CosOptions,
+ CreateCosOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleCustom *node) { export_node(_ctx, node); }
+
+void OperationExporter::visit(luci::CircleDepthToSpace *node)
+{
+ export_simple(node, circle::BuiltinOperator_DEPTH_TO_SPACE,
+ circle::BuiltinOptions_DepthToSpaceOptions,
+ CreateDepthToSpaceOptions(_ctx.builder, node->block_size()).Union());
+}
+
+void OperationExporter::visit(luci::CircleDepthwiseConv2D *node)
+{
+ export_simple(node, circle::BuiltinOperator_DEPTHWISE_CONV_2D,
+ circle::BuiltinOptions_DepthwiseConv2DOptions,
+ CreateDepthwiseConv2DOptions(_ctx.builder, getOpPadding(node->padding()),
+ node->stride()->w(), node->stride()->h(),
+ node->depthMultiplier(),
+ to_circle_actfunc(node->fusedActivationFunction()),
+ node->dilation()->w(), node->dilation()->h())
+ .Union());
+}
+
+void OperationExporter::visit(luci::CircleDequantize *node)
+{
+ export_simple(node, circle::BuiltinOperator_DEQUANTIZE);
+}
+
+void OperationExporter::visit(luci::CircleDiv *node)
+{
+ export_simple(
+ node, circle::BuiltinOperator_DIV, circle::BuiltinOptions_DivOptions,
+ CreateDivOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
+}
+
+void OperationExporter::visit(luci::CircleElu *node)
+{
+ export_simple(node, circle::BuiltinOperator_ELU);
+}
+
+void OperationExporter::visit(luci::CircleEqual *node)
+{
+ export_simple(node, circle::BuiltinOperator_EQUAL, circle::BuiltinOptions_EqualOptions,
+ CreateEqualOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleExp *node)
+{
+ export_simple(node, circle::BuiltinOperator_EXP, circle::BuiltinOptions_ExpOptions,
+ CreateExpOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleExpandDims *node)
+{
+ export_simple(node, circle::BuiltinOperator_EXPAND_DIMS, circle::BuiltinOptions_ExpandDimsOptions,
+ CreateExpandDimsOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleFill *node)
+{
+ export_simple(node, circle::BuiltinOperator_FILL, circle::BuiltinOptions_FillOptions,
+ CreateFillOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleFloor *node)
+{
+ export_simple(node, circle::BuiltinOperator_FLOOR);
+}
+
+void OperationExporter::visit(luci::CircleFloorDiv *node)
+{
+ export_simple(node, circle::BuiltinOperator_FLOOR_DIV, circle::BuiltinOptions_FloorDivOptions,
+ CreateFloorDivOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleFloorMod *node)
+{
+ export_simple(node, circle::BuiltinOperator_FLOOR_MOD, circle::BuiltinOptions_FloorModOptions,
+ CreateFloorModOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleFullyConnected *node)
+{
+ export_simple(
+ node, circle::BuiltinOperator_FULLY_CONNECTED, circle::BuiltinOptions_FullyConnectedOptions,
+ CreateFullyConnectedOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction()))
+ .Union());
+}
+
+void OperationExporter::visit(luci::CircleGather *node)
+{
+ export_simple(node, circle::BuiltinOperator_GATHER, circle::BuiltinOptions_GatherOptions,
+ CreateGatherOptions(_ctx.builder, node->axis()).Union());
+}
+
+void OperationExporter::visit(luci::CircleGatherNd *node)
+{
+ export_simple(node, circle::BuiltinOperator_GATHER_ND, circle::BuiltinOptions_GatherNdOptions,
+ CreateGatherNdOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleGreater *node)
+{
+ export_simple(node, circle::BuiltinOperator_GREATER, circle::BuiltinOptions_GreaterOptions,
+ CreateGreaterOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleGreaterEqual *node)
+{
+ export_simple(node, circle::BuiltinOperator_GREATER_EQUAL,
+ circle::BuiltinOptions_GreaterEqualOptions,
+ CreateGreaterEqualOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleIf *node) { export_node(_ctx, node); }
+
+void OperationExporter::visit(luci::CircleL2Normalize *node)
+{
+ export_simple(
+ node, circle::BuiltinOperator_L2_NORMALIZATION, circle::BuiltinOptions_L2NormOptions,
+ CreateL2NormOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction()))
+ .Union());
+}
+
+void OperationExporter::visit(luci::CircleL2Pool2D *node)
+{
+ export_pool_2d<luci::CircleL2Pool2D>(_ctx, node, circle::BuiltinOperator_L2_POOL_2D);
+}
+
+void OperationExporter::visit(luci::CircleLeakyRelu *node)
+{
+ export_simple(node, circle::BuiltinOperator_LEAKY_RELU, circle::BuiltinOptions_LeakyReluOptions,
+ CreateLeakyReluOptions(_ctx.builder, node->alpha()).Union());
+}
+
+void OperationExporter::visit(luci::CircleLess *node)
+{
+ export_simple(node, circle::BuiltinOperator_LESS, circle::BuiltinOptions_LessOptions,
+ CreateLessOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleLessEqual *node)
+{
+ export_simple(node, circle::BuiltinOperator_LESS_EQUAL, circle::BuiltinOptions_LessEqualOptions,
+ CreateLessEqualOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleLocalResponseNormalization *node)
+{
+ export_simple(node, circle::BuiltinOperator_LOCAL_RESPONSE_NORMALIZATION,
+ circle::BuiltinOptions_LocalResponseNormalizationOptions,
+ CreateLocalResponseNormalizationOptions(_ctx.builder, node->radius(), node->bias(),
+ node->alpha(), node->beta())
+ .Union());
+}
+
+void OperationExporter::visit(luci::CircleLog *node)
+{
+ export_simple(node, circle::BuiltinOperator_LOG);
+}
+
+void OperationExporter::visit(luci::CircleLogicalAnd *node)
+{
+ export_simple(node, circle::BuiltinOperator_LOGICAL_AND, circle::BuiltinOptions_LogicalAndOptions,
+ CreateLogicalAndOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleLogicalNot *node)
+{
+ export_simple(node, circle::BuiltinOperator_LOGICAL_NOT, circle::BuiltinOptions_LogicalNotOptions,
+ CreateLogicalNotOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleLogicalOr *node)
+{
+ export_simple(node, circle::BuiltinOperator_LOGICAL_OR, circle::BuiltinOptions_LogicalOrOptions,
+ CreateLogicalOrOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleLogistic *node)
+{
+ export_simple(node, circle::BuiltinOperator_LOGISTIC);
+}
+
+void OperationExporter::visit(luci::CircleLogSoftmax *node)
+{
+ export_simple(node, circle::BuiltinOperator_LOG_SOFTMAX, circle::BuiltinOptions_LogSoftmaxOptions,
+ CreateLogSoftmaxOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleMatrixDiag *node)
+{
+ export_simple(node, circle::BuiltinOperator_MATRIX_DIAG, circle::BuiltinOptions_MatrixDiagOptions,
+ CreateMatrixDiagOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleMatrixSetDiag *node)
+{
+ export_simple(node, circle::BuiltinOperator_MATRIX_SET_DIAG,
+ circle::BuiltinOptions_MatrixSetDiagOptions,
+ CreateMatrixSetDiagOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleMaximum *node)
+{
+ export_simple(node, circle::BuiltinOperator_MAXIMUM, circle::BuiltinOptions_MaximumMinimumOptions,
+ CreateMaximumMinimumOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleMaxPool2D *node)
+{
+ export_pool_2d<luci::CircleMaxPool2D>(_ctx, node, circle::BuiltinOperator_MAX_POOL_2D);
+}
+
+void OperationExporter::visit(luci::CircleMean *node)
+{
+ export_simple(node, circle::BuiltinOperator_MEAN, circle::BuiltinOptions_ReducerOptions,
+ CreateReducerOptions(_ctx.builder, node->keep_dims()).Union());
+}
+
+void OperationExporter::visit(luci::CircleMinimum *node)
+{
+ export_simple(node, circle::BuiltinOperator_MINIMUM, circle::BuiltinOptions_MaximumMinimumOptions,
+ CreateMaximumMinimumOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleMirrorPad *node)
+{
+ export_simple(
+ node, circle::BuiltinOperator_MIRROR_PAD, circle::BuiltinOptions_MirrorPadOptions,
+ CreateMirrorPadOptions(_ctx.builder, to_circle_mirrorpadmode(node->mode())).Union());
+}
+
+void OperationExporter::visit(luci::CircleMul *node)
+{
+ export_simple(
+ node, circle::BuiltinOperator_MUL, circle::BuiltinOptions_MulOptions,
+ CreateMulOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
+}
+
+void OperationExporter::visit(luci::CircleNeg *node)
+{
+ export_simple(node, circle::BuiltinOperator_NEG, circle::BuiltinOptions_NegOptions,
+ CreateNegOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleNonMaxSuppressionV4 *node) { export_node(_ctx, node); }
+
+void OperationExporter::visit(luci::CircleNonMaxSuppressionV5 *node) { export_node(_ctx, node); }
+
+void OperationExporter::visit(luci::CircleNotEqual *node)
+{
+ export_simple(node, circle::BuiltinOperator_NOT_EQUAL, circle::BuiltinOptions_NotEqualOptions,
+ CreateNotEqualOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleOneHot *node)
+{
+ export_simple(node, circle::BuiltinOperator_ONE_HOT, circle::BuiltinOptions_OneHotOptions,
+ CreateOneHotOptions(_ctx.builder, node->axis()).Union());
+}
+
+void OperationExporter::visit(luci::CirclePack *node)
+{
+ export_simple(node, circle::BuiltinOperator_PACK, circle::BuiltinOptions_PackOptions,
+ CreatePackOptions(_ctx.builder, node->values_count(), node->axis()).Union());
+}
+
+void OperationExporter::visit(luci::CirclePad *node)
+{
+ export_simple(node, circle::BuiltinOperator_PAD, circle::BuiltinOptions_PadOptions,
+ CreatePadOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CirclePadV2 *node)
+{
+ export_simple(node, circle::BuiltinOperator_PADV2, circle::BuiltinOptions_PadV2Options,
+ CreatePadV2Options(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CirclePow *node)
+{
+ export_simple(node, circle::BuiltinOperator_POW, circle::BuiltinOptions_PowOptions,
+ CreatePowOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CirclePRelu *node)
+{
+ export_simple(node, circle::BuiltinOperator_PRELU);
+}
+
+void OperationExporter::visit(luci::CircleRange *node)
+{
+ export_simple(node, circle::BuiltinOperator_RANGE, circle::BuiltinOptions_RangeOptions,
+ CreateRangeOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleRank *node)
+{
+ export_simple(node, circle::BuiltinOperator_RANK, circle::BuiltinOptions_RankOptions,
+ CreateRankOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleReduceAny *node)
+{
+ export_simple(node, circle::BuiltinOperator_REDUCE_ANY, circle::BuiltinOptions_ReducerOptions,
+ CreateReducerOptions(_ctx.builder, node->keep_dims()).Union());
+}
+
+void OperationExporter::visit(luci::CircleReduceMax *node)
+{
+ export_simple(node, circle::BuiltinOperator_REDUCE_MAX, circle::BuiltinOptions_ReducerOptions,
+ CreateReducerOptions(_ctx.builder, node->keep_dims()).Union());
+}
+
+void OperationExporter::visit(luci::CircleReduceMin *node)
+{
+ export_simple(node, circle::BuiltinOperator_REDUCE_MIN, circle::BuiltinOptions_ReducerOptions,
+ CreateReducerOptions(_ctx.builder, node->keep_dims()).Union());
+}
+
+void OperationExporter::visit(luci::CircleReduceProd *node)
+{
+ export_simple(node, circle::BuiltinOperator_REDUCE_PROD, circle::BuiltinOptions_ReducerOptions,
+ CreateReducerOptions(_ctx.builder, node->keep_dims()).Union());
+}
+
+void OperationExporter::visit(luci::CircleRelu *node)
+{
+ export_simple(node, circle::BuiltinOperator_RELU);
+}
+
+void OperationExporter::visit(luci::CircleRelu6 *node)
+{
+ export_simple(node, circle::BuiltinOperator_RELU6);
+}
+
+void OperationExporter::visit(luci::CircleReluN1To1 *node)
+{
+ export_simple(node, circle::BuiltinOperator_RELU_N1_TO_1);
+}
+
+void OperationExporter::visit(luci::CircleReshape *node)
+{
+ auto new_shape = _ctx.builder.CreateVector<int32_t>(
+ node->newShape()->rank(), [node](size_t i) { return node->newShape()->dim(i); });
+
+ export_simple(node, circle::BuiltinOperator_RESHAPE, circle::BuiltinOptions_ReshapeOptions,
+ CreateReshapeOptions(_ctx.builder, new_shape).Union());
+}
+
+void OperationExporter::visit(luci::CircleResizeBilinear *node)
+{
+ export_simple(
+ node, circle::BuiltinOperator_RESIZE_BILINEAR, circle::BuiltinOptions_ResizeBilinearOptions,
+ CreateResizeBilinearOptions(_ctx.builder, node->align_corners(), node->half_pixel_centers())
+ .Union());
+}
+
+void OperationExporter::visit(luci::CircleResizeNearestNeighbor *node)
+{
+ export_simple(node, circle::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR,
+ circle::BuiltinOptions_ResizeNearestNeighborOptions,
+ CreateResizeNearestNeighborOptions(_ctx.builder, node->align_corners()).Union());
+}
+
+void OperationExporter::visit(luci::CircleReverseSequence *node)
+{
+ export_simple(
+ node, circle::BuiltinOperator_REVERSE_SEQUENCE, circle::BuiltinOptions_ReverseSequenceOptions,
+ CreateReverseSequenceOptions(_ctx.builder, node->seq_axis(), node->batch_axis()).Union());
+}
+
+void OperationExporter::visit(luci::CircleReverseV2 *node) { export_node(_ctx, node); }
+
+void OperationExporter::visit(luci::CircleRound *node)
+{
+ export_simple(node, circle::BuiltinOperator_ROUND);
+}
+
+void OperationExporter::visit(luci::CircleRsqrt *node)
+{
+ export_simple(node, circle::BuiltinOperator_RSQRT);
+}
+
+void OperationExporter::visit(luci::CircleScatterNd *node)
+{
+ export_simple(node, circle::BuiltinOperator_SCATTER_ND, circle::BuiltinOptions_ScatterNdOptions,
+ CreateScatterNdOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleSegmentSum *node)
+{
+ export_simple(node, circle::BuiltinOperator_SEGMENT_SUM, circle::BuiltinOptions_SegmentSumOptions,
+ CreateSegmentSumOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleSelect *node)
+{
+ export_simple(node, circle::BuiltinOperator_SELECT, circle::BuiltinOptions_SelectOptions,
+ CreateSelectOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleSelectV2 *node)
+{
+ export_simple(node, circle::BuiltinOperator_SELECT_V2, circle::BuiltinOptions_SelectV2Options,
+ CreateSelectV2Options(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleShape *node)
+{
+ export_simple(node, circle::BuiltinOperator_SHAPE, circle::BuiltinOptions_ShapeOptions,
+ CreateShapeOptions(_ctx.builder, to_circle_tensortype(node->out_type())).Union());
+}
+
+void OperationExporter::visit(luci::CircleSin *node)
+{
+ export_simple(node, circle::BuiltinOperator_SIN);
+}
+
+void OperationExporter::visit(luci::CircleSlice *node)
+{
+ export_simple(node, circle::BuiltinOperator_SLICE, circle::BuiltinOptions_SliceOptions,
+ CreateSliceOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleSoftmax *node)
+{
+ export_simple(node, circle::BuiltinOperator_SOFTMAX, circle::BuiltinOptions_SoftmaxOptions,
+ CreateSoftmaxOptions(_ctx.builder, node->beta()).Union());
+}
+
+void OperationExporter::visit(luci::CircleSpaceToBatchND *node)
+{
+ export_simple(node, circle::BuiltinOperator_SPACE_TO_BATCH_ND,
+ circle::BuiltinOptions_SpaceToBatchNDOptions,
+ CreateSpaceToBatchNDOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleSpaceToDepth *node)
+{
+ export_simple(node, circle::BuiltinOperator_SPACE_TO_DEPTH,
+ circle::BuiltinOptions_SpaceToDepthOptions,
+ CreateSpaceToDepthOptions(_ctx.builder, node->block_size()).Union());
+}
+
+void OperationExporter::visit(luci::CircleSparseToDense *node)
+{
+ export_simple(node, circle::BuiltinOperator_SPARSE_TO_DENSE,
+ circle::BuiltinOptions_SparseToDenseOptions,
+ CreateSparseToDenseOptions(_ctx.builder, node->validate_indices()).Union());
+}
+
+void OperationExporter::visit(luci::CircleSplit *node) { export_node(_ctx, node); }
+
+void OperationExporter::visit(luci::CircleSplitV *node) { export_node(_ctx, node); }
+
+void OperationExporter::visit(luci::CircleSqrt *node)
+{
+ export_simple(node, circle::BuiltinOperator_SQRT);
+}
+
+void OperationExporter::visit(luci::CircleSquare *node)
+{
+ export_simple(node, circle::BuiltinOperator_SQUARE, circle::BuiltinOptions_SquareOptions,
+ CreateSquareOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleSquaredDifference *node)
+{
+ export_simple(node, circle::BuiltinOperator_SQUARED_DIFFERENCE,
+ circle::BuiltinOptions_SquaredDifferenceOptions,
+ CreateSquaredDifferenceOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleSqueeze *node)
+{
+ auto squeeze_dims = _ctx.builder.CreateVector<int32_t>(node->squeeze_dims());
+ export_simple(node, circle::BuiltinOperator_SQUEEZE, circle::BuiltinOptions_SqueezeOptions,
+ CreateSqueezeOptions(_ctx.builder, squeeze_dims).Union());
+}
+
+void OperationExporter::visit(luci::CircleStridedSlice *node)
+{
+ export_simple(node, circle::BuiltinOperator_STRIDED_SLICE,
+ circle::BuiltinOptions_StridedSliceOptions,
+ CreateStridedSliceOptions(_ctx.builder, node->begin_mask(), node->end_mask(),
+ node->ellipsis_mask(), node->new_axis_mask(),
+ node->shrink_axis_mask())
+ .Union());
+}
+
+void OperationExporter::visit(luci::CircleSub *node)
+{
+ export_simple(
+ node, circle::BuiltinOperator_SUB, circle::BuiltinOptions_SubOptions,
+ CreateSubOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
+}
+
+void OperationExporter::visit(luci::CircleSum *node)
+{
+ export_simple(node, circle::BuiltinOperator_SUM, circle::BuiltinOptions_ReducerOptions,
+ CreateReducerOptions(_ctx.builder, node->keep_dims()).Union());
+}
+
+void OperationExporter::visit(luci::CircleTanh *node)
+{
+ export_simple(node, circle::BuiltinOperator_TANH);
+}
+
+void OperationExporter::visit(luci::CircleTile *node)
+{
+ export_simple(node, circle::BuiltinOperator_TILE, circle::BuiltinOptions_TileOptions,
+ CreateTileOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleTopKV2 *node) { export_node(_ctx, node); }
+
+void OperationExporter::visit(luci::CircleTranspose *node)
+{
+ export_simple(node, circle::BuiltinOperator_TRANSPOSE, circle::BuiltinOptions_TransposeOptions,
+ CreateTransposeOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleTransposeConv *node)
+{
+ export_simple(node, circle::BuiltinOperator_TRANSPOSE_CONV,
+ circle::BuiltinOptions_TransposeConvOptions,
+ CreateTransposeConvOptions(_ctx.builder, getOpPadding(node->padding()),
+ node->stride()->w(), node->stride()->h())
+ .Union());
+}
+
+void OperationExporter::visit(luci::CircleUnidirectionalSequenceLSTM *node)
+{
+ export_simple(node, circle::BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM,
+ circle::BuiltinOptions_UnidirectionalSequenceLSTMOptions,
+ CreateUnidirectionalSequenceLSTMOptions(
+ _ctx.builder, to_circle_actfunc(node->fusedActivationFunction()),
+ node->cell_clip(), node->proj_clip(), node->time_major(),
+ node->asymmetric_quantize_inputs())
+ .Union());
+}
+
+void OperationExporter::visit(luci::CircleUnique *node) { export_node(_ctx, node); }
+
+void OperationExporter::visit(luci::CircleUnpack *node) { export_node(_ctx, node); }
+
+void OperationExporter::visit(luci::CircleWhere *node)
+{
+ export_simple(node, circle::BuiltinOperator_WHERE, circle::BuiltinOptions_WhereOptions,
+ CreateWhereOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleWhile *node) { export_node(_ctx, node); }
+
+void OperationExporter::visit(luci::CircleZerosLike *node)
+{
+ export_simple(node, circle::BuiltinOperator_ZEROS_LIKE, circle::BuiltinOptions_ZerosLikeOptions,
+ CreateZerosLikeOptions(_ctx.builder).Union());
+}
+
+void OperationExporter::visit(luci::CircleBCQFullyConnected *node)
+{
+ export_simple(node, circle::BuiltinOperator_BCQ_FULLY_CONNECTED,
+ circle::BuiltinOptions_BCQFullyConnectedOptions,
+ CreateBCQFullyConnectedOptions(_ctx.builder, node->weights_hidden_size(),
+ to_circle_actfunc(node->fusedActivationFunction()))
+ .Union());
+}
+
+void OperationExporter::visit(luci::CircleBCQGather *node)
+{
+ export_simple(
+ node, circle::BuiltinOperator_BCQ_GATHER, circle::BuiltinOptions_BCQGatherOptions,
+ CreateBCQGatherOptions(_ctx.builder, node->input_hidden_size(), node->axis()).Union());
+}
+
+void OperationExporter::visit(luci::CircleInstanceNorm *node)
+{
+ export_simple(node, circle::BuiltinOperator_INSTANCE_NORM,
+ circle::BuiltinOptions_InstanceNormOptions,
+ CreateInstanceNormOptions(_ctx.builder, node->epsilon(),
+ to_circle_actfunc(node->fusedActivationFunction()))
+ .Union());
+}
+
+void exportNode(loco::Node *node, flatbuffers::FlatBufferBuilder &builder, SerializedModelData &md,
+ SerializedGraphData &gd)
+{
+ if (auto circle_node = dynamic_cast<luci::CircleNode *>(node))
+ {
+ ExportContext ctx{builder, md, gd};
+ OperationExporter exporter{ctx};
+ circle_node->accept(&exporter);
+ }
+ else
+ {
+ INTERNAL_EXN("Node with unsupported dialect found");
+ }
+}
+
+} // namespace
+
+namespace luci
+{
+
+void exportNodes(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &md,
+ SerializedGraphData &gd)
+{
+ for (auto node : loco::postorder_traversal(loco::output_nodes(g)))
+ {
+ exportNode(node, builder, md, gd);
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/export/src/CircleOperationExporter.h b/compiler/luci/export/src/CircleOperationExporter.h
new file mode 100644
index 000000000..de6abfc54
--- /dev/null
+++ b/compiler/luci/export/src/CircleOperationExporter.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_OPERATION_EXPORTER_H__
+#define __CIRCLE_OPERATION_EXPORTER_H__
+
+#include "CircleExporterUtils.h"
+
+#include <loco/IR/Graph.h>
+
+namespace luci
+{
+
+/**
+ * @brief create Operators corresponding to model nodes
+ * @param nodes container with nodes
+ * @param gd information about serializer parts of model
+ */
+void exportNodes(loco::Graph *g, flatbuffers::FlatBufferBuilder &builder, SerializedModelData &md,
+ SerializedGraphData &gd);
+
+} // namespace luci
+
+#endif // __CIRCLE_OPERATION_EXPORTER_H__
diff --git a/compiler/luci/export/src/CircleTensorExporter.cpp b/compiler/luci/export/src/CircleTensorExporter.cpp
new file mode 100644
index 000000000..1429d2810
--- /dev/null
+++ b/compiler/luci/export/src/CircleTensorExporter.cpp
@@ -0,0 +1,502 @@
+/*
+ * 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 "CircleTensorExporter.h"
+#include "TypeBridge.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/CircleNodeVisitor.h>
+#include <luci/IR/CircleShapeSignature.h>
+#include <luci/Service/CircleTypeInference.h>
+#include <luci/Service/CircleShapeInference.h>
+#include <luci/Log.h>
+
+#include <loco/IR/Algorithm.h>
+#include <loco/IR/CanonicalNode.h>
+#include <loco/IR/CanonicalNodeVisitor.h>
+#include <loco/IR/DataTypeTraits.h>
+#include <oops/InternalExn.h>
+
+using namespace circle;
+using namespace flatbuffers;
+
+namespace
+{
+
+using namespace luci;
+
+class CircleTensoInfo
+{
+public:
+ CircleTensoInfo() = default;
+
+public:
+ void name(const std::string &name) { _name = name; }
+ const std::string &name(void) const { return _name; }
+
+public:
+ const circle::TensorType &dtype(void) const { return _dtype; }
+ void dtype(const circle::TensorType &dtype) { _dtype = dtype; }
+
+ const ShapeDescription &shape(void) const { return _shape; }
+ void shape(const ShapeDescription &shape) { _shape = shape; }
+
+ const ShapeSignature &shape_signature(void) const { return _shape_signature; }
+ void shape_signature(const ShapeSignature &ss) { _shape_signature = ss; }
+
+ luci::ShapeStatus shape_status(void) const { return _shape_status; }
+ void shape_status(luci::ShapeStatus ss) { _shape_status = ss; }
+
+public:
+ luci::CircleConst *content(void) const { return _content; }
+ void content(luci::CircleConst *c) { _content = c; }
+
+ luci::CircleQuantParam *quantparam(void) const { return _quantparam; }
+ void quantparam(luci::CircleQuantParam *qp) { _quantparam = qp; }
+
+ luci::SparsityParam *sparsityparam(void) const { return _sparsityparam; }
+ void sparsityparam(luci::SparsityParam *sp) { _sparsityparam = sp; }
+
+private:
+ std::string _name;
+
+ circle::TensorType _dtype{circle::TensorType_FLOAT32};
+ ShapeDescription _shape{};
+ ShapeSignature _shape_signature;
+ luci::ShapeStatus _shape_status{luci::ShapeStatus::UNDEFINED};
+
+ luci::CircleConst *_content = nullptr;
+ luci::CircleQuantParam *_quantparam = nullptr;
+ luci::SparsityParam *_sparsityparam = nullptr;
+};
+
+using CircleTensorContext = std::vector<CircleTensoInfo>;
+
+struct NoOpDetector final : public luci::CircleNodeMutableVisitor<bool>
+{
+ // Input is Virtual but does produce a Tensor
+ // Output is Virtual that does not produce any Tensor
+ bool visit(luci::CircleOutput *) final { return true; }
+ bool visit(luci::CircleOutputExclude *) final { return true; }
+
+ // Return false by default
+ bool visit(luci::CircleNode *) final { return false; }
+};
+
+void allocateCircleTensorInfo(CircleNode *node, CircleTensorContext &ctx)
+{
+ LOGGER(l);
+
+ auto tensor_index = static_cast<CircleTensorIndex>(ctx.size());
+ // TODO Use Graph-level metadata for Input & Output
+ // auto tensor_name = "t_" + std::to_string(tensor_index);
+ std::string tensor_name = node->name();
+ if (tensor_name.empty())
+ tensor_name = "t_" + std::to_string(tensor_index);
+ INFO(l) << "[luci] Tensor for " << tensor_name << ": " << tensor_index << std::endl;
+
+ CircleTensoInfo tensor_info;
+
+ tensor_info.name(tensor_name);
+ tensor_info.dtype(to_circle_tensortype(luci::node_dtype(node)));
+ tensor_info.shape_signature(node->shape_signature());
+ if (node->shape_status() == ShapeStatus::VALID)
+ tensor_info.shape(to_shape_description(luci::node_shape(node)));
+ tensor_info.shape_status(node->shape_status());
+
+ tensor_info.content(dynamic_cast<luci::CircleConst *>(node));
+ tensor_info.quantparam(node->quantparam());
+ tensor_info.sparsityparam(node->sparsityparam());
+
+ set_tensor_index(node, tensor_index);
+
+ ctx.emplace_back(tensor_info);
+}
+
+class MultiOutputDetector final : public luci::CircleNodeMutableVisitor<bool>
+{
+public:
+ MultiOutputDetector(CircleTensorContext &ctx) : _ctx(ctx) {}
+
+private:
+ void store_outputs(luci::CircleNode *node, uint32_t count)
+ {
+ auto outs = loco::succs(node);
+ assert(outs.size() == count);
+ (void)count; // for unused variable error in release build
+ for (auto out : outs)
+ {
+ auto circle_out = loco::must_cast<luci::CircleNode *>(out);
+ allocateCircleTensorInfo(circle_out, _ctx);
+ }
+ set_tensor_index(node, -1);
+ }
+
+public:
+ bool visit(luci::CircleIfOut *) final { return true; }
+ bool visit(luci::CircleSplitOut *) final { return true; }
+ bool visit(luci::CircleSplitVOut *) final { return true; }
+ bool visit(luci::CircleTopKV2Out *) final { return true; }
+ bool visit(luci::CircleUnpackOut *) final { return true; }
+ bool visit(luci::CircleWhileOut *) final { return true; }
+
+ bool visit(luci::CircleIf *node) final
+ {
+ store_outputs(node, node->output_count());
+ return true;
+ }
+
+ bool visit(luci::CircleSplit *node) final
+ {
+ store_outputs(node, uint32_t(node->num_split()));
+ return true;
+ }
+
+ bool visit(luci::CircleSplitV *node) final
+ {
+ store_outputs(node, uint32_t(node->num_split()));
+ return true;
+ }
+
+ bool visit(luci::CircleTopKV2 *node) final
+ {
+ store_outputs(node, 2);
+ return true;
+ }
+
+ bool visit(luci::CircleUnpack *node) final
+ {
+ store_outputs(node, node->num());
+ return true;
+ }
+
+ bool visit(luci::CircleWhile *node) final
+ {
+ store_outputs(node, node->output_count());
+ return true;
+ }
+
+ // Return false by default
+ bool visit(luci::CircleNode *) final { return false; }
+
+private:
+ CircleTensorContext &_ctx;
+};
+
+void allocateCircleTensor(CircleNode *node, CircleTensorContext &ctx)
+{
+ if (node == nullptr)
+ throw std::runtime_error("allocateCIrcleTensor Failed : node is nullptr");
+
+ auto isNoOp = [](loco::Node *node) {
+ if (auto circle_node = dynamic_cast<luci::CircleNode *>(node))
+ {
+ NoOpDetector d;
+ return circle_node->accept(&d);
+ }
+ return false;
+ };
+
+ if (isNoOp(node))
+ {
+ set_tensor_index(node, -1);
+ return;
+ }
+
+ // TODO revise this when loco supports multiple outputs
+ // NOTE this will store all virtual output tensors and skip for the real node
+ if (auto circle_node = dynamic_cast<luci::CircleNode *>(node))
+ {
+ MultiOutputDetector d(ctx);
+ if (circle_node->accept(&d))
+ return;
+ }
+
+ allocateCircleTensorInfo(node, ctx);
+}
+
+} // namespace
+
+namespace
+{
+
+flatbuffers::Offset<Vector<int32_t>> encodeShape(FlatBufferBuilder &builder,
+ const ShapeDescription &shape)
+{
+ assert(shape._rank_known && "unknown number of dimensions is not supported");
+ return builder.CreateVector(shape._dims);
+}
+
+flatbuffers::Offset<Vector<int32_t>> encodeShapeSignature(FlatBufferBuilder &builder,
+ const ShapeSignature &shape_signature)
+{
+ return builder.CreateVector(shape_signature.as_vector());
+}
+
+flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder)
+{
+ return CreateBuffer(builder);
+}
+
+template <typename NodeT>
+flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, NodeT *)
+{
+ return CreateBuffer(builder);
+}
+
+template <loco::DataType DT>
+flatbuffers::Offset<circle::Buffer> encodeOpBufferByDType(FlatBufferBuilder &builder,
+ luci::CircleConst *c)
+{
+ using NativeType = typename loco::DataTypeImpl<DT>::Type;
+
+ std::vector<NativeType> raw_data;
+ const uint32_t size = c->size<DT>();
+ raw_data.reserve(size);
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ raw_data.push_back(c->at<DT>(i));
+ }
+ const size_t raw_size = size * sizeof(NativeType);
+ auto array_offset = builder.CreateVector(reinterpret_cast<uint8_t *>(raw_data.data()), raw_size);
+ return CreateBuffer(builder, array_offset);
+}
+
+template <>
+flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, luci::CircleConst *c)
+{
+ switch (c->dtype())
+ {
+ case loco::DataType::FLOAT32:
+ return encodeOpBufferByDType<loco::DataType::FLOAT32>(builder, c);
+ case loco::DataType::S8:
+ return encodeOpBufferByDType<loco::DataType::S8>(builder, c);
+ case loco::DataType::S16:
+ return encodeOpBufferByDType<loco::DataType::S16>(builder, c);
+ case loco::DataType::S32:
+ return encodeOpBufferByDType<loco::DataType::S32>(builder, c);
+ case loco::DataType::S64:
+ return encodeOpBufferByDType<loco::DataType::S64>(builder, c);
+ case loco::DataType::U8:
+ return encodeOpBufferByDType<loco::DataType::U8>(builder, c);
+ case loco::DataType::BOOL:
+ return encodeOpBufferByDType<loco::DataType::BOOL>(builder, c);
+ default:
+ break;
+ }
+
+ INTERNAL_EXN_V("Unsupported datatype", oops::to_uint32(c->dtype()));
+}
+
+flatbuffers::Offset<circle::QuantizationParameters>
+encodeQuantizationParameters(FlatBufferBuilder &builder, luci::CircleQuantParam *quantparam)
+{
+ if (quantparam == nullptr)
+ return 0;
+
+ flatbuffers::Offset<flatbuffers::Vector<float>> min;
+ flatbuffers::Offset<flatbuffers::Vector<float>> max;
+ flatbuffers::Offset<flatbuffers::Vector<float>> scale;
+ flatbuffers::Offset<flatbuffers::Vector<int64_t>> zero_point;
+ if (quantparam->min.size() && quantparam->max.size())
+ {
+ min = builder.CreateVector(quantparam->min);
+ max = builder.CreateVector(quantparam->max);
+ }
+ if (quantparam->scale.size() && quantparam->zerop.size())
+ {
+ scale = builder.CreateVector(quantparam->scale);
+ zero_point = builder.CreateVector(quantparam->zerop);
+ }
+ // Note: QuantizationDetails is not supported
+ return circle::CreateQuantizationParameters(builder, min, max, scale, zero_point,
+ circle::QuantizationDetails::QuantizationDetails_NONE,
+ 0, quantparam->quantized_dimension);
+}
+
+flatbuffers::Offset<circle::SparsityParameters>
+encodeSparsityParameters(FlatBufferBuilder &builder, luci::SparsityParam *sparsityparam)
+{
+ if (sparsityparam == nullptr)
+ return 0;
+
+ std::vector<flatbuffers::Offset<circle::DimensionMetadata>> dim_metadata_vec;
+ auto luci_dim_metadata = sparsityparam->dim_metadata;
+ for (auto it : luci_dim_metadata)
+ {
+ // array_segments
+ auto circle_array_segments = to_circle_sparse_index_vector(builder, it.array_segments());
+ auto circle_array_segments_type =
+ to_circle_sparse_index_vector_type(it.array_segments().type());
+
+ // array_indices
+ auto circle_array_indices = to_circle_sparse_index_vector(builder, it.array_indices());
+ auto circle_array_indices_type = to_circle_sparse_index_vector_type(it.array_indices().type());
+ auto dim_metadata = circle::CreateDimensionMetadata(
+ builder, to_circle_dimensiontype(it.format()), it.dense_size(), circle_array_segments_type,
+ circle_array_segments, circle_array_indices_type, circle_array_indices);
+ dim_metadata_vec.emplace_back(dim_metadata);
+ }
+
+ return circle::CreateSparsityParametersDirect(builder, &sparsityparam->traversal_order,
+ &sparsityparam->block_map, &dim_metadata_vec);
+}
+
+bool has_same_values(luci::CircleConst *lhs, luci::CircleConst *rhs)
+{
+ if (lhs->dtype() != rhs->dtype())
+ return false;
+
+ if (lhs->rank() != rhs->rank())
+ return false;
+
+ for (uint32_t i = 0; i < lhs->rank(); ++i)
+ if (!(lhs->dim(i) == rhs->dim(i)))
+ return false;
+
+ switch (lhs->dtype())
+ {
+ case loco::DataType::FLOAT32:
+ for (uint32_t i = 0; i < lhs->size<loco::DataType::FLOAT32>(); ++i)
+ if (lhs->at<loco::DataType::FLOAT32>(i) != rhs->at<loco::DataType::FLOAT32>(i))
+ return false;
+ break;
+
+ case loco::DataType::S32:
+ for (uint32_t i = 0; i < lhs->size<loco::DataType::S32>(); ++i)
+ if (lhs->at<loco::DataType::S32>(i) != rhs->at<loco::DataType::S32>(i))
+ return false;
+ break;
+
+ case loco::DataType::S64:
+ for (uint32_t i = 0; i < lhs->size<loco::DataType::S64>(); ++i)
+ if (lhs->at<loco::DataType::S64>(i) != rhs->at<loco::DataType::S64>(i))
+ return false;
+ break;
+
+ case loco::DataType::BOOL:
+ for (uint32_t i = 0; i < lhs->size<loco::DataType::BOOL>(); ++i)
+ if (lhs->at<loco::DataType::BOOL>(i) != rhs->at<loco::DataType::BOOL>(i))
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+uint32_t get_buffer_id(FlatBufferBuilder &builder, SerializedModelData &md, luci::CircleConst *node)
+{
+ if (node != nullptr)
+ {
+ // When buffer with same values is found, use the buffer id.
+ for (auto key_value : md._cached_buffer_id)
+ {
+ if (has_same_values(key_value.first, node))
+ return key_value.second;
+ }
+
+ // When buffer with same values is not found, generate new buffer
+ auto buffer = encodeOpBuffer(builder, node);
+
+ auto buffer_id = static_cast<uint32_t>(md._buffers.size());
+ md._buffers.push_back(buffer);
+
+ // Cache the newly generated buffer id
+ md._cached_buffer_id.insert({node, buffer_id});
+
+ return buffer_id;
+ }
+ else
+ {
+ // When there is no CircleConst, the operation do not use buffer.
+ // So return buffer id as 0 which means empty buffer in circle schema.
+ return 0;
+ }
+}
+
+void exportOpDefinedTensor(const CircleTensoInfo &info, FlatBufferBuilder &builder,
+ SerializedModelData &md, SerializedGraphData &gd)
+{
+ // Create and register output tensor shape
+ flatbuffers::Offset<Vector<int32_t>> shape_offset;
+ if (info.shape_status() == ShapeStatus::VALID)
+ shape_offset = encodeShape(builder, info.shape());
+
+ auto quantparam = encodeQuantizationParameters(builder, info.quantparam());
+
+ auto sparsityparam = encodeSparsityParameters(builder, info.sparsityparam());
+
+ auto shape_signature_offset = encodeShapeSignature(builder, info.shape_signature());
+
+ auto buffer_id = get_buffer_id(builder, md, info.content());
+
+ auto name_offset = builder.CreateString(info.name());
+ auto tensor_offset =
+ CreateTensor(builder, shape_offset, info.dtype(), buffer_id, name_offset, quantparam,
+ /*is_variable*/ false, sparsityparam, shape_signature_offset);
+ gd._tensors.push_back(tensor_offset);
+}
+
+} // namespace
+
+namespace luci
+{
+
+void prepareModelData(FlatBufferBuilder &builder, SerializedModelData &md)
+{
+ // add one empty buffer
+ // note: this follows TFLite
+ // note: there's a comment in tflite fbs file
+ // - Note the 0th entry of this array must be an empty buffer (sentinel).
+ // - This is a convention so that tensors without a buffer can provide 0 as
+ // - their buffer.
+ auto buffer = encodeOpBuffer(builder);
+ md._buffers.push_back(buffer);
+}
+
+void exportOpDefinedTensors(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &md,
+ SerializedGraphData &gd)
+{
+ CircleTensorContext tensor_ctx;
+
+ // NOTE There may exist dangle CircleInput that is not visited with postorder_traversal()
+ // All dangle CircleOutput should be visited by postorder_traversal()
+ auto nodes = g->nodes();
+ for (uint32_t n = 0; n < nodes->size(); ++n)
+ {
+ auto node = dynamic_cast<luci::CircleInput *>(nodes->at(n));
+ if (node != nullptr)
+ allocateCircleTensor(node, tensor_ctx);
+ }
+
+ for (auto node : loco::postorder_traversal(loco::output_nodes(g)))
+ {
+ CircleNode *circle_node = loco::must_cast<luci::CircleNode *>(node);
+ if (dynamic_cast<const luci::CircleInput *>(circle_node) != nullptr)
+ continue;
+ allocateCircleTensor(circle_node, tensor_ctx);
+ }
+
+ for (const auto &tensor_info : tensor_ctx)
+ {
+ exportOpDefinedTensor(tensor_info, builder, md, gd);
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/export/src/CircleTensorExporter.h b/compiler/luci/export/src/CircleTensorExporter.h
new file mode 100644
index 000000000..f9d6107b4
--- /dev/null
+++ b/compiler/luci/export/src/CircleTensorExporter.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_TENSOR_EXPORTER_H__
+#define __CIRCLE_TENSOR_EXPORTER_H__
+
+#include "CircleExporterUtils.h"
+
+#include <loco/IR/Graph.h>
+
+#include <flatbuffers/flatbuffers.h>
+
+namespace luci
+{
+
+/**
+ * @brief one time preparation for SerializedModelData
+ */
+void prepareModelData(flatbuffers::FlatBufferBuilder &builder, SerializedModelData &md);
+
+/**
+ * @brief create Tensors corresponding to results of all nodes in graph
+ * @param computational graph
+ * @param gd information about serialized parts of model
+ */
+void exportOpDefinedTensors(loco::Graph *g, flatbuffers::FlatBufferBuilder &builder,
+ SerializedModelData &md, SerializedGraphData &gd);
+
+} // namespace luci
+
+#endif // __CIRCLE_TENSOR_EXPORTER_H__
diff --git a/compiler/luci/export/src/Optimize.cpp b/compiler/luci/export/src/Optimize.cpp
new file mode 100644
index 000000000..6fa50b564
--- /dev/null
+++ b/compiler/luci/export/src/Optimize.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Optimize.h"
+#include "ProgressReporter.h"
+
+#include <luci/Pass/ShapeInferencePass.h>
+#include <luci/Pass/TypeInferencePass.h>
+
+#include <logo/Phase.h>
+
+#include <memory>
+
+namespace luci
+{
+
+void optimize(loco::Graph *g)
+{
+ logo::Phase phase;
+ {
+ // prepare type and shape before optimization
+ phase.emplace_back(std::make_unique<TypeInferencePass>());
+ phase.emplace_back(std::make_unique<ShapeInferencePass>());
+
+ // TODO add more optimization passes (with a knob)
+ }
+
+ logo::PhaseRunner<logo::PhaseStrategy::Restart> phase_runner{g};
+
+ ProgressReporter prog(g, logo::PhaseStrategy::Restart);
+ phase_runner.attach(&prog);
+ phase_runner.run(phase);
+}
+
+} // namespace luci
diff --git a/compiler/luci/export/src/Optimize.h b/compiler/luci/export/src/Optimize.h
new file mode 100644
index 000000000..c3af7a04c
--- /dev/null
+++ b/compiler/luci/export/src/Optimize.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OPTIMIZE_H__
+#define __OPTIMIZE_H__
+
+#include <loco.h>
+
+namespace luci
+{
+
+/**
+ * @brief Run passes of graph transformations
+ *
+ */
+void optimize(loco::Graph *);
+
+} // namespace luci
+
+#endif // __OPTIMIZE_H__
diff --git a/compiler/luci/export/src/ProgressReporter.cpp b/compiler/luci/export/src/ProgressReporter.cpp
new file mode 100644
index 000000000..216bd3f2a
--- /dev/null
+++ b/compiler/luci/export/src/ProgressReporter.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ProgressReporter.h"
+
+#include "luci/Log.h"
+#include "luci/LogHelper.h"
+
+#include <logo/Phase.h>
+#include <logo/Pass.h>
+
+#include <cassert>
+
+namespace
+{
+
+char to_char(bool b) { return b ? 'Y' : 'N'; }
+
+const char *to_str(logo::PhaseStrategy s)
+{
+ switch (s)
+ {
+ case logo::PhaseStrategy::Saturate:
+ return "Saturate";
+ case logo::PhaseStrategy::Restart:
+ return "Restart";
+ }
+ assert(false);
+ return "";
+}
+
+} // namespace
+
+namespace luci
+{
+
+void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseBegin> *)
+{
+ LOGGER(l);
+
+ VERBOSE(l, 4) << "==============================================================";
+ VERBOSE(l, 4) << "luci::PhaseRunner<" << to_str(strategy()) << ">";
+ VERBOSE(l, 4) << "Initial graph";
+ VERBOSE(l, 4) << fmt(graph());
+}
+
+void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseEnd> *)
+{
+ LOGGER(l);
+
+ VERBOSE(l, 4) << "luci::PhaseRunner<" << to_str(strategy()) << "> - done";
+}
+
+void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassBegin> *info)
+{
+ LOGGER(l);
+
+ VERBOSE(l, 4) << "--------------------------------------------------------------";
+ VERBOSE(l, 4) << "Before " << logo::pass_name(info->pass());
+}
+
+void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassEnd> *info)
+{
+ LOGGER(l);
+
+ VERBOSE(l, 4) << "After " << logo::pass_name(info->pass())
+ << " (changed: " << to_char(info->changed()) << ")";
+ VERBOSE(l, 4) << fmt(graph());
+}
+
+} // namespace luci
diff --git a/compiler/luci/export/src/ProgressReporter.h b/compiler/luci/export/src/ProgressReporter.h
new file mode 100644
index 000000000..e91f42592
--- /dev/null
+++ b/compiler/luci/export/src/ProgressReporter.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __PROGRESSREPORTER_H__
+#define __PROGRESSREPORTER_H__
+
+#include <logo/Phase.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+class ProgressReporter : public logo::PhaseEventListener
+{
+public:
+ ProgressReporter(loco::Graph *graph, logo::PhaseStrategy strategy)
+ : _graph{graph}, _strategy{strategy}
+ {
+ // DO NOTHING
+ }
+
+public:
+ void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseBegin> *) override;
+ void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseEnd> *) override;
+ void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassBegin> *) override;
+ void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassEnd> *) override;
+
+public:
+ loco::Graph *graph(void) const { return _graph; }
+ logo::PhaseStrategy strategy(void) const { return _strategy; }
+
+private:
+ loco::Graph *_graph;
+ logo::PhaseStrategy _strategy;
+};
+
+} // namespace luci
+
+#endif // __PROGRESSREPORTER_H__
diff --git a/compiler/luci/export/src/SerializedData.h b/compiler/luci/export/src/SerializedData.h
new file mode 100644
index 000000000..46b1ac2d5
--- /dev/null
+++ b/compiler/luci/export/src/SerializedData.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SERIALIZED_DATA_H__
+#define __SERIALIZED_DATA_H__
+
+#include <mio/circle/schema_generated.h>
+
+#include <luci/IR/CircleNodes.h>
+
+#include <vector>
+
+#include <unordered_map>
+#include <map>
+
+namespace luci
+{
+
+struct OpCode
+{
+ circle::BuiltinOperator opcode;
+ std::string custom_code{""};
+ int32_t version = 1;
+
+ bool operator==(const OpCode &rhs) const
+ {
+ if (opcode == circle::BuiltinOperator_CUSTOM)
+ {
+ return custom_code == rhs.custom_code;
+ }
+ else
+ {
+ return opcode == rhs.opcode;
+ }
+ }
+};
+
+} // namespace luci
+
+namespace std
+{
+
+template <> struct hash<luci::OpCode>
+{
+ size_t operator()(const luci::OpCode &x) const { return hash<int>()(x.opcode); }
+};
+
+} // namespace std
+
+namespace luci
+{
+
+/**
+ * @breif Record the information of T/F Lite SubGraph and its mapping to loco
+ */
+struct SubGraphContext
+{
+ /// @brief SubGraph name
+ std::string _name;
+ /// @brief SubGraph input tensor id
+ std::vector<int32_t> _inputs;
+ /// @brief SubGraph output tensor id
+ std::vector<int32_t> _outputs;
+ /// @brief DataFormat for SubGraph
+ circle::DataFormat _data_format{circle::DataFormat::DataFormat_CHANNELS_LAST};
+};
+
+// Prerequisites for circle::Model object creation
+struct SerializedModelData final
+{
+ SerializedModelData() = default;
+ SerializedModelData(const SerializedModelData &) = delete;
+
+ std::unordered_map<OpCode, uint32_t> _operator_codes;
+ std::vector<flatbuffers::Offset<circle::Buffer>> _buffers;
+
+ // This is used for removing buffers with same values
+ std::map<luci::CircleConst *, uint32_t> _cached_buffer_id;
+
+ /**
+ * @brief if opcode is not registered in table of opcodes add it
+ * @param builtin_code
+ * @return idx of opcode in table of opcodes (see schema)
+ */
+ uint32_t registerBuiltinOpcode(circle::BuiltinOperator builtin_code, const int32_t op_version);
+ uint32_t registerCustomOpcode(const std::string &custom_op);
+};
+
+// Prerequisites for circle::Model object creation
+struct SerializedGraphData final : public SubGraphContext
+{
+ SerializedGraphData() = default;
+ SerializedGraphData(const SerializedModelData &) = delete;
+
+ std::vector<flatbuffers::Offset<circle::Operator>> _operators;
+ std::vector<flatbuffers::Offset<circle::Tensor>> _tensors;
+};
+
+} // namespace luci
+
+#endif // __SERIALIZED_DATA_H__
diff --git a/compiler/luci/export/src/TypeBridge.cpp b/compiler/luci/export/src/TypeBridge.cpp
new file mode 100644
index 000000000..9ccd52376
--- /dev/null
+++ b/compiler/luci/export/src/TypeBridge.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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 "TypeBridge.h"
+
+#include "CircleExporterUtils.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/CircleNodeVisitor.h>
+#include <luci/Service/CircleTypeInference.h>
+#include <luci/Service/CircleShapeInference.h>
+
+#include <loco/Service/TypeInference.h>
+#include <loco/Service/ShapeInference.h>
+
+namespace
+{
+
+/**
+ * @brief CopySelector will return condition of copy shape/type inference to node
+ */
+struct CopySelector final : public luci::CircleNodeVisitor<bool>
+{
+ // return false(don't copy) for nodes that provides shape/type from nature
+ bool visit(const luci::CircleInput *) final { return false; }
+ bool visit(const luci::CircleConst *) final { return false; }
+
+ // default is copy attributes
+ bool visit(const luci::CircleNode *) { return true; }
+};
+
+} // namespace
+
+namespace luci
+{
+
+loco::TensorShape node_shape(CircleNode *node)
+{
+ loco::TensorShape shape;
+
+ shape.rank(node->rank());
+ for (uint32_t r = 0; r < node->rank(); ++r)
+ {
+ shape.dim(r) = loco::Dimension(node->dim(r).value());
+ }
+ return shape;
+}
+
+loco::DataType node_dtype(CircleNode *node) { return node->dtype(); }
+
+void copy_shape_dtype(loco::Graph *graph)
+{
+ /**
+ * @note We will iterate all the nodes in the graph to include dangle nodes
+ */
+ auto nodes = graph->nodes();
+ for (uint32_t n = 0; n < nodes->size(); ++n)
+ {
+ auto node = loco::must_cast<luci::CircleNode *>(nodes->at(n));
+
+ CopySelector cs;
+ if (node->accept(&cs))
+ {
+ // NOTE not all nodes have infered shape/dtype: multiple outs may not be
+ // visited when outputs are not used
+ // TODO fix shape inference traversal
+ // NOTE when loco supports multiple outputs in nature this issue should be
+ // resolved also
+
+ if (loco::dtype_known(node))
+ {
+ node->dtype(loco::dtype_get(node));
+ }
+
+ if (loco::shape_known(node))
+ {
+ auto shape = loco::shape_get(node).as<loco::TensorShape>();
+ node->rank(shape.rank());
+ for (uint32_t r = 0; r < shape.rank(); ++r)
+ {
+ node->dim(r) = loco::Dimension(shape.dim(r).value());
+ }
+
+ // ShapeStatus should be update only when the status was UNDEFINED
+ if (node->shape_status() == ShapeStatus::UNDEFINED)
+ node->shape_status(ShapeStatus::VALID);
+ }
+ }
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/export/src/TypeBridge.h b/compiler/luci/export/src/TypeBridge.h
new file mode 100644
index 000000000..a63fbce54
--- /dev/null
+++ b/compiler/luci/export/src/TypeBridge.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TYPE_BRIDGE_H__
+#define __TYPE_BRIDGE_H__
+
+#include <luci/IR/CircleNode.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+/**
+ * @brief node_shape() will return loco::TensorShape of CircleNode
+ */
+loco::TensorShape node_shape(CircleNode *node);
+
+/**
+ * @brief node_dtype() will return loco::DataType of CircleNode
+ */
+loco::DataType node_dtype(CircleNode *node);
+
+/**
+ * @brief copy_shape_dtype() will copy shape and dtype inference data to CircleNode
+ */
+void copy_shape_dtype(loco::Graph *graph);
+
+} // namespace luci
+
+#endif // __TYPE_BRIDGE_H__
diff --git a/compiler/luci/import/CMakeLists.txt b/compiler/luci/import/CMakeLists.txt
new file mode 100644
index 000000000..2ae00b837
--- /dev/null
+++ b/compiler/luci/import/CMakeLists.txt
@@ -0,0 +1,27 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(luci_import SHARED ${SOURCES})
+target_include_directories(luci_import PRIVATE src)
+target_include_directories(luci_import PUBLIC include)
+target_link_libraries(luci_import PUBLIC luci_lang)
+target_link_libraries(luci_import PUBLIC mio_circle)
+target_link_libraries(luci_import PRIVATE luci_env)
+target_link_libraries(luci_import PRIVATE luci_log)
+target_link_libraries(luci_import PRIVATE luci_logex)
+target_link_libraries(luci_import PRIVATE nncc_common)
+target_link_libraries(luci_import PRIVATE locop)
+target_link_libraries(luci_import PRIVATE oops)
+install(TARGETS luci_import DESTINATION lib)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(luci_import_test ${TESTS})
+target_include_directories(luci_import_test PRIVATE src)
+target_link_libraries(luci_import_test luci_import)
+target_link_libraries(luci_import_test oops)
diff --git a/compiler/luci/import/README.md b/compiler/luci/import/README.md
new file mode 100644
index 000000000..4ae81ff67
--- /dev/null
+++ b/compiler/luci/import/README.md
@@ -0,0 +1,3 @@
+# luci-import
+
+_luci-import_ provides importing Circle model file to _loco_ graph of _luci_ Circle Dialect IR
diff --git a/compiler/luci/import/include/luci/Import/CircleReader.h b/compiler/luci/import/include/luci/Import/CircleReader.h
new file mode 100644
index 000000000..8636b1d9a
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/CircleReader.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_GRAPHREADER_H__
+#define __LUCI_IMPORT_GRAPHREADER_H__
+
+#include <mio/circle/schema_generated.h>
+
+#include <luci/IR/AttrFusedActFunc.h>
+#include <luci/IR/AttrPadding.h>
+#include <luci/IR/CircleNode.h>
+#include <luci/IR/CircleQuantParam.h>
+#include <luci/IR/CircleShapeSignature.h>
+#include <luci/IR/SparsityParam.h>
+
+#include <loco.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace luci
+{
+
+bool is_valid(const circle::OperatorCodeT &opcode);
+bool is_custom(const circle::OperatorCodeT &opcode);
+std::string opcode_name(const circle::OperatorCodeT &opcode);
+const char *tensor_name(const circle::TensorT &tensor);
+const circle::QuantizationParametersT *tensor_quantization(const circle::TensorT &tensor);
+
+loco::DataType luci_datatype(circle::TensorType type);
+FusedActFunc luci_actfunc(const circle::ActivationFunctionType type);
+Padding luci_padding(const circle::Padding padding);
+MirrorPadMode luci_mirrorpad_mode(const circle::MirrorPadMode mode);
+std::unique_ptr<CircleQuantParam>
+luci_quantparam(const circle::QuantizationParametersT *quantization);
+
+/// @brief Copy common tensor attributes such as name, type, etc. to node.
+void copy_tensor_attributes(const circle::TensorT &tensor, CircleNode *node);
+
+/**
+ * @brief Loads Circle file and provides helpers to access attributes
+ */
+class CircleReader
+{
+private:
+ using CircleBuffers_t = std::vector<std::unique_ptr<circle::BufferT>>;
+ using CircleTensors_t = std::vector<std::unique_ptr<circle::TensorT>>;
+ using CircleOperators_t = std::vector<std::unique_ptr<circle::OperatorT>>;
+ using CircleOperatorCodes_t = std::vector<std::unique_ptr<circle::OperatorCodeT>>;
+
+ using CircleSubGraphsPtr_t = flatbuffers::Vector<flatbuffers::Offset<circle::SubGraph>>;
+ using CircleTensorsPtr_t = flatbuffers::Vector<flatbuffers::Offset<circle::Tensor>>;
+
+public:
+ CircleReader() = default;
+
+public:
+ const CircleOperatorCodes_t &opcodes() const { return _model->operator_codes; }
+ const CircleBuffers_t &buffers() const { return _model->buffers; }
+ const CircleTensors_t &tensors() const { return _current_subgraph->tensors; }
+ const CircleOperators_t &operators() const { return _current_subgraph->operators; }
+ const std::vector<int32_t> &inputs() const { return _current_subgraph->inputs; }
+ const std::vector<int32_t> &outputs() const { return _current_subgraph->outputs; }
+ const std::string &name() const { return _current_subgraph->name; }
+
+ const CircleTensorsPtr_t *tensors_ptr() const { return _tensors_ptr; }
+
+ uint32_t num_subgraph() const { return _model->subgraphs.size(); }
+
+ circle::BuiltinOperator builtin_code(const circle::OperatorT &op) const;
+ std::string opcode_name(const circle::OperatorT &op) const;
+
+public:
+ bool parse(const circle::Model *model);
+ bool select_subgraph(uint32_t subgraph);
+
+private:
+ std::unique_ptr<const circle::ModelT> _model;
+ const circle::SubGraphT *_current_subgraph{nullptr};
+
+ const circle::Model *_model_ptr{nullptr};
+ const CircleTensorsPtr_t *_tensors_ptr{nullptr};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_GRAPHREADER_H__
diff --git a/compiler/luci/import/include/luci/Import/GraphBuilder.h b/compiler/luci/import/include/luci/Import/GraphBuilder.h
new file mode 100644
index 000000000..548264dac
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/GraphBuilder.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_GRAPH_BUILDER_H__
+#define __LUCI_IMPORT_GRAPH_BUILDER_H__
+
+#include "GraphBuilderContext.h"
+#include "GraphBuilderBase.h"
+
+#include <mio/circle/schema_generated.h>
+
+namespace luci
+{
+
+/**
+ * @brief Base of general single output graph builder(e.g., Conv2DGraphBuilder)
+ */
+class GraphBuilder : public GraphBuilderBase
+{
+public:
+ virtual ~GraphBuilder() = default;
+
+ void build(const circle::OperatorT &op, GraphBuilderContext *context) const final;
+
+private:
+ virtual CircleNode *build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const = 0;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_GRAPH_BUILDER_H__
diff --git a/compiler/luci/import/include/luci/Import/GraphBuilderBase.h b/compiler/luci/import/include/luci/Import/GraphBuilderBase.h
new file mode 100644
index 000000000..a0cd008e0
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/GraphBuilderBase.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_GRAPH_BUILDER_BASE_H__
+#define __LUCI_IMPORT_GRAPH_BUILDER_BASE_H__
+
+#include "GraphBuilderContext.h"
+
+#include <mio/circle/schema_generated.h>
+
+namespace luci
+{
+
+/**
+ * @brief Interface of convert circle::OperatorT to CircleNode
+ */
+struct GraphBuilderBase
+{
+ struct ValidateArgs
+ {
+ ValidateArgs(const circle::OperatorT &o, const CircleReader &r) : op(o), reader(r) {}
+
+ const circle::OperatorT &op;
+ const CircleReader &reader;
+ };
+
+ virtual bool validate(const ValidateArgs &) const = 0;
+ virtual void build(const circle::OperatorT &op, GraphBuilderContext *context) const = 0;
+
+ virtual ~GraphBuilderBase() = default;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_GRAPH_BUILDER_BASE_H__
diff --git a/compiler/luci/import/include/luci/Import/GraphBuilderContext.h b/compiler/luci/import/include/luci/Import/GraphBuilderContext.h
new file mode 100644
index 000000000..72e237abc
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/GraphBuilderContext.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_GRAPH_BUILDER_CONTEXT_H__
+#define __LUCI_IMPORT_GRAPH_BUILDER_CONTEXT_H__
+
+#include "CircleReader.h"
+
+#include <luci/IR/CircleNode.h>
+
+#include <loco.h>
+
+#include <map>
+#include <set>
+
+namespace luci
+{
+
+using TensorIndex = int32_t;
+
+/*
+ * @brief Tensor Index to CircleNode
+ * To find CircleNode from TensorIndex
+ */
+class IndexNodeFinder
+{
+public:
+ void enroll(TensorIndex idx, CircleNode *node);
+
+ CircleNode *node(TensorIndex idx) const;
+
+private:
+ using MapIndexNode_t = std::map<TensorIndex, CircleNode *>;
+
+ MapIndexNode_t _table;
+};
+
+/**
+ * @brief Set of Tensor Index of outputs of operators
+ * including graph input nodes
+ */
+class IndexTensorOutputs
+{
+public:
+ void enroll(TensorIndex idx);
+
+ bool find(TensorIndex idx);
+
+private:
+ std::set<TensorIndex> _set;
+};
+
+/**
+ * @brief Class to store context to build loco graph IR from TensorFlow
+ */
+class GraphBuilderContext
+{
+public:
+ GraphBuilderContext(loco::Graph *g, CircleReader *reader, IndexNodeFinder *nodefinder,
+ IndexTensorOutputs *tensoroutputs)
+ : _g(g), _reader(reader), _indexnodefinder(nodefinder), _indextensoroutputs(tensoroutputs)
+ {
+ // DO NOTHING
+ }
+
+ GraphBuilderContext(const GraphBuilderContext &) = delete;
+ GraphBuilderContext(GraphBuilderContext &&) = delete;
+
+public:
+ loco::Graph *graph() { return _g; }
+ CircleReader *reader() { return _reader; }
+
+ IndexNodeFinder *nodefinder() { return _indexnodefinder; }
+ IndexTensorOutputs *tensoroutputs() { return _indextensoroutputs; }
+
+private:
+ loco::Graph *_g;
+ CircleReader *_reader;
+ IndexNodeFinder *_indexnodefinder;
+ IndexTensorOutputs *_indextensoroutputs;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_GRAPH_BUILDER_CONTEXT_H__
diff --git a/compiler/luci/import/include/luci/Import/GraphBuilderRegistry.h b/compiler/luci/import/include/luci/Import/GraphBuilderRegistry.h
new file mode 100644
index 000000000..b8dc22fdd
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/GraphBuilderRegistry.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_GRAPH_BUILDER_REGISTRY_H__
+#define __LUCI_IMPORT_GRAPH_BUILDER_REGISTRY_H__
+
+#include "GraphBuilderBase.h"
+
+#include <map>
+
+namespace luci
+{
+
+struct GraphBuilderSource
+{
+ virtual ~GraphBuilderSource() = default;
+
+ /**
+ * @brief Returns registered GraphBuilder pointer for operator (nullptr if not present)
+ */
+ virtual const GraphBuilderBase *lookup(const circle::BuiltinOperator &op) const = 0;
+};
+
+/**
+ * @brief Class to return graph builder for Circle nodes
+ */
+class GraphBuilderRegistry final : public GraphBuilderSource
+{
+public:
+ GraphBuilderRegistry();
+
+public:
+ GraphBuilderRegistry(const GraphBuilderSource *parent) : _parent{parent}
+ {
+ // DO NOTHING
+ }
+
+public:
+ /**
+ * @brief Returns registered GraphBuilder pointer for operator or
+ * nullptr if not registered
+ */
+ const GraphBuilderBase *lookup(const circle::BuiltinOperator &op) const final
+ {
+ if (_builder_map.find(op) == _builder_map.end())
+ return (_parent == nullptr) ? nullptr : _parent->lookup(op);
+
+ return _builder_map.at(op).get();
+ }
+
+ static GraphBuilderRegistry &get()
+ {
+ static GraphBuilderRegistry me;
+ return me;
+ }
+
+public:
+ void add(const circle::BuiltinOperator op, std::unique_ptr<GraphBuilderBase> &&builder)
+ {
+ _builder_map[op] = std::move(builder);
+ }
+
+private:
+ const GraphBuilderSource *_parent = nullptr;
+
+private:
+ std::map<const circle::BuiltinOperator, std::unique_ptr<GraphBuilderBase>> _builder_map;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_GRAPH_BUILDER_REGISTRY_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes.h b/compiler/luci/import/include/luci/Import/Nodes.h
new file mode 100644
index 000000000..28741064e
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_NODES_H__
+#define __LUCI_IMPORT_NODES_H__
+
+#include "Nodes/CircleAbs.h"
+#include "Nodes/CircleAdd.h"
+#include "Nodes/CircleAddN.h"
+#include "Nodes/CircleArgMax.h"
+#include "Nodes/CircleArgMin.h"
+#include "Nodes/CircleAveragePool2D.h"
+#include "Nodes/CircleBatchMatMul.h"
+#include "Nodes/CircleBatchToSpaceND.h"
+#include "Nodes/CircleBCQFullyConnected.h"
+#include "Nodes/CircleBCQGather.h"
+#include "Nodes/CircleCast.h"
+#include "Nodes/CircleCeil.h"
+#include "Nodes/CircleConcatenation.h"
+#include "Nodes/CircleConst.h"
+#include "Nodes/CircleConv2D.h"
+#include "Nodes/CircleCos.h"
+#include "Nodes/CircleCustom.h"
+#include "Nodes/CircleDepthToSpace.h"
+#include "Nodes/CircleDepthwiseConv2D.h"
+#include "Nodes/CircleDequantize.h"
+#include "Nodes/CircleDiv.h"
+#include "Nodes/CircleElu.h"
+#include "Nodes/CircleEqual.h"
+#include "Nodes/CircleExp.h"
+#include "Nodes/CircleExpandDims.h"
+#include "Nodes/CircleFill.h"
+#include "Nodes/CircleFloor.h"
+#include "Nodes/CircleFloorDiv.h"
+#include "Nodes/CircleFloorMod.h"
+#include "Nodes/CircleFullyConnected.h"
+#include "Nodes/CircleGather.h"
+#include "Nodes/CircleGatherNd.h"
+#include "Nodes/CircleGreater.h"
+#include "Nodes/CircleGreaterEqual.h"
+#include "Nodes/CircleIf.h"
+#include "Nodes/CircleInstanceNorm.h"
+#include "Nodes/CircleL2Normalize.h"
+#include "Nodes/CircleL2Pool2D.h"
+#include "Nodes/CircleLeakyRelu.h"
+#include "Nodes/CircleLess.h"
+#include "Nodes/CircleLessEqual.h"
+#include "Nodes/CircleLocalResponseNormalization.h"
+#include "Nodes/CircleLog.h"
+#include "Nodes/CircleLogicalAnd.h"
+#include "Nodes/CircleLogicalNot.h"
+#include "Nodes/CircleLogicalOr.h"
+#include "Nodes/CircleLogistic.h"
+#include "Nodes/CircleLogSoftmax.h"
+#include "Nodes/CircleMatrixSetDiag.h"
+#include "Nodes/CircleMaximum.h"
+#include "Nodes/CircleMaxPool2D.h"
+#include "Nodes/CircleMatrixDiag.h"
+#include "Nodes/CircleMean.h"
+#include "Nodes/CircleMinimum.h"
+#include "Nodes/CircleMirrorPad.h"
+#include "Nodes/CircleMul.h"
+#include "Nodes/CircleNeg.h"
+#include "Nodes/CircleNonMaxSuppressionV4.h"
+#include "Nodes/CircleNonMaxSuppressionV5.h"
+#include "Nodes/CircleNotEqual.h"
+#include "Nodes/CircleOneHot.h"
+#include "Nodes/CirclePack.h"
+#include "Nodes/CirclePad.h"
+#include "Nodes/CirclePadV2.h"
+#include "Nodes/CirclePow.h"
+#include "Nodes/CirclePRelu.h"
+#include "Nodes/CircleRange.h"
+#include "Nodes/CircleRank.h"
+#include "Nodes/CircleReduceAny.h"
+#include "Nodes/CircleReduceMax.h"
+#include "Nodes/CircleReduceMin.h"
+#include "Nodes/CircleReduceProd.h"
+#include "Nodes/CircleRelu.h"
+#include "Nodes/CircleRelu6.h"
+#include "Nodes/CircleReluN1To1.h"
+#include "Nodes/CircleReshape.h"
+#include "Nodes/CircleResizeBilinear.h"
+#include "Nodes/CircleResizeNearestNeighbor.h"
+#include "Nodes/CircleReverseSequence.h"
+#include "Nodes/CircleReverseV2.h"
+#include "Nodes/CircleRound.h"
+#include "Nodes/CircleRsqrt.h"
+#include "Nodes/CircleScatterNd.h"
+#include "Nodes/CircleSegmentSum.h"
+#include "Nodes/CircleSelect.h"
+#include "Nodes/CircleSelectV2.h"
+#include "Nodes/CircleShape.h"
+#include "Nodes/CircleSin.h"
+#include "Nodes/CircleSlice.h"
+#include "Nodes/CircleSoftmax.h"
+#include "Nodes/CircleSpaceToBatchND.h"
+#include "Nodes/CircleSpaceToDepth.h"
+#include "Nodes/CircleSparseToDense.h"
+#include "Nodes/CircleSplit.h"
+#include "Nodes/CircleSplitV.h"
+#include "Nodes/CircleSqrt.h"
+#include "Nodes/CircleSquare.h"
+#include "Nodes/CircleSquaredDifference.h"
+#include "Nodes/CircleSqueeze.h"
+#include "Nodes/CircleStridedSlice.h"
+#include "Nodes/CircleSub.h"
+#include "Nodes/CircleSum.h"
+#include "Nodes/CircleTanh.h"
+#include "Nodes/CircleTile.h"
+#include "Nodes/CircleTopKV2.h"
+#include "Nodes/CircleTranspose.h"
+#include "Nodes/CircleTransposeConv.h"
+#include "Nodes/CircleUnidirectionalSequenceLSTM.h"
+#include "Nodes/CircleUnique.h"
+#include "Nodes/CircleUnpack.h"
+#include "Nodes/CircleWhere.h"
+#include "Nodes/CircleWhile.h"
+#include "Nodes/CircleZerosLike.h"
+
+#endif // __LUCI_IMPORT_NODES_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleAbs.h b/compiler/luci/import/include/luci/Import/Nodes/CircleAbs.h
new file mode 100644
index 000000000..e0cec26d9
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleAbs.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_ABS_H__
+#define __LUCI_IMPORT_OP_CIRCLE_ABS_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleAbsGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_ABS_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleAdd.h b/compiler/luci/import/include/luci/Import/Nodes/CircleAdd.h
new file mode 100644
index 000000000..d852ee8b3
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleAdd.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_ADD_H__
+#define __LUCI_IMPORT_OP_CIRCLE_ADD_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleAddGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_ADD_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleAddN.h b/compiler/luci/import/include/luci/Import/Nodes/CircleAddN.h
new file mode 100644
index 000000000..3ec6b2a45
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleAddN.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_ADD_N_H__
+#define __LUCI_IMPORT_OP_CIRCLE_ADD_N_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleAddNGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_ADD_N_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleArgMax.h b/compiler/luci/import/include/luci/Import/Nodes/CircleArgMax.h
new file mode 100644
index 000000000..dae4691dc
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleArgMax.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_ARGMAX_H__
+#define __LUCI_IMPORT_OP_CIRCLE_ARGMAX_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleArgMaxGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_ARGMAX_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleArgMin.h b/compiler/luci/import/include/luci/Import/Nodes/CircleArgMin.h
new file mode 100644
index 000000000..746f52837
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleArgMin.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_ARGMIN_H__
+#define __LUCI_IMPORT_OP_CIRCLE_ARGMIN_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleArgMinGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_ARGMIN_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleAveragePool2D.h b/compiler/luci/import/include/luci/Import/Nodes/CircleAveragePool2D.h
new file mode 100644
index 000000000..07f6565bc
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleAveragePool2D.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_AVERAGEPOOL2D_H__
+#define __LUCI_IMPORT_OP_CIRCLE_AVERAGEPOOL2D_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleAveragePool2DGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_AVERAGEPOOL2D_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleBCQFullyConnected.h b/compiler/luci/import/include/luci/Import/Nodes/CircleBCQFullyConnected.h
new file mode 100644
index 000000000..be58acd8d
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleBCQFullyConnected.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_BCQFULLYCONNECTED_H__
+#define __LUCI_IMPORT_OP_CIRCLE_BCQFULLYCONNECTED_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleBCQFullyConnectedGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_BCQFULLYCONNECTED_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleBCQGather.h b/compiler/luci/import/include/luci/Import/Nodes/CircleBCQGather.h
new file mode 100644
index 000000000..ff1c1f7e9
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleBCQGather.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_BCQGATHER_H__
+#define __LUCI_IMPORT_OP_CIRCLE_BCQGATHER_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleBCQGatherGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_BCQGATHER_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleBatchMatMul.h b/compiler/luci/import/include/luci/Import/Nodes/CircleBatchMatMul.h
new file mode 100644
index 000000000..b46a8715c
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleBatchMatMul.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_BATCHMATMUL_H__
+#define __LUCI_IMPORT_OP_CIRCLE_BATCHMATMUL_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleBatchMatMulGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_BATCHMATMUL_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleBatchToSpaceND.h b/compiler/luci/import/include/luci/Import/Nodes/CircleBatchToSpaceND.h
new file mode 100644
index 000000000..4168d248e
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleBatchToSpaceND.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_BATCHTOSPACEND_H__
+#define __LUCI_IMPORT_OP_CIRCLE_BATCHTOSPACEND_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleBatchToSpaceNDGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_BATCHTOSPACEND_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleCast.h b/compiler/luci/import/include/luci/Import/Nodes/CircleCast.h
new file mode 100644
index 000000000..1cd850bc7
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleCast.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_CAST_H__
+#define __LUCI_IMPORT_OP_CIRCLE_CAST_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleCastGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_CAST_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleCeil.h b/compiler/luci/import/include/luci/Import/Nodes/CircleCeil.h
new file mode 100644
index 000000000..f1bdf2397
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleCeil.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_CEIL_H__
+#define __LUCI_IMPORT_OP_CIRCLE_CEIL_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleCeilGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_CEIL_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleConcatenation.h b/compiler/luci/import/include/luci/Import/Nodes/CircleConcatenation.h
new file mode 100644
index 000000000..9b4c9ffd1
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleConcatenation.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_CONCATENATION_H__
+#define __LUCI_IMPORT_OP_CIRCLE_CONCATENATION_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleConcatenationGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_CONCATENATION_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleConst.h b/compiler/luci/import/include/luci/Import/Nodes/CircleConst.h
new file mode 100644
index 000000000..7d4f10a59
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleConst.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_CONST_H__
+#define __LUCI_IMPORT_OP_CIRCLE_CONST_H__
+
+#include "luci/Import/GraphBuilderContext.h"
+
+#include <luci/IR/Nodes/CircleConst.h>
+
+/*
+ * @note Circle does not have Const operator.
+ * Methods here provide helper that creates CircleConst from
+ * Tensor and Buffer in circle flatbuffer file.
+ */
+
+namespace luci
+{
+
+CircleConst *create_circleconst(GraphBuilderContext *context, int32_t tensor_index);
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_CONST_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleConv2D.h b/compiler/luci/import/include/luci/Import/Nodes/CircleConv2D.h
new file mode 100644
index 000000000..4529a4f11
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleConv2D.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_CONV_2D_H__
+#define __LUCI_IMPORT_OP_CIRCLE_CONV_2D_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleConv2DGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_CONV_2D_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleCos.h b/compiler/luci/import/include/luci/Import/Nodes/CircleCos.h
new file mode 100644
index 000000000..fb472977e
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleCos.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_COS_H__
+#define __LUCI_IMPORT_OP_CIRCLE_COS_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleCosGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_COS_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleCustom.h b/compiler/luci/import/include/luci/Import/Nodes/CircleCustom.h
new file mode 100644
index 000000000..65745be4b
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleCustom.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_CUSTOM_H__
+#define __LUCI_IMPORT_OP_CIRCLE_CUSTOM_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleCustomGraphBuilder : public GraphBuilderBase
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+ void build(const circle::OperatorT &op, GraphBuilderContext *context) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_CUSTOM_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleDepthToSpace.h b/compiler/luci/import/include/luci/Import/Nodes/CircleDepthToSpace.h
new file mode 100644
index 000000000..a479cbd20
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleDepthToSpace.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_DEPTHTOSPACE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_DEPTHTOSPACE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleDepthToSpaceGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_DEPTHTOSPACE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleDepthwiseConv2D.h b/compiler/luci/import/include/luci/Import/Nodes/CircleDepthwiseConv2D.h
new file mode 100644
index 000000000..1953cb76c
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleDepthwiseConv2D.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_DEPTHWISECONV_2D_H__
+#define __LUCI_IMPORT_OP_CIRCLE_DEPTHWISECONV_2D_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleDepthwiseConv2DGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_DEPTHWISECONV_2D_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleDequantize.h b/compiler/luci/import/include/luci/Import/Nodes/CircleDequantize.h
new file mode 100644
index 000000000..e25b80b0e
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleDequantize.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_DEQUANTIZE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_DEQUANTIZE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleDequantizeGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_DEQUANTIZE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleDiv.h b/compiler/luci/import/include/luci/Import/Nodes/CircleDiv.h
new file mode 100644
index 000000000..6a38118fe
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleDiv.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_DIV_H__
+#define __LUCI_IMPORT_OP_CIRCLE_DIV_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleDivGraphBuilder : public GraphBuilder
+{
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const override;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_DIV_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleElu.h b/compiler/luci/import/include/luci/Import/Nodes/CircleElu.h
new file mode 100644
index 000000000..2ec5642ce
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleElu.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_ELU_H__
+#define __LUCI_IMPORT_OP_CIRCLE_ELU_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleEluGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_ELU_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleEqual.h b/compiler/luci/import/include/luci/Import/Nodes/CircleEqual.h
new file mode 100644
index 000000000..a98adcd08
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleEqual.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_EQUAL_H__
+#define __LUCI_IMPORT_OP_CIRCLE_EQUAL_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleEqualGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_EQUAL_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleExp.h b/compiler/luci/import/include/luci/Import/Nodes/CircleExp.h
new file mode 100644
index 000000000..521809fe4
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleExp.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_EXP_H__
+#define __LUCI_IMPORT_OP_CIRCLE_EXP_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleExpGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_EXP_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleExpandDims.h b/compiler/luci/import/include/luci/Import/Nodes/CircleExpandDims.h
new file mode 100644
index 000000000..acbfe7aea
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleExpandDims.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_EXPAND_DIMS_H__
+#define __LUCI_IMPORT_OP_CIRCLE_EXPAND_DIMS_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleExpandDimsGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_EXPAND_DIMS_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleFill.h b/compiler/luci/import/include/luci/Import/Nodes/CircleFill.h
new file mode 100644
index 000000000..3539dcd56
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleFill.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_FILL_H__
+#define __LUCI_IMPORT_OP_CIRCLE_FILL_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleFillGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_FILL_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleFloor.h b/compiler/luci/import/include/luci/Import/Nodes/CircleFloor.h
new file mode 100644
index 000000000..057800865
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleFloor.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_FLOOR_H__
+#define __LUCI_IMPORT_OP_CIRCLE_FLOOR_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleFloorGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_FLOOR_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleFloorDiv.h b/compiler/luci/import/include/luci/Import/Nodes/CircleFloorDiv.h
new file mode 100644
index 000000000..ddc2ab2ff
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleFloorDiv.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_FLOOR_DIV_H__
+#define __LUCI_IMPORT_OP_CIRCLE_FLOOR_DIV_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleFloorDivGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_FLOOR_DIV_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleFloorMod.h b/compiler/luci/import/include/luci/Import/Nodes/CircleFloorMod.h
new file mode 100644
index 000000000..1d6aa87c2
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleFloorMod.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_FLOOR_MOD_H__
+#define __LUCI_IMPORT_OP_CIRCLE_FLOOR_MOD_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleFloorModGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_FLOOR_MOD_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleFullyConnected.h b/compiler/luci/import/include/luci/Import/Nodes/CircleFullyConnected.h
new file mode 100644
index 000000000..b7798c688
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleFullyConnected.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_FULLYCONNECTED_H__
+#define __LUCI_IMPORT_OP_CIRCLE_FULLYCONNECTED_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleFullyConnectedGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_FULLYCONNECTED_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleGather.h b/compiler/luci/import/include/luci/Import/Nodes/CircleGather.h
new file mode 100644
index 000000000..0680c9451
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleGather.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_GATHER_H__
+#define __LUCI_IMPORT_OP_CIRCLE_GATHER_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleGatherGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_GATHER_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleGatherNd.h b/compiler/luci/import/include/luci/Import/Nodes/CircleGatherNd.h
new file mode 100644
index 000000000..be96b7dbe
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleGatherNd.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_GATHER_ND_H__
+#define __LUCI_IMPORT_OP_CIRCLE_GATHER_ND_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleGatherNdGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_GATHER_ND_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleGreater.h b/compiler/luci/import/include/luci/Import/Nodes/CircleGreater.h
new file mode 100644
index 000000000..87f0a8d83
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleGreater.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_GREATER_H__
+#define __LUCI_IMPORT_OP_CIRCLE_GREATER_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleGreaterGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_GREATER_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleGreaterEqual.h b/compiler/luci/import/include/luci/Import/Nodes/CircleGreaterEqual.h
new file mode 100644
index 000000000..4d24314e9
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleGreaterEqual.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_GREATEREQUAL_H__
+#define __LUCI_IMPORT_OP_CIRCLE_GREATEREQUAL_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleGreaterEqualGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_GREATEREQUAL_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleIf.h b/compiler/luci/import/include/luci/Import/Nodes/CircleIf.h
new file mode 100644
index 000000000..8faf09cae
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleIf.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_IF_H__
+#define __LUCI_IMPORT_OP_CIRCLE_IF_H__
+
+#include "luci/Import/GraphBuilderBase.h"
+
+namespace luci
+{
+
+class CircleIfGraphBuilder : public GraphBuilderBase
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+ void build(const circle::OperatorT &op, GraphBuilderContext *context) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_IF_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleInstanceNorm.h b/compiler/luci/import/include/luci/Import/Nodes/CircleInstanceNorm.h
new file mode 100644
index 000000000..5fd8f148a
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleInstanceNorm.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_INSTANCE_NORM_H__
+#define __LUCI_IMPORT_OP_CIRCLE_INSTANCE_NORM_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleInstanceNormGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_INSTANCE_NORM_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleL2Normalize.h b/compiler/luci/import/include/luci/Import/Nodes/CircleL2Normalize.h
new file mode 100644
index 000000000..116605f09
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleL2Normalize.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_L2_NORMALIZE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_L2_NORMALIZE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleL2NormalizeGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_L2_NORMALIZE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleL2Pool2D.h b/compiler/luci/import/include/luci/Import/Nodes/CircleL2Pool2D.h
new file mode 100644
index 000000000..2211c4751
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleL2Pool2D.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_L2_POOL2D_H__
+#define __LUCI_IMPORT_OP_CIRCLE_L2_POOL2D_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleL2Pool2DGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_L2_POOL2D_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleLeakyRelu.h b/compiler/luci/import/include/luci/Import/Nodes/CircleLeakyRelu.h
new file mode 100644
index 000000000..b7fa41f25
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleLeakyRelu.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_LEAKY_RELU_H__
+#define __LUCI_IMPORT_OP_CIRCLE_LEAKY_RELU_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleLeakyReluGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_LEAKY_RELU_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleLess.h b/compiler/luci/import/include/luci/Import/Nodes/CircleLess.h
new file mode 100644
index 000000000..b93155bc4
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleLess.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_LESS_H__
+#define __LUCI_IMPORT_OP_CIRCLE_LESS_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleLessGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_LESS_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleLessEqual.h b/compiler/luci/import/include/luci/Import/Nodes/CircleLessEqual.h
new file mode 100644
index 000000000..e54a4cb8c
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleLessEqual.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_LESS_EQUAL_H__
+#define __LUCI_IMPORT_OP_CIRCLE_LESS_EQUAL_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleLessEqualGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_LESS_EQUAL_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleLocalResponseNormalization.h b/compiler/luci/import/include/luci/Import/Nodes/CircleLocalResponseNormalization.h
new file mode 100644
index 000000000..95e6ea880
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleLocalResponseNormalization.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_LOCAL_RESPONSE_NORMALIZATION_H__
+#define __LUCI_IMPORT_OP_CIRCLE_LOCAL_RESPONSE_NORMALIZATION_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleLocalResponseNormalizationGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_LOCAL_RESPONSE_NORMALIZATION_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleLog.h b/compiler/luci/import/include/luci/Import/Nodes/CircleLog.h
new file mode 100644
index 000000000..5b3321014
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleLog.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_LOG_H__
+#define __LUCI_IMPORT_OP_CIRCLE_LOG_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleLogGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_LOG_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleLogSoftmax.h b/compiler/luci/import/include/luci/Import/Nodes/CircleLogSoftmax.h
new file mode 100644
index 000000000..ef29833f5
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleLogSoftmax.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_LOG_SOFTMAX_H__
+#define __LUCI_IMPORT_OP_CIRCLE_LOG_SOFTMAX_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleLogSoftmaxGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_LOG_SOFTMAX_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalAnd.h b/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalAnd.h
new file mode 100644
index 000000000..9336f4ac8
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalAnd.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_LOGICALAND_H__
+#define __LUCI_IMPORT_OP_CIRCLE_LOGICALAND_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleLogicalAndGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_LOGICALAND_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalNot.h b/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalNot.h
new file mode 100644
index 000000000..ec890ecf7
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalNot.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_LOGICALNOT_H__
+#define __LUCI_IMPORT_OP_CIRCLE_LOGICALNOT_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleLogicalNotGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_LOGICALNOT_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalOr.h b/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalOr.h
new file mode 100644
index 000000000..9fb0086c1
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalOr.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_LOGICALOR_H__
+#define __LUCI_IMPORT_OP_CIRCLE_LOGICALOR_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleLogicalOrGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_LOGICALOR_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleLogistic.h b/compiler/luci/import/include/luci/Import/Nodes/CircleLogistic.h
new file mode 100644
index 000000000..67c6c1f1f
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleLogistic.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_LOGISTIC_H__
+#define __LUCI_IMPORT_OP_CIRCLE_LOGISTIC_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleLogisticGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_LOGISTIC_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleMatrixDiag.h b/compiler/luci/import/include/luci/Import/Nodes/CircleMatrixDiag.h
new file mode 100644
index 000000000..e038c3e0a
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleMatrixDiag.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_MATRIX_DIAG_H__
+#define __LUCI_IMPORT_OP_CIRCLE_MATRIX_DIAG_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleMatrixDiagGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_MATRIX_DIAG_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleMatrixSetDiag.h b/compiler/luci/import/include/luci/Import/Nodes/CircleMatrixSetDiag.h
new file mode 100644
index 000000000..a9ea0ac3d
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleMatrixSetDiag.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_MATRIX_SET_DIAG_H__
+#define __LUCI_IMPORT_OP_CIRCLE_MATRIX_SET_DIAG_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleMatrixSetDiagGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_MATRIX_SET_DIAG_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleMaxPool2D.h b/compiler/luci/import/include/luci/Import/Nodes/CircleMaxPool2D.h
new file mode 100644
index 000000000..bcd2acb30
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleMaxPool2D.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_MAXPOOL2D_H__
+#define __LUCI_IMPORT_OP_CIRCLE_MAXPOOL2D_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleMaxPool2DGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_MAXPOOL2D_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleMaximum.h b/compiler/luci/import/include/luci/Import/Nodes/CircleMaximum.h
new file mode 100644
index 000000000..9705d3a36
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleMaximum.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_MAXIMUM_H__
+#define __LUCI_IMPORT_OP_CIRCLE_MAXIMUM_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleMaximumGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_MAXIMUM_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleMean.h b/compiler/luci/import/include/luci/Import/Nodes/CircleMean.h
new file mode 100644
index 000000000..a7919a57c
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleMean.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_MEAN_H__
+#define __LUCI_IMPORT_OP_CIRCLE_MEAN_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleMeanGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_MEAN_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleMinimum.h b/compiler/luci/import/include/luci/Import/Nodes/CircleMinimum.h
new file mode 100644
index 000000000..d9546ecf8
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleMinimum.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_MINIMUM_H__
+#define __LUCI_IMPORT_OP_CIRCLE_MINIMUM_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleMinimumGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_MINIMUM_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleMirrorPad.h b/compiler/luci/import/include/luci/Import/Nodes/CircleMirrorPad.h
new file mode 100644
index 000000000..7f512cda7
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleMirrorPad.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_MIRROR_PAD_H__
+#define __LUCI_IMPORT_OP_CIRCLE_MIRROR_PAD_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleMirrorPadGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_MIRROR_PAD_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleMul.h b/compiler/luci/import/include/luci/Import/Nodes/CircleMul.h
new file mode 100644
index 000000000..13027a155
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleMul.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_MUL_H__
+#define __LUCI_IMPORT_OP_CIRCLE_MUL_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleMulGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const override;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_MUL_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleNeg.h b/compiler/luci/import/include/luci/Import/Nodes/CircleNeg.h
new file mode 100644
index 000000000..3d0bac19f
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleNeg.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_NEG_H__
+#define __LUCI_IMPORT_OP_CIRCLE_NEG_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleNegGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_NEG_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleNonMaxSuppressionV4.h b/compiler/luci/import/include/luci/Import/Nodes/CircleNonMaxSuppressionV4.h
new file mode 100644
index 000000000..f193aae35
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleNonMaxSuppressionV4.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_NON_MAX_SUPPRESSION_V4_H__
+#define __LUCI_IMPORT_OP_CIRCLE_NON_MAX_SUPPRESSION_V4_H__
+
+#include "luci/Import/GraphBuilderBase.h"
+
+namespace luci
+{
+
+class CircleNonMaxSuppressionV4GraphBuilder : public GraphBuilderBase
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+ void build(const circle::OperatorT &op, GraphBuilderContext *context) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_NON_MAX_SUPPRESSION_V4_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleNonMaxSuppressionV5.h b/compiler/luci/import/include/luci/Import/Nodes/CircleNonMaxSuppressionV5.h
new file mode 100644
index 000000000..62be0758e
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleNonMaxSuppressionV5.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_NON_MAX_SUPPRESSION_V5_H__
+#define __LUCI_IMPORT_OP_CIRCLE_NON_MAX_SUPPRESSION_V5_H__
+
+#include "luci/Import/GraphBuilderBase.h"
+
+namespace luci
+{
+
+class CircleNonMaxSuppressionV5GraphBuilder : public GraphBuilderBase
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+ void build(const circle::OperatorT &op, GraphBuilderContext *context) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_NON_MAX_SUPPRESSION_V5_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleNotEqual.h b/compiler/luci/import/include/luci/Import/Nodes/CircleNotEqual.h
new file mode 100644
index 000000000..10c79b75e
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleNotEqual.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_NOTEQUAL_H__
+#define __LUCI_IMPORT_OP_CIRCLE_NOTEQUAL_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleNotEqualGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_NOTEQUAL_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleOneHot.h b/compiler/luci/import/include/luci/Import/Nodes/CircleOneHot.h
new file mode 100644
index 000000000..8d9526d0e
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleOneHot.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_ONEHOT_H__
+#define __LUCI_IMPORT_OP_CIRCLE_ONEHOT_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleOneHotGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_ONEHOT_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CirclePRelu.h b/compiler/luci/import/include/luci/Import/Nodes/CirclePRelu.h
new file mode 100644
index 000000000..822862cfd
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CirclePRelu.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_PRELU_H__
+#define __LUCI_IMPORT_OP_CIRCLE_PRELU_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CirclePReluGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_PRELU_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CirclePack.h b/compiler/luci/import/include/luci/Import/Nodes/CirclePack.h
new file mode 100644
index 000000000..8e4b71995
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CirclePack.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_PACK_H__
+#define __LUCI_IMPORT_OP_CIRCLE_PACK_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CirclePackGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const override;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_PACK_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CirclePad.h b/compiler/luci/import/include/luci/Import/Nodes/CirclePad.h
new file mode 100644
index 000000000..e333ee912
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CirclePad.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_PAD_H__
+#define __LUCI_IMPORT_OP_CIRCLE_PAD_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CirclePadGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_PAD_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CirclePadV2.h b/compiler/luci/import/include/luci/Import/Nodes/CirclePadV2.h
new file mode 100644
index 000000000..089f52c81
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CirclePadV2.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_PADV2_H__
+#define __LUCI_IMPORT_OP_CIRCLE_PADV2_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CirclePadV2GraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_PADV2_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CirclePow.h b/compiler/luci/import/include/luci/Import/Nodes/CirclePow.h
new file mode 100644
index 000000000..284aa9b89
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CirclePow.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_POW_H__
+#define __LUCI_IMPORT_OP_CIRCLE_POW_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CirclePowGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_POW_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleRange.h b/compiler/luci/import/include/luci/Import/Nodes/CircleRange.h
new file mode 100644
index 000000000..bc63286b2
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleRange.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_RANGE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_RANGE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleRangeGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_RANGE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleRank.h b/compiler/luci/import/include/luci/Import/Nodes/CircleRank.h
new file mode 100644
index 000000000..43a7fdb7b
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleRank.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_RANK_H__
+#define __LUCI_IMPORT_OP_CIRCLE_RANK_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleRankGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_RANK_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleReduceAny.h b/compiler/luci/import/include/luci/Import/Nodes/CircleReduceAny.h
new file mode 100644
index 000000000..5ee517999
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleReduceAny.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_REDUCE_ANY_H__
+#define __LUCI_IMPORT_OP_CIRCLE_REDUCE_ANY_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleReduceAnyGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_REDUCE_ANY_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleReduceMax.h b/compiler/luci/import/include/luci/Import/Nodes/CircleReduceMax.h
new file mode 100644
index 000000000..0bc7021c1
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleReduceMax.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_REDUCE_MAX_H__
+#define __LUCI_IMPORT_OP_CIRCLE_REDUCE_MAX_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleReduceMaxGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_REDUCE_MAX_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleReduceMin.h b/compiler/luci/import/include/luci/Import/Nodes/CircleReduceMin.h
new file mode 100644
index 000000000..0c05457f0
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleReduceMin.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_REDUCE_MIN_H__
+#define __LUCI_IMPORT_OP_CIRCLE_REDUCE_MIN_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleReduceMinGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_REDUCE_MIN_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleReduceProd.h b/compiler/luci/import/include/luci/Import/Nodes/CircleReduceProd.h
new file mode 100644
index 000000000..446bc7866
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleReduceProd.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_REDUCE_PROD_H__
+#define __LUCI_IMPORT_OP_CIRCLE_REDUCE_PROD_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleReduceProdGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_REDUCE_PROD_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleRelu.h b/compiler/luci/import/include/luci/Import/Nodes/CircleRelu.h
new file mode 100644
index 000000000..deb913243
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleRelu.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_RELU_H__
+#define __LUCI_IMPORT_OP_CIRCLE_RELU_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleReluGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_RELU_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleRelu6.h b/compiler/luci/import/include/luci/Import/Nodes/CircleRelu6.h
new file mode 100644
index 000000000..d17b4e200
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleRelu6.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_RELU6_H__
+#define __LUCI_IMPORT_OP_CIRCLE_RELU6_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleRelu6GraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_RELU6_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleReluN1To1.h b/compiler/luci/import/include/luci/Import/Nodes/CircleReluN1To1.h
new file mode 100644
index 000000000..059431565
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleReluN1To1.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_RELU_N1_TO_1_H__
+#define __LUCI_IMPORT_OP_CIRCLE_RELU_N1_TO_1_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleReluN1To1GraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_RELU_N1_TO_1_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleReshape.h b/compiler/luci/import/include/luci/Import/Nodes/CircleReshape.h
new file mode 100644
index 000000000..eb4fb13ba
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleReshape.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_RESHAPE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_RESHAPE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleReshapeGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_RESHAPE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleResizeBilinear.h b/compiler/luci/import/include/luci/Import/Nodes/CircleResizeBilinear.h
new file mode 100644
index 000000000..8c20ecc24
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleResizeBilinear.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_RESIZE_BILINEAR_H__
+#define __LUCI_IMPORT_OP_CIRCLE_RESIZE_BILINEAR_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleResizeBilinearGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_RESIZE_BILINEAR_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleResizeNearestNeighbor.h b/compiler/luci/import/include/luci/Import/Nodes/CircleResizeNearestNeighbor.h
new file mode 100644
index 000000000..5b0647163
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleResizeNearestNeighbor.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_RESIZE_NEAREST_NEIGHBOR_H__
+#define __LUCI_IMPORT_OP_CIRCLE_RESIZE_NEAREST_NEIGHBOR_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleResizeNearestNeighborGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_RESIZE_NEAREST_NEIGHBOR_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleReverseSequence.h b/compiler/luci/import/include/luci/Import/Nodes/CircleReverseSequence.h
new file mode 100644
index 000000000..cbeed3013
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleReverseSequence.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_REVERSE_SEQUENCE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_REVERSE_SEQUENCE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleReverseSequenceGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_REVERSE_SEQUENCE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleReverseV2.h b/compiler/luci/import/include/luci/Import/Nodes/CircleReverseV2.h
new file mode 100644
index 000000000..f354298dd
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleReverseV2.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_REVERSE_V2_H__
+#define __LUCI_IMPORT_OP_CIRCLE_REVERSE_V2_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleReverseV2GraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_REVERSE_V2_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleRound.h b/compiler/luci/import/include/luci/Import/Nodes/CircleRound.h
new file mode 100644
index 000000000..8b027d7ef
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleRound.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_ROUND_H__
+#define __LUCI_IMPORT_OP_CIRCLE_ROUND_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleRoundGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_ROUND_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleRsqrt.h b/compiler/luci/import/include/luci/Import/Nodes/CircleRsqrt.h
new file mode 100644
index 000000000..90d568f1f
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleRsqrt.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_RSQRT_H__
+#define __LUCI_IMPORT_OP_CIRCLE_RSQRT_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleRsqrtGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_RSQRT_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleScatterNd.h b/compiler/luci/import/include/luci/Import/Nodes/CircleScatterNd.h
new file mode 100644
index 000000000..8fa7a2f91
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleScatterNd.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SCATTER_ND_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SCATTER_ND_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleScatterNdGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SCATTER_ND_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSegmentSum.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSegmentSum.h
new file mode 100644
index 000000000..7c33dee41
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSegmentSum.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SEGMENT_SUM_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SEGMENT_SUM_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleSegmentSumGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SEGMENT_SUM_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSelect.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSelect.h
new file mode 100644
index 000000000..87bd1a7fe
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSelect.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SELECT_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SELECT_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleSelectGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SELECT_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSelectV2.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSelectV2.h
new file mode 100644
index 000000000..28c73b087
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSelectV2.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SELECT_V2_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SELECT_V2_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleSelectV2GraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SELECT_V2_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleShape.h b/compiler/luci/import/include/luci/Import/Nodes/CircleShape.h
new file mode 100644
index 000000000..3002084a5
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleShape.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SHAPE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SHAPE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleShapeGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SHAPE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSin.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSin.h
new file mode 100644
index 000000000..605f5a5a0
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSin.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SIN_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SIN_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleSinGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SIN_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSlice.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSlice.h
new file mode 100644
index 000000000..3bb4c51b7
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSlice.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SLICE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SLICE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleSliceGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SLICE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSoftmax.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSoftmax.h
new file mode 100644
index 000000000..b93846d67
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSoftmax.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SOFTMAX_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SOFTMAX_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleSoftmaxGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SOFTMAX_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSpaceToBatchND.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSpaceToBatchND.h
new file mode 100644
index 000000000..b8723098d
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSpaceToBatchND.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SPACETOBATCHND_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SPACETOBATCHND_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleSpaceToBatchNDGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SPACETOBATCHND_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSpaceToDepth.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSpaceToDepth.h
new file mode 100644
index 000000000..75a54dd26
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSpaceToDepth.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SPACETODEPTH_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SPACETODEPTH_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleSpaceToDepthGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SPACETODEPTH_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSparseToDense.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSparseToDense.h
new file mode 100644
index 000000000..baf240919
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSparseToDense.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SPARSETODENSE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SPARSETODENSE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleSparseToDenseGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SPARSETODENSE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSplit.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSplit.h
new file mode 100644
index 000000000..3395e40fd
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSplit.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SPLIT_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SPLIT_H__
+
+#include "luci/Import/GraphBuilderBase.h"
+
+namespace luci
+{
+
+class CircleSplitGraphBuilder : public GraphBuilderBase
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+ void build(const circle::OperatorT &op, GraphBuilderContext *context) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SPLIT_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSplitV.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSplitV.h
new file mode 100644
index 000000000..3e53df362
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSplitV.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SPLIT_V_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SPLIT_V_H__
+
+#include "luci/Import/GraphBuilderBase.h"
+
+namespace luci
+{
+
+class CircleSplitVGraphBuilder : public GraphBuilderBase
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+ void build(const circle::OperatorT &op, GraphBuilderContext *context) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SPLIT_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSqrt.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSqrt.h
new file mode 100644
index 000000000..4fd79951c
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSqrt.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SQRT_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SQRT_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleSqrtGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SQRT_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSquare.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSquare.h
new file mode 100644
index 000000000..3a1299102
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSquare.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SQUARE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SQUARE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleSquareGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SQUARE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSquaredDifference.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSquaredDifference.h
new file mode 100644
index 000000000..95f08412b
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSquaredDifference.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SQUAREDDIFFERENCE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SQUAREDDIFFERENCE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleSquaredDifferenceGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SQUAREDDIFFERENCE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSqueeze.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSqueeze.h
new file mode 100644
index 000000000..4f0dfb5ef
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSqueeze.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SQUEEZE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SQUEEZE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleSqueezeGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SQUEEZE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleStridedSlice.h b/compiler/luci/import/include/luci/Import/Nodes/CircleStridedSlice.h
new file mode 100644
index 000000000..f535c3a61
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleStridedSlice.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_STRIDED_SLICE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_STRIDED_SLICE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleStridedSliceGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_STRIDED_SLICE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSub.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSub.h
new file mode 100644
index 000000000..315d1c2f9
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSub.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SUB_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SUB_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleSubGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SUB_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSum.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSum.h
new file mode 100644
index 000000000..e65dd46ad
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSum.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_SUM_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SUM_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleSumGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SUM_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleTanh.h b/compiler/luci/import/include/luci/Import/Nodes/CircleTanh.h
new file mode 100644
index 000000000..b3795acba
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleTanh.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_TANH_H__
+#define __LUCI_IMPORT_OP_CIRCLE_TANH_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleTanhGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_TANH_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleTile.h b/compiler/luci/import/include/luci/Import/Nodes/CircleTile.h
new file mode 100644
index 000000000..1da6cdbde
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleTile.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_TILE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_TILE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleTileGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_TILE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleTopKV2.h b/compiler/luci/import/include/luci/Import/Nodes/CircleTopKV2.h
new file mode 100644
index 000000000..8ec3f3311
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleTopKV2.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_TOPK_V2_H__
+#define __LUCI_IMPORT_OP_CIRCLE_TOPK_V2_H__
+
+#include "luci/Import/GraphBuilderBase.h"
+
+namespace luci
+{
+
+class CircleTopKV2GraphBuilder : public GraphBuilderBase
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+ void build(const circle::OperatorT &op, GraphBuilderContext *context) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_TOPK_V2_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleTranspose.h b/compiler/luci/import/include/luci/Import/Nodes/CircleTranspose.h
new file mode 100644
index 000000000..ac0f1fb41
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleTranspose.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_TRANSPOSE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_TRANSPOSE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleTransposeGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const override;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_TRANSPOSE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleTransposeConv.h b/compiler/luci/import/include/luci/Import/Nodes/CircleTransposeConv.h
new file mode 100644
index 000000000..2614d0d0d
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleTransposeConv.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_TRANSPOSE_CONV_H__
+#define __LUCI_IMPORT_OP_CIRCLE_TRANSPOSE_CONV_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleTransposeConvGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_TRANSPOSE_CONV_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleUnidirectionalSequenceLSTM.h b/compiler/luci/import/include/luci/Import/Nodes/CircleUnidirectionalSequenceLSTM.h
new file mode 100644
index 000000000..4cc3320dc
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleUnidirectionalSequenceLSTM.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_UNIDIRECTIONALSEQUENCELSTM_H__
+#define __LUCI_IMPORT_OP_CIRCLE_UNIDIRECTIONALSEQUENCELSTM_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleUnidirectionalSequenceLSTMGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_UNIDIRECTIONALSEQUENCELSTM_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleUnique.h b/compiler/luci/import/include/luci/Import/Nodes/CircleUnique.h
new file mode 100644
index 000000000..ed5b5035d
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleUnique.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_UNIQUE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_UNIQUE_H__
+
+#include "luci/Import/GraphBuilderBase.h"
+
+namespace luci
+{
+
+class CircleUniqueGraphBuilder : public GraphBuilderBase
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+ void build(const circle::OperatorT &op, GraphBuilderContext *context) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_UNIQUE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleUnpack.h b/compiler/luci/import/include/luci/Import/Nodes/CircleUnpack.h
new file mode 100644
index 000000000..f1a21de22
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleUnpack.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_UNPACK_H__
+#define __LUCI_IMPORT_OP_CIRCLE_UNPACK_H__
+
+#include "luci/Import/GraphBuilderBase.h"
+
+namespace luci
+{
+
+class CircleUnpackGraphBuilder : public GraphBuilderBase
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+ void build(const circle::OperatorT &op, GraphBuilderContext *context) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_UNPACK_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleWhere.h b/compiler/luci/import/include/luci/Import/Nodes/CircleWhere.h
new file mode 100644
index 000000000..72f98ef92
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleWhere.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_WHERE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_WHERE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleWhereGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const override;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_WHERE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleWhile.h b/compiler/luci/import/include/luci/Import/Nodes/CircleWhile.h
new file mode 100644
index 000000000..68c56b3c6
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleWhile.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_WHILE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_WHILE_H__
+
+#include "luci/Import/GraphBuilderBase.h"
+
+namespace luci
+{
+
+class CircleWhileGraphBuilder : public GraphBuilderBase
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+ void build(const circle::OperatorT &op, GraphBuilderContext *context) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_WHILE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleZerosLike.h b/compiler/luci/import/include/luci/Import/Nodes/CircleZerosLike.h
new file mode 100644
index 000000000..2a3410379
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleZerosLike.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORT_OP_CIRCLE_ZEROS_LIKE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_ZEROS_LIKE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleZerosLikeGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const override;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_ZEROS_LIKE_H__
diff --git a/compiler/luci/import/include/luci/Importer.h b/compiler/luci/import/include/luci/Importer.h
new file mode 100644
index 000000000..f08ddcda7
--- /dev/null
+++ b/compiler/luci/import/include/luci/Importer.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IMPORTER_H__
+#define __LUCI_IMPORTER_H__
+
+#include "luci/Import/GraphBuilderRegistry.h"
+
+#include "luci/IR/Module.h"
+
+#include <loco.h>
+
+#include <mio/circle/schema_generated.h>
+
+#include <memory>
+
+namespace luci
+{
+
+class Importer final
+{
+public:
+ Importer();
+
+public:
+ explicit Importer(const GraphBuilderSource *source) : _source{source}
+ {
+ // DO NOTHING
+ }
+
+public:
+ std::unique_ptr<loco::Graph> import(const circle::Model *model) const;
+ std::unique_ptr<Module> importModule(const circle::Model *model) const;
+
+private:
+ const GraphBuilderSource *_source = nullptr;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORTER_H__
diff --git a/compiler/luci/import/src/CircleReader.cpp b/compiler/luci/import/src/CircleReader.cpp
new file mode 100644
index 000000000..068de5239
--- /dev/null
+++ b/compiler/luci/import/src/CircleReader.cpp
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/CircleReader.h"
+
+#include <memory>
+#include <sstream>
+#include <string>
+
+namespace luci
+{
+
+bool is_valid(const circle::OperatorCodeT &opcode)
+{
+ circle::BuiltinOperator code = opcode.builtin_code;
+ return (circle::BuiltinOperator_MIN <= code && code <= circle::BuiltinOperator_MAX);
+}
+
+bool is_custom(const circle::OperatorCodeT &opcode)
+{
+ circle::BuiltinOperator code = opcode.builtin_code;
+ return (code == circle::BuiltinOperator_CUSTOM);
+}
+
+std::string opcode_name(const circle::OperatorCodeT &opcode)
+{
+ if (!is_valid(opcode))
+ {
+ std::ostringstream oss;
+ oss << "(invalid)";
+ return oss.str();
+ }
+
+ if (is_custom(opcode))
+ {
+ if (opcode.custom_code.empty())
+ return "(invalid custom)";
+
+ return opcode.custom_code;
+ }
+
+ circle::BuiltinOperator code = opcode.builtin_code;
+ return circle::EnumNameBuiltinOperator(code);
+}
+
+const char *tensor_name(const circle::TensorT &tensor)
+{
+ static const char *kEmptyTensorName = "(noname)";
+
+ if (!tensor.name.empty())
+ return tensor.name.c_str();
+
+ return kEmptyTensorName;
+}
+
+const circle::QuantizationParametersT *tensor_quantization(const circle::TensorT &tensor)
+{
+ return tensor.quantization.get();
+}
+
+loco::DataType luci_datatype(const circle::TensorType type)
+{
+ switch (type)
+ {
+ case circle::TensorType_FLOAT32:
+ return loco::DataType::FLOAT32;
+ case circle::TensorType_FLOAT16:
+ return loco::DataType::FLOAT16;
+ case circle::TensorType_INT32:
+ return loco::DataType::S32;
+ case circle::TensorType_UINT8:
+ return loco::DataType::U8;
+ case circle::TensorType_INT64:
+ return loco::DataType::S64;
+ case circle::TensorType_STRING:
+ break;
+ case circle::TensorType_BOOL:
+ return loco::DataType::BOOL;
+ case circle::TensorType_INT16:
+ return loco::DataType::S16;
+ case circle::TensorType_COMPLEX64:
+ break;
+ case circle::TensorType_INT8:
+ return loco::DataType::S8;
+ default:
+ break;
+ }
+ assert(false);
+ return loco::DataType::Unknown;
+}
+
+FusedActFunc luci_actfunc(const circle::ActivationFunctionType type)
+{
+ switch (type)
+ {
+ case circle::ActivationFunctionType::ActivationFunctionType_NONE:
+ return luci::FusedActFunc::NONE;
+ case circle::ActivationFunctionType::ActivationFunctionType_RELU:
+ return luci::FusedActFunc::RELU;
+ case circle::ActivationFunctionType::ActivationFunctionType_RELU_N1_TO_1:
+ return luci::FusedActFunc::RELU_N1_TO_1;
+ case circle::ActivationFunctionType::ActivationFunctionType_RELU6:
+ return luci::FusedActFunc::RELU6;
+ case circle::ActivationFunctionType::ActivationFunctionType_TANH:
+ return luci::FusedActFunc::TANH;
+ case circle::ActivationFunctionType::ActivationFunctionType_SIGN_BIT:
+ return luci::FusedActFunc::SIGN_BIT;
+ default:
+ break;
+ }
+ assert(false);
+ return luci::FusedActFunc::UNDEFINED;
+}
+
+Padding luci_padding(const circle::Padding padding)
+{
+ switch (padding)
+ {
+ case circle::Padding::Padding_SAME:
+ return Padding::SAME;
+ case circle::Padding::Padding_VALID:
+ return Padding::VALID;
+ }
+ assert(false);
+ return Padding::UNDEFINED;
+}
+
+MirrorPadMode luci_mirrorpad_mode(const circle::MirrorPadMode mode)
+{
+ switch (mode)
+ {
+ case circle::MirrorPadMode::MirrorPadMode_REFLECT:
+ return MirrorPadMode::REFLECT;
+ case circle::MirrorPadMode::MirrorPadMode_SYMMETRIC:
+ return MirrorPadMode::SYMMETRIC;
+ }
+ assert(false);
+ return MirrorPadMode::UNDEFINED;
+}
+
+DimensionType luci_dim_type(const circle::DimensionType dim_type)
+{
+ switch (dim_type)
+ {
+ case circle::DimensionType_DENSE:
+ return DimensionType::DENSE;
+ case circle::DimensionType_SPARSE_CSR:
+ return DimensionType::SPARSE_CSR;
+ default:
+ throw std::runtime_error("Invalid DimensionType");
+ }
+}
+
+SparseIndexVector
+luci_sparse_index_vector(const circle::SparseIndexVectorUnion &sparse_index_vector)
+{
+ switch (sparse_index_vector.type)
+ {
+ case circle::SparseIndexVector_NONE:
+ return SparseIndexVector{SparseIndexVectorType::NONE, nullptr};
+ case circle::SparseIndexVector_Int32Vector:
+ {
+ const auto const_vec_ptr =
+ static_cast<const void *>(&(sparse_index_vector.AsInt32Vector()->values));
+ return SparseIndexVector{SparseIndexVectorType::I32, const_vec_ptr};
+ }
+ case circle::SparseIndexVector_Uint16Vector:
+ {
+ const auto const_vec_ptr =
+ static_cast<const void *>(&(sparse_index_vector.AsUint16Vector()->values));
+ return SparseIndexVector{SparseIndexVectorType::U16, const_vec_ptr};
+ }
+ case circle::SparseIndexVector_Uint8Vector:
+ {
+ const auto const_vec_ptr =
+ static_cast<const void *>(&(sparse_index_vector.AsUint8Vector()->values));
+ return SparseIndexVector{SparseIndexVectorType::U8, const_vec_ptr};
+ }
+ default:
+ throw std::runtime_error("Invalid SparseIndexVector type");
+ }
+}
+
+std::unique_ptr<CircleQuantParam>
+luci_quantparam(const circle::QuantizationParametersT *quantization)
+{
+ const auto &min = quantization->min;
+ const auto &max = quantization->max;
+ const auto &scale = quantization->scale;
+ const auto &zero_point = quantization->zero_point;
+ const auto &quantized_dimension = quantization->quantized_dimension;
+
+ if ((!min.empty() && !max.empty()) || (!scale.empty() && !zero_point.empty()))
+ {
+ auto quantparam = std::make_unique<CircleQuantParam>();
+
+ quantparam->min = min;
+ quantparam->max = max;
+ quantparam->scale = scale;
+ quantparam->zerop = zero_point;
+ quantparam->quantized_dimension = quantized_dimension;
+
+ return quantparam;
+ }
+
+ return nullptr;
+}
+
+std::unique_ptr<SparsityParam> luci_sparsityparam(const circle::SparsityParametersT *sparsity)
+{
+ assert(sparsity);
+ const auto &traversal_order = sparsity->traversal_order;
+ const auto &block_map = sparsity->block_map;
+ const auto &dim_metadata = sparsity->dim_metadata;
+
+ // TODO find a condition that should return nullptr
+ auto sparsityparam = std::make_unique<SparsityParam>();
+
+ sparsityparam->traversal_order = traversal_order;
+ sparsityparam->block_map = block_map;
+ for (const auto &dm : dim_metadata)
+ {
+ sparsityparam->dim_metadata.emplace_back(luci_dim_type(dm->format), dm->dense_size,
+ luci_sparse_index_vector(dm->array_segments),
+ luci_sparse_index_vector(dm->array_indices));
+ }
+
+ return sparsityparam;
+}
+
+void copy_tensor_attributes(const circle::TensorT &tensor, CircleNode *node)
+{
+ node->name(tensor_name(tensor));
+ node->dtype(luci_datatype(tensor.type));
+
+ std::vector<int32_t> dims = tensor.shape; // in NHWC
+ node->rank(dims.size());
+ for (uint32_t r = 0; r < dims.size(); ++r)
+ {
+ node->dim(r) = loco::Dimension(dims[r]);
+ }
+
+ node->shape_signature(tensor.shape_signature);
+
+ const auto *quantization = tensor.quantization.get();
+ if (quantization != nullptr)
+ {
+ auto quantparam = luci_quantparam(quantization);
+ if (quantparam)
+ node->quantparam(std::move(quantparam));
+ }
+
+ const auto *sparsity = tensor.sparsity.get();
+ if (sparsity != nullptr)
+ {
+ auto sparsityparam = luci_sparsityparam(sparsity);
+ if (sparsityparam)
+ node->sparsityparam(std::move(sparsityparam));
+ }
+}
+
+circle::BuiltinOperator CircleReader::builtin_code(const circle::OperatorT &op) const
+{
+ const auto &op_codes = opcodes();
+ uint32_t index = op.opcode_index;
+ assert(index < op_codes.size());
+ const circle::OperatorCodeT &opcode = *op_codes[index];
+
+ return opcode.builtin_code;
+}
+
+std::string CircleReader::opcode_name(const circle::OperatorT &op) const
+{
+ const auto &op_codes = opcodes();
+ uint32_t index = op.opcode_index;
+ assert(index < op_codes.size());
+ const circle::OperatorCodeT &opcode = *op_codes[index];
+
+ if (!is_valid(opcode))
+ {
+ std::ostringstream oss;
+ oss << "(invalid: " << index << ")";
+ return oss.str();
+ }
+
+ return ::luci::opcode_name(opcode);
+}
+
+bool CircleReader::parse(const circle::Model *model)
+{
+ assert(model != nullptr);
+
+ _model.reset(model->UnPack());
+
+ // for direct pointer access
+ _model_ptr = model;
+
+ return true;
+}
+
+bool CircleReader::select_subgraph(uint32_t sgindex)
+{
+ if (_model->subgraphs.size() <= sgindex)
+ {
+ assert(false);
+ return false;
+ }
+
+ _current_subgraph = _model->subgraphs[sgindex].get();
+
+ // for direct pointer access
+ auto subgraphs = _model_ptr->subgraphs();
+ const circle::SubGraph *subgraph = (*subgraphs)[sgindex];
+
+ _tensors_ptr = subgraph->tensors();
+
+ return true;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/GraphBuilder.cpp b/compiler/luci/import/src/GraphBuilder.cpp
new file mode 100644
index 000000000..80a9f986a
--- /dev/null
+++ b/compiler/luci/import/src/GraphBuilder.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/GraphBuilder.h"
+
+#include <luci/Log.h>
+
+namespace luci
+{
+
+void GraphBuilder::build(const circle::OperatorT &op, GraphBuilderContext *context) const
+{
+ LOGGER(l);
+
+ assert(context != nullptr);
+
+ const std::vector<int32_t> &inputs = op.inputs;
+ const std::vector<int32_t> &outputs = op.outputs;
+ const auto &tensors = context->reader()->tensors();
+ const auto &opcodes = context->reader()->opcodes();
+ auto tensors_ptr = context->reader()->tensors_ptr();
+ assert(tensors_ptr != nullptr);
+
+ std::vector<CircleNode *> input_nodes;
+ for (const int32_t input_tensor_index : inputs)
+ {
+ if (input_tensor_index >= 0)
+ {
+ auto input = context->nodefinder()->node(input_tensor_index);
+ if (input == nullptr)
+ INFO(l) << "[luci] Warning: input node is null " << input_tensor_index << std::endl;
+ input_nodes.push_back(input);
+ }
+ else
+ {
+ // If there is no tensor, insert CircleOutputExclude.
+ input_nodes.push_back(context->graph()->nodes()->create<luci::CircleOutputExclude>());
+ }
+ }
+
+ CircleNode *node = build_node(op, input_nodes, context->graph());
+
+ // Set up node parameters.
+ assert(outputs.size() == 1);
+ {
+ const circle::TensorT &output_tensor = *tensors[outputs[0]];
+ copy_tensor_attributes(output_tensor, node);
+ // mark shape_status
+ if (tensors_ptr->Get(outputs[0])->shape() == nullptr)
+ node->shape_status(ShapeStatus::NOSHAPE);
+ else
+ node->shape_status(ShapeStatus::VALID);
+
+ // mark operator version
+ node->op_version(opcodes[op.opcode_index].get()->version);
+ }
+
+ // Register node's only output.
+ assert(outputs.size() == 1);
+ {
+ context->nodefinder()->enroll(outputs[0], node);
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/GraphBuilderContext.cpp b/compiler/luci/import/src/GraphBuilderContext.cpp
new file mode 100644
index 000000000..21adfa7e2
--- /dev/null
+++ b/compiler/luci/import/src/GraphBuilderContext.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/GraphBuilderContext.h"
+
+#include <luci/Log.h>
+
+#include <oops/UserExn.h>
+
+namespace luci
+{
+
+void IndexNodeFinder::enroll(TensorIndex idx, CircleNode *node)
+{
+ auto iter = _table.find(idx);
+ if (iter != _table.end())
+ {
+ LOGGER(l);
+ INFO(l) << "[luci] NodeFinder SKIP (" << idx << ") " << node << ":" << node->name()
+ << " existing: " << iter->second << ":" << iter->second->name() << std::endl;
+ return;
+ }
+
+ _table[idx] = node;
+}
+
+CircleNode *IndexNodeFinder::node(TensorIndex idx) const
+{
+ MapIndexNode_t::const_iterator iter = _table.find(idx);
+
+ // dangle output node may exist that are not enrolled
+ return (iter != _table.end()) ? iter->second : nullptr;
+}
+
+void IndexTensorOutputs::enroll(TensorIndex idx)
+{
+ auto iter = _set.find(idx);
+ if (iter != _set.end())
+ {
+ LOGGER(l);
+ INFO(l) << "[luci] TensorOutputs SKIP (" << idx << ") existing" << std::endl;
+ return;
+ }
+ _set.insert(idx);
+}
+
+bool IndexTensorOutputs::find(TensorIndex idx) { return (_set.find(idx) != _set.end()); }
+
+} // namespace luci
diff --git a/compiler/luci/import/src/GraphBuilderRegistry.cpp b/compiler/luci/import/src/GraphBuilderRegistry.cpp
new file mode 100644
index 000000000..d598d30f4
--- /dev/null
+++ b/compiler/luci/import/src/GraphBuilderRegistry.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/GraphBuilderRegistry.h"
+
+#include "luci/Import/Nodes.h"
+
+#include <memory>
+
+namespace luci
+{
+
+GraphBuilderRegistry::GraphBuilderRegistry()
+{
+#define CIRCLE_NODE(OPCODE, CLASS) add(circle::BuiltinOperator_##OPCODE, std::make_unique<CLASS>());
+
+ CIRCLE_NODE(ABS, CircleAbsGraphBuilder); // 101
+ CIRCLE_NODE(ADD, CircleAddGraphBuilder); // 0
+ CIRCLE_NODE(ADD_N, CircleAddNGraphBuilder); // 106
+ CIRCLE_NODE(ARG_MAX, CircleArgMaxGraphBuilder); // 56
+ CIRCLE_NODE(ARG_MIN, CircleArgMinGraphBuilder); // 79
+ CIRCLE_NODE(AVERAGE_POOL_2D, CircleAveragePool2DGraphBuilder); // 1
+ CIRCLE_NODE(BATCH_MATMUL, CircleBatchMatMulGraphBuilder); // 126
+ CIRCLE_NODE(BATCH_TO_SPACE_ND, CircleBatchToSpaceNDGraphBuilder); // 37
+ CIRCLE_NODE(BCQ_FULLY_CONNECTED, CircleBCQFullyConnectedGraphBuilder); // 253
+ CIRCLE_NODE(BCQ_GATHER, CircleBCQGatherGraphBuilder); // 252
+ CIRCLE_NODE(CAST, CircleCastGraphBuilder); // 53
+ CIRCLE_NODE(CEIL, CircleCeilGraphBuilder); // 104
+ CIRCLE_NODE(CUSTOM, CircleCustomGraphBuilder); // 32
+ CIRCLE_NODE(CONCATENATION, CircleConcatenationGraphBuilder); // 2
+ CIRCLE_NODE(CONV_2D, CircleConv2DGraphBuilder); // 3
+ CIRCLE_NODE(COS, CircleCosGraphBuilder); // 108
+ CIRCLE_NODE(DEPTH_TO_SPACE, CircleDepthToSpaceGraphBuilder); // 5
+ CIRCLE_NODE(DEPTHWISE_CONV_2D, CircleDepthwiseConv2DGraphBuilder); // 4
+ CIRCLE_NODE(DEQUANTIZE, CircleDequantizeGraphBuilder); // 6
+ CIRCLE_NODE(DIV, CircleDivGraphBuilder); // 42
+ CIRCLE_NODE(ELU, CircleEluGraphBuilder); // 111
+ CIRCLE_NODE(EQUAL, CircleEqualGraphBuilder); // 71
+ CIRCLE_NODE(EXP, CircleExpGraphBuilder); // 47
+ CIRCLE_NODE(EXPAND_DIMS, CircleExpandDimsGraphBuilder); // 70
+ CIRCLE_NODE(FILL, CircleFillGraphBuilder); // 94
+ CIRCLE_NODE(FLOOR, CircleFloorGraphBuilder); // 8
+ CIRCLE_NODE(FLOOR_DIV, CircleFloorDivGraphBuilder); // 90
+ CIRCLE_NODE(FLOOR_MOD, CircleFloorModGraphBuilder); // 95
+ CIRCLE_NODE(FULLY_CONNECTED, CircleFullyConnectedGraphBuilder); // 9
+ CIRCLE_NODE(GATHER, CircleGatherGraphBuilder); // 36
+ CIRCLE_NODE(GATHER_ND, CircleGatherNdGraphBuilder); // 107
+ CIRCLE_NODE(GREATER, CircleGreaterGraphBuilder); // 61
+ CIRCLE_NODE(GREATER_EQUAL, CircleGreaterEqualGraphBuilder); // 62
+ 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(LESS, CircleLessGraphBuilder); // 58
+ CIRCLE_NODE(LESS_EQUAL, CircleLessEqualGraphBuilder); // 63
+ CIRCLE_NODE(LOCAL_RESPONSE_NORMALIZATION, CircleLocalResponseNormalizationGraphBuilder); // 13
+ CIRCLE_NODE(LOG, CircleLogGraphBuilder); // 73
+ CIRCLE_NODE(LOGICAL_AND, CircleLogicalAndGraphBuilder); // 86
+ CIRCLE_NODE(LOGICAL_NOT, CircleLogicalNotGraphBuilder); // 87
+ CIRCLE_NODE(LOGICAL_OR, CircleLogicalOrGraphBuilder); // 84
+ CIRCLE_NODE(LOGISTIC, CircleLogisticGraphBuilder); // 14
+ CIRCLE_NODE(LOG_SOFTMAX, CircleLogSoftmaxGraphBuilder); // 50
+ CIRCLE_NODE(MATRIX_DIAG, CircleMatrixDiagGraphBuilder); // 113
+ CIRCLE_NODE(MATRIX_SET_DIAG, CircleMatrixSetDiagGraphBuilder); // 115
+ CIRCLE_NODE(MAXIMUM, CircleMaximumGraphBuilder); // 55
+ CIRCLE_NODE(MAX_POOL_2D, CircleMaxPool2DGraphBuilder); // 17
+ CIRCLE_NODE(MEAN, CircleMeanGraphBuilder); // 40
+ CIRCLE_NODE(MINIMUM, CircleMinimumGraphBuilder); // 57
+ 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(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(RANGE, CircleRangeGraphBuilder); // 96
+ CIRCLE_NODE(RANK, CircleRankGraphBuilder); // 110
+ CIRCLE_NODE(REDUCE_ANY, CircleReduceAnyGraphBuilder); // 91
+ CIRCLE_NODE(REDUCE_MAX, CircleReduceMaxGraphBuilder); // 82
+ CIRCLE_NODE(REDUCE_MIN, CircleReduceMinGraphBuilder); // 89
+ CIRCLE_NODE(REDUCE_PROD, CircleReduceProdGraphBuilder); // 81
+ CIRCLE_NODE(RELU, CircleReluGraphBuilder); // 19
+ CIRCLE_NODE(RELU6, CircleRelu6GraphBuilder); // 21
+ CIRCLE_NODE(RELU_N1_TO_1, CircleReluN1To1GraphBuilder); // 20
+ CIRCLE_NODE(RESHAPE, CircleReshapeGraphBuilder); // 22
+ CIRCLE_NODE(RESIZE_BILINEAR, CircleResizeBilinearGraphBuilder); // 23
+ CIRCLE_NODE(RESIZE_NEAREST_NEIGHBOR, CircleResizeNearestNeighborGraphBuilder); // 97
+ CIRCLE_NODE(REVERSE_SEQUENCE, CircleReverseSequenceGraphBuilder); // 112
+ CIRCLE_NODE(REVERSE_V2, CircleReverseV2GraphBuilder); // 105
+ CIRCLE_NODE(ROUND, CircleRoundGraphBuilder); // 116
+ CIRCLE_NODE(RSQRT, CircleRsqrtGraphBuilder); // 76
+ CIRCLE_NODE(SCATTER_ND, CircleScatterNdGraphBuilder); // 122
+ CIRCLE_NODE(SEGMENT_SUM, CircleSegmentSumGraphBuilder); // 125
+ CIRCLE_NODE(SELECT, CircleSelectGraphBuilder); // 64
+ CIRCLE_NODE(SELECT_V2, CircleSelectV2GraphBuilder); // 123
+ CIRCLE_NODE(SHAPE, CircleShapeGraphBuilder); // 77
+ CIRCLE_NODE(SIN, CircleSinGraphBuilder); // 66
+ CIRCLE_NODE(SLICE, CircleSliceGraphBuilder); // 65
+ CIRCLE_NODE(SOFTMAX, CircleSoftmaxGraphBuilder); // 25
+ CIRCLE_NODE(SPACE_TO_BATCH_ND, CircleSpaceToBatchNDGraphBuilder); // 38
+ CIRCLE_NODE(SPACE_TO_DEPTH, CircleSpaceToDepthGraphBuilder); // 26
+ CIRCLE_NODE(SPARSE_TO_DENSE, CircleSparseToDenseGraphBuilder); // 68
+ CIRCLE_NODE(SPLIT, CircleSplitGraphBuilder); // 49
+ CIRCLE_NODE(SPLIT_V, CircleSplitVGraphBuilder); // 102
+ CIRCLE_NODE(SQRT, CircleSqrtGraphBuilder); // 75
+ CIRCLE_NODE(SQUARE, CircleSquareGraphBuilder); // 92
+ CIRCLE_NODE(SQUARED_DIFFERENCE, CircleSquaredDifferenceGraphBuilder); // 99
+ CIRCLE_NODE(SQUEEZE, CircleSqueezeGraphBuilder); // 43
+ CIRCLE_NODE(STRIDED_SLICE, CircleStridedSliceGraphBuilder); // 45
+ CIRCLE_NODE(SUB, CircleSubGraphBuilder); // 41
+ CIRCLE_NODE(SUM, CircleSumGraphBuilder); // 74
+ CIRCLE_NODE(TANH, CircleTanhGraphBuilder); // 28
+ CIRCLE_NODE(TILE, CircleTileGraphBuilder); // 69
+ CIRCLE_NODE(TOPK_V2, CircleTopKV2GraphBuilder); // 48
+ CIRCLE_NODE(TRANSPOSE, CircleTransposeGraphBuilder); // 39
+ CIRCLE_NODE(TRANSPOSE_CONV, CircleTransposeConvGraphBuilder); // 67
+ CIRCLE_NODE(UNIDIRECTIONAL_SEQUENCE_LSTM, CircleUnidirectionalSequenceLSTMGraphBuilder); // 44
+ CIRCLE_NODE(UNIQUE, CircleUniqueGraphBuilder); // 103
+ CIRCLE_NODE(UNPACK, CircleUnpackGraphBuilder); // 88
+ CIRCLE_NODE(WHERE, CircleWhereGraphBuilder); // 109
+ CIRCLE_NODE(WHILE, CircleWhileGraphBuilder); // 119
+ CIRCLE_NODE(ZEROS_LIKE, CircleZerosLikeGraphBuilder); // 93
+
+#undef CIRCLE_NODE
+
+ // BuiltinOperator_EMBEDDING_LOOKUP = 7,
+ // BuiltinOperator_HASHTABLE_LOOKUP = 10,
+ // BuiltinOperator_LSH_PROJECTION = 15,
+ // BuiltinOperator_LSTM = 16,
+ // BuiltinOperator_RNN = 24,
+ // BuiltinOperator_SVDF = 27,
+ // BuiltinOperator_CONCAT_EMBEDDINGS = 29,
+ // BuiltinOperator_SKIP_GRAM = 30,
+ // BuiltinOperator_CALL = 31,
+ // BuiltinOperator_EMBEDDING_LOOKUP_SPARSE = 33,
+ // BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_RNN = 35,
+ // BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN = 46,
+ // BuiltinOperator_DELEGATE = 51,
+ // BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM = 52,
+ // BuiltinOperator_ARG_MAX = 56,
+ // BuiltinOperator_FAKE_QUANT = 80,
+ // BuiltinOperator_QUANTIZE = 114,
+ // BuiltinOperator_HARD_SWISH = 117,
+ // BuiltinOperator_DENSIFY = 124,
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Importer.cpp b/compiler/luci/import/src/Importer.cpp
new file mode 100644
index 000000000..ab89f3587
--- /dev/null
+++ b/compiler/luci/import/src/Importer.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Importer.h"
+#include "PostImport.h"
+
+#include "luci/Import/GraphBuilder.h"
+#include "luci/Import/GraphBuilderContext.h"
+#include "luci/Import/GraphBuilderRegistry.h"
+#include "luci/Import/CircleReader.h"
+#include "luci/Import/Nodes/CircleConst.h"
+
+#include <luci/IR/Module.h>
+#include <luci/IR/CircleNodes.h>
+#include <luci/Log.h>
+#include <luci/LogHelper.h>
+
+#include <oops/InternalExn.h>
+#include <oops/UserExn.h>
+
+#include <memory>
+
+namespace
+{
+
+void convert_graph(const luci::GraphBuilderSource &source, luci::CircleReader &reader,
+ loco::Graph *graph)
+{
+ LOGGER(l);
+
+ auto nodefinder = std::make_unique<luci::IndexNodeFinder>();
+ auto tensoroutputs = std::make_unique<luci::IndexTensorOutputs>();
+
+ luci::GraphBuilderContext gb_context(graph, &reader, nodefinder.get(), tensoroutputs.get());
+
+ const auto &operators = reader.operators();
+ const auto &tensors = reader.tensors();
+ auto tensors_ptr = reader.tensors_ptr();
+ assert(tensors_ptr != nullptr);
+
+ // build a cache to identify if a tensor is output of an operator
+ // if this is set, we should not create a CircleConst for this tensor
+ for (uint32_t i = 0; i < operators.size(); ++i)
+ {
+ const circle::OperatorT &op = *operators[i];
+ const auto &outputs = op.outputs;
+
+ for (uint32_t j = 0; j < outputs.size(); ++j)
+ {
+ auto tidx = outputs[j];
+ tensoroutputs->enroll(tidx);
+ }
+ }
+
+ // graph inputs; there are no input nodes in TFlite but just Tensors
+ // creating virtual input nodes will make possible to connect nodes that uses them
+ // all attributes of tensor should be copied to CircleInput node
+ for (const auto input : reader.inputs())
+ {
+ auto input_node = graph->nodes()->create<luci::CircleInput>();
+ assert(input_node != nullptr);
+ const circle::TensorT &tensor = *tensors[input];
+
+ luci::copy_tensor_attributes(tensor, input_node);
+ if (tensors_ptr->Get(input)->shape() == nullptr)
+ input_node->shape_status(luci::ShapeStatus::NOSHAPE);
+ else
+ input_node->shape_status(luci::ShapeStatus::VALID);
+
+ INFO(l) << "[luci] NodeFinder INPUT(" << input << ") = " << input_node << std::endl;
+ nodefinder->enroll(input, input_node);
+
+ // input_node is also an output to a tensor
+ tensoroutputs->enroll(input);
+
+ // Name
+ auto graph_input = graph->inputs()->create();
+ graph_input->name(input_node->name());
+
+ // Set GraphInputOutputIndex for graph
+ input_node->index(graph_input->index());
+
+ // Data type
+ graph_input->dtype(input_node->dtype());
+
+ // Shape of GraphInput
+ auto input_shape = std::make_unique<loco::TensorShape>();
+ const std::vector<int32_t> &input_dims = tensor.shape; // in NHWC
+ input_shape->rank(input_dims.size());
+ for (uint32_t r = 0; r < input_dims.size(); ++r)
+ input_shape->dim(r) = loco::Dimension(input_dims[r]);
+ graph_input->shape(std::move(input_shape));
+ }
+
+ // Create CircleConst nodes for constant tensors.
+ for (uint32_t i = 0; i < tensors.size(); ++i)
+ {
+ luci::CircleConst *const_node = luci::create_circleconst(&gb_context, i);
+ if (const_node != nullptr)
+ nodefinder->enroll(i, const_node);
+ }
+
+ // Import the operators.
+ // Note that operators in model are stored in execution order. This means that when importing
+ // an operator, its input operators have already been imported. We exploit this fact to set up
+ // node's inputs right after creating the node.
+ for (uint32_t i = 0; i < operators.size(); ++i)
+ {
+ const circle::OperatorT &op = *operators[i];
+ circle::BuiltinOperator builtincode = reader.builtin_code(op);
+
+ if (const auto *builder = source.lookup(builtincode))
+ {
+ luci::GraphBuilder::ValidateArgs args(op, reader);
+ if (!builder->validate(args))
+ {
+ throw oops::UserExn("Invalid operator", reader.opcode_name(op));
+ }
+
+ builder->build(op, &gb_context);
+ }
+ else
+ {
+ throw oops::UserExn("Not supported", reader.opcode_name(op));
+ }
+ }
+
+ // graph outputs
+ for (auto output : reader.outputs())
+ {
+ const circle::TensorT &tensor = *tensors[output];
+
+ auto output_node = graph->nodes()->create<luci::CircleOutput>();
+ assert(output_node != nullptr);
+ auto output_from = nodefinder->node(output);
+ if (output_from != nullptr)
+ output_node->from(output_from);
+ else
+ {
+ // NOTE loco::Graph requires all input node(s) to a node should exist.
+ // Here, CircleOutput needs an input node.
+ // We add a dummy node to make it happy.
+ auto output_dummy = graph->nodes()->create<luci::CircleOutputDummy>();
+ assert(output_dummy != nullptr);
+ output_node->from(output_dummy);
+
+ luci::copy_tensor_attributes(tensor, output_dummy);
+ if (tensors_ptr->Get(output)->shape() == nullptr)
+ output_dummy->shape_status(luci::ShapeStatus::NOSHAPE);
+ else
+ output_dummy->shape_status(luci::ShapeStatus::VALID);
+ }
+
+ INFO(l) << "[luci] NodeFinder OUTPUT(" << output << ") = " << output_node << std::endl;
+
+ // set the graph output name and node object
+ auto graph_output = graph->outputs()->create();
+ std::string tname = luci::tensor_name(tensor);
+ graph_output->name("output_" + tname);
+
+ luci::copy_tensor_attributes(tensor, output_node);
+
+ // Set GraphInputOutputIndex for graph
+ output_node->index(graph_output->index());
+
+ // Shape of Output
+ auto output_shape = std::make_unique<loco::TensorShape>();
+ const std::vector<int32_t> &output_dims = tensor.shape; // in NHWC
+ output_shape->rank(output_dims.size());
+ for (uint32_t r = 0; r < output_dims.size(); ++r)
+ output_shape->dim(r) = loco::Dimension(output_dims[r]);
+ graph_output->shape(std::move(output_shape));
+
+ // Data type
+ auto dtype = luci::luci_datatype(tensor.type);
+ graph_output->dtype(dtype);
+ }
+}
+
+class ValidateCollector final : public loco::ErrorListener
+{
+public:
+ void notify(const loco::ErrorDetail<loco::ErrorCategory::MissingArgument> &d) override
+ {
+ LOGGER(l);
+ INFO(l) << "[luci] GraphValidate error " << d.node() << "(" << d.index() << ")" << std::endl;
+ }
+};
+
+} // namespace
+
+namespace luci
+{
+
+Importer::Importer()
+{
+ // DO NOTHING
+}
+
+std::unique_ptr<loco::Graph> Importer::import(const circle::Model *model) const
+{
+ auto graph = loco::make_graph();
+
+ const GraphBuilderSource *source_ptr = &GraphBuilderRegistry::get();
+
+ if (_source != nullptr)
+ {
+ // Use user-defined GraphBuilderSource
+ source_ptr = _source;
+ }
+
+ CircleReader reader;
+ if (!reader.parse(model))
+ return nullptr;
+
+ if (reader.num_subgraph() != 1)
+ {
+ INTERNAL_EXN("Use 'importModule()' for multiple subgraphs");
+ }
+ if (!reader.select_subgraph(0))
+ return nullptr;
+
+ // Convert circle::Model to loco::Graph
+ convert_graph(*source_ptr, reader, graph.get());
+
+ LOGGER(l);
+ VERBOSE(l, 3) << "--- graph dump begin -------------------------------------------";
+ VERBOSE(l, 3) << "Name: " << graph->name();
+ VERBOSE(l, 3) << fmt(graph.get());
+ VERBOSE(l, 3) << "--- graph dump end ---------------------------------------------";
+
+ assert(loco::valid(graph.get(), std::make_unique<ValidateCollector>()));
+
+ return graph;
+}
+
+std::unique_ptr<Module> Importer::importModule(const circle::Model *model) const
+{
+ auto module = make_module();
+
+ const GraphBuilderSource *source_ptr = &GraphBuilderRegistry::get();
+
+ if (_source != nullptr)
+ {
+ // Use user-defined GraphBuilderSource
+ source_ptr = _source;
+ }
+
+ CircleReader reader;
+ if (!reader.parse(model))
+ return nullptr;
+
+ for (uint32_t g = 0; g < reader.num_subgraph(); ++g)
+ {
+ auto graph = loco::make_graph();
+
+ if (!reader.select_subgraph(g))
+ return nullptr;
+
+ graph->name(reader.name());
+
+ // Convert circle::Model to loco::Graph
+ convert_graph(*source_ptr, reader, graph.get());
+
+ LOGGER(l);
+ VERBOSE(l, 3) << "--- graph dump begin -------------------------------------------";
+ VERBOSE(l, 3) << "Name: " << graph->name();
+ VERBOSE(l, 3) << fmt(graph.get());
+ VERBOSE(l, 3) << "--- graph dump end ---------------------------------------------";
+
+ assert(loco::valid(graph.get(), std::make_unique<ValidateCollector>()));
+
+ module->add(std::move(graph));
+ }
+
+ post_import_graph(module.get(), reader);
+
+ return module;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Importer.test.cpp b/compiler/luci/import/src/Importer.test.cpp
new file mode 100644
index 000000000..8366546f0
--- /dev/null
+++ b/compiler/luci/import/src/Importer.test.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Importer.h"
+
+#include <loco.h>
+
+#include <gtest/gtest.h>
+
+TEST(TensorFlowLiteImport, Dummy)
+{
+ luci::Importer import;
+
+ SUCCEED();
+}
diff --git a/compiler/luci/import/src/Nodes/CircleAbs.cpp b/compiler/luci/import/src/Nodes/CircleAbs.cpp
new file mode 100644
index 000000000..3556dc7fa
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleAbs.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleAbs.h"
+
+#include <luci/IR/Nodes/CircleAbs.h>
+
+#include <loco.h>
+
+namespace luci
+{
+bool CircleAbsGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ // TODO Support type check
+ return true;
+}
+
+CircleNode *CircleAbsGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleAbs>();
+ node->x(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleAdd.cpp b/compiler/luci/import/src/Nodes/CircleAdd.cpp
new file mode 100644
index 000000000..b767d4af2
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleAdd.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleAdd.h"
+
+#include <luci/IR/Nodes/CircleAdd.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleAddGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleAddGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleAdd>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsAddOptions();
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleAddN.cpp b/compiler/luci/import/src/Nodes/CircleAddN.cpp
new file mode 100644
index 000000000..2f1716e62
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleAddN.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleAddN.h"
+
+#include <luci/IR/Nodes/CircleAdd.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleAddNGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() < 1)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleAddNGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleAddN>(inputs.size());
+ for (uint32_t i = 0; i < inputs.size(); ++i)
+ {
+ node->inputs(i, inputs[i]);
+ }
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleArgMax.cpp b/compiler/luci/import/src/Nodes/CircleArgMax.cpp
new file mode 100644
index 000000000..10e8516f4
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleArgMax.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleArgMax.h"
+
+#include <luci/IR/Nodes/CircleArgMax.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleArgMaxGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleArgMaxGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleArgMax>();
+ node->input(inputs.at(0));
+ node->dimension(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsArgMaxOptions();
+ node->output_type(luci_datatype(options->output_type));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleArgMin.cpp b/compiler/luci/import/src/Nodes/CircleArgMin.cpp
new file mode 100644
index 000000000..5ff534dbb
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleArgMin.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleArgMin.h"
+
+#include <luci/IR/Nodes/CircleArgMin.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleArgMinGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleArgMinGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleArgMin>();
+ node->input(inputs.at(0));
+ node->dimension(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsArgMinOptions();
+ node->output_type(luci_datatype(options->output_type));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleAveragePool2D.cpp b/compiler/luci/import/src/Nodes/CircleAveragePool2D.cpp
new file mode 100644
index 000000000..ad011f71f
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleAveragePool2D.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleAveragePool2D.h"
+
+#include <luci/IR/Nodes/CircleAveragePool2D.h>
+
+namespace luci
+{
+
+bool CircleAveragePool2DGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleAveragePool2DGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleAveragePool2D>();
+ node->value(inputs.at(0));
+
+ const auto *options = op.builtin_options.AsPool2DOptions();
+ node->padding(luci_padding(options->padding));
+ node->stride()->w(options->stride_w);
+ node->stride()->h(options->stride_h);
+ node->filter()->w(options->filter_width);
+ node->filter()->h(options->filter_height);
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleBCQFullyConnected.cpp b/compiler/luci/import/src/Nodes/CircleBCQFullyConnected.cpp
new file mode 100644
index 000000000..16ecebd5c
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleBCQFullyConnected.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleBCQFullyConnected.h"
+
+#include <luci/IR/Nodes/CircleBCQFullyConnected.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleBCQFullyConnectedGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 5)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleBCQFullyConnectedGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleBCQFullyConnected>();
+
+ node->input(inputs.at(0));
+ node->weights_scales(inputs.at(1));
+ node->weights_binary(inputs.at(2));
+ node->bias(inputs.at(3));
+ node->weights_clusters(inputs.at(4));
+
+ // TODO Find and move to appropriate place for setting optional input
+ if (auto bias = dynamic_cast<luci::CircleOutputExclude *>(node->bias()))
+ {
+ // bias is not used for type inference, but node itself should have a type
+ bias->dtype(loco::DataType::FLOAT32);
+
+ // bias is not used for shape inference
+ }
+
+ const auto *options = op.builtin_options.AsBCQFullyConnectedOptions();
+ node->weights_hidden_size(options->weights_hidden_size);
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleBCQGather.cpp b/compiler/luci/import/src/Nodes/CircleBCQGather.cpp
new file mode 100644
index 000000000..464f1ac18
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleBCQGather.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleBCQGather.h"
+
+#include <luci/IR/Nodes/CircleBCQGather.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleBCQGatherGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 4)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleBCQGatherGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleBCQGather>();
+
+ node->input_scales(inputs.at(0));
+ node->input_binary(inputs.at(1));
+ node->indices(inputs.at(2));
+ node->input_clusters(inputs.at(3));
+
+ const auto *options = op.builtin_options.AsBCQGatherOptions();
+ node->input_hidden_size(options->input_hidden_size);
+ node->axis(options->axis);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleBatchMatMul.cpp b/compiler/luci/import/src/Nodes/CircleBatchMatMul.cpp
new file mode 100644
index 000000000..330775691
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleBatchMatMul.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleBatchMatMul.h"
+
+#include <luci/IR/Nodes/CircleBatchMatMul.h>
+
+namespace luci
+{
+
+bool CircleBatchMatMulGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleBatchMatMulGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleBatchMatMul>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsBatchMatMulOptions();
+ node->adj_x(options->adjoint_lhs);
+ node->adj_y(options->adjoint_rhs);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleBatchToSpaceND.cpp b/compiler/luci/import/src/Nodes/CircleBatchToSpaceND.cpp
new file mode 100644
index 000000000..7faab141c
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleBatchToSpaceND.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleBatchToSpaceND.h"
+
+#include <luci/IR/Nodes/CircleBatchToSpaceND.h>
+
+#include "ValidateHelpers.h"
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleBatchToSpaceNDGraphBuilder::validate(const ValidateArgs &args) const
+{
+ return validate_batch_space_nd(args);
+}
+
+CircleNode *CircleBatchToSpaceNDGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleBatchToSpaceND>();
+ node->input(inputs.at(0));
+ node->block_shape(inputs.at(1));
+ node->crops(inputs.at(2));
+
+ // No options for BatchToSpaceND
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleCast.cpp b/compiler/luci/import/src/Nodes/CircleCast.cpp
new file mode 100644
index 000000000..7bdb63044
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleCast.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleCast.h"
+
+#include <luci/IR/Nodes/CircleCast.h>
+
+#include <luci/UserSettings.h>
+#include <luci/Log.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleCastGraphBuilder::validate(const ValidateArgs &args) const
+{
+ LOGGER(l);
+
+ auto settings = luci::UserSettings::settings();
+
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+ if (inputs.size() != 1)
+ return false;
+ if (outputs.size() != 1)
+ return false;
+
+ // NOTE real models do have type mismatch
+ const auto *options = args.op.builtin_options.AsCastOptions();
+ if (options != nullptr)
+ {
+ const auto &tensors = args.reader.tensors();
+ const circle::TensorT &output_tensor = *tensors[outputs[0]];
+ auto name = tensor_name(output_tensor);
+
+ const auto &tensor_in = tensors.at(inputs.at(0));
+ if (tensor_in->type != options->in_data_type)
+ {
+ if (settings->get(luci::UserSettings::Key::DisableValidation))
+ {
+ WARN(l) << "Warning: import Cast(" << name << ") dtype mismatch";
+ }
+ else
+ return false;
+ }
+ const auto &tensor_out = tensors.at(outputs[0]);
+ if (tensor_out->type != options->out_data_type)
+ {
+ if (settings->get(luci::UserSettings::Key::DisableValidation))
+ {
+ WARN(l) << "Warning: import Cast(" << name << ") dtype mismatch";
+ }
+ else
+ return false;
+ }
+ }
+
+ return true;
+}
+
+CircleNode *CircleCastGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleCast>();
+ node->x(inputs.at(0));
+
+ const auto *options = op.builtin_options.AsCastOptions();
+ if (options != nullptr)
+ {
+ node->in_data_type(luci_datatype(options->in_data_type));
+ node->out_data_type(luci_datatype(options->out_data_type));
+ }
+ else
+ {
+ node->in_data_type(inputs.at(0)->dtype());
+ node->out_data_type(loco::DataType::Unknown);
+ // type inference should use node->dtype() for Unknown
+ // export should use BuiltinOptions_NONE for Unknown
+ }
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleCeil.cpp b/compiler/luci/import/src/Nodes/CircleCeil.cpp
new file mode 100644
index 000000000..2e1aaa295
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleCeil.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleCeil.h"
+
+#include <luci/IR/Nodes/CircleCeil.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleCeilGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+ if (inputs.size() != 1)
+ return false;
+ if (outputs.size() != 1)
+ return false;
+
+ // TODO dtype check
+
+ return true;
+}
+
+CircleNode *CircleCeilGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleCeil>();
+ node->x(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleConcatenation.cpp b/compiler/luci/import/src/Nodes/CircleConcatenation.cpp
new file mode 100644
index 000000000..7fc616aa0
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleConcatenation.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleConcatenation.h"
+
+#include <luci/IR/Nodes/CircleConcatenation.h>
+
+namespace luci
+{
+
+bool CircleConcatenationGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() < 1)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleConcatenationGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleConcatenation>(inputs.size());
+ for (uint32_t i = 0; i < inputs.size(); ++i)
+ {
+ node->values(i, inputs[i]);
+ }
+
+ const auto *options = op.builtin_options.AsConcatenationOptions();
+ node->axis(options->axis);
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleConst.cpp b/compiler/luci/import/src/Nodes/CircleConst.cpp
new file mode 100644
index 000000000..f69448dfe
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleConst.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleConst.h"
+
+#include <luci/IR/Nodes/CircleConst.h>
+#include <luci/Log.h>
+
+#include <loco.h>
+#include <oops/UserExn.h>
+
+#include <cassert>
+
+namespace
+{
+
+std::ostream &operator<<(std::ostream &os, const std::vector<int32_t> &vect)
+{
+ uint32_t seq = 0;
+ for (auto &v : vect)
+ {
+ if (seq)
+ os << ", ";
+ os << v;
+ seq++;
+ }
+ return os;
+}
+
+} // namespace
+
+namespace luci
+{
+
+template <loco::DataType DT>
+static void copy_data(const std::vector<uint8_t> &raw_data, uint32_t num_elements,
+ CircleConst *const_node)
+{
+ using T = typename loco::DataTypeImpl<DT>::Type;
+
+ // TODO calculate the exact buffer size of sparse tensor
+ if (const_node->sparsityparam())
+ {
+ num_elements = raw_data.size() / sizeof(T);
+ }
+
+ assert(raw_data.size() == num_elements * sizeof(T));
+ const auto *data = reinterpret_cast<const T *>(raw_data.data());
+
+ const_node->size<DT>(num_elements);
+ for (uint32_t i = 0; i < num_elements; ++i)
+ {
+ const_node->at<DT>(i) = data[i];
+ }
+}
+
+CircleConst *create_circleconst(GraphBuilderContext *context, int32_t tensor_index)
+{
+ LOGGER(l);
+
+ auto graph = context->graph();
+ auto reader = context->reader();
+ const auto &tensors = reader->tensors();
+ const circle::TensorT &const_tensor = *tensors[tensor_index];
+
+ const std::vector<uint8_t> &buffer = reader->buffers()[const_tensor.buffer]->data;
+ std::vector<int32_t> const_dims = const_tensor.shape; // in NHWC
+ if (const_dims.size() == 0 && buffer.empty())
+ {
+ // unknown shape tensor and scalar tensor
+ return nullptr;
+ }
+
+ // if tensor_index is used as output to some other operator, this is not a constant
+ auto tensoroutputs = context->tensoroutputs();
+ if (tensoroutputs->find(tensor_index))
+ {
+ // other operator output tensor
+ return nullptr;
+ }
+
+ uint32_t num_elements = 1;
+ for (uint32_t r = 0; r < const_dims.size(); ++r)
+ {
+ num_elements = num_elements * const_dims[r];
+ }
+
+ if (buffer.empty() && num_elements > 0)
+ {
+ // normal empty tensor
+ return nullptr;
+ }
+
+ auto const_node = graph->nodes()->create<CircleConst>();
+ copy_tensor_attributes(const_tensor, const_node);
+ const_node->shape_status(luci::ShapeStatus::VALID);
+ INFO(l) << "[luci] NodeFinder const_node(" << tensor_index << ") -> " << const_node << " "
+ << const_dims << std::endl;
+ if (num_elements > 0)
+ {
+ switch (luci_datatype(const_tensor.type))
+ {
+ case loco::DataType::FLOAT32:
+ copy_data<loco::DataType::FLOAT32>(buffer, num_elements, const_node);
+ break;
+
+ case loco::DataType::U8:
+ copy_data<loco::DataType::U8>(buffer, num_elements, const_node);
+ break;
+
+ case loco::DataType::S8:
+ copy_data<loco::DataType::S8>(buffer, num_elements, const_node);
+ break;
+
+ case loco::DataType::S16:
+ copy_data<loco::DataType::S16>(buffer, num_elements, const_node);
+ break;
+
+ case loco::DataType::S32:
+ copy_data<loco::DataType::S32>(buffer, num_elements, const_node);
+ break;
+
+ case loco::DataType::S64:
+ copy_data<loco::DataType::S64>(buffer, num_elements, const_node);
+ break;
+
+ case loco::DataType::BOOL:
+ copy_data<loco::DataType::BOOL>(buffer, num_elements, const_node);
+ break;
+
+ default:
+ throw oops::UserExn("Unsupported tensor type",
+ circle::EnumNameTensorType(const_tensor.type));
+ }
+ }
+
+ return const_node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleConv2D.cpp b/compiler/luci/import/src/Nodes/CircleConv2D.cpp
new file mode 100644
index 000000000..9516ef16a
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleConv2D.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleConv2D.h"
+
+#include <luci/IR/Nodes/CircleConv2D.h>
+
+#include <loco.h>
+
+#include <cassert>
+
+namespace luci
+{
+
+bool CircleConv2DGraphBuilder::validate(const ValidateArgs &args) const
+{
+ // Circle Conv2D may not have a bias but we won't support this
+ if (args.op.inputs.size() != 3)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleConv2DGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleConv2D>();
+ node->input(inputs.at(0));
+ node->filter(inputs.at(1));
+ // For now, bias is required (checked in `verify` method).
+ assert(inputs.size() == 3);
+ node->bias(inputs.at(2));
+
+ const auto *options = op.builtin_options.AsConv2DOptions();
+ 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));
+ node->dilation()->w(options->dilation_w_factor);
+ node->dilation()->h(options->dilation_h_factor);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleCos.cpp b/compiler/luci/import/src/Nodes/CircleCos.cpp
new file mode 100644
index 000000000..27d60c62c
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleCos.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleCos.h"
+
+#include <luci/IR/Nodes/CircleCos.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleCosGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleCosGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleCos>();
+ node->x(inputs.at(0));
+
+ // No options for Cos
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleCustom.cpp b/compiler/luci/import/src/Nodes/CircleCustom.cpp
new file mode 100644
index 000000000..d541ee87b
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleCustom.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleCustom.h"
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleCustomGraphBuilder::validate(const ValidateArgs &) const
+{
+ // DO NOTHING
+ return true;
+}
+
+void CircleCustomGraphBuilder::build(const circle::OperatorT &op,
+ GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ auto graph = context->graph();
+
+ const std::vector<int32_t> &inputs = op.inputs;
+ const std::vector<int32_t> &outputs = op.outputs;
+ const auto &tensors = context->reader()->tensors();
+ auto tensors_ptr = context->reader()->tensors_ptr();
+ assert(tensors_ptr != nullptr);
+
+ // Create CircleCustom
+ const auto &opcodes = context->reader()->opcodes();
+ const uint32_t opcode_index = op.opcode_index;
+ const circle::OperatorCodeT &opcode = *opcodes[opcode_index];
+
+ auto *node = graph->nodes()->create<CircleCustom>(inputs.size());
+ uint32_t input_idx = 0;
+ for (const int32_t input_tensor_index : inputs)
+ {
+ node->inputs(input_idx++, context->nodefinder()->node(input_tensor_index));
+ }
+ node->custom_options(std::vector<uint8_t>{op.custom_options.begin(), op.custom_options.end()});
+ node->custom_code(opcode.custom_code);
+ // Operator version of custom is always 1, so do nothing
+
+ uint32_t output_count = outputs.size();
+
+ assert(output_count > 0);
+ {
+ // Let's use attributes from output 0 for this node
+ const circle::TensorT &output_tensor = *tensors[outputs[0]];
+ node->name(tensor_name(output_tensor));
+ node->dtype(luci_datatype(output_tensor.type));
+ }
+
+ // Create virtual outputs of Custom
+ for (uint32_t n = 0; n < output_count; ++n)
+ {
+ const circle::TensorT &output_tensor = *tensors[outputs[n]];
+
+ auto *nodeout = graph->nodes()->create<CircleCustomOut>();
+ copy_tensor_attributes(output_tensor, nodeout);
+ // mark shape_status
+ if (tensors_ptr->Get(outputs[n])->shape() == nullptr)
+ nodeout->shape_status(ShapeStatus::NOSHAPE);
+ else
+ nodeout->shape_status(ShapeStatus::VALID);
+
+ nodeout->input(node);
+ nodeout->index(n);
+
+ context->nodefinder()->enroll(outputs[n], nodeout);
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleDepthToSpace.cpp b/compiler/luci/import/src/Nodes/CircleDepthToSpace.cpp
new file mode 100644
index 000000000..49d31bb99
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleDepthToSpace.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleDepthToSpace.h"
+
+#include <luci/IR/Nodes/CircleDepthToSpace.h>
+
+#include <loco.h>
+
+#include <cassert>
+
+namespace luci
+{
+
+bool CircleDepthToSpaceGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ const auto *options = args.op.builtin_options.AsDepthToSpaceOptions();
+
+ if (inputs.size() != 1)
+ return false;
+
+ if (outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+
+ if (tensors[outputs[0]]->type != tensors[inputs.at(0)]->type)
+ {
+ return false;
+ }
+
+ if (options->block_size < 2)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleDepthToSpaceGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleDepthToSpace>();
+ node->input(inputs.at(0));
+
+ const auto *options = op.builtin_options.AsDepthToSpaceOptions();
+ node->block_size(options->block_size);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleDepthwiseConv2D.cpp b/compiler/luci/import/src/Nodes/CircleDepthwiseConv2D.cpp
new file mode 100644
index 000000000..53f85f2f5
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleDepthwiseConv2D.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleDepthwiseConv2D.h"
+
+#include <luci/IR/Nodes/CircleDepthwiseConv2D.h>
+
+#include <oops/UserExn.h>
+
+namespace luci
+{
+
+bool CircleDepthwiseConv2DGraphBuilder::validate(const ValidateArgs &args) const
+{
+ // Circle DepthwiseConv2D may not have a bias but we won't support this
+ if (args.op.inputs.size() != 3 && args.op.inputs.size() != 2)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleDepthwiseConv2DGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleDepthwiseConv2D>();
+ node->input(inputs.at(0));
+ node->filter(inputs.at(1));
+ if (inputs.size() != 3)
+ throw oops::UserExn("DepthwiseConv2d without bias is unsupported");
+ node->bias(inputs.at(2));
+
+ const auto *options = op.builtin_options.AsDepthwiseConv2DOptions();
+ node->padding(luci_padding(options->padding));
+ node->stride()->w(options->stride_w);
+ node->stride()->h(options->stride_h);
+ node->depthMultiplier(options->depth_multiplier);
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+ node->dilation()->w(options->dilation_w_factor);
+ node->dilation()->h(options->dilation_h_factor);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleDequantize.cpp b/compiler/luci/import/src/Nodes/CircleDequantize.cpp
new file mode 100644
index 000000000..1936da97c
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleDequantize.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleDequantize.h"
+
+#include <luci/IR/Nodes/CircleDequantize.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleDequantizeGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleDequantizeGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleDequantize>();
+ node->input(inputs.at(0));
+
+ // No options for Dequantize
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleDiv.cpp b/compiler/luci/import/src/Nodes/CircleDiv.cpp
new file mode 100644
index 000000000..615c224d7
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleDiv.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleDiv.h"
+
+#include <luci/IR/Nodes/CircleDiv.h>
+
+namespace luci
+{
+
+bool CircleDivGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleDivGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto node = graph->nodes()->create<CircleDiv>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsDivOptions();
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleElu.cpp b/compiler/luci/import/src/Nodes/CircleElu.cpp
new file mode 100644
index 000000000..919e95ee4
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleElu.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleElu.h"
+
+#include <luci/IR/Nodes/CircleElu.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleEluGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 1)
+ return false;
+
+ if (outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs.at(0));
+
+ switch (tensor->type)
+ {
+ case circle::TensorType_FLOAT32:
+ break;
+ default:
+ return false;
+ }
+
+ if (tensors[outputs[0]]->type != tensor->type)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleEluGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleElu>();
+ node->features(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleEqual.cpp b/compiler/luci/import/src/Nodes/CircleEqual.cpp
new file mode 100644
index 000000000..1db33b8ac
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleEqual.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleEqual.h"
+
+#include <luci/IR/Nodes/CircleEqual.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleEqualGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+
+ if (inputs.size() != 2)
+ {
+ return false;
+ }
+
+ const auto &tensors = args.reader.tensors();
+
+ return tensors[inputs.at(0)]->type == tensors[inputs.at(1)]->type;
+}
+
+CircleNode *CircleEqualGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleEqual>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleExp.cpp b/compiler/luci/import/src/Nodes/CircleExp.cpp
new file mode 100644
index 000000000..2c031d6b3
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleExp.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleExp.h"
+
+#include <luci/IR/Nodes/CircleExp.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleExpGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ if (inputs.size() != 1)
+ return false;
+
+ // input type check
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs.at(0));
+ switch (tensor->type)
+ {
+ case circle::TensorType_FLOAT16:
+ case circle::TensorType_FLOAT32:
+ case circle::TensorType_FLOAT64:
+ break;
+ // TODO support TensorType_COMPLEX64, complex128, bfloat16
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+CircleNode *CircleExpGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleExp>();
+ node->x(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleExpandDims.cpp b/compiler/luci/import/src/Nodes/CircleExpandDims.cpp
new file mode 100644
index 000000000..ab537c710
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleExpandDims.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleExpandDims.h"
+
+#include <luci/IR/Nodes/CircleExpandDims.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleExpandDimsGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+
+ if (inputs.size() != 2)
+ {
+ return false;
+ }
+
+ const auto &tensors = args.reader.tensors();
+
+ return tensors[inputs.at(1)]->type == circle::TensorType_INT32;
+}
+
+CircleNode *CircleExpandDimsGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleExpandDims>();
+ node->input(inputs.at(0));
+ node->axis(inputs.at(1));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleFill.cpp b/compiler/luci/import/src/Nodes/CircleFill.cpp
new file mode 100644
index 000000000..95d5b876b
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleFill.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleFill.h"
+
+#include <luci/IR/Nodes/CircleFill.h>
+
+namespace luci
+{
+
+bool CircleFillGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleFillGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleFill>();
+ node->dims(inputs.at(0));
+ node->value(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsFillOptions();
+ (void)options;
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleFloor.cpp b/compiler/luci/import/src/Nodes/CircleFloor.cpp
new file mode 100644
index 000000000..ce756b3b1
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleFloor.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleFloor.h"
+
+#include <luci/IR/Nodes/CircleFloor.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleFloorGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+ if (inputs.size() != 1)
+ return false;
+ if (outputs.size() != 1)
+ return false;
+
+ // TODO dtype check
+
+ return true;
+}
+
+CircleNode *CircleFloorGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleFloor>();
+ node->x(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleFloorDiv.cpp b/compiler/luci/import/src/Nodes/CircleFloorDiv.cpp
new file mode 100644
index 000000000..55f385d60
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleFloorDiv.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleFloorDiv.h"
+
+#include <luci/IR/Nodes/CircleFloorDiv.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleFloorDivGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 2)
+ {
+ return false;
+ }
+
+ if (outputs.size() != 1)
+ {
+ return false;
+ }
+
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor_in_0 = tensors.at(inputs.at(0));
+ const auto &tensor_in_1 = tensors.at(inputs.at(1));
+ const auto &tensor_out = tensors.at(outputs[0]);
+
+ if (tensor_in_0->type != tensor_in_1->type)
+ return false;
+
+ if (tensor_out->type != tensor_in_1->type)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+CircleNode *CircleFloorDivGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleFloorDiv>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleFloorMod.cpp b/compiler/luci/import/src/Nodes/CircleFloorMod.cpp
new file mode 100644
index 000000000..2101e417e
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleFloorMod.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleFloorMod.h"
+
+#include <luci/IR/Nodes/CircleFloorMod.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleFloorModGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+ if (inputs.size() != 2)
+ return false;
+ if (outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor_in_0 = tensors.at(inputs.at(0));
+ const auto &tensor_in_1 = tensors.at(inputs.at(1));
+ if (tensor_in_0->type != tensor_in_1->type)
+ return false;
+
+ // TODO dtype check
+
+ return true;
+}
+
+CircleNode *CircleFloorModGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleFloorMod>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleFullyConnected.cpp b/compiler/luci/import/src/Nodes/CircleFullyConnected.cpp
new file mode 100644
index 000000000..65a863bde
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleFullyConnected.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleFullyConnected.h"
+
+#include <luci/IR/Nodes/CircleFullyConnected.h>
+#include <luci/IR/Nodes/CircleOutput.h>
+
+#include <loco.h>
+#include <oops/UserExn.h>
+
+namespace luci
+{
+
+bool CircleFullyConnectedGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 3)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleFullyConnectedGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleFullyConnected>();
+ node->input(inputs.at(0));
+ node->weights(inputs.at(1));
+ node->bias(inputs.at(2)); // bias is optional
+
+ // TODO Find and move to appropriate place for setting optional input
+ if (auto bias = dynamic_cast<luci::CircleOutputExclude *>(node->bias()))
+ {
+ // bias is not used for type inference, but node itself should have a type
+ bias->dtype(loco::DataType::FLOAT32);
+
+ // bias is not used for shape inference
+ }
+
+ const auto *options = op.builtin_options.AsFullyConnectedOptions();
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+ if (options->weights_format != circle::FullyConnectedOptionsWeightsFormat_DEFAULT)
+ {
+ throw oops::UserExn(
+ "Unsupported weights format",
+ circle::EnumNameFullyConnectedOptionsWeightsFormat(options->weights_format));
+ }
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleGather.cpp b/compiler/luci/import/src/Nodes/CircleGather.cpp
new file mode 100644
index 000000000..75447a38a
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleGather.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleGather.h"
+
+#include <luci/IR/Nodes/CircleGather.h>
+
+#include <loco.h>
+#include <oops/UserExn.h>
+
+namespace luci
+{
+
+bool CircleGatherGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+ const auto *options = args.op.builtin_options.AsGatherOptions();
+
+ int32_t axis = options->axis;
+
+ if (inputs.size() != 2)
+ return false;
+
+ if (outputs.size() != 1)
+ return false;
+
+ if (axis < 0)
+ axis += inputs.size();
+
+ if (axis < 0)
+ return false;
+
+ // TODO do indices type check
+ // TODO do axis check when shape information is given
+
+ return true;
+}
+
+CircleNode *CircleGatherGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleGather>();
+
+ node->params(inputs.at(0));
+ node->indices(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsGatherOptions();
+ node->axis(options->axis);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleGatherNd.cpp b/compiler/luci/import/src/Nodes/CircleGatherNd.cpp
new file mode 100644
index 000000000..981adbf63
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleGatherNd.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleGatherNd.h"
+
+#include <luci/IR/Nodes/CircleGatherNd.h>
+
+#include <loco.h>
+#include <oops/UserExn.h>
+#include <mio/circle/schema_generated.h>
+
+namespace luci
+{
+
+bool CircleGatherNdGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 2)
+ return false;
+
+ if (outputs.size() != 1)
+ return false;
+
+ auto &indices_tensor = args.reader.tensors()[inputs.at(1)];
+
+ if (!(indices_tensor->type == circle::TensorType::TensorType_INT32 ||
+ indices_tensor->type == circle::TensorType::TensorType_INT64))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+CircleNode *CircleGatherNdGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleGatherNd>();
+
+ node->params(inputs.at(0));
+ node->indices(inputs.at(1));
+
+ // GatherNd options empty
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleGreater.cpp b/compiler/luci/import/src/Nodes/CircleGreater.cpp
new file mode 100644
index 000000000..1ad0467e4
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleGreater.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleGreater.h"
+
+#include <luci/IR/Nodes/CircleGreater.h>
+
+#include <luci/UserSettings.h>
+#include <luci/Log.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleGreaterGraphBuilder::validate(const ValidateArgs &args) const
+{
+ LOGGER(l);
+
+ auto settings = luci::UserSettings::settings();
+
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 2)
+ return false;
+
+ if (outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+
+ if (tensors[inputs.at(0)]->type != tensors[inputs.at(1)]->type)
+ return false;
+
+ // NOTE: real models do have output dtype NOT BOOL
+ if (tensors[outputs[0]]->type != circle::TensorType_BOOL)
+ {
+ if (settings->get(luci::UserSettings::Key::DisableValidation))
+ {
+ const circle::TensorT &output_tensor = *tensors[outputs[0]];
+ auto name = tensor_name(output_tensor);
+ WARN(l) << "Warning: import Greater(" << name << ") output dtype is not boolean";
+ }
+ else
+ return false;
+ }
+
+ return true;
+}
+
+CircleNode *CircleGreaterGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleGreater>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleGreaterEqual.cpp b/compiler/luci/import/src/Nodes/CircleGreaterEqual.cpp
new file mode 100644
index 000000000..0ac63b017
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleGreaterEqual.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleGreaterEqual.h"
+
+#include <luci/IR/Nodes/CircleGreaterEqual.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleGreaterEqualGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 2)
+ {
+ return false;
+ }
+
+ if (outputs.size() != 1)
+ {
+ return false;
+ }
+
+ const auto &tensors = args.reader.tensors();
+
+ if (tensors[inputs.at(0)]->type != tensors[inputs.at(1)]->type)
+ {
+ return false;
+ }
+
+ return tensors[outputs[0]]->type == circle::TensorType::TensorType_BOOL;
+}
+
+CircleNode *CircleGreaterEqualGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleGreaterEqual>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleIf.cpp b/compiler/luci/import/src/Nodes/CircleIf.cpp
new file mode 100644
index 000000000..db9ffe1cd
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleIf.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleIf.h"
+
+#include <luci/IR/Nodes/CircleIf.h>
+#include <luci/IR/Nodes/CircleIfOut.h>
+
+#include <loco.h>
+#include <oops/UserExn.h>
+
+namespace luci
+{
+
+bool CircleIfGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto *options = args.op.builtin_options.AsIfOptions();
+
+ if (inputs.size() < 2) // cond + input
+ return false;
+ if (args.op.outputs.size() < 1) // output
+ return false;
+
+ auto num_graphs = static_cast<int32_t>(args.reader.num_subgraph());
+ if (options->then_subgraph_index >= num_graphs)
+ return false;
+ if (options->else_subgraph_index >= num_graphs)
+ return false;
+
+ // input 0 should be BOOL type
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs.at(0));
+ if (tensor->type != circle::TensorType_BOOL)
+ return false;
+
+ const auto &shape = tensor->shape;
+ if (shape.size() != 1 && shape.size() != 0)
+ return false;
+
+ return true;
+}
+
+/**
+ * @brief If Node builder
+ *
+ * @note Current loco does not provide multiple outputs
+ * We will create multiple CircleIfOut nodes to emulate this
+ * For two outputs that may look like this
+ *
+ * --- CircleIf --- Node ---
+ * \- Node ---
+ *
+ * will be created like this
+ *
+ * --- CircleIf --- CircleIfOut --- Node ---
+ * \- CircleIfOut --- Node ---
+ */
+
+void CircleIfGraphBuilder::build(const circle::OperatorT &op, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ auto graph = context->graph();
+
+ const std::vector<int32_t> &inputs = op.inputs;
+ const std::vector<int32_t> &outputs = op.outputs;
+ const auto &tensors = context->reader()->tensors();
+ const auto &opcodes = context->reader()->opcodes();
+ auto tensors_ptr = context->reader()->tensors_ptr();
+ assert(tensors_ptr != nullptr);
+
+ std::vector<CircleNode *> input_nodes;
+ for (const int32_t input_tensor_index : inputs)
+ {
+ input_nodes.push_back(context->nodefinder()->node(input_tensor_index));
+ }
+
+ uint32_t input_count = inputs.size() - 1;
+ uint32_t output_count = outputs.size();
+
+ // Create CircleIf
+ CircleIf *node = graph->nodes()->create<CircleIf>(input_count, output_count);
+
+ node->cond(input_nodes[0]);
+ for (uint32_t idx = 0; idx < input_count; ++idx)
+ {
+ node->input(idx, input_nodes[idx + 1]);
+ }
+
+ const auto *options = op.builtin_options.AsIfOptions();
+ node->then_branch(options->then_subgraph_index);
+ node->else_branch(options->else_subgraph_index);
+
+ assert(outputs.size() > 0);
+ {
+ // Lets use name of output 0 as If name
+ const circle::TensorT &output_tensor = *tensors[outputs[0]];
+ node->name(tensor_name(output_tensor));
+ node->op_version(opcodes[op.opcode_index].get()->version);
+
+ // NOTE We don't set quantization for If itself but to virtual outputs
+ }
+
+ // Create virtual outputs of If
+ for (uint32_t n = 0; n < output_count; ++n)
+ {
+ const circle::TensorT &output_tensor = *tensors[outputs[n]];
+
+ auto *nodeout = graph->nodes()->create<CircleIfOut>();
+ copy_tensor_attributes(output_tensor, nodeout);
+ // mark shape_status
+ if (tensors_ptr->Get(outputs[n])->shape() == nullptr)
+ nodeout->shape_status(ShapeStatus::NOSHAPE);
+ else
+ nodeout->shape_status(ShapeStatus::VALID);
+
+ nodeout->input(node);
+ nodeout->index(n);
+
+ context->nodefinder()->enroll(outputs[n], nodeout);
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleInstanceNorm.cpp b/compiler/luci/import/src/Nodes/CircleInstanceNorm.cpp
new file mode 100644
index 000000000..6349fd3b7
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleInstanceNorm.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleInstanceNorm.h"
+
+#include <luci/IR/Nodes/CircleInstanceNorm.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleInstanceNormGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 3)
+ return false;
+
+ // TODO check dtypes
+
+ return true;
+}
+
+CircleNode *CircleInstanceNormGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleInstanceNorm>();
+ node->input(inputs.at(0));
+ node->gamma(inputs.at(1));
+ node->beta(inputs.at(2));
+
+ const auto *options = op.builtin_options.AsInstanceNormOptions();
+ node->epsilon(options->epsilon);
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleL2Normalize.cpp b/compiler/luci/import/src/Nodes/CircleL2Normalize.cpp
new file mode 100644
index 000000000..e4fdc200c
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleL2Normalize.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleL2Normalize.h"
+
+#include <luci/IR/Nodes/CircleL2Normalize.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleL2NormalizeGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 1)
+ {
+ return false;
+ }
+
+ if (outputs.size() != 1)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+CircleNode *CircleL2NormalizeGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleL2Normalize>();
+ node->x(inputs.at(0));
+ const auto *options = op.builtin_options.AsL2NormOptions();
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleL2Pool2D.cpp b/compiler/luci/import/src/Nodes/CircleL2Pool2D.cpp
new file mode 100644
index 000000000..202d9d6fb
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleL2Pool2D.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleL2Pool2D.h"
+
+#include <luci/IR/Nodes/CircleL2Pool2D.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleL2Pool2DGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ // TODO check dtypes
+
+ return true;
+}
+
+CircleNode *CircleL2Pool2DGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleL2Pool2D>();
+ node->value(inputs.at(0));
+
+ const auto *options = op.builtin_options.AsPool2DOptions();
+ node->padding(luci_padding(options->padding));
+ node->stride()->w(options->stride_w);
+ node->stride()->h(options->stride_h);
+ node->filter()->w(options->filter_width);
+ node->filter()->h(options->filter_height);
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleLeakyRelu.cpp b/compiler/luci/import/src/Nodes/CircleLeakyRelu.cpp
new file mode 100644
index 000000000..ad4979f39
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleLeakyRelu.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleLeakyRelu.h"
+
+#include <luci/IR/Nodes/CircleLeakyRelu.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleLeakyReluGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleLeakyReluGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleLeakyRelu>();
+ node->features(inputs.at(0));
+
+ const auto *options = op.builtin_options.AsLeakyReluOptions();
+ node->alpha(options->alpha);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleLess.cpp b/compiler/luci/import/src/Nodes/CircleLess.cpp
new file mode 100644
index 000000000..506036908
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleLess.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleLess.h"
+
+#include <luci/IR/Nodes/CircleLess.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleLessGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 2)
+ {
+ return false;
+ }
+
+ if (outputs.size() != 1)
+ {
+ return false;
+ }
+
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs.at(0));
+
+ switch (tensor->type)
+ {
+ case circle::TensorType_FLOAT32:
+ case circle::TensorType_FLOAT64:
+ case circle::TensorType_INT32:
+ case circle::TensorType_UINT8:
+ case circle::TensorType_INT16:
+ case circle::TensorType_INT8:
+ case circle::TensorType_INT64:
+ case circle::TensorType_FLOAT16:
+ break;
+ default:
+ return false;
+ }
+
+ if (tensors[inputs.at(1)]->type != tensor->type)
+ {
+ return false;
+ }
+
+ return tensors[outputs[0]]->type == circle::TensorType_BOOL;
+}
+
+CircleNode *CircleLessGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleLess>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleLessEqual.cpp b/compiler/luci/import/src/Nodes/CircleLessEqual.cpp
new file mode 100644
index 000000000..9b4f934a5
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleLessEqual.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleLessEqual.h"
+
+#include <luci/IR/Nodes/CircleLessEqual.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleLessEqualGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 2)
+ {
+ return false;
+ }
+
+ if (outputs.size() != 1)
+ {
+ return false;
+ }
+
+ const auto &tensors = args.reader.tensors();
+
+ if (tensors[inputs.at(0)]->type != tensors[inputs.at(1)]->type)
+ {
+ return false;
+ }
+
+ return tensors[outputs[0]]->type == circle::TensorType::TensorType_BOOL;
+}
+
+CircleNode *CircleLessEqualGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleLessEqual>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleLocalResponseNormalization.cpp b/compiler/luci/import/src/Nodes/CircleLocalResponseNormalization.cpp
new file mode 100644
index 000000000..0e32f62de
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleLocalResponseNormalization.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleLocalResponseNormalization.h"
+
+#include <luci/IR/Nodes/CircleLocalResponseNormalization.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleLocalResponseNormalizationGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ // TODO do attribute checks
+
+ return true;
+}
+
+CircleNode *CircleLocalResponseNormalizationGraphBuilder::build_node(
+ const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleLocalResponseNormalization>();
+ node->input(inputs.at(0));
+
+ const auto *options = op.builtin_options.AsLocalResponseNormalizationOptions();
+ node->radius(options->radius);
+ node->bias(options->bias);
+ node->alpha(options->alpha);
+ node->beta(options->beta);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleLog.cpp b/compiler/luci/import/src/Nodes/CircleLog.cpp
new file mode 100644
index 000000000..346fc43bb
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleLog.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleLog.h"
+
+#include <luci/IR/Nodes/CircleLog.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleLogGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ if (inputs.size() != 1)
+ return false;
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ // input type check
+ // Must be one of bfloat16, half, float32, float64, complex64, complex128.
+ // Currently circle supports half(float16), float32, float64, complex64.
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs.at(0));
+ switch (tensor->type)
+ {
+ case circle::TensorType_FLOAT16:
+ case circle::TensorType_FLOAT32:
+ case circle::TensorType_FLOAT64:
+ case circle::TensorType_COMPLEX64:
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+CircleNode *CircleLogGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleLog>();
+ node->x(inputs.at(0));
+
+ // No options for Log
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleLogSoftmax.cpp b/compiler/luci/import/src/Nodes/CircleLogSoftmax.cpp
new file mode 100644
index 000000000..ef69e868a
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleLogSoftmax.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleLogSoftmax.h"
+
+#include <luci/IR/Nodes/CircleLogSoftmax.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleLogSoftmaxGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ // TODO do attribute checks
+
+ return true;
+}
+
+CircleNode *CircleLogSoftmaxGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleLogSoftmax>();
+ node->logits(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleLogicalAnd.cpp b/compiler/luci/import/src/Nodes/CircleLogicalAnd.cpp
new file mode 100644
index 000000000..7844da0f6
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleLogicalAnd.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleLogicalAnd.h"
+
+#include <luci/IR/Nodes/CircleLogicalAnd.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleLogicalAndGraphBuilder::validate(const ValidateArgs &args) const
+{
+ // Only BOOL type is allowed for inputs
+ const auto &inputs = args.op.inputs;
+ if (inputs.size() != 2)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ for (auto input : inputs)
+ {
+ const auto &tensor = tensors.at(input);
+ if (tensor->type != circle::TensorType::TensorType_BOOL)
+ return false;
+ }
+
+ return true;
+}
+
+CircleNode *CircleLogicalAndGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleLogicalAnd>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleLogicalNot.cpp b/compiler/luci/import/src/Nodes/CircleLogicalNot.cpp
new file mode 100644
index 000000000..3758642e4
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleLogicalNot.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleLogicalNot.h"
+
+#include <luci/IR/Nodes/CircleLogicalNot.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleLogicalNotGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ // Only BOOL type is allowed for the input
+ const auto &inputs = args.op.inputs;
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs.at(0));
+ if (tensor->type != circle::TensorType::TensorType_BOOL)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleLogicalNotGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleLogicalNot>();
+ node->x(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleLogicalOr.cpp b/compiler/luci/import/src/Nodes/CircleLogicalOr.cpp
new file mode 100644
index 000000000..1b87e6f9c
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleLogicalOr.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleLogicalOr.h"
+
+#include <luci/IR/Nodes/CircleLogicalOr.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleLogicalOrGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ // Only BOOL type is allowed for inputs
+ const auto &inputs = args.op.inputs;
+ const auto &tensors = args.reader.tensors();
+ for (auto input : inputs)
+ {
+ const auto &tensor = tensors.at(input);
+ if (tensor->type != circle::TensorType::TensorType_BOOL)
+ return false;
+ }
+
+ return true;
+}
+
+CircleNode *CircleLogicalOrGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleLogicalOr>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleLogistic.cpp b/compiler/luci/import/src/Nodes/CircleLogistic.cpp
new file mode 100644
index 000000000..9606e19cd
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleLogistic.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleLogistic.h"
+
+#include <luci/IR/Nodes/CircleLogistic.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleLogisticGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ if (inputs.size() != 1)
+ return false;
+ const auto &outputs = args.op.outputs;
+ if (outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ if (tensors.at(inputs.at(0))->type != tensors.at(outputs[0])->type)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleLogisticGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleLogistic>();
+ node->x(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleMatrixDiag.cpp b/compiler/luci/import/src/Nodes/CircleMatrixDiag.cpp
new file mode 100644
index 000000000..a4a21a8b7
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleMatrixDiag.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleMatrixDiag.h"
+
+#include <luci/IR/Nodes/CircleMatrixDiag.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleMatrixDiagGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 1)
+ return false;
+
+ if (outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs.at(0));
+
+ if (tensors[outputs[0]]->type != tensor->type)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleMatrixDiagGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleMatrixDiag>();
+ node->diagonal(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleMatrixSetDiag.cpp b/compiler/luci/import/src/Nodes/CircleMatrixSetDiag.cpp
new file mode 100644
index 000000000..cf0313149
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleMatrixSetDiag.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleMatrixSetDiag.h"
+
+#include <luci/IR/Nodes/CircleMatrixSetDiag.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleMatrixSetDiagGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 2)
+ return false;
+
+ if (outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs.at(0));
+
+ if (tensors[outputs[0]]->type != tensor->type)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleMatrixSetDiagGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleMatrixSetDiag>();
+ node->input(inputs.at(0));
+ node->diagonal(inputs.at(1));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleMaxPool2D.cpp b/compiler/luci/import/src/Nodes/CircleMaxPool2D.cpp
new file mode 100644
index 000000000..4bca0f40b
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleMaxPool2D.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleMaxPool2D.h"
+
+#include <luci/IR/Nodes/CircleMaxPool2D.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleMaxPool2DGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleMaxPool2DGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleMaxPool2D>();
+ node->value(inputs.at(0));
+
+ const auto *options = op.builtin_options.AsPool2DOptions();
+ node->padding(luci_padding(options->padding));
+ node->stride()->w(options->stride_w);
+ node->stride()->h(options->stride_h);
+ node->filter()->w(options->filter_width);
+ node->filter()->h(options->filter_height);
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleMaximum.cpp b/compiler/luci/import/src/Nodes/CircleMaximum.cpp
new file mode 100644
index 000000000..805d5bc89
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleMaximum.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleMaximum.h"
+
+#include <luci/IR/Nodes/CircleMaximum.h>
+
+#include "ValidateHelpers.h"
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleMaximumGraphBuilder::validate(const ValidateArgs &args) const
+{
+ return validate_minmax(args);
+}
+
+CircleNode *CircleMaximumGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleMaximum>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleMean.cpp b/compiler/luci/import/src/Nodes/CircleMean.cpp
new file mode 100644
index 000000000..d8fa9a53d
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleMean.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleMean.h"
+
+#include <luci/IR/Nodes/CircleMean.h>
+
+namespace luci
+{
+
+bool CircleMeanGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleMeanGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleMean>();
+ node->input(inputs.at(0));
+ node->reduction_indices(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsReducerOptions();
+ node->keep_dims(options->keep_dims);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleMinimum.cpp b/compiler/luci/import/src/Nodes/CircleMinimum.cpp
new file mode 100644
index 000000000..381039e88
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleMinimum.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleMinimum.h"
+
+#include <luci/IR/Nodes/CircleMinimum.h>
+
+#include "ValidateHelpers.h"
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleMinimumGraphBuilder::validate(const ValidateArgs &args) const
+{
+ return validate_minmax(args);
+}
+
+CircleNode *CircleMinimumGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleMinimum>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleMirrorPad.cpp b/compiler/luci/import/src/Nodes/CircleMirrorPad.cpp
new file mode 100644
index 000000000..e0ddd4c11
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleMirrorPad.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleMirrorPad.h"
+
+#include <luci/IR/Nodes/CircleMirrorPad.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleMirrorPadGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ // TODO check others
+
+ return true;
+}
+
+CircleNode *CircleMirrorPadGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleMirrorPad>();
+ node->input(inputs.at(0));
+ node->paddings(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsMirrorPadOptions();
+ node->mode(luci_mirrorpad_mode(options->mode));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleMul.cpp b/compiler/luci/import/src/Nodes/CircleMul.cpp
new file mode 100644
index 000000000..e3c4a7ee5
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleMul.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleMul.h"
+
+#include <luci/IR/Nodes/CircleMul.h>
+
+namespace luci
+{
+
+bool CircleMulGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleMulGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleMul>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsMulOptions();
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleNeg.cpp b/compiler/luci/import/src/Nodes/CircleNeg.cpp
new file mode 100644
index 000000000..a64a69560
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleNeg.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleNeg.h"
+
+#include <luci/IR/Nodes/CircleNeg.h>
+
+#include <loco.h>
+
+namespace luci
+{
+bool CircleNegGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ // TODO Support type check
+ return true;
+}
+
+CircleNode *CircleNegGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleNeg>();
+ node->x(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleNonMaxSuppressionV4.cpp b/compiler/luci/import/src/Nodes/CircleNonMaxSuppressionV4.cpp
new file mode 100644
index 000000000..a4ad4a53d
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleNonMaxSuppressionV4.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleNonMaxSuppressionV4.h"
+
+#include <luci/IR/Nodes/CircleNonMaxSuppressionV4.h>
+#include <luci/IR/Nodes/CircleNonMaxSuppressionV4Out.h>
+
+#include <loco.h>
+#include <oops/UserExn.h>
+
+namespace luci
+{
+
+bool CircleNonMaxSuppressionV4GraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 5)
+ return false;
+ if (outputs.size() != 2)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &boxes_tensor = tensors.at(inputs[0]);
+ if (boxes_tensor->shape.size() != 2)
+ return false;
+ if (boxes_tensor->shape.at(1) != 4)
+ return false;
+ if (boxes_tensor->shape.at(0) != tensors.at(inputs[1])->shape.at(0))
+ return false;
+
+ if (tensors.at(inputs[2])->type != circle::TensorType_INT32)
+ return false;
+ if (tensors.at(inputs[3])->type != circle::TensorType_FLOAT32)
+ return false;
+ if (tensors.at(inputs[4])->type != circle::TensorType_FLOAT32)
+ return false;
+
+ return true;
+}
+
+/**
+ * @brief NonMaxSuppressionV4 Node builder
+ *
+ * @note Current loco does not provide multiple outputs
+ * We will create multiple NonMasSuppressionV4Oout nodes to emulate this
+ */
+
+void CircleNonMaxSuppressionV4GraphBuilder::build(const circle::OperatorT &op,
+ GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ auto graph = context->graph();
+
+ const std::vector<int32_t> &inputs = op.inputs;
+ const std::vector<int32_t> &outputs = op.outputs;
+ const auto &tensors = context->reader()->tensors();
+ const auto &opcodes = context->reader()->opcodes();
+ auto tensors_ptr = context->reader()->tensors_ptr();
+ assert(tensors_ptr != nullptr);
+
+ std::vector<CircleNode *> input_nodes;
+ for (const int32_t input_tensor_index : inputs)
+ {
+ input_nodes.push_back(context->nodefinder()->node(input_tensor_index));
+ }
+
+ // Create CircleNonMaxSuppressionV4
+ auto node = graph->nodes()->create<CircleNonMaxSuppressionV4>();
+ node->boxes(input_nodes[0]);
+ node->scores(input_nodes[1]);
+ node->max_output_size(input_nodes[2]);
+ node->iou_threshold(input_nodes[3]);
+ node->score_threshold(input_nodes[4]);
+
+ assert(outputs.size() == 2);
+ {
+ // Let's use name of output 0 as NonMaxSuppressionV4 name
+ const circle::TensorT &output_tensor = *tensors[outputs[0]];
+ node->name(tensor_name(output_tensor));
+ node->op_version(opcodes[op.opcode_index].get()->version);
+
+ // NOTE We don't set quantization for NonMaxSuppressionV4 itself but to virtual outputs
+ }
+
+ // Create virtual outputs of NonMaxSuppressionV4
+ for (size_t n = 0; n < outputs.size(); ++n)
+ {
+ const circle::TensorT &output_tensor = *tensors[outputs[n]];
+
+ auto *nodeout = graph->nodes()->create<CircleNonMaxSuppressionV4Out>();
+ copy_tensor_attributes(output_tensor, nodeout);
+
+ // mark shape_status
+ if (tensors_ptr->Get(outputs[n])->shape() == nullptr)
+ nodeout->shape_status(ShapeStatus::NOSHAPE);
+ else
+ nodeout->shape_status(ShapeStatus::VALID);
+
+ nodeout->input(node);
+ nodeout->index(n);
+
+ context->nodefinder()->enroll(outputs[n], nodeout);
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleNonMaxSuppressionV5.cpp b/compiler/luci/import/src/Nodes/CircleNonMaxSuppressionV5.cpp
new file mode 100644
index 000000000..241dbf5ff
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleNonMaxSuppressionV5.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleNonMaxSuppressionV5.h"
+
+#include <luci/IR/Nodes/CircleNonMaxSuppressionV5.h>
+#include <luci/IR/Nodes/CircleNonMaxSuppressionV5Out.h>
+
+#include <loco.h>
+#include <oops/UserExn.h>
+
+namespace luci
+{
+
+bool CircleNonMaxSuppressionV5GraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 6)
+ return false;
+ if (outputs.size() != 3)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &boxes_tensor = tensors.at(inputs[0]);
+ if (boxes_tensor->shape.size() != 2)
+ return false;
+ if (boxes_tensor->shape.at(1) != 4)
+ return false;
+ if (boxes_tensor->shape.at(0) != tensors.at(inputs[1])->shape.at(0))
+ return false;
+
+ if (tensors.at(inputs[2])->type != circle::TensorType_INT32)
+ return false;
+ if (tensors.at(inputs[3])->type != circle::TensorType_FLOAT32)
+ return false;
+ if (tensors.at(inputs[4])->type != circle::TensorType_FLOAT32)
+ return false;
+ if (tensors.at(inputs[5])->type != circle::TensorType_FLOAT32)
+ return false;
+
+ return true;
+}
+
+/**
+ * @brief NonMaxSuppressionV5 Node builder
+ *
+ * @note Current loco does not provide multiple outputs
+ * We will create multiple NonMasSuppressionV5Oout nodes to emulate this
+ */
+
+void CircleNonMaxSuppressionV5GraphBuilder::build(const circle::OperatorT &op,
+ GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ auto graph = context->graph();
+
+ const std::vector<int32_t> &inputs = op.inputs;
+ const std::vector<int32_t> &outputs = op.outputs;
+ const auto &tensors = context->reader()->tensors();
+ const auto &opcodes = context->reader()->opcodes();
+ auto tensors_ptr = context->reader()->tensors_ptr();
+ assert(tensors_ptr != nullptr);
+
+ std::vector<CircleNode *> input_nodes;
+ for (const int32_t input_tensor_index : inputs)
+ {
+ input_nodes.push_back(context->nodefinder()->node(input_tensor_index));
+ }
+
+ // Create CircleNonMaxSuppressionV5
+ auto node = graph->nodes()->create<CircleNonMaxSuppressionV5>();
+ node->boxes(input_nodes[0]);
+ node->scores(input_nodes[1]);
+ node->max_output_size(input_nodes[2]);
+ node->iou_threshold(input_nodes[3]);
+ node->score_threshold(input_nodes[4]);
+ node->soft_nms_sigma(input_nodes[5]);
+
+ assert(outputs.size() == 3);
+ {
+ // Let's use name of output 0 as NonMaxSuppressionV5 name
+ const circle::TensorT &output_tensor = *tensors[outputs[0]];
+ node->name(tensor_name(output_tensor));
+ node->op_version(opcodes[op.opcode_index].get()->version);
+
+ // NOTE We don't set quantization for NonMaxSuppressionV5 itself but to virtual outputs
+ }
+
+ // Create virtual outputs of NonMaxSuppressionV5
+ for (size_t n = 0; n < outputs.size(); ++n)
+ {
+ const circle::TensorT &output_tensor = *tensors[outputs[n]];
+
+ auto *nodeout = graph->nodes()->create<CircleNonMaxSuppressionV5Out>();
+ copy_tensor_attributes(output_tensor, nodeout);
+
+ // mark shape_status
+ if (tensors_ptr->Get(outputs[n])->shape() == nullptr)
+ nodeout->shape_status(ShapeStatus::NOSHAPE);
+ else
+ nodeout->shape_status(ShapeStatus::VALID);
+
+ nodeout->input(node);
+ nodeout->index(n);
+
+ context->nodefinder()->enroll(outputs[n], nodeout);
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleNotEqual.cpp b/compiler/luci/import/src/Nodes/CircleNotEqual.cpp
new file mode 100644
index 000000000..77e986de1
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleNotEqual.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleNotEqual.h"
+
+#include <luci/IR/Nodes/CircleNotEqual.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleNotEqualGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 2)
+ {
+ return false;
+ }
+
+ if (outputs.size() != 1)
+ {
+ return false;
+ }
+
+ const auto &tensors = args.reader.tensors();
+
+ if (tensors[inputs.at(0)]->type != tensors[inputs.at(1)]->type)
+ {
+ return false;
+ }
+
+ return tensors[outputs[0]]->type == circle::TensorType::TensorType_BOOL;
+}
+
+CircleNode *CircleNotEqualGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleNotEqual>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleOneHot.cpp b/compiler/luci/import/src/Nodes/CircleOneHot.cpp
new file mode 100644
index 000000000..69294e1ed
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleOneHot.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleOneHot.h"
+
+#include <luci/IR/Nodes/CircleOneHot.h>
+
+#include <loco.h>
+#include <oops/UserExn.h>
+
+namespace luci
+{
+
+bool CircleOneHotGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+ const auto *options = args.op.builtin_options.AsOneHotOptions();
+
+ // Only 4 Input come refered from
+ if (inputs.size() != 4)
+ return false;
+
+ if (outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &indices = tensors.at(inputs.at(0));
+ const auto &depth = tensors.at(inputs.at(1));
+ const auto &on_value = tensors.at(inputs.at(2));
+ const auto &off_value = tensors.at(inputs.at(3));
+
+ if (options->axis < -1 || options->axis > static_cast<int32_t>(indices->shape.size()))
+ return false;
+ if (depth->shape.size() != 0)
+ return false;
+ if (on_value->shape.size() != 0)
+ return false;
+ if (off_value->shape.size() != 0)
+ return false;
+ if (on_value->type != off_value->type)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleOneHotGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleOneHot>();
+
+ node->indices(inputs.at(0));
+ node->depth(inputs.at(1));
+ node->on_value(inputs.at(2));
+ node->off_value(inputs.at(3));
+
+ const auto *options = op.builtin_options.AsOneHotOptions();
+ node->axis(options->axis);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CirclePRelu.cpp b/compiler/luci/import/src/Nodes/CirclePRelu.cpp
new file mode 100644
index 000000000..c07920f7c
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CirclePRelu.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CirclePRelu.h"
+
+#include <luci/IR/Nodes/CirclePRelu.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CirclePReluGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CirclePReluGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CirclePRelu>();
+ node->input(inputs.at(0));
+ node->alpha(inputs.at(1));
+
+ // PRelu options are empty
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CirclePack.cpp b/compiler/luci/import/src/Nodes/CirclePack.cpp
new file mode 100644
index 000000000..6ba6fae11
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CirclePack.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CirclePack.h"
+
+#include <luci/IR/Nodes/CirclePack.h>
+
+#include <loco.h>
+#include <oops/UserExn.h>
+
+namespace luci
+{
+
+bool CirclePackGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+ const auto *options = args.op.builtin_options.AsPackOptions();
+
+ if (options->values_count < 1)
+ return false;
+
+ if (inputs.size() != static_cast<uint32_t>(options->values_count))
+ return false;
+
+ if (outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CirclePackGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CirclePack>(inputs.size());
+ for (uint32_t i = 0; i < inputs.size(); ++i)
+ {
+ node->values(i, inputs[i]);
+ }
+
+ const auto *options = op.builtin_options.AsPackOptions();
+ node->axis(options->axis);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CirclePad.cpp b/compiler/luci/import/src/Nodes/CirclePad.cpp
new file mode 100644
index 000000000..999173b90
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CirclePad.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CirclePad.h"
+
+#include <luci/IR/Nodes/CirclePad.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CirclePadGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ // TODO do attribute checks
+
+ return true;
+}
+
+CircleNode *CirclePadGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CirclePad>();
+ node->input(inputs.at(0));
+ node->paddings(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsPadOptions();
+ (void)options; // There are no options.
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CirclePadV2.cpp b/compiler/luci/import/src/Nodes/CirclePadV2.cpp
new file mode 100644
index 000000000..493876e68
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CirclePadV2.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CirclePadV2.h"
+
+#include <luci/IR/Nodes/CirclePadV2.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CirclePadV2GraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 3)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CirclePadV2GraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CirclePadV2>();
+ node->input(inputs[0]);
+ node->paddings(inputs[1]);
+ node->constant_values(inputs[2]);
+
+ const auto *options = op.builtin_options.AsPadV2Options();
+ (void)options; // There are no options.
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CirclePow.cpp b/compiler/luci/import/src/Nodes/CirclePow.cpp
new file mode 100644
index 000000000..def012614
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CirclePow.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CirclePow.h"
+
+#include <luci/IR/Nodes/CirclePow.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CirclePowGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CirclePowGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CirclePow>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ // Pow options are empty
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleRange.cpp b/compiler/luci/import/src/Nodes/CircleRange.cpp
new file mode 100644
index 000000000..38dc44ed6
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleRange.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleRange.h"
+
+#include <luci/IR/Nodes/CircleRange.h>
+
+#include <loco.h>
+
+namespace luci
+{
+bool CircleRangeGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 3)
+ return false;
+
+ // TODO Support type check
+ return true;
+}
+
+CircleNode *CircleRangeGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleRange>();
+ node->start(inputs.at(0));
+ node->limit(inputs.at(1));
+ node->delta(inputs.at(2));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleRank.cpp b/compiler/luci/import/src/Nodes/CircleRank.cpp
new file mode 100644
index 000000000..12658b192
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleRank.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleRank.h"
+
+#include <luci/IR/Nodes/CircleRank.h>
+
+#include <loco.h>
+
+namespace luci
+{
+bool CircleRankGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleRankGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleRank>();
+ node->input(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleReduceAny.cpp b/compiler/luci/import/src/Nodes/CircleReduceAny.cpp
new file mode 100644
index 000000000..21a821951
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleReduceAny.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleReduceAny.h"
+
+#include <luci/IR/Nodes/CircleReduceAny.h>
+
+namespace luci
+{
+
+bool CircleReduceAnyGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+ if (inputs.size() != 2)
+ return false;
+ if (outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor_0 = tensors.at(inputs.at(0));
+ const auto &tensor_1 = tensors.at(inputs.at(1));
+ const auto &tensor_o = tensors.at(outputs[0]);
+
+ if (tensor_0->type != circle::TensorType_BOOL)
+ return false;
+ if (tensor_o->type != circle::TensorType_BOOL)
+ return false;
+
+ switch (tensor_1->type)
+ {
+ case circle::TensorType_INT32:
+ case circle::TensorType_INT64:
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+CircleNode *CircleReduceAnyGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleReduceAny>();
+ node->input(inputs.at(0));
+ node->reduction_indices(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsReducerOptions();
+ node->keep_dims(options->keep_dims);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleReduceMax.cpp b/compiler/luci/import/src/Nodes/CircleReduceMax.cpp
new file mode 100644
index 000000000..e633abf7d
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleReduceMax.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleReduceMax.h"
+
+#include <luci/IR/Nodes/CircleReduceMax.h>
+
+#include "ValidateHelpers.h"
+
+namespace luci
+{
+
+bool CircleReduceMaxGraphBuilder::validate(const ValidateArgs &args) const
+{
+ return validate_reduce_minmax(args);
+}
+
+CircleNode *CircleReduceMaxGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleReduceMax>();
+ node->input(inputs.at(0));
+ node->reduction_indices(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsReducerOptions();
+ node->keep_dims(options->keep_dims);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleReduceMin.cpp b/compiler/luci/import/src/Nodes/CircleReduceMin.cpp
new file mode 100644
index 000000000..bfc3001f8
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleReduceMin.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleReduceMin.h"
+
+#include <luci/IR/Nodes/CircleReduceMin.h>
+
+#include "ValidateHelpers.h"
+
+namespace luci
+{
+
+bool CircleReduceMinGraphBuilder::validate(const ValidateArgs &args) const
+{
+ return validate_reduce_minmax(args);
+}
+
+CircleNode *CircleReduceMinGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleReduceMin>();
+ node->input(inputs.at(0));
+ node->reduction_indices(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsReducerOptions();
+ node->keep_dims(options->keep_dims);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleReduceProd.cpp b/compiler/luci/import/src/Nodes/CircleReduceProd.cpp
new file mode 100644
index 000000000..5f054586e
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleReduceProd.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleReduceProd.h"
+
+#include <luci/IR/Nodes/CircleReduceProd.h>
+
+namespace luci
+{
+
+bool CircleReduceProdGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ if (inputs.size() != 2)
+ return false;
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor_1 = tensors.at(inputs.at(1));
+
+ // TODO check input types
+
+ // Check for reduction_indices types
+ switch (tensor_1->type)
+ {
+ case circle::TensorType_INT32:
+ case circle::TensorType_INT64:
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+CircleNode *CircleReduceProdGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleReduceProd>();
+ node->input(inputs.at(0));
+ node->reduction_indices(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsReducerOptions();
+ node->keep_dims(options->keep_dims);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleRelu.cpp b/compiler/luci/import/src/Nodes/CircleRelu.cpp
new file mode 100644
index 000000000..8e1c32a3a
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleRelu.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleRelu.h"
+
+#include <luci/IR/Nodes/CircleRelu.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleReluGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleReluGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleRelu>();
+ node->features(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleRelu6.cpp b/compiler/luci/import/src/Nodes/CircleRelu6.cpp
new file mode 100644
index 000000000..0283d7350
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleRelu6.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleRelu6.h"
+
+#include <luci/IR/Nodes/CircleRelu6.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleRelu6GraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleRelu6GraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleRelu6>();
+ node->features(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleReluN1To1.cpp b/compiler/luci/import/src/Nodes/CircleReluN1To1.cpp
new file mode 100644
index 000000000..7f517bc0d
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleReluN1To1.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleReluN1To1.h"
+
+#include <luci/IR/Nodes/CircleReluN1To1.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleReluN1To1GraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ // TODO check dtypes
+
+ return true;
+}
+
+CircleNode *CircleReluN1To1GraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleReluN1To1>();
+ 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
new file mode 100644
index 000000000..996ae9d20
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleReshape.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleReshape.h"
+
+#include <luci/IR/Nodes/CircleConst.h>
+#include <luci/IR/Nodes/CircleReshape.h>
+
+namespace luci
+{
+
+bool CircleReshapeGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1 && args.op.inputs.size() != 2)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+static void setup_shape_attribute(const std::vector<int32_t> &shape, CircleReshape *node)
+{
+ node->newShape()->rank(shape.size());
+ for (uint32_t i = 0; i < shape.size(); ++i)
+ {
+ node->newShape()->dim(i) = shape[i];
+ }
+}
+
+static CircleNode *create_shape_node(const std::vector<int32_t> &shape, loco::Graph *graph)
+{
+ auto *shape_node = graph->nodes()->create<luci::CircleConst>();
+ shape_node->dtype(loco::DataType::S32);
+ shape_node->rank(1);
+ shape_node->dim(0) = shape.size();
+ shape_node->size<loco::DataType::S32>(shape.size());
+ for (uint32_t i = 0; i < shape.size(); ++i)
+ {
+ shape_node->at<loco::DataType::S32>(i) = shape[i];
+ }
+ return shape_node;
+}
+
+CircleNode *CircleReshapeGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ // If the second input is not provided, generate it based on the value of the attribute.
+ // TODO Presence of the second input is the current requirement of the IR.
+ auto *shape_node = (inputs.size() == 2) ? inputs.at(1) : nullptr;
+ if (shape_node == nullptr)
+ {
+ const auto *options = op.builtin_options.AsReshapeOptions();
+ if (options != nullptr)
+ shape_node = create_shape_node(options->new_shape, graph);
+ else
+ {
+ shape_node = graph->nodes()->create<CircleOutputDummy>();
+ shape_node->dtype(loco::DataType::S32);
+ shape_node->rank(0);
+ }
+ }
+
+ auto *node = graph->nodes()->create<CircleReshape>();
+ node->tensor(inputs.at(0));
+ node->shape(shape_node);
+
+ const auto *options = op.builtin_options.AsReshapeOptions();
+ if (options)
+ setup_shape_attribute(options->new_shape, node);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleResizeBilinear.cpp b/compiler/luci/import/src/Nodes/CircleResizeBilinear.cpp
new file mode 100644
index 000000000..0fccb7b44
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleResizeBilinear.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleResizeBilinear.h"
+
+#include <luci/IR/Nodes/CircleConst.h>
+#include <luci/IR/Nodes/CircleResizeBilinear.h>
+
+namespace luci
+{
+
+bool CircleResizeBilinearGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleResizeBilinearGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleResizeBilinear>();
+ node->input(inputs.at(0));
+ node->size(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsResizeBilinearOptions();
+ node->align_corners(options->align_corners);
+ node->half_pixel_centers(options->half_pixel_centers);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleResizeNearestNeighbor.cpp b/compiler/luci/import/src/Nodes/CircleResizeNearestNeighbor.cpp
new file mode 100644
index 000000000..324323f59
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleResizeNearestNeighbor.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleResizeNearestNeighbor.h"
+
+#include <luci/IR/Nodes/CircleConst.h>
+#include <luci/IR/Nodes/CircleResizeNearestNeighbor.h>
+
+namespace luci
+{
+
+bool CircleResizeNearestNeighborGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleResizeNearestNeighborGraphBuilder::build_node(
+ const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleResizeNearestNeighbor>();
+ node->input(inputs.at(0));
+ node->size(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsResizeNearestNeighborOptions();
+ node->align_corners(options->align_corners);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleReverseSequence.cpp b/compiler/luci/import/src/Nodes/CircleReverseSequence.cpp
new file mode 100644
index 000000000..ad11d4c63
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleReverseSequence.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleReverseSequence.h"
+
+#include <luci/IR/Nodes/CircleReverseSequence.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleReverseSequenceGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 2)
+ return false;
+ if (outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor_in = tensors.at(inputs.at(0));
+ const auto &tensor_lengths = tensors.at(inputs.at(1));
+ const auto &tensor_out = tensors.at(outputs[0]);
+
+ switch (tensor_lengths->type)
+ {
+ case circle::TensorType_INT32:
+ case circle::TensorType_INT64:
+ break;
+ default:
+ return false;
+ }
+
+ if (tensor_in->type != tensor_out->type)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleReverseSequenceGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleReverseSequence>();
+ node->input(inputs.at(0));
+ node->seq_lengths(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsReverseSequenceOptions();
+ node->seq_axis(options->seq_dim);
+ node->batch_axis(options->batch_dim);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleReverseV2.cpp b/compiler/luci/import/src/Nodes/CircleReverseV2.cpp
new file mode 100644
index 000000000..e2e53bb4b
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleReverseV2.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleReverseV2.h"
+
+#include <luci/IR/Nodes/CircleReverseV2.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleReverseV2GraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 2)
+ return false;
+ if (outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor_in = tensors.at(inputs.at(0));
+ const auto &tensor_axis = tensors.at(inputs.at(1));
+ const auto &tensor_out = tensors.at(outputs[0]);
+
+ switch (tensor_axis->type)
+ {
+ case circle::TensorType_INT32:
+ case circle::TensorType_INT64:
+ break;
+ default:
+ return false;
+ }
+
+ if (tensor_out->type != tensor_in->type)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleReverseV2GraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleReverseV2>();
+ node->tensor(inputs.at(0));
+ node->axis(inputs.at(1));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleRound.cpp b/compiler/luci/import/src/Nodes/CircleRound.cpp
new file mode 100644
index 000000000..ad77f9f03
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleRound.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleRound.h"
+
+#include <luci/IR/Nodes/CircleRound.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleRoundGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 1)
+ return false;
+ if (outputs.size() != 1)
+ return false;
+
+ // Must be one of the following types
+ // bfloat16, half (float16), float32, float64, complex64, complex128
+ // Currently, circle supports float16, float32, complex64
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor_in = tensors.at(inputs.at(0));
+ const auto &tensor_out = tensors.at(outputs[0]);
+
+ switch (tensor_in->type)
+ {
+ case circle::TensorType_FLOAT16:
+ case circle::TensorType_FLOAT32:
+ case circle::TensorType_FLOAT64:
+ case circle::TensorType_INT32:
+ case circle::TensorType_INT64:
+ break;
+ default:
+ return false;
+ }
+
+ if (tensor_out->type != tensor_in->type)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleRoundGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleRound>();
+ node->x(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleRsqrt.cpp b/compiler/luci/import/src/Nodes/CircleRsqrt.cpp
new file mode 100644
index 000000000..ae05fbbf9
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleRsqrt.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleRsqrt.h"
+
+#include <luci/IR/Nodes/CircleRsqrt.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleRsqrtGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ if (inputs.size() != 1)
+ return false;
+
+ // Must be one of the following types
+ // bfloat16, half (float16), float32, float64, complex64, complex128
+ // Currently, circle supports float16, float32, complex64
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs.at(0));
+ switch (tensor->type)
+ {
+ case circle::TensorType_FLOAT16:
+ case circle::TensorType_FLOAT32:
+ case circle::TensorType_COMPLEX64:
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+CircleNode *CircleRsqrtGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleRsqrt>();
+ node->x(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleScatterNd.cpp b/compiler/luci/import/src/Nodes/CircleScatterNd.cpp
new file mode 100644
index 000000000..7f86aeb74
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleScatterNd.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleScatterNd.h"
+
+#include <luci/IR/Nodes/CircleScatterNd.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleScatterNdGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ if (inputs.size() != 3)
+ return false;
+
+ // indices must have the same type as shape
+ const auto &tensors = args.reader.tensors();
+
+ if (tensors[inputs.at(0)]->type != tensors[inputs.at(2)]->type)
+ return false;
+
+ // indices must be either int32 or int64
+ if (tensors[inputs.at(0)]->type != circle::TensorType_INT32 &&
+ tensors[inputs.at(0)]->type != circle::TensorType_INT64)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleScatterNdGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleScatterNd>();
+ node->indices(inputs.at(0));
+ node->updates(inputs.at(1));
+ node->shape(inputs.at(2));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSegmentSum.cpp b/compiler/luci/import/src/Nodes/CircleSegmentSum.cpp
new file mode 100644
index 000000000..fb84e5d52
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSegmentSum.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleSegmentSum.h"
+
+#include <luci/IR/Nodes/CircleSegmentSum.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleSegmentSumGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+ if (inputs.size() != 2)
+ return false;
+ if (outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor_in = tensors.at(inputs.at(0));
+ const auto &tensor_out = tensors.at(outputs[0]);
+ const auto &tensor_ids = tensors.at(inputs.at(1));
+
+ switch (tensor_ids->type)
+ {
+ case circle::TensorType_INT32:
+ case circle::TensorType_INT64:
+ break;
+ default:
+ return false;
+ }
+
+ if (tensor_out->type != tensor_in->type)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+CircleNode *CircleSegmentSumGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleSegmentSum>();
+ node->input(inputs.at(0));
+ node->segment_ids(inputs.at(1));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSelect.cpp b/compiler/luci/import/src/Nodes/CircleSelect.cpp
new file mode 100644
index 000000000..1e649f1e0
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSelect.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleSelect.h"
+
+#include <luci/IR/Nodes/CircleSelect.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleSelectGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+ if (inputs.size() != 3)
+ return false;
+ if (outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs.at(0));
+ if (tensor->type != circle::TensorType_BOOL)
+ return false;
+ // TODO check dtypes for input 1, 2
+
+ return true;
+}
+
+CircleNode *CircleSelectGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleSelect>();
+ node->condition(inputs.at(0));
+ node->t(inputs.at(1));
+ node->e(inputs.at(2));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSelectV2.cpp b/compiler/luci/import/src/Nodes/CircleSelectV2.cpp
new file mode 100644
index 000000000..e6dd04de0
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSelectV2.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleSelectV2.h"
+
+#include <luci/IR/Nodes/CircleSelectV2.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleSelectV2GraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+ if (inputs.size() != 3)
+ return false;
+ if (outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &condition = tensors.at(inputs.at(0));
+ if (condition->type != circle::TensorType_BOOL)
+ return false;
+
+ const auto &t = tensors.at(inputs.at(1));
+ const auto &e = tensors.at(inputs.at(2));
+ if (t->type != e->type)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleSelectV2GraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleSelectV2>();
+ node->condition(inputs.at(0));
+ node->t(inputs.at(1));
+ node->e(inputs.at(2));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleShape.cpp b/compiler/luci/import/src/Nodes/CircleShape.cpp
new file mode 100644
index 000000000..bd7dfc9d9
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleShape.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleShape.h"
+
+#include <luci/IR/Nodes/CircleShape.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleShapeGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+ if (inputs.size() != 1)
+ return false;
+ if (outputs.size() != 1)
+ return false;
+
+ // TODO check shape, dtype
+
+ return true;
+}
+
+CircleNode *CircleShapeGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleShape>();
+ node->input(inputs.at(0));
+
+ const auto *options = op.builtin_options.AsShapeOptions();
+ node->out_type(luci_datatype(options->out_type));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSin.cpp b/compiler/luci/import/src/Nodes/CircleSin.cpp
new file mode 100644
index 000000000..4b245ef6b
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSin.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleSin.h"
+
+#include <luci/IR/Nodes/CircleSin.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleSinGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ if (inputs.size() != 1)
+ return false;
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ // input type check
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs.at(0));
+ switch (tensor->type)
+ {
+ case circle::TensorType_FLOAT16:
+ case circle::TensorType_FLOAT32:
+ case circle::TensorType_FLOAT64:
+ break;
+ // TODO support TensorType_COMPLEX64, complex128, bfloat16
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+CircleNode *CircleSinGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleSin>();
+ node->x(inputs.at(0));
+
+ // No options for Sin
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSlice.cpp b/compiler/luci/import/src/Nodes/CircleSlice.cpp
new file mode 100644
index 000000000..8601fbf21
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSlice.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleSlice.h"
+
+#include <luci/IR/Nodes/CircleSlice.h>
+
+#include <loco.h>
+
+#include <cassert>
+
+namespace luci
+{
+
+bool CircleSliceGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 3)
+ return false;
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ // TODO check shapes and types
+
+ return true;
+}
+
+CircleNode *CircleSliceGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleSlice>();
+ node->input(inputs.at(0));
+ node->begin(inputs.at(1));
+ node->size(inputs.at(2));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSoftmax.cpp b/compiler/luci/import/src/Nodes/CircleSoftmax.cpp
new file mode 100644
index 000000000..0ef0b5418
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSoftmax.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleSoftmax.h"
+
+#include <luci/IR/Nodes/CircleSoftmax.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleSoftmaxGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ // TODO do attribute checks
+
+ return true;
+}
+
+CircleNode *CircleSoftmaxGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleSoftmax>();
+ node->logits(inputs.at(0));
+
+ const auto *options = op.builtin_options.AsSoftmaxOptions();
+ node->beta(options->beta);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSpaceToBatchND.cpp b/compiler/luci/import/src/Nodes/CircleSpaceToBatchND.cpp
new file mode 100644
index 000000000..fbf9f6b12
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSpaceToBatchND.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleSpaceToBatchND.h"
+
+#include <luci/IR/Nodes/CircleSpaceToBatchND.h>
+
+#include "ValidateHelpers.h"
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleSpaceToBatchNDGraphBuilder::validate(const ValidateArgs &args) const
+{
+ return validate_batch_space_nd(args);
+}
+
+CircleNode *CircleSpaceToBatchNDGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleSpaceToBatchND>();
+ node->input(inputs.at(0));
+ node->block_shape(inputs.at(1));
+ node->paddings(inputs.at(2));
+
+ // No options for SpaceToBatchND
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSpaceToDepth.cpp b/compiler/luci/import/src/Nodes/CircleSpaceToDepth.cpp
new file mode 100644
index 000000000..8ccd55dc6
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSpaceToDepth.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleSpaceToDepth.h"
+
+#include <luci/IR/Nodes/CircleSpaceToDepth.h>
+
+#include <loco.h>
+
+#include <cassert>
+
+namespace luci
+{
+
+bool CircleSpaceToDepthGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ if (inputs.size() != 1)
+ return false;
+
+ // TODO do attribute checks
+
+ return true;
+}
+
+CircleNode *CircleSpaceToDepthGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleSpaceToDepth>();
+ node->input(inputs.at(0));
+
+ const auto *options = op.builtin_options.AsSpaceToDepthOptions();
+ node->block_size(options->block_size);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSparseToDense.cpp b/compiler/luci/import/src/Nodes/CircleSparseToDense.cpp
new file mode 100644
index 000000000..ac756b1f3
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSparseToDense.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleSparseToDense.h"
+
+#include <luci/IR/Nodes/CircleSparseToDense.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleSparseToDenseGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 4)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleSparseToDenseGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleSparseToDense>();
+ node->indices(inputs.at(0));
+ node->output_shape(inputs.at(1));
+ node->values(inputs.at(2));
+ node->default_value(inputs.at(3));
+
+ const auto *options = op.builtin_options.AsSparseToDenseOptions();
+ if (options)
+ node->validate_indices(options->validate_indices);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSplit.cpp b/compiler/luci/import/src/Nodes/CircleSplit.cpp
new file mode 100644
index 000000000..07b6cc939
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSplit.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleSplit.h"
+
+#include <luci/IR/Nodes/CircleSplit.h>
+#include <luci/IR/Nodes/CircleSplitOut.h>
+
+#include <loco.h>
+#include <oops/UserExn.h>
+
+namespace luci
+{
+
+bool CircleSplitGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+ const auto *options = args.op.builtin_options.AsSplitOptions();
+
+ if (inputs.size() != 2)
+ return false;
+
+ if (static_cast<int32_t>(outputs.size()) != options->num_splits)
+ return false;
+
+ // TODO check types
+
+ return true;
+}
+
+/**
+ * @brief Split Node builder
+ *
+ * @note Current loco does not provide multiple outputs
+ * We will create multiple CircleSplitOut nodes to emulate this
+ * For two outputs that may look like this
+ *
+ * --- CircleSplit --- FullyConnected ---
+ * \- FullyConnected ---
+ *
+ * will be created like this
+ *
+ * --- CircleSplit --- CircleSplitOut --- FullyConnected ---
+ * \- CircleSplitOut --- FullyConnected ---
+ */
+
+void CircleSplitGraphBuilder::build(const circle::OperatorT &op, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ auto graph = context->graph();
+
+ const std::vector<int32_t> &inputs = op.inputs;
+ const std::vector<int32_t> &outputs = op.outputs;
+ const auto &tensors = context->reader()->tensors();
+ const auto &opcodes = context->reader()->opcodes();
+ auto tensors_ptr = context->reader()->tensors_ptr();
+ assert(tensors_ptr != nullptr);
+
+ std::vector<CircleNode *> input_nodes;
+ for (const int32_t input_tensor_index : inputs)
+ {
+ input_nodes.push_back(context->nodefinder()->node(input_tensor_index));
+ }
+
+ // Create CircleSplit
+ auto node = graph->nodes()->create<CircleSplit>();
+ node->split_dim(input_nodes[0]);
+ node->input(input_nodes[1]);
+
+ const auto *options = op.builtin_options.AsSplitOptions();
+ node->num_split(options->num_splits);
+
+ assert(outputs.size() > 0);
+ assert(int32_t(outputs.size()) == options->num_splits);
+ {
+ // Let's use name of output 0 as Split name
+ const circle::TensorT &output_tensor = *tensors[outputs[0]];
+ node->name(tensor_name(output_tensor));
+ node->op_version(opcodes[op.opcode_index].get()->version);
+
+ // NOTE We don't set quantization for Split itself but to virtual outputs
+ }
+
+ // Create virtual outputs of Split
+ for (int32_t n = 0; n < options->num_splits; ++n)
+ {
+ const circle::TensorT &output_tensor = *tensors[outputs[n]];
+
+ auto *nodeout = graph->nodes()->create<CircleSplitOut>();
+ copy_tensor_attributes(output_tensor, nodeout);
+ // mark shape_status
+ if (tensors_ptr->Get(outputs[n])->shape() == nullptr)
+ nodeout->shape_status(ShapeStatus::NOSHAPE);
+ else
+ nodeout->shape_status(ShapeStatus::VALID);
+
+ nodeout->input(node);
+ nodeout->index(n);
+
+ context->nodefinder()->enroll(outputs[n], nodeout);
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSplitV.cpp b/compiler/luci/import/src/Nodes/CircleSplitV.cpp
new file mode 100644
index 000000000..7c6e83e17
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSplitV.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleSplitV.h"
+
+#include <luci/IR/Nodes/CircleSplitV.h>
+#include <luci/IR/Nodes/CircleSplitVOut.h>
+
+#include <loco.h>
+#include <oops/UserExn.h>
+
+namespace luci
+{
+
+bool CircleSplitVGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+ const auto *options = args.op.builtin_options.AsSplitVOptions();
+
+ if (inputs.size() != 3)
+ return false;
+
+ if (static_cast<int32_t>(outputs.size()) != options->num_splits)
+ return false;
+
+ // TODO check types
+
+ return true;
+}
+
+/**
+ * @brief SplitV Node builder
+ *
+ * @note Current loco does not provide multiple outputs
+ * We will create multiple CircleSplitVOut nodes to emulate this
+ * For two outputs that may look like this
+ *
+ * --- CircleSplitV --- FullyConnected ---
+ * \- FullyConnected ---
+ *
+ * will be created like this
+ *
+ * --- CircleSplitV --- CircleSplitVOut --- FullyConnected ---
+ * \- CircleSplitVOut --- FullyConnected ---
+ */
+
+void CircleSplitVGraphBuilder::build(const circle::OperatorT &op,
+ GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ auto graph = context->graph();
+
+ const std::vector<int32_t> &inputs = op.inputs;
+ const std::vector<int32_t> &outputs = op.outputs;
+ const auto &tensors = context->reader()->tensors();
+ const auto &opcodes = context->reader()->opcodes();
+ auto tensors_ptr = context->reader()->tensors_ptr();
+ assert(tensors_ptr != nullptr);
+
+ std::vector<CircleNode *> input_nodes;
+ for (const int32_t input_tensor_index : inputs)
+ {
+ input_nodes.push_back(context->nodefinder()->node(input_tensor_index));
+ }
+
+ // Create CircleSplitV
+ auto node = graph->nodes()->create<CircleSplitV>();
+ node->input(input_nodes[0]);
+ node->size_splits(input_nodes[1]);
+ node->split_dim(input_nodes[2]);
+
+ const auto *options = op.builtin_options.AsSplitVOptions();
+ node->num_split(options->num_splits);
+
+ assert(outputs.size() > 0);
+ assert(int32_t(outputs.size()) == options->num_splits);
+ {
+ // Let's use name of output 0 as Split name
+ const circle::TensorT &output_tensor = *tensors[outputs[0]];
+ node->name(tensor_name(output_tensor));
+ node->op_version(opcodes[op.opcode_index].get()->version);
+
+ // NOTE We don't set quantization for Split itself but to virtual outputs
+ }
+
+ // Create virtual outputs of Split
+ for (int32_t n = 0; n < options->num_splits; ++n)
+ {
+ const circle::TensorT &output_tensor = *tensors[outputs[n]];
+
+ auto *nodeout = graph->nodes()->create<CircleSplitVOut>();
+ copy_tensor_attributes(output_tensor, nodeout);
+ // mark shape_status
+ if (tensors_ptr->Get(outputs[n])->shape() == nullptr)
+ nodeout->shape_status(ShapeStatus::NOSHAPE);
+ else
+ nodeout->shape_status(ShapeStatus::VALID);
+
+ nodeout->input(node);
+ nodeout->index(n);
+
+ context->nodefinder()->enroll(outputs[n], nodeout);
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSqrt.cpp b/compiler/luci/import/src/Nodes/CircleSqrt.cpp
new file mode 100644
index 000000000..c8beaee0d
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSqrt.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleSqrt.h"
+
+#include <luci/IR/Nodes/CircleSqrt.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleSqrtGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleSqrtGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleSqrt>();
+ node->x(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSquare.cpp b/compiler/luci/import/src/Nodes/CircleSquare.cpp
new file mode 100644
index 000000000..b5ba048d7
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSquare.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleSquare.h"
+
+#include <luci/IR/Nodes/CircleSquare.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleSquareGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ if (inputs.size() != 1)
+ return false;
+
+ // Must be one of the following types
+ // bfloat16, half (float16), float32, float64, complex64, complex128
+ // Currently, circle supports float16, float32, complex64
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs.at(0));
+ switch (tensor->type)
+ {
+ case circle::TensorType_INT32:
+ case circle::TensorType_INT64:
+ case circle::TensorType_FLOAT16:
+ case circle::TensorType_FLOAT32:
+ case circle::TensorType_FLOAT64:
+ case circle::TensorType_COMPLEX64:
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+CircleNode *CircleSquareGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleSquare>();
+ node->x(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSquaredDifference.cpp b/compiler/luci/import/src/Nodes/CircleSquaredDifference.cpp
new file mode 100644
index 000000000..6deae94c5
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSquaredDifference.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleSquaredDifference.h"
+
+#include <luci/IR/Nodes/CircleSquaredDifference.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleSquaredDifferenceGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 2)
+ return false;
+
+ if (outputs.size() != 1)
+ return false;
+
+ // Inputs must be one of the following types
+ // bfloat16, half(float16), float32, float64, int32, int64, complex64, complex128
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs.at(0));
+ switch (tensor->type)
+ {
+ case circle::TensorType_FLOAT16:
+ case circle::TensorType_FLOAT32:
+ case circle::TensorType_FLOAT64:
+ case circle::TensorType_INT32:
+ case circle::TensorType_INT64:
+ case circle::TensorType_COMPLEX64:
+ break;
+ // TODO support bfloat16, complex128
+ default:
+ return false;
+ }
+
+ // Input types must match
+ if (tensors.at(inputs.at(0))->type != tensors.at(inputs.at(1))->type)
+ return false;
+
+ // Input and output types must match
+ if (tensors.at(inputs.at(0))->type != tensors.at(outputs[0])->type)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleSquaredDifferenceGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleSquaredDifference>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSqueeze.cpp b/compiler/luci/import/src/Nodes/CircleSqueeze.cpp
new file mode 100644
index 000000000..32792c266
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSqueeze.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleSqueeze.h"
+
+#include <luci/IR/Nodes/CircleConst.h>
+#include <luci/IR/Nodes/CircleSqueeze.h>
+
+namespace luci
+{
+
+bool CircleSqueezeGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleSqueezeGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleSqueeze>();
+ node->input(inputs.at(0));
+
+ const auto *options = op.builtin_options.AsSqueezeOptions();
+ assert(options);
+
+ node->squeeze_dims(options->squeeze_dims);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleStridedSlice.cpp b/compiler/luci/import/src/Nodes/CircleStridedSlice.cpp
new file mode 100644
index 000000000..8f943a682
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleStridedSlice.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleStridedSlice.h"
+
+#include <luci/IR/Nodes/CircleStridedSlice.h>
+
+#include <loco.h>
+
+#include <cassert>
+
+namespace luci
+{
+
+bool CircleStridedSliceGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 4)
+ return false;
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ // TODO check shapes and types
+
+ return true;
+}
+
+CircleNode *CircleStridedSliceGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleStridedSlice>();
+ node->input(inputs.at(0));
+ node->begin(inputs.at(1));
+ node->end(inputs.at(2));
+ node->strides(inputs.at(3));
+
+ const auto *options = op.builtin_options.AsStridedSliceOptions();
+ node->begin_mask(options->begin_mask);
+ node->end_mask(options->end_mask);
+ node->ellipsis_mask(options->ellipsis_mask);
+ node->new_axis_mask(options->new_axis_mask);
+ node->shrink_axis_mask(options->shrink_axis_mask);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSub.cpp b/compiler/luci/import/src/Nodes/CircleSub.cpp
new file mode 100644
index 000000000..9acf83d40
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSub.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleSub.h"
+
+#include <luci/IR/Nodes/CircleSub.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleSubGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleSubGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleSub>();
+ node->x(inputs.at(0));
+ node->y(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsSubOptions();
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSum.cpp b/compiler/luci/import/src/Nodes/CircleSum.cpp
new file mode 100644
index 000000000..bd3cb6239
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSum.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleSum.h"
+
+#include <luci/IR/Nodes/CircleSum.h>
+
+namespace luci
+{
+
+bool CircleSumGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleSumGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleSum>();
+ node->input(inputs.at(0));
+ node->reduction_indices(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsReducerOptions();
+ node->keep_dims(options->keep_dims);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleTanh.cpp b/compiler/luci/import/src/Nodes/CircleTanh.cpp
new file mode 100644
index 000000000..018f5701b
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleTanh.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleTanh.h"
+
+#include <luci/IR/Nodes/CircleTanh.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleTanhGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ if (inputs.size() != 1)
+ return false;
+ const auto &outputs = args.op.outputs;
+ if (outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ if (tensors.at(inputs.at(0))->type != tensors.at(outputs[0])->type)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleTanhGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleTanh>();
+ node->x(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleTile.cpp b/compiler/luci/import/src/Nodes/CircleTile.cpp
new file mode 100644
index 000000000..bc6f320ba
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleTile.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleTile.h"
+
+#include <luci/IR/Nodes/CircleTile.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleTileGraphBuilder::validate(const ValidateArgs &args) const
+{
+ auto inputs = args.op.inputs;
+ auto outputs = args.op.outputs;
+
+ if (inputs.size() != 2)
+ return false;
+
+ if (outputs.size() != 1)
+ return false;
+
+ // Multiples (inputs.at(1)) must be one of the following types
+ // int32, int64
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs.at(1));
+ switch (tensor->type)
+ {
+ case circle::TensorType_INT32:
+ case circle::TensorType_INT64:
+ break;
+ default:
+ return false;
+ }
+
+ // Type of input and output must be the same
+ if (tensors.at(inputs.at(0))->type != tensors.at(outputs[0])->type)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleTileGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleTile>();
+ node->input(inputs.at(0));
+ node->multiples(inputs.at(1));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleTopKV2.cpp b/compiler/luci/import/src/Nodes/CircleTopKV2.cpp
new file mode 100644
index 000000000..f0677de86
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleTopKV2.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleTopKV2.h"
+
+#include <luci/IR/Nodes/CircleTopKV2.h>
+#include <luci/IR/Nodes/CircleTopKV2Out.h>
+
+#include <loco.h>
+#include <oops/UserExn.h>
+
+namespace luci
+{
+
+bool CircleTopKV2GraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 2)
+ return false;
+ if (outputs.size() != 2)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs.at(1));
+ if (tensor->type != circle::TensorType_INT32)
+ return false;
+
+ return true;
+}
+
+/**
+ * @brief TopKV2 Node builder
+ *
+ * @note Current loco does not provide multiple outputs
+ * We will create multiple CircleTopKV2Out nodes to emulate this
+ * For two outputs that may look like this
+ *
+ * --- CircleTopKV2--- FullyConnected ---
+ * \- FullyConnected ---
+ *
+ * will be created like this
+ *
+ * --- CircleTopKV2 --- CircleTopKV2Out --- FullyConnected ---
+ * \- CircleTopKV2Out --- FullyConnected ---
+ */
+
+void CircleTopKV2GraphBuilder::build(const circle::OperatorT &op,
+ GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ auto graph = context->graph();
+
+ const std::vector<int32_t> &inputs = op.inputs;
+ const std::vector<int32_t> &outputs = op.outputs;
+ const auto &tensors = context->reader()->tensors();
+ const auto &opcodes = context->reader()->opcodes();
+ auto tensors_ptr = context->reader()->tensors_ptr();
+ assert(tensors_ptr != nullptr);
+
+ std::vector<CircleNode *> input_nodes;
+ for (const int32_t input_tensor_index : inputs)
+ {
+ input_nodes.push_back(context->nodefinder()->node(input_tensor_index));
+ }
+
+ // Create CircleTopKV2
+ auto node = graph->nodes()->create<CircleTopKV2>();
+ node->input(input_nodes[0]);
+ node->k(input_nodes[1]);
+
+ assert(outputs.size() == 2);
+ {
+ // Let's use name of output 0 as TopKV2 name
+ const circle::TensorT &output_tensor = *tensors[outputs[0]];
+ node->name(tensor_name(output_tensor));
+ node->op_version(opcodes[op.opcode_index].get()->version);
+
+ // NOTE We don't set quantization for TopKV2 itself but to virtual outputs
+ }
+
+ // Create virtual outputs of TopKV2
+ for (size_t n = 0; n < outputs.size(); ++n)
+ {
+ const circle::TensorT &output_tensor = *tensors[outputs[n]];
+
+ auto *nodeout = graph->nodes()->create<CircleTopKV2Out>();
+ copy_tensor_attributes(output_tensor, nodeout);
+ // mark shape_status
+ if (tensors_ptr->Get(outputs[n])->shape() == nullptr)
+ nodeout->shape_status(ShapeStatus::NOSHAPE);
+ else
+ nodeout->shape_status(ShapeStatus::VALID);
+
+ nodeout->input(node);
+ nodeout->index(n);
+
+ context->nodefinder()->enroll(outputs[n], nodeout);
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleTranspose.cpp b/compiler/luci/import/src/Nodes/CircleTranspose.cpp
new file mode 100644
index 000000000..cc3153085
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleTranspose.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleTranspose.h"
+
+#include <luci/IR/Nodes/CircleTranspose.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleTransposeGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleTransposeGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleTranspose>();
+ node->a(inputs.at(0));
+ node->perm(inputs.at(1));
+
+ const auto *options = op.builtin_options.AsTransposeOptions();
+ (void)options;
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleTransposeConv.cpp b/compiler/luci/import/src/Nodes/CircleTransposeConv.cpp
new file mode 100644
index 000000000..c280faaf5
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleTransposeConv.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleTransposeConv.h"
+
+#include <luci/IR/Nodes/CircleTransposeConv.h>
+
+#include <loco.h>
+
+#include <cassert>
+
+namespace luci
+{
+
+bool CircleTransposeConvGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 3 && args.op.inputs.size() != 4)
+ return false;
+
+ const auto &inputs = args.op.inputs;
+ const auto &tensors = args.reader.tensors();
+ const auto &filter_tensor = tensors.at(inputs.at(1));
+ const auto &filter_shape = filter_tensor.get()->shape;
+ const auto &ifm_tensor = tensors.at(inputs.at(2));
+ const auto &ifm_shape = ifm_tensor.get()->shape;
+
+ // ifm and filters must be 4-D tensor
+ if (ifm_shape.size() != 4)
+ return false;
+ if (filter_shape.size() != 4)
+ return false;
+
+ // input shape : [batch, height, width, in_channels]
+ // filters shape : [output_channels, height, weight, in_channels]
+ if (ifm_tensor.get()->shape.at(3) != filter_tensor.get()->shape.at(3))
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleTransposeConvGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleTransposeConv>();
+
+ node->inputSizes(inputs.at(0));
+ node->filter(inputs.at(1));
+ node->outBackprop(inputs.at(2));
+ if (inputs.size() == 3)
+ node->bias(graph->nodes()->create<CircleOutputExclude>());
+ else
+ node->bias(inputs.at(3));
+
+ if (auto bias = dynamic_cast<luci::CircleOutputExclude *>(node->bias()))
+ {
+ // CircleOutputExclude doesn't need a type, but since all nodes must have a type, a dummy type
+ // is inserted.
+ bias->dtype(loco::DataType::FLOAT32);
+ }
+
+ const auto *options = op.builtin_options.AsTransposeConvOptions();
+ node->padding(luci_padding(options->padding));
+ node->stride()->w(options->stride_w);
+ node->stride()->h(options->stride_h);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp b/compiler/luci/import/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp
new file mode 100644
index 000000000..c41cf4def
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleUnidirectionalSequenceLSTM.h"
+
+#include <luci/IR/Nodes/CircleUnidirectionalSequenceLSTM.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleUnidirectionalSequenceLSTMGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 24)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleUnidirectionalSequenceLSTMGraphBuilder::build_node(
+ const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleUnidirectionalSequenceLSTM>();
+ node->input(inputs.at(0));
+ node->input_to_input_weights(inputs.at(1)); // Optional
+ node->input_to_cell_weights(inputs.at(2));
+ node->input_to_forget_weights(inputs.at(3));
+ node->input_to_output_weights(inputs.at(4));
+ node->recurrent_to_input_weights(inputs.at(5)); // Optional
+ node->recurrent_to_cell_weights(inputs.at(6));
+ node->recurrent_to_forget_weights(inputs.at(7));
+ node->recurrent_to_output_weights(inputs.at(8));
+ node->cell_to_input_weights(inputs.at(9)); // Optional
+ node->cell_to_forget_weights(inputs.at(10)); // Optional
+ node->cell_to_output_weights(inputs.at(11)); // Optional
+ node->input_gate_bias(inputs.at(12)); // Optional
+ node->forget_gate_bias(inputs.at(13));
+ node->cell_gate_bias(inputs.at(14));
+ node->output_gate_bias(inputs.at(15));
+ node->projection_weights(inputs.at(16)); // Optional
+ node->projection_bias(inputs.at(17)); // Optional
+ node->activation_state(inputs.at(18));
+ node->cell_state(inputs.at(19));
+ node->input_layer_norm_coefficients(inputs.at(20)); // Optional
+ node->forget_layer_norm_coefficients(inputs.at(21)); // Optional
+ node->cell_layer_norm_coefficients(inputs.at(22)); // Optional
+ node->output_layer_norm_coefficients(inputs.at(23)); // Optional
+ const std::vector<int32_t> optionals = {1, 5, 9, 10, 11, 12, 16, 17, 20, 21, 22, 23};
+ for (auto optional : optionals)
+ {
+ if (auto inp = dynamic_cast<luci::CircleOutputExclude *>(node->arg(optional)))
+ {
+ // CircleOutputExclude doesn't need a type, but since all nodes must have a type, a dummy type
+ // is inserted.
+ inp->dtype(loco::DataType::FLOAT32);
+ }
+ }
+
+ const auto *options = op.builtin_options.AsUnidirectionalSequenceLSTMOptions();
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+ node->cell_clip(options->cell_clip);
+ node->proj_clip(options->proj_clip);
+ node->time_major(options->time_major);
+ node->asymmetric_quantize_inputs(options->asymmetric_quantize_inputs);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleUnique.cpp b/compiler/luci/import/src/Nodes/CircleUnique.cpp
new file mode 100644
index 000000000..5e79a2920
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleUnique.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleUnique.h"
+
+#include <luci/IR/Nodes/CircleUnique.h>
+#include <luci/IR/Nodes/CircleUniqueOut.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleUniqueGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ if (args.op.outputs.size() != 2)
+ return false;
+
+ return true;
+}
+
+void CircleUniqueGraphBuilder::build(const circle::OperatorT &op,
+ GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ auto graph = context->graph();
+
+ const std::vector<int32_t> &inputs = op.inputs;
+ const std::vector<int32_t> &outputs = op.outputs;
+ const auto &tensors = context->reader()->tensors();
+ auto tensors_ptr = context->reader()->tensors_ptr();
+ assert(tensors_ptr != nullptr);
+
+ std::vector<CircleNode *> input_nodes;
+ for (const int32_t input_tensor_index : inputs)
+ {
+ input_nodes.push_back(context->nodefinder()->node(input_tensor_index));
+ }
+
+ // Create CircleUnique
+ auto node = graph->nodes()->create<CircleUnique>();
+ node->input(input_nodes[0]);
+
+ const auto *options = op.builtin_options.AsUniqueOptions();
+ node->output_type(luci_datatype(options->idx_out_type));
+
+ assert(int32_t(outputs.size()) == 2);
+ // Let's use name of output 0 as Unique name
+ const circle::TensorT &output_tensor = *tensors[outputs[0]];
+ node->name(tensor_name(output_tensor));
+
+ // Create virtual outputs of Unique
+ for (int32_t n = 0; n < 2; ++n)
+ {
+ const circle::TensorT &output_tensor = *tensors[outputs[n]];
+
+ auto *nodeout = graph->nodes()->create<CircleUniqueOut>();
+ copy_tensor_attributes(output_tensor, nodeout);
+ // mark shape_status
+ if (tensors_ptr->Get(outputs[n])->shape() == nullptr)
+ nodeout->shape_status(ShapeStatus::NOSHAPE);
+ else
+ nodeout->shape_status(ShapeStatus::VALID);
+
+ nodeout->input(node);
+ nodeout->index(n);
+
+ context->nodefinder()->enroll(outputs[n], nodeout);
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleUnpack.cpp b/compiler/luci/import/src/Nodes/CircleUnpack.cpp
new file mode 100644
index 000000000..9e7f3d3e1
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleUnpack.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleUnpack.h"
+
+#include <luci/IR/Nodes/CircleUnpack.h>
+#include <luci/IR/Nodes/CircleUnpackOut.h>
+
+#include <luci/UserSettings.h>
+#include <luci/Log.h>
+
+#include <loco.h>
+#include <oops/UserExn.h>
+
+namespace luci
+{
+
+bool CircleUnpackGraphBuilder::validate(const ValidateArgs &args) const
+{
+ LOGGER(l);
+
+ auto settings = luci::UserSettings::settings();
+
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+ const auto *options = args.op.builtin_options.AsUnpackOptions();
+
+ if (inputs.size() != 1)
+ return false;
+
+ // NOTE real models may have mismatch
+ if (static_cast<int32_t>(outputs.size()) != options->num)
+ {
+ if (settings->get(luci::UserSettings::Key::DisableValidation))
+ {
+ const auto &tensors = args.reader.tensors();
+ const circle::TensorT &output_tensor = *tensors[outputs[0]];
+ auto name = tensor_name(output_tensor);
+ WARN(l) << "Warning: import Unpack(" << name << ") 'num' is not same as outputs used";
+ }
+ else
+ return false;
+ }
+
+ if (options->num < 0)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs.at(0));
+ const auto &shape = tensor->shape;
+ auto shape_size = static_cast<int32_t>(shape.size());
+ if (shape_size > 0)
+ {
+ // NOTE for unknown shape, shape_size is 0
+ if (options->axis < -shape_size || options->axis >= shape_size)
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * @brief Unpack Node builder
+ *
+ * @note Current loco does not provide multiple outputs
+ * We will create multiple CircleUnpackOut nodes to emulate this
+ * For two outputs that may look like this
+ *
+ * --- CircleUnpack --- FullyConnected ---
+ * \- FullyConnected ---
+ *
+ * will be created like this
+ *
+ * --- CircleUnpack --- CircleUnpackOut --- FullyConnected ---
+ * \- CircleUnpackOut --- FullyConnected ---
+ */
+
+void CircleUnpackGraphBuilder::build(const circle::OperatorT &op,
+ GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ auto graph = context->graph();
+
+ const std::vector<int32_t> &inputs = op.inputs;
+ const std::vector<int32_t> &outputs = op.outputs;
+ const auto &tensors = context->reader()->tensors();
+ const auto &opcodes = context->reader()->opcodes();
+ auto tensors_ptr = context->reader()->tensors_ptr();
+ assert(tensors_ptr != nullptr);
+
+ // NOTE Unpack has only one input so running a loop is not necessary
+ // This is provided as a reference for other Ops as a reference
+ std::vector<CircleNode *> input_nodes;
+ for (const int32_t input_tensor_index : inputs)
+ {
+ input_nodes.push_back(context->nodefinder()->node(input_tensor_index));
+ }
+
+ // Create CircleUnpack
+ CircleUnpack *node = graph->nodes()->create<CircleUnpack>();
+ node->value(input_nodes[0]);
+
+ const auto *options = op.builtin_options.AsUnpackOptions();
+ node->num(options->num);
+ node->axis(options->axis);
+
+ assert(outputs.size() > 0);
+ {
+ // Let's use name of output 0 as Unpack name
+ const circle::TensorT &output_tensor = *tensors[outputs[0]];
+ node->name(tensor_name(output_tensor));
+ node->op_version(opcodes[op.opcode_index].get()->version);
+
+ // NOTE We don't set quantization for Unpack itself but to virtual outputs
+ }
+
+ // Create virtual outputs of Unpack
+ for (int32_t n = 0; n < options->num; ++n)
+ {
+ const circle::TensorT &output_tensor = *tensors[outputs[n]];
+
+ auto *nodeout = graph->nodes()->create<CircleUnpackOut>();
+ copy_tensor_attributes(output_tensor, nodeout);
+ // mark shape_status
+ if (tensors_ptr->Get(outputs[n])->shape() == nullptr)
+ nodeout->shape_status(ShapeStatus::NOSHAPE);
+ else
+ nodeout->shape_status(ShapeStatus::VALID);
+
+ nodeout->input(node);
+ nodeout->index(n);
+
+ context->nodefinder()->enroll(outputs[n], nodeout);
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleWhere.cpp b/compiler/luci/import/src/Nodes/CircleWhere.cpp
new file mode 100644
index 000000000..f4c5f0c66
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleWhere.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleWhere.h"
+
+#include <luci/IR/Nodes/CircleWhere.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleWhereGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 1)
+ return false;
+
+ if (outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor_condition = tensors.at(inputs.at(0));
+ const auto &tensor_out = tensors.at(outputs[0]);
+
+ if (tensor_condition->type != circle::TensorType_BOOL)
+ return false;
+
+ if (tensor_out->type != circle::TensorType_INT64)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleWhereGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleWhere>();
+ node->condition(inputs.at(0));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleWhile.cpp b/compiler/luci/import/src/Nodes/CircleWhile.cpp
new file mode 100644
index 000000000..aead25071
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleWhile.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleWhile.h"
+
+#include <luci/IR/Nodes/CircleWhile.h>
+#include <luci/IR/Nodes/CircleWhileOut.h>
+
+#include <loco.h>
+#include <oops/UserExn.h>
+
+namespace luci
+{
+
+bool CircleWhileGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto *options = args.op.builtin_options.AsWhileOptions();
+
+ if (inputs.size() != args.op.outputs.size())
+ return false;
+
+ auto num_graphs = static_cast<int32_t>(args.reader.num_subgraph());
+ if (options->cond_subgraph_index >= num_graphs)
+ return false;
+ if (options->body_subgraph_index >= num_graphs)
+ return false;
+
+ return true;
+}
+
+/**
+ * @brief While Node builder
+ *
+ * @note Current loco does not provide multiple outputs
+ * We will create multiple CircleWhileOut nodes to emulate this
+ * For two outputs that may look like this
+ *
+ * --- CircleWhile --- Node ---
+ * \- Node ---
+ *
+ * will be created like this
+ *
+ * --- CircleWhile --- CircleWhileOut --- Node ---
+ * \- CircleWhileOut --- Node ---
+ */
+
+void CircleWhileGraphBuilder::build(const circle::OperatorT &op, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ auto graph = context->graph();
+
+ const std::vector<int32_t> &inputs = op.inputs;
+ const std::vector<int32_t> &outputs = op.outputs;
+ const auto &tensors = context->reader()->tensors();
+ const auto &opcodes = context->reader()->opcodes();
+
+ std::vector<CircleNode *> input_nodes;
+ for (const int32_t input_tensor_index : inputs)
+ {
+ auto input_node = context->nodefinder()->node(input_tensor_index);
+ assert(input_node != nullptr);
+ input_nodes.push_back(input_node);
+ }
+
+ uint32_t input_count = inputs.size();
+ uint32_t output_count = outputs.size();
+
+ // Create CircleWhile
+ CircleWhile *node = graph->nodes()->create<CircleWhile>(input_count, output_count);
+
+ for (uint32_t idx = 0; idx < input_count; ++idx)
+ {
+ node->input(idx, input_nodes[idx]);
+ }
+
+ const auto *options = op.builtin_options.AsWhileOptions();
+ node->cond_branch(options->cond_subgraph_index);
+ node->body_branch(options->body_subgraph_index);
+
+ assert(outputs.size() > 0);
+ {
+ // Lets use name of output 0 as While name
+ const circle::TensorT &output_tensor = *tensors[outputs[0]];
+ node->name(tensor_name(output_tensor));
+ node->op_version(opcodes[op.opcode_index].get()->version);
+
+ // NOTE We don't set quantization for While itself but to virtual outputs
+ }
+
+ // Create virtual outputs of While
+ for (uint32_t n = 0; n < output_count; ++n)
+ {
+ const circle::TensorT &output_tensor = *tensors[outputs[n]];
+
+ auto *nodeout = graph->nodes()->create<CircleWhileOut>();
+
+ nodeout->input(node);
+ nodeout->index(n);
+
+ copy_tensor_attributes(output_tensor, nodeout);
+
+ // Note: leave shape_status to UNKNOWN to run shape inference
+
+ context->nodefinder()->enroll(outputs[n], nodeout);
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleZerosLike.cpp b/compiler/luci/import/src/Nodes/CircleZerosLike.cpp
new file mode 100644
index 000000000..e60424def
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleZerosLike.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Import/Nodes/CircleZerosLike.h"
+
+#include <luci/IR/Nodes/CircleZerosLike.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleZerosLikeGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleZerosLikeGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleZerosLike>();
+ node->input(inputs.at(0));
+
+ // ZerosLikeOptinos are empty
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/PostImport.cpp b/compiler/luci/import/src/PostImport.cpp
new file mode 100644
index 000000000..f436b48e8
--- /dev/null
+++ b/compiler/luci/import/src/PostImport.cpp
@@ -0,0 +1,354 @@
+/*
+ * 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 "PostImport.h"
+
+#include "luci/Import/CircleReader.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/CircleDialect.h>
+#include <luci/IR/CircleNodeVisitor.h>
+#include <luci/Log.h>
+
+#include <loco.h>
+#include <oops/InternalExn.h>
+
+namespace
+{
+
+/**
+ * @brief FixInterGraphNodes will fix inter graph connections for each Nodes
+ */
+class FixInterGraphNodes final : public luci::CircleNodeMutableVisitor<void>
+{
+public:
+ FixInterGraphNodes(const luci::Module *m, const luci::CircleReader &r) : _module(m), _reader(r) {}
+
+ /**
+ * @note This will set Graph* to every CircleIf nodes 'else' and 'then'
+ */
+ void visit(luci::CircleIf *node) final
+ {
+ LOGGER(l);
+ INFO(l) << "CircleIf " << node->name() << std::endl;
+
+ auto then_branch = node->then_branch();
+ auto else_branch = node->else_branch();
+ auto num_graphs = static_cast<int32_t>(_module->size());
+ (void)num_graphs;
+
+ assert(num_graphs > 0);
+ assert(then_branch >= 0 && then_branch < num_graphs);
+ assert(else_branch >= 0 && else_branch < num_graphs);
+
+ auto then_graph = _module->graph(then_branch);
+ auto else_graph = _module->graph(else_branch);
+ assert(then_graph != nullptr);
+ assert(else_graph != nullptr);
+
+ node->then_graph(then_graph);
+ node->else_graph(else_graph);
+ }
+
+ void visit(luci::CircleWhile *node) final
+ {
+ LOGGER(l);
+ INFO(l) << "CircleWhile " << node->name() << std::endl;
+
+ auto cond_branch = node->cond_branch();
+ auto body_branch = node->body_branch();
+ auto num_graphs = static_cast<int32_t>(_module->size());
+ (void)num_graphs;
+
+ assert(num_graphs > 0);
+ assert(cond_branch >= 0 && cond_branch < num_graphs);
+ assert(body_branch >= 0 && body_branch < num_graphs);
+
+ auto cond_graph = _module->graph(cond_branch);
+ auto body_graph = _module->graph(body_branch);
+ assert(cond_graph != nullptr);
+ assert(body_graph != nullptr);
+
+ node->cond_graph(cond_graph);
+ node->body_graph(body_graph);
+ }
+
+ void visit(luci::CircleNode *) final
+ {
+ // DO NOTHING
+ }
+
+private:
+ const luci::Module *_module;
+ const luci::CircleReader &_reader;
+};
+
+/**
+ * @brief FixInterGraph will fix inter graph connections
+ */
+class FixInterGraph final
+{
+public:
+ void run(loco::Graph *g, const luci::Module *m, const luci::CircleReader &r)
+ {
+ for (auto node : loco::postorder_traversal(loco::output_nodes(g)))
+ {
+ if (recognize(node->dialect()))
+ {
+ auto cn = loco::must_cast<luci::CircleNode *>(node);
+
+ fix(cn, m, r);
+ }
+ }
+ }
+
+private:
+ bool recognize(const loco::Dialect *dialect) { return (dialect == luci::CircleDialect::get()); }
+
+ void fix(luci::CircleNode *node, const luci::Module *module, const luci::CircleReader &reader)
+ {
+ FixInterGraphNodes fix(module, reader);
+ node->accept(&fix);
+ }
+};
+
+} // namespace
+
+namespace
+{
+/**
+ * @brief ValidateNodeProp will validate inter graph connections for each Nodes
+ */
+class ValidateNodeProp final : public luci::CircleNodeMutableVisitor<void>
+{
+public:
+ ValidateNodeProp(const luci::Module *m, const luci::CircleReader &r) : _module(m), _reader(r) {}
+
+ /**
+ * @note Validate CircleIf node 'else' and 'then' graph input/output count
+ * shape and type
+ */
+ void visit(luci::CircleIf *node) final
+ {
+ LOGGER(l);
+ INFO(l) << "CircleIf " << node->name() << std::endl;
+
+ auto then_graph = node->then_graph();
+ auto else_graph = node->else_graph();
+ assert(then_graph != nullptr);
+ assert(else_graph != nullptr);
+
+ // TODO support for differnt shape; but how?
+ // NODE Shape/Type inference assume below conditions
+
+ // Check both "then" and "else" subgraph outputs are same in count
+ auto then_outputs = loco::output_nodes(then_graph); // CircleOutput nodes
+ auto else_outputs = loco::output_nodes(else_graph);
+ if (then_outputs.size() != else_outputs.size())
+ {
+ INTERNAL_EXN("CircleIf THEN and ELSE Graph are not same in size");
+ }
+
+ // check outputs have same shape and dtype
+ auto then_graph_outputs = then_graph->outputs(); // loco::GraphOutput items
+ auto else_graph_outputs = else_graph->outputs();
+ for (size_t idx = 0; idx < then_outputs.size(); ++idx)
+ {
+ auto then_out = loco::must_cast<luci::CircleOutput *>(then_outputs.at(idx));
+ auto else_out = loco::must_cast<luci::CircleOutput *>(else_outputs.at(idx));
+
+ auto then_graph_output = then_graph_outputs->at(then_out->index());
+ auto else_graph_output = else_graph_outputs->at(else_out->index());
+ if (!(*then_graph_output->shape() == *else_graph_output->shape()))
+ {
+ INTERNAL_EXN_V("CircleIf THEN and ELSE Graph Output shape mismatch ", idx);
+ }
+ if (then_graph_output->dtype() != else_graph_output->dtype())
+ {
+ INTERNAL_EXN_V("CircleIf THEN and ELSE Graph Output type mismatch ", idx);
+ }
+ }
+ }
+
+ /**
+ * @note Validate CircleWhile node 'cond' and 'body' graph input/output count
+ * shape and type
+ */
+ void visit(luci::CircleWhile *node) final
+ {
+ LOGGER(l);
+ INFO(l) << "CircleWhile " << node->name() << std::endl;
+
+ auto cond_graph = node->cond_graph();
+ auto body_graph = node->body_graph();
+ assert(cond_graph != nullptr);
+ assert(body_graph != nullptr);
+
+ // Check input of "cond" and input/output of "body" subgraph have the same size
+ auto cond_inputs = loco::input_nodes(cond_graph);
+ auto cond_outputs = loco::output_nodes(cond_graph);
+ auto body_inputs = loco::input_nodes(body_graph);
+ auto body_outputs = loco::output_nodes(body_graph);
+ if (cond_inputs.size() != body_outputs.size())
+ {
+ INTERNAL_EXN("CircleWhile COND input and BODY output have different sizes");
+ }
+ if (cond_inputs.size() != body_inputs.size())
+ {
+ INTERNAL_EXN("CircleWhile COND input and BODY input have different sizes");
+ }
+ if (cond_outputs.size() != 1)
+ {
+ INTERNAL_EXN("CircleWhile COND output must have size 1");
+ }
+ auto cond_out = loco::must_cast<luci::CircleOutput *>(cond_outputs.at(0));
+ if (cond_out->dtype() != loco::DataType::BOOL)
+ {
+ INTERNAL_EXN("CircleWhile COND output must have bool type");
+ }
+
+ // input of "cond" and input/output of "body" subgraph must have the same shape and type
+ // First we compare input of "cond" with input of "body"
+ auto cond_graph_inputs = cond_graph->inputs();
+ auto body_graph_inputs = body_graph->inputs();
+ for (size_t idx = 0; idx < cond_inputs.size(); ++idx)
+ {
+ auto cond_in = loco::must_cast<luci::CircleInput *>(cond_inputs.at(idx));
+ auto body_in = loco::must_cast<luci::CircleInput *>(body_inputs.at(idx));
+
+ auto cond_graph_input = cond_graph_inputs->at(cond_in->index());
+ auto body_graph_input = body_graph_inputs->at(body_in->index());
+ if ((cond_in->rank() != body_in->rank()))
+ {
+ INTERNAL_EXN_V("CircleWhile COND input and BODY input shape mismatch ", idx);
+ }
+ if (cond_in->rank() > 0 && body_in->rank() > 0)
+ {
+ if (!(*cond_graph_input->shape() == *body_graph_input->shape()))
+ {
+ INTERNAL_EXN_V("CircleWhile COND input and BODY input shape mismatch ", idx);
+ }
+ }
+ if (cond_in->dtype() != body_in->dtype())
+ {
+ INTERNAL_EXN_V("CircleWhile COND input and BODY input type mismatch ", idx);
+ }
+ }
+
+ // Next we compare input of "cond" with output of "body"
+ auto body_graph_outputs = body_graph->outputs();
+ for (size_t idx = 0; idx < cond_inputs.size(); ++idx)
+ {
+ auto cond_in = loco::must_cast<luci::CircleInput *>(cond_inputs.at(idx));
+ auto body_out = loco::must_cast<luci::CircleOutput *>(body_outputs.at(idx));
+
+ auto cond_graph_input = cond_graph_inputs->at(cond_in->index());
+ auto body_graph_output = body_graph_outputs->at(body_out->index());
+ if ((cond_in->rank() != body_out->rank()))
+ {
+ INTERNAL_EXN_V("CircleWhile COND input and BODY output shape mismatch ", idx);
+ }
+ if (cond_in->rank() > 0 && body_out->rank() > 0)
+ {
+ if (!(*cond_graph_input->shape() == *body_graph_output->shape()))
+ {
+ INTERNAL_EXN_V("CircleWhile COND input and BODY output shape mismatch ", idx);
+ }
+ }
+ if (cond_in->dtype() != body_out->dtype())
+ {
+ INTERNAL_EXN_V("CircleWhile COND input and BODY output type mismatch ", idx);
+ }
+ }
+ }
+
+ void visit(luci::CircleNode *) final
+ {
+ // DO NOTHING
+ }
+
+private:
+ const luci::Module *_module;
+ const luci::CircleReader &_reader;
+};
+
+/**
+ * @brief ValidateGraphProp will validate inter graph node properties
+ */
+class ValidateGraphProp final
+{
+public:
+ void run(loco::Graph *g, const luci::Module *m, const luci::CircleReader &r)
+ {
+ for (auto node : loco::postorder_traversal(loco::output_nodes(g)))
+ {
+ if (recognize(node->dialect()))
+ {
+ auto cn = loco::must_cast<luci::CircleNode *>(node);
+
+ eval(cn, m, r);
+ }
+ }
+ }
+
+private:
+ bool recognize(const loco::Dialect *dialect) { return (dialect == luci::CircleDialect::get()); }
+
+ void eval(luci::CircleNode *node, const luci::Module *module, const luci::CircleReader &reader)
+ {
+ ValidateNodeProp val(module, reader);
+ node->accept(&val);
+ }
+};
+
+} // namespace
+
+namespace luci
+{
+
+/**
+ * @brief Do post import actions
+ */
+void post_import_graph(luci::Module *module, const luci::CircleReader &reader)
+{
+ LOGGER(l);
+
+ auto count = module->size();
+
+ for (size_t s = 0; s < count; ++s)
+ {
+ auto g = module->graph(s);
+ assert(g != nullptr);
+
+ INFO(l) << "--- FixInterGraph " << g->name() << "-------------------------";
+ FixInterGraph fix;
+ fix.run(g, module, reader);
+ }
+
+ for (size_t s = 0; s < count; ++s)
+ {
+ auto g = module->graph(s);
+ assert(g != nullptr);
+
+ INFO(l) << "--- ValidateGraphProp " << g->name() << "---------------------";
+ ValidateGraphProp prop;
+ prop.run(g, module, reader);
+ }
+
+ INFO(l) << "--- post_import_graph done -------------------------------------";
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/PostImport.h b/compiler/luci/import/src/PostImport.h
new file mode 100644
index 000000000..c719c588a
--- /dev/null
+++ b/compiler/luci/import/src/PostImport.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_POST_IMPORT_H__
+#define __LUCI_POST_IMPORT_H__
+
+#include "luci/Import/CircleReader.h"
+
+#include "luci/IR/Module.h"
+
+namespace luci
+{
+
+/**
+ * @brief Do post import actions
+ */
+void post_import_graph(luci::Module *module, const luci::CircleReader &reader);
+
+} // namespace luci
+
+#endif // __LUCI_POST_IMPORT_H__
diff --git a/compiler/luci/import/src/ValidateHelpers.cpp b/compiler/luci/import/src/ValidateHelpers.cpp
new file mode 100644
index 000000000..27306ba90
--- /dev/null
+++ b/compiler/luci/import/src/ValidateHelpers.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ValidateHelpers.h"
+
+namespace luci
+{
+
+bool validate_batch_space_nd(const GraphBuilderBase::ValidateArgs &args)
+{
+ const auto &inputs = args.op.inputs;
+ if (inputs.size() != 3)
+ return false;
+
+ // input 1 and 2 should have INT32/INT64 type
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor_1 = tensors.at(inputs.at(1));
+ switch (tensor_1->type)
+ {
+ case circle::TensorType_INT32:
+ case circle::TensorType_INT64:
+ break;
+ default:
+ return false;
+ }
+ const auto &tensor_2 = tensors.at(inputs.at(2));
+ switch (tensor_2->type)
+ {
+ case circle::TensorType_INT32:
+ case circle::TensorType_INT64:
+ break;
+ default:
+ return false;
+ }
+
+ // Only support input shape dimension 3 and 4 only
+ const auto &tensor_0 = tensors.at(inputs.at(0));
+ const auto t_0_s = tensor_0->shape.size();
+ if (t_0_s != 3 && t_0_s != 4)
+ return false;
+
+ // TODO check input shape
+
+ return true;
+}
+
+bool validate_minmax(const GraphBuilderBase::ValidateArgs &args)
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 2)
+ return false;
+
+ if (outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs.at(0));
+
+ switch (tensor->type)
+ {
+ case circle::TensorType_FLOAT16:
+ case circle::TensorType_FLOAT32:
+ case circle::TensorType_FLOAT64:
+ case circle::TensorType_INT32:
+ case circle::TensorType_INT64:
+ case circle::TensorType_UINT8:
+ break;
+ default:
+ return false;
+ }
+
+ if (tensors[inputs.at(1)]->type != tensor->type)
+ return false;
+
+ if (tensors[outputs[0]]->type != tensor->type)
+ return false;
+
+ return true;
+}
+
+bool validate_reduce_minmax(const GraphBuilderBase::ValidateArgs &args)
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+
+ if (inputs.size() != 2)
+ return false;
+
+ if (outputs.size() != 1)
+ return false;
+
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor_axis = tensors.at(inputs.at(1));
+
+ switch (tensor_axis->type)
+ {
+ case circle::TensorType_INT32:
+ case circle::TensorType_INT64:
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/ValidateHelpers.h b/compiler/luci/import/src/ValidateHelpers.h
new file mode 100644
index 000000000..4047b2f08
--- /dev/null
+++ b/compiler/luci/import/src/ValidateHelpers.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_VALIDATE_HELPERS_H__
+#define __LUCI_VALIDATE_HELPERS_H__
+
+#include "luci/Import/GraphBuilderBase.h"
+
+/**
+ * @Note Methods in this file provides helper functions to reduce duplicate codes
+ */
+
+namespace luci
+{
+
+bool validate_batch_space_nd(const GraphBuilderBase::ValidateArgs &args);
+bool validate_minmax(const GraphBuilderBase::ValidateArgs &args);
+bool validate_reduce_minmax(const GraphBuilderBase::ValidateArgs &args);
+
+} // namespace luci
+
+#endif // __LUCI_VALIDATE_HELPERS_H__
diff --git a/compiler/luci/lang/CMakeLists.txt b/compiler/luci/lang/CMakeLists.txt
new file mode 100644
index 000000000..32d0a890d
--- /dev/null
+++ b/compiler/luci/lang/CMakeLists.txt
@@ -0,0 +1,24 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(luci_lang SHARED ${SOURCES})
+target_include_directories(luci_lang PRIVATE src)
+target_include_directories(luci_lang PUBLIC include)
+target_link_libraries(luci_lang PUBLIC loco)
+target_link_libraries(luci_lang PUBLIC oops)
+target_link_libraries(luci_lang PRIVATE logo)
+target_link_libraries(luci_lang PRIVATE nncc_common)
+
+install(TARGETS luci_lang DESTINATION lib)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(luci_lang_test ${TESTS})
+target_include_directories(luci_lang_test PRIVATE src)
+target_link_libraries(luci_lang_test luci_lang)
+target_link_libraries(luci_lang_test logo)
diff --git a/compiler/luci/lang/README.md b/compiler/luci/lang/README.md
new file mode 100644
index 000000000..ea0e3d5da
--- /dev/null
+++ b/compiler/luci/lang/README.md
@@ -0,0 +1,3 @@
+# luci-lang
+
+`luci-lang` provides TensorFlow Lite and Circle Dialect IR
diff --git a/compiler/luci/lang/include/luci/IR/AttrDilation.h b/compiler/luci/lang/include/luci/IR/AttrDilation.h
new file mode 100644
index 000000000..c2b28d77d
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/AttrDilation.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_ATTRDILATION_H__
+#define __LUCI_IR_ATTRDILATION_H__
+
+#include <stdint.h>
+
+namespace luci
+{
+
+class Dilation final
+{
+public:
+ Dilation() : _w(1), _h(1) {}
+
+ int32_t w() const { return _w; }
+ void w(int32_t w) { _w = w; }
+
+ int32_t h() const { return _h; }
+ void h(int32_t h) { _h = h; }
+
+private:
+ int32_t _w;
+ int32_t _h;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_ATTRDILATION_H__
diff --git a/compiler/luci/lang/include/luci/IR/AttrFilter.h b/compiler/luci/lang/include/luci/IR/AttrFilter.h
new file mode 100644
index 000000000..7909fa523
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/AttrFilter.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_ATTRFILTER_H__
+#define __LUCI_IR_ATTRFILTER_H__
+
+#include <stdint.h>
+
+namespace luci
+{
+
+class Filter final
+{
+public:
+ Filter() : _w(1), _h(1) {}
+
+ int32_t w() const { return _w; }
+ void w(int32_t w) { _w = w; }
+
+ int32_t h() const { return _h; }
+ void h(int32_t h) { _h = h; }
+
+private:
+ int32_t _w;
+ int32_t _h;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_ATTRFILTER_H__
diff --git a/compiler/luci/lang/include/luci/IR/AttrFusedActFunc.h b/compiler/luci/lang/include/luci/IR/AttrFusedActFunc.h
new file mode 100644
index 000000000..3f21d5858
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/AttrFusedActFunc.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_ATTRFUSEDACTFUNC_H__
+#define __LUCI_IR_ATTRFUSEDACTFUNC_H__
+
+namespace luci
+{
+
+// TODO Divide into TFL version and Circle version when they go different approach
+enum class FusedActFunc
+{
+ UNDEFINED, // This is not defined by TFLite or Circle. This was added to
+ // prevent programming error.
+ NONE,
+ RELU,
+ RELU_N1_TO_1,
+ RELU6,
+ TANH,
+ SIGN_BIT
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_ATTRFUSEDACTFUNC_H__
diff --git a/compiler/luci/lang/include/luci/IR/AttrMirrorPadMode.h b/compiler/luci/lang/include/luci/IR/AttrMirrorPadMode.h
new file mode 100644
index 000000000..7ca9d5d99
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/AttrMirrorPadMode.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_ATTR_MIRROR_PAD_MODE_H__
+#define __LUCI_IR_ATTR_MIRROR_PAD_MODE_H__
+
+namespace luci
+{
+
+enum class MirrorPadMode
+{
+ UNDEFINED, // This is not defined by Circle. This was added to prevent programming error.
+
+ REFLECT,
+ SYMMETRIC,
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_ATTR_MIRROR_PAD_MODE_H__
diff --git a/compiler/luci/lang/include/luci/IR/AttrPadding.h b/compiler/luci/lang/include/luci/IR/AttrPadding.h
new file mode 100644
index 000000000..5c295e0cd
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/AttrPadding.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_ATTRPADDING_H__
+#define __LUCI_IR_ATTRPADDING_H__
+
+namespace luci
+{
+
+enum class Padding
+{
+ UNDEFINED, // This is not defined by TFLite. This was added to prevent programming error.
+
+ SAME,
+ VALID,
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_ATTRPADDING_H__
diff --git a/compiler/luci/lang/include/luci/IR/AttrStride.h b/compiler/luci/lang/include/luci/IR/AttrStride.h
new file mode 100644
index 000000000..654967d73
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/AttrStride.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_ATTRSTRIDE_H__
+#define __LUCI_IR_ATTRSTRIDE_H__
+
+#include <stdint.h>
+
+namespace luci
+{
+
+class Stride final
+{
+public:
+ Stride() : _w(1), _h(1) {}
+
+ int32_t w() const { return _w; }
+ void w(int32_t w) { _w = w; }
+
+ int32_t h() const { return _h; }
+ void h(int32_t h) { _h = h; }
+
+private:
+ int32_t _w;
+ int32_t _h;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_ATTRSTRIDE_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleDialect.h b/compiler/luci/lang/include/luci/IR/CircleDialect.h
new file mode 100644
index 000000000..1b25dc9c2
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleDialect.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEDIALECT_H__
+#define __LUCI_IR_CIRCLEDIALECT_H__
+
+#include <loco/IR/Dialect.h>
+
+namespace luci
+{
+
+/**
+ * @brief A singleton for Circle Dialect
+ */
+class CircleDialect final : public loco::Dialect
+{
+private:
+ CircleDialect();
+
+public:
+ CircleDialect(const CircleDialect &) = delete;
+ CircleDialect(CircleDialect &&) = delete;
+
+public:
+ static loco::Dialect *get(void);
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEDIALECT_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleNode.h b/compiler/luci/lang/include/luci/IR/CircleNode.h
new file mode 100644
index 000000000..92816ef04
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleNode.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLENODE_H__
+#define __LUCI_IR_CIRCLENODE_H__
+
+#include "CircleNodeDecl.h"
+#include "CircleNodeImpl.h"
+
+#endif // __LUCI_IR_CIRCLENODE_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleNodeDecl.h b/compiler/luci/lang/include/luci/IR/CircleNodeDecl.h
new file mode 100644
index 000000000..e6410d154
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleNodeDecl.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLENODEDECL_H__
+#define __LUCI_IR_CIRCLENODEDECL_H__
+
+#include <loco/IR/Dialect.h>
+#include <loco/IR/Node.h>
+#include <loco/IR/NodeMixins.h>
+#include <luci/IR/CircleShapeSignature.h>
+#include <luci/IR/PropertyShapeStatus.h>
+
+#include "CircleOpcode.h"
+#include "CircleNodeVisitor.forward.h"
+#include "CircleQuantParam.h"
+#include "SparsityParam.h"
+
+#include <memory>
+
+namespace luci
+{
+
+using NodeName = std::string;
+
+struct CircleNode : public loco::Node,
+ public loco::NodeMixin<loco::NodeTrait::DataType>,
+ public loco::NodeMixin<loco::NodeTrait::TensorShape>
+{
+ virtual ~CircleNode() = default;
+
+ const loco::Dialect *dialect(void) const final;
+ virtual CircleOpcode opcode(void) const = 0;
+
+ template <typename T> T accept(CircleNodeVisitorBase<T> *) const;
+ template <typename T> T accept(CircleNodeMutableVisitorBase<T> *);
+
+ NodeName name(void) const { return _name; }
+ void name(const NodeName &name) { _name = name; }
+
+ CircleQuantParam *quantparam(void) const { return _quantparam.get(); }
+ void quantparam(std::unique_ptr<CircleQuantParam> &&quantparam)
+ {
+ _quantparam = std::move(quantparam);
+ }
+
+ SparsityParam *sparsityparam(void) const { return _sparsityparam.get(); }
+ void sparsityparam(std::unique_ptr<SparsityParam> &&sparsityparam)
+ {
+ _sparsityparam = std::move(sparsityparam);
+ }
+
+ const ShapeSignature &shape_signature(void) const { return _shape_signature; }
+ void shape_signature(const ShapeSignature &ss) { _shape_signature = ss; }
+
+ ShapeStatus shape_status(void) const { return _shape_status; }
+ void shape_status(ShapeStatus ss) { _shape_status = ss; }
+
+ int32_t op_version(void) const { return _op_version; }
+ void op_version(int32_t op_version) { _op_version = op_version; }
+
+private:
+ NodeName _name;
+ std::unique_ptr<CircleQuantParam> _quantparam;
+ std::unique_ptr<SparsityParam> _sparsityparam;
+ ShapeSignature _shape_signature;
+ ShapeStatus _shape_status{ShapeStatus::UNDEFINED};
+ int32_t _op_version = 1;
+};
+
+template <CircleOpcode Code> struct CircleNodeImpl : public CircleNode
+{
+ virtual ~CircleNodeImpl() = default;
+
+ uint32_t opnum(void) const final { return static_cast<uint32_t>(Code); }
+ CircleOpcode opcode(void) const final { return Code; }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLENODEDECL_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleNodeImpl.h b/compiler/luci/lang/include/luci/IR/CircleNodeImpl.h
new file mode 100644
index 000000000..a6b9488db
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleNodeImpl.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLENODEIMPL_H__
+#define __LUCI_IR_CIRCLENODEIMPL_H__
+
+#include "CircleNodes.h"
+
+#include <oops/InternalExn.h>
+
+#include <cassert>
+
+namespace luci
+{
+
+template <typename T> T CircleNode::accept(CircleNodeVisitorBase<T> *v) const
+{
+ switch (this->opcode())
+ {
+#define CIRCLE_NODE(OPCODE, CLASS) \
+ \
+ case CircleOpcode::OPCODE: \
+ return v->visit(dynamic_cast<const CLASS *>(this));
+
+#include "CircleNodes.lst"
+#undef CIRCLE_NODE
+
+ default:
+ break;
+ }
+
+ INTERNAL_EXN("CircleNode::accept(CircleNodeVisitorBase) not handled");
+}
+
+template <typename T> T CircleNode::accept(CircleNodeMutableVisitorBase<T> *v)
+{
+ switch (this->opcode())
+ {
+#define CIRCLE_NODE(OPCODE, CLASS) \
+ \
+ case CircleOpcode::OPCODE: \
+ return v->visit(dynamic_cast<CLASS *>(this));
+
+#include "CircleNodes.lst"
+#undef CIRCLE_NODE
+
+ default:
+ break;
+ }
+
+ INTERNAL_EXN("CircleNode::accept(CircleNodeMutableVisitorBase) not handled");
+}
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLENODEIMPL_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.forward.h b/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.forward.h
new file mode 100644
index 000000000..70901ca87
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.forward.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLENODE_VISITOR_FORWARD_H__
+#define __LUCI_IR_CIRCLENODE_VISITOR_FORWARD_H__
+
+namespace luci
+{
+
+// NOTE These forward declarations SHOULD BE aligned with Node delcarations in
+// "CircleNodeVisitor.h"
+template <typename T> struct CircleNodeVisitorBase;
+template <typename T> struct CircleNodeMutableVisitorBase;
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLENODE_VISITOR_FORWARD_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.h b/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.h
new file mode 100644
index 000000000..43339fe84
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLENODE_VISITOR_H__
+#define __LUCI_IR_CIRCLENODE_VISITOR_H__
+
+#include "CircleNode.h"
+#include "CircleNodes.h"
+
+#include <oops/InternalExn.h>
+
+namespace luci
+{
+
+/**
+ * DO NOT use this class. Use CircleNodeVisitor instead.
+ */
+template <typename T> struct CircleNodeVisitorBase
+{
+ virtual ~CircleNodeVisitorBase() = default;
+
+#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) virtual T visit(const CIRCLE_CLASS *) = 0;
+
+#include "CircleNodes.lst"
+#undef CIRCLE_NODE
+};
+
+template <typename T> struct CircleNodeVisitor : public CircleNodeVisitorBase<T>
+{
+ virtual ~CircleNodeVisitor() = default;
+
+#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) \
+ virtual T visit(const CIRCLE_CLASS *node) { return visit(static_cast<const CircleNode *>(node)); }
+
+#include "CircleNodes.lst"
+
+#undef CIRCLE_NODE
+
+ /// @brief Default fallback
+ virtual T visit(const CircleNode *) { INTERNAL_EXN("CircleNodeVisitor: NYI node"); }
+};
+
+/**
+ * DO NOT use this class. Use CircleNodeMutableVisitor instead.
+ */
+template <typename T> struct CircleNodeMutableVisitorBase
+{
+ virtual ~CircleNodeMutableVisitorBase() = default;
+
+#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) virtual T visit(CIRCLE_CLASS *) = 0;
+
+#include "CircleNodes.lst"
+
+#undef CIRCLE_NODE
+};
+
+template <typename T> struct CircleNodeMutableVisitor : public CircleNodeMutableVisitorBase<T>
+{
+ virtual ~CircleNodeMutableVisitor() = default;
+
+#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) \
+ virtual T visit(CIRCLE_CLASS *node) { return visit(static_cast<CircleNode *>(node)); }
+
+#include "CircleNodes.lst"
+
+#undef CIRCLE_NODE
+
+ /// @brief Default fallback
+ virtual T visit(CircleNode *) { INTERNAL_EXN("CircleNodeMutableVisitor: NYI node"); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CircleNode_VISITOR_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleNodes.h b/compiler/luci/lang/include/luci/IR/CircleNodes.h
new file mode 100644
index 000000000..fde0b612b
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleNodes.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLENODES_H__
+#define __LUCI_IR_CIRCLENODES_H__
+
+#include "Nodes/CircleAbs.h"
+#include "Nodes/CircleAdd.h"
+#include "Nodes/CircleAddN.h"
+#include "Nodes/CircleArgMax.h"
+#include "Nodes/CircleArgMin.h"
+#include "Nodes/CircleAveragePool2D.h"
+#include "Nodes/CircleBatchMatMul.h"
+#include "Nodes/CircleBatchToSpaceND.h"
+#include "Nodes/CircleCast.h"
+#include "Nodes/CircleCeil.h"
+#include "Nodes/CircleConcatenation.h"
+#include "Nodes/CircleConst.h"
+#include "Nodes/CircleConv2D.h"
+#include "Nodes/CircleCos.h"
+#include "Nodes/CircleCustom.h"
+#include "Nodes/CircleDepthToSpace.h"
+#include "Nodes/CircleDepthwiseConv2D.h"
+#include "Nodes/CircleDequantize.h"
+#include "Nodes/CircleDiv.h"
+#include "Nodes/CircleElu.h"
+#include "Nodes/CircleEqual.h"
+#include "Nodes/CircleExp.h"
+#include "Nodes/CircleExpandDims.h"
+#include "Nodes/CircleFill.h"
+#include "Nodes/CircleFloor.h"
+#include "Nodes/CircleFloorDiv.h"
+#include "Nodes/CircleFloorMod.h"
+#include "Nodes/CircleFullyConnected.h"
+#include "Nodes/CircleGather.h"
+#include "Nodes/CircleGatherNd.h"
+#include "Nodes/CircleGreater.h"
+#include "Nodes/CircleGreaterEqual.h"
+#include "Nodes/CircleIf.h"
+#include "Nodes/CircleL2Normalize.h"
+#include "Nodes/CircleL2Pool2D.h"
+#include "Nodes/CircleLeakyRelu.h"
+#include "Nodes/CircleLess.h"
+#include "Nodes/CircleLessEqual.h"
+#include "Nodes/CircleLocalResponseNormalization.h"
+#include "Nodes/CircleLog.h"
+#include "Nodes/CircleLogicalAnd.h"
+#include "Nodes/CircleLogicalNot.h"
+#include "Nodes/CircleLogicalOr.h"
+#include "Nodes/CircleLogistic.h"
+#include "Nodes/CircleLogSoftmax.h"
+#include "Nodes/CircleMatrixDiag.h"
+#include "Nodes/CircleMatrixSetDiag.h"
+#include "Nodes/CircleMaximum.h"
+#include "Nodes/CircleMaxPool2D.h"
+#include "Nodes/CircleMean.h"
+#include "Nodes/CircleMinimum.h"
+#include "Nodes/CircleMirrorPad.h"
+#include "Nodes/CircleMul.h"
+#include "Nodes/CircleNeg.h"
+#include "Nodes/CircleNonMaxSuppressionV4.h"
+#include "Nodes/CircleNonMaxSuppressionV5.h"
+#include "Nodes/CircleNotEqual.h"
+#include "Nodes/CircleOneHot.h"
+#include "Nodes/CirclePack.h"
+#include "Nodes/CirclePad.h"
+#include "Nodes/CirclePadV2.h"
+#include "Nodes/CirclePow.h"
+#include "Nodes/CirclePRelu.h"
+#include "Nodes/CircleRange.h"
+#include "Nodes/CircleRank.h"
+#include "Nodes/CircleReduceAny.h"
+#include "Nodes/CircleReduceMax.h"
+#include "Nodes/CircleReduceMin.h"
+#include "Nodes/CircleReduceProd.h"
+#include "Nodes/CircleRelu.h"
+#include "Nodes/CircleRelu6.h"
+#include "Nodes/CircleReluN1To1.h"
+#include "Nodes/CircleReshape.h"
+#include "Nodes/CircleResizeBilinear.h"
+#include "Nodes/CircleResizeNearestNeighbor.h"
+#include "Nodes/CircleReverseSequence.h"
+#include "Nodes/CircleReverseV2.h"
+#include "Nodes/CircleRound.h"
+#include "Nodes/CircleRsqrt.h"
+#include "Nodes/CircleScatterNd.h"
+#include "Nodes/CircleSegmentSum.h"
+#include "Nodes/CircleSelect.h"
+#include "Nodes/CircleSelectV2.h"
+#include "Nodes/CircleShape.h"
+#include "Nodes/CircleSin.h"
+#include "Nodes/CircleSlice.h"
+#include "Nodes/CircleSoftmax.h"
+#include "Nodes/CircleSpaceToBatchND.h"
+#include "Nodes/CircleSpaceToDepth.h"
+#include "Nodes/CircleSparseToDense.h"
+#include "Nodes/CircleSplit.h"
+#include "Nodes/CircleSplitV.h"
+#include "Nodes/CircleSqrt.h"
+#include "Nodes/CircleSquare.h"
+#include "Nodes/CircleSquaredDifference.h"
+#include "Nodes/CircleSqueeze.h"
+#include "Nodes/CircleStridedSlice.h"
+#include "Nodes/CircleSub.h"
+#include "Nodes/CircleSum.h"
+#include "Nodes/CircleTanh.h"
+#include "Nodes/CircleTile.h"
+#include "Nodes/CircleTopKV2.h"
+#include "Nodes/CircleTranspose.h"
+#include "Nodes/CircleTransposeConv.h"
+#include "Nodes/CircleUnidirectionalSequenceLSTM.h"
+#include "Nodes/CircleUnique.h"
+#include "Nodes/CircleUnpack.h"
+#include "Nodes/CircleWhere.h"
+#include "Nodes/CircleWhile.h"
+#include "Nodes/CircleZerosLike.h"
+// Circle only
+#include "Nodes/CircleBCQFullyConnected.h"
+#include "Nodes/CircleBCQGather.h"
+#include "Nodes/CircleInstanceNorm.h"
+// Virtual nodes
+#include "Nodes/CircleInput.h"
+#include "Nodes/CircleOutput.h"
+#include "Nodes/CircleCustomOut.h"
+#include "Nodes/CircleIfOut.h"
+#include "Nodes/CircleNonMaxSuppressionV4Out.h"
+#include "Nodes/CircleNonMaxSuppressionV5Out.h"
+#include "Nodes/CircleUnpackOut.h"
+#include "Nodes/CircleUniqueOut.h"
+#include "Nodes/CircleSplitOut.h"
+#include "Nodes/CircleSplitVOut.h"
+#include "Nodes/CircleTopKV2Out.h"
+#include "Nodes/CircleWhileOut.h"
+
+#include <loco/IR/Graph.h>
+
+namespace luci
+{
+
+/**
+ * @brief Set both CircleReshape's 2nd input as CircleConst, and newShape attribute
+ * with same value
+ * @note Shape inference for TFLReshape forces them to be same
+ *
+ * TODO find better place for this helper
+ */
+void set_new_shape(CircleReshape *node, int32_t *base, uint32_t size);
+
+/// @brief Link GraphOutput with CircleOutput node
+void link(loco::GraphOutput *, CircleOutput *);
+
+/// @brief Link GraphInput with CircleInput node
+void link(loco::GraphInput *, CircleInput *);
+
+/// @brief Find a CircleOutput node with a given output index
+CircleOutput *output_node(loco::Graph *g, const loco::GraphOutputIndex &index);
+
+/// @brief Find a Pull node with a given input index
+CircleInput *input_node(loco::Graph *g, const loco::GraphInputIndex &index);
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLENODES_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleNodes.lst b/compiler/luci/lang/include/luci/IR/CircleNodes.lst
new file mode 100644
index 000000000..b9d545893
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleNodes.lst
@@ -0,0 +1,142 @@
+#ifndef CIRCLE_NODE
+#error "Define CIRCLE_NODE"
+#endif // CIRCLE_NODE
+
+//
+// PLEASE SORT NODE DECLS IN ALPHABETICAL ORDER
+//
+// Naming rule: Follow names in TensorFlow C++ source; same as TFDialect
+// ex) for AvgPool, tensorflow/core/ops/nn_ops.cc
+// REGISTER_OP("AvgPool") <-- OPCODE: AvgPool. Prefix `Circle` for CLASS name
+// .Input("value: T") <-- Input name is 'value'
+//
+
+CIRCLE_NODE(ABS, luci::CircleAbs)
+CIRCLE_NODE(ADD, luci::CircleAdd)
+CIRCLE_NODE(ADD_N, luci::CircleAddN)
+CIRCLE_NODE(ARG_MAX, luci::CircleArgMax)
+CIRCLE_NODE(ARG_MIN, luci::CircleArgMin)
+CIRCLE_NODE(AVERAGE_POOL_2D, luci::CircleAveragePool2D)
+CIRCLE_NODE(BATCH_TO_SPACE_ND, luci::CircleBatchToSpaceND)
+CIRCLE_NODE(BATCHMATMUL, luci::CircleBatchMatMul)
+CIRCLE_NODE(CAST, luci::CircleCast)
+CIRCLE_NODE(CEIL, luci::CircleCeil)
+CIRCLE_NODE(CONCATENATION, luci::CircleConcatenation)
+CIRCLE_NODE(CONV_2D, luci::CircleConv2D)
+CIRCLE_NODE(COS, luci::CircleCos)
+CIRCLE_NODE(CUSTOM, luci::CircleCustom)
+CIRCLE_NODE(DEPTH_TO_SPACE, luci::CircleDepthToSpace)
+CIRCLE_NODE(DEPTHWISE_CONV_2D, luci::CircleDepthwiseConv2D)
+CIRCLE_NODE(DEQUANTIZE, luci::CircleDequantize)
+CIRCLE_NODE(DIV, luci::CircleDiv)
+CIRCLE_NODE(ELU, luci::CircleElu)
+CIRCLE_NODE(EQUAL, luci::CircleEqual)
+CIRCLE_NODE(EXP, luci::CircleExp)
+CIRCLE_NODE(EXPAND_DIMS, luci::CircleExpandDims)
+CIRCLE_NODE(FILL, luci::CircleFill)
+CIRCLE_NODE(FLOOR, luci::CircleFloor)
+CIRCLE_NODE(FLOOR_DIV, luci::CircleFloorDiv)
+CIRCLE_NODE(FLOOR_MOD, luci::CircleFloorMod)
+CIRCLE_NODE(FULLY_CONNECTED, luci::CircleFullyConnected)
+CIRCLE_NODE(GATHER, luci::CircleGather)
+CIRCLE_NODE(GATHER_ND, luci::CircleGatherNd)
+CIRCLE_NODE(GREATER, luci::CircleGreater)
+CIRCLE_NODE(GREATER_EQUAL, luci::CircleGreaterEqual)
+CIRCLE_NODE(IF, luci::CircleIf)
+CIRCLE_NODE(L2_NORMALIZATION, luci::CircleL2Normalize)
+CIRCLE_NODE(L2_POOL_2D, luci::CircleL2Pool2D)
+CIRCLE_NODE(LEAKY_RELU, luci::CircleLeakyRelu)
+CIRCLE_NODE(LESS, luci::CircleLess)
+CIRCLE_NODE(LESS_EQUAL, luci::CircleLessEqual)
+CIRCLE_NODE(LOCAL_RESPONSE_NORMALIZATION, luci::CircleLocalResponseNormalization)
+CIRCLE_NODE(LOG, luci::CircleLog)
+CIRCLE_NODE(LOGICAL_AND, luci::CircleLogicalAnd)
+CIRCLE_NODE(LOGICAL_NOT, luci::CircleLogicalNot)
+CIRCLE_NODE(LOGICAL_OR, luci::CircleLogicalOr)
+CIRCLE_NODE(LOGISTIC, luci::CircleLogistic)
+CIRCLE_NODE(LOG_SOFTMAX, luci::CircleLogSoftmax)
+CIRCLE_NODE(MATRIX_DIAG, luci::CircleMatrixDiag)
+CIRCLE_NODE(MAX_POOL_2D, luci::CircleMaxPool2D)
+CIRCLE_NODE(MATRIX_SET_DIAG, luci::CircleMatrixSetDiag)
+CIRCLE_NODE(MAXIMUM, luci::CircleMaximum)
+CIRCLE_NODE(MEAN, luci::CircleMean)
+CIRCLE_NODE(MINIMUM, luci::CircleMinimum)
+CIRCLE_NODE(MIRROR_PAD, luci::CircleMirrorPad)
+CIRCLE_NODE(MUL, luci::CircleMul)
+CIRCLE_NODE(NEG, luci::CircleNeg)
+CIRCLE_NODE(NON_MAX_SUPPRESSION_V4, luci::CircleNonMaxSuppressionV4)
+CIRCLE_NODE(NON_MAX_SUPPRESSION_V5, luci::CircleNonMaxSuppressionV5)
+CIRCLE_NODE(NOT_EQUAL, luci::CircleNotEqual)
+CIRCLE_NODE(ONE_HOT, luci::CircleOneHot)
+CIRCLE_NODE(PACK, luci::CirclePack)
+CIRCLE_NODE(PAD, luci::CirclePad)
+CIRCLE_NODE(PADV2, luci::CirclePadV2)
+CIRCLE_NODE(POW, luci::CirclePow)
+CIRCLE_NODE(PRELU, luci::CirclePRelu)
+CIRCLE_NODE(RANGE, luci::CircleRange)
+CIRCLE_NODE(RANK, luci::CircleRank)
+CIRCLE_NODE(REDUCE_ANY, luci::CircleReduceAny)
+CIRCLE_NODE(REDUCE_MAX, luci::CircleReduceMax)
+CIRCLE_NODE(REDUCE_MIN, luci::CircleReduceMin)
+CIRCLE_NODE(REDUCE_PROD, luci::CircleReduceProd)
+CIRCLE_NODE(RELU, luci::CircleRelu)
+CIRCLE_NODE(RELU6, luci::CircleRelu6)
+CIRCLE_NODE(RELU_N1_TO_1, luci::CircleReluN1To1)
+CIRCLE_NODE(RESHAPE, luci::CircleReshape)
+CIRCLE_NODE(RESIZE_BILINEAR, luci::CircleResizeBilinear)
+CIRCLE_NODE(RESIZE_NEAREST_NEIGHBOR, luci::CircleResizeNearestNeighbor)
+CIRCLE_NODE(REVERSE_SEQUENCE, luci::CircleReverseSequence)
+CIRCLE_NODE(REVERSE_V2, luci::CircleReverseV2)
+CIRCLE_NODE(ROUND, luci::CircleRound)
+CIRCLE_NODE(RSQRT, luci::CircleRsqrt)
+CIRCLE_NODE(SCATTER_ND, luci::CircleScatterNd)
+CIRCLE_NODE(SEGMENT_SUM, luci::CircleSegmentSum)
+CIRCLE_NODE(SELECT, luci::CircleSelect)
+CIRCLE_NODE(SELECT_V2, luci::CircleSelectV2)
+CIRCLE_NODE(SHAPE, luci::CircleShape)
+CIRCLE_NODE(SIN, luci::CircleSin)
+CIRCLE_NODE(SLICE, luci::CircleSlice)
+CIRCLE_NODE(SOFTMAX, luci::CircleSoftmax)
+CIRCLE_NODE(SPACE_TO_BATCH_ND, luci::CircleSpaceToBatchND)
+CIRCLE_NODE(SPACE_TO_DEPTH, luci::CircleSpaceToDepth)
+CIRCLE_NODE(SPARSE_TO_DENSE, luci::CircleSparseToDense)
+CIRCLE_NODE(SPLIT, luci::CircleSplit)
+CIRCLE_NODE(SPLIT_V, luci::CircleSplitV)
+CIRCLE_NODE(SQRT, luci::CircleSqrt)
+CIRCLE_NODE(SQUARE, luci::CircleSquare)
+CIRCLE_NODE(SQUARED_DIFFERENCE, luci::CircleSquaredDifference)
+CIRCLE_NODE(SQUEEZE, luci::CircleSqueeze)
+CIRCLE_NODE(STRIDED_SLICE, luci::CircleStridedSlice)
+CIRCLE_NODE(SUB, luci::CircleSub)
+CIRCLE_NODE(SUM, luci::CircleSum)
+CIRCLE_NODE(TANH, luci::CircleTanh)
+CIRCLE_NODE(TILE, luci::CircleTile)
+CIRCLE_NODE(TOPK_V2, luci::CircleTopKV2)
+CIRCLE_NODE(TRANSPOSE, luci::CircleTranspose)
+CIRCLE_NODE(TRANSPOSE_CONV, luci::CircleTransposeConv)
+CIRCLE_NODE(UNIDIRECTIONAL_SEQUENCE_LSTM, luci::CircleUnidirectionalSequenceLSTM)
+CIRCLE_NODE(UNIQUE, luci::CircleUnique)
+CIRCLE_NODE(UNPACK, luci::CircleUnpack)
+CIRCLE_NODE(WHERE, luci::CircleWhere)
+CIRCLE_NODE(WHILE, luci::CircleWhile)
+CIRCLE_NODE(ZEROS_LIKE, luci::CircleZerosLike)
+// Circle Only
+CIRCLE_NODE(BCQ_FULLY_CONNECTED, luci::CircleBCQFullyConnected)
+CIRCLE_NODE(BCQ_GATHER, luci::CircleBCQGather)
+CIRCLE_NODE(INSTANCE_NORM, luci::CircleInstanceNorm)
+// Virtual node(s)
+CIRCLE_NODE(CIRCLECONST, luci::CircleConst)
+CIRCLE_NODE(CIRCLEINPUT, luci::CircleInput)
+CIRCLE_NODE(CIRCLEOUTPUT, luci::CircleOutput)
+CIRCLE_NODE(CIRCLEOUTPUTDUMMY, luci::CircleOutputDummy)
+CIRCLE_NODE(CIRCLEOUTPUTEXCLUDE, luci::CircleOutputExclude)
+CIRCLE_NODE(CIRCLECUSTOMOUT, luci::CircleCustomOut)
+CIRCLE_NODE(CIRCLEIFOUT, luci::CircleIfOut)
+CIRCLE_NODE(CIRCLENONMAXSUPPRESSIONV4OUT, luci::CircleNonMaxSuppressionV4Out)
+CIRCLE_NODE(CIRCLENONMAXSUPPRESSIONV5OUT, luci::CircleNonMaxSuppressionV5Out)
+CIRCLE_NODE(CIRCLESPLITOUT, luci::CircleSplitOut)
+CIRCLE_NODE(CIRCLESPLITVOUT, luci::CircleSplitVOut)
+CIRCLE_NODE(CIRCLETOPKV2OUT, luci::CircleTopKV2Out)
+CIRCLE_NODE(CIRCLEUNIQUEOUT, luci::CircleUniqueOut)
+CIRCLE_NODE(CIRCLEUNPACKOUT, luci::CircleUnpackOut)
+CIRCLE_NODE(CIRCLEWHILEOUT, luci::CircleWhileOut)
diff --git a/compiler/luci/lang/include/luci/IR/CircleOpcode.h b/compiler/luci/lang/include/luci/IR/CircleOpcode.h
new file mode 100644
index 000000000..703b70da2
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleOpcode.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEOPCODE_H__
+#define __LUCI_IR_CIRCLEOPCODE_H__
+
+namespace luci
+{
+
+enum class CircleOpcode
+{
+#define CIRCLE_NODE(OPCODE, CLASS) OPCODE,
+#include "CircleNodes.lst"
+#undef CIRCLE_NODE
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEOPCODE_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleQuantParam.h b/compiler/luci/lang/include/luci/IR/CircleQuantParam.h
new file mode 100644
index 000000000..694437303
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleQuantParam.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEQUANTPARAM_H__
+#define __LUCI_IR_CIRCLEQUANTPARAM_H__
+
+#include <cstdint>
+#include <vector>
+
+namespace luci
+{
+
+struct CircleQuantParam
+{
+ std::vector<float> min;
+ std::vector<float> max;
+ std::vector<float> scale;
+ std::vector<int64_t> zerop;
+ int32_t quantized_dimension{0};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEQUANTPARAM_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleShapeSignature.h b/compiler/luci/lang/include/luci/IR/CircleShapeSignature.h
new file mode 100644
index 000000000..970f1b521
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleShapeSignature.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_SHAPE_SIGNATURE_H__
+#define __LUCI_IR_SHAPE_SIGNATURE_H__
+
+#include <stdint.h>
+#include <vector>
+
+namespace luci
+{
+
+class ShapeSignature
+{
+public:
+ ShapeSignature() = default;
+
+ ShapeSignature(const std::vector<int32_t> &shape_signature)
+ {
+ _shape_signature = shape_signature;
+ }
+
+public:
+ const std::vector<int32_t> &as_vector() const { return _shape_signature; }
+
+ int32_t dim(uint32_t d) const { return _shape_signature.at(d); }
+ int32_t &dim(uint32_t d) { return _shape_signature.at(d); }
+
+ uint32_t rank(void) const { return _shape_signature.size(); }
+ void rank(uint32_t rank) { _shape_signature.resize(rank); }
+
+private:
+ std::vector<int32_t> _shape_signature{};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_SHAPE_SIGNATURE_H__
diff --git a/compiler/luci/lang/include/luci/IR/LuciNodeMixins.h b/compiler/luci/lang/include/luci/IR/LuciNodeMixins.h
new file mode 100644
index 000000000..c1bb0db11
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/LuciNodeMixins.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_LUCINODEMIXINS_H__
+#define __LUCI_IR_LUCINODEMIXINS_H__
+
+#include "luci/IR/AttrFusedActFunc.h"
+
+#include <loco/IR/Node.h>
+#include <loco/IR/NodeMixins.h>
+
+#include <vector>
+
+namespace luci
+{
+
+/// @brief enumeration of mixin class
+enum class LuciNodeTrait
+{
+ FusedActFunc,
+ Bias
+};
+
+template <LuciNodeTrait T> class LuciNodeMixin;
+
+template <> class LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ LuciNodeMixin() = default;
+
+public:
+ FusedActFunc fusedActivationFunction() const { return _fused_act_fun; }
+ void fusedActivationFunction(FusedActFunc fused_act_fun) { _fused_act_fun = fused_act_fun; }
+
+private:
+ FusedActFunc _fused_act_fun = FusedActFunc::UNDEFINED;
+};
+
+/**
+ * @brief Mixin class for nodes that has a bias input
+ */
+template <> class LuciNodeMixin<LuciNodeTrait::Bias>
+{
+public:
+ LuciNodeMixin() = default;
+
+public:
+ virtual loco::Node *bias(void) const = 0; /// @brief get the input for bias.
+ virtual void bias(loco::Node *node) = 0; /// @brief set the input for bias.
+};
+
+/**
+ * @brief Nodes with the fixed number of inputs
+ *
+ * TODO Deprecated this class, and use loco::FixedArity instead
+ */
+template <unsigned N, typename Base> class FixedArityNode : public Base
+{
+public:
+ FixedArityNode()
+ {
+ _args.resize(N);
+ for (uint32_t n = 0; n < N; ++n)
+ {
+ _args[n] = std::make_unique<loco::Use>(this);
+ }
+ }
+
+ virtual ~FixedArityNode() = default;
+
+public:
+ unsigned arity(void) const final { return N; }
+
+ loco::Node *arg(uint32_t n) const final { return _args.at(n)->node(); }
+
+ void drop(void) final
+ {
+ for (uint32_t n = 0; n < N; ++n)
+ {
+ _args.at(n)->node(nullptr);
+ }
+ }
+
+protected:
+ // This API allows inherited classes to access "_args" field.
+ loco::Use *at(unsigned n) const { return _args.at(n).get(); }
+
+private:
+ std::vector<std::unique_ptr<loco::Use>> _args{};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_LUCINODEMIXINS_H__
diff --git a/compiler/luci/lang/include/luci/IR/Module.h b/compiler/luci/lang/include/luci/IR/Module.h
new file mode 100644
index 000000000..30eac59ce
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Module.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_MODULE_H__
+#define __LUCI_MODULE_H__
+
+#include <loco/IR/Graph.h>
+
+#include <memory>
+#include <vector>
+
+namespace luci
+{
+
+/**
+ * @brief Collection of 'loco::Graph's
+ */
+class Module final
+{
+public:
+ Module() = default;
+
+ // Copy/Move is not allowed for Module
+ Module(const Module &) = delete;
+ Module(Module &&) = delete;
+
+ ~Module() = default;
+
+public:
+ size_t size(void) const { return _graphs.size(); }
+
+public:
+ void add(std::unique_ptr<loco::Graph> &&g);
+
+ /**
+ * @brief provide main graph
+ */
+ loco::Graph *graph(void) const;
+
+ /**
+ * @brief provide graph with an index
+ *
+ * @note graph(0) is interpreted as a main graph
+ */
+ loco::Graph *graph(size_t idx) const;
+
+ // TODO provide graph accessor with a name
+
+private:
+ std::vector<std::unique_ptr<loco::Graph>> _graphs;
+};
+
+std::unique_ptr<Module> make_module(void);
+
+} // namespace luci
+
+#endif // __LUCI_MODULE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleAbs.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleAbs.h
new file mode 100644
index 000000000..45dba15bf
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleAbs.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCELABS_H__
+#define __LUCI_IR_CIRCELABS_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief ABS in Circle
+ */
+class CircleAbs final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::ABS>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCELABS_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleAdd.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleAdd.h
new file mode 100644
index 000000000..f26eccd1a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleAdd.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCELADD_H__
+#define __LUCI_IR_CIRCELADD_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief ADD in Circle
+ */
+class CircleAdd final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::ADD>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCELADD_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleAddN.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleAddN.h
new file mode 100644
index 000000000..6ba4a96bc
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleAddN.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCEL_ADD_N_H__
+#define __LUCI_IR_CIRCEL_ADD_N_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/VariadicArityNode.h"
+
+namespace luci
+{
+
+/**
+ * @brief ADD_N in Circle
+ */
+class CircleAddN final : public VariadicArityNode<CircleNodeImpl<CircleOpcode::ADD_N>>
+{
+public:
+ CircleAddN(uint32_t arity) : VariadicArityNode<CircleNodeImpl<CircleOpcode::ADD_N>>(arity)
+ {
+ assert(arity >= 1);
+ }
+
+public:
+ Node *inputs(uint32_t index) const { return at(index)->node(); }
+ void inputs(uint32_t index, Node *node) { at(index)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCEL_ADD_N_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMax.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMax.h
new file mode 100644
index 000000000..dbc4b2b3a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMax.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCELARGMAX_H__
+#define __LUCI_IR_CIRCELARGMAX_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief ARG_MAX in Circle
+ */
+class CircleArgMax final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::ARG_MAX>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *dimension(void) const { return at(1)->node(); }
+ void dimension(loco::Node *node) { at(1)->node(node); }
+
+public:
+ loco::DataType output_type(void) const { return _output_type; }
+ void output_type(loco::DataType ot) { _output_type = ot; }
+
+private:
+ loco::DataType _output_type{loco::DataType::S64};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCELARGMAX_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMin.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMin.h
new file mode 100644
index 000000000..8cb561983
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMin.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCELARGMIN_H__
+#define __LUCI_IR_CIRCELARGMIN_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief ARG_Min in Circle
+ */
+class CircleArgMin final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::ARG_MIN>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *dimension(void) const { return at(1)->node(); }
+ void dimension(loco::Node *node) { at(1)->node(node); }
+
+public:
+ loco::DataType output_type(void) const { return _output_type; }
+ void output_type(loco::DataType ot) { _output_type = ot; }
+
+private:
+ loco::DataType _output_type{loco::DataType::S64};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCELARGMIN_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleAveragePool2D.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleAveragePool2D.h
new file mode 100644
index 000000000..0b43b40c8
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleAveragePool2D.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEAVERAGEPOOL2D_H__
+#define __LUCI_IR_CIRCLEAVERAGEPOOL2D_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFilter.h"
+#include "luci/IR/AttrPadding.h"
+#include "luci/IR/AttrStride.h"
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief AVERAGE_POOL_2D in Circle
+ */
+class CircleAveragePool2D final
+ : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::AVERAGE_POOL_2D>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ CircleAveragePool2D() : _padding(Padding::UNDEFINED) { /* empty */}
+
+public:
+ loco::Node *value(void) const { return at(0)->node(); }
+ void value(loco::Node *node) { at(0)->node(node); }
+
+ Padding padding() const { return _padding; }
+ void padding(Padding padding) { _padding = padding; }
+
+ const Filter *filter(void) const { return &_filter; }
+ Filter *filter(void) { return &_filter; }
+
+ const Stride *stride(void) const { return &_stride; }
+ Stride *stride(void) { return &_stride; }
+
+private:
+ Padding _padding;
+ Stride _stride;
+ Filter _filter;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEAVERAGEPOOL2D_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleBCQFullyConnected.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleBCQFullyConnected.h
new file mode 100644
index 000000000..7d12d593a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleBCQFullyConnected.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEBCQFULLYCONNECTED_H__
+#define __LUCI_IR_CIRCLEBCQFULLYCONNECTED_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief BCQ_FULLY_CONNECTED in Circle
+ */
+class CircleBCQFullyConnected final
+ : public FixedArityNode<5, CircleNodeImpl<CircleOpcode::BCQ_FULLY_CONNECTED>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>,
+ public LuciNodeMixin<LuciNodeTrait::Bias>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *weights_scales(void) const { return at(1)->node(); }
+ void weights_scales(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *weights_binary(void) const { return at(2)->node(); }
+ void weights_binary(loco::Node *node) { at(2)->node(node); }
+
+ loco::Node *bias(void) const override { return at(3)->node(); }
+ void bias(loco::Node *node) override { at(3)->node(node); }
+
+ loco::Node *weights_clusters(void) const { return at(4)->node(); }
+ void weights_clusters(loco::Node *node) { at(4)->node(node); }
+
+public:
+ int32_t weights_hidden_size(void) const { return _weights_hidden_size; }
+ void weights_hidden_size(int32_t weights_hidden_size)
+ {
+ _weights_hidden_size = weights_hidden_size;
+ }
+
+private:
+ int32_t _weights_hidden_size = 0;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEBCQFULLYCONNECTED_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleBCQGather.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleBCQGather.h
new file mode 100644
index 000000000..f7638261d
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleBCQGather.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEBCQGATHER_H__
+#define __LUCI_IR_CIRCLEBCQGATHER_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief BCQ_GATHER in Circle
+ */
+class CircleBCQGather final : public FixedArityNode<4, CircleNodeImpl<CircleOpcode::BCQ_GATHER>>
+{
+public:
+ loco::Node *input_scales(void) const { return at(0)->node(); }
+ void input_scales(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *input_binary(void) const { return at(1)->node(); }
+ void input_binary(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *indices(void) const { return at(2)->node(); }
+ void indices(loco::Node *node) { at(2)->node(node); }
+
+ loco::Node *input_clusters(void) const { return at(3)->node(); }
+ void input_clusters(loco::Node *node) { at(3)->node(node); }
+
+public:
+ int32_t axis(void) const { return _axis; }
+ void axis(int32_t axis) { _axis = axis; }
+
+ int32_t input_hidden_size(void) const { return _input_hidden_size; }
+ void input_hidden_size(int32_t input_hidden_size) { _input_hidden_size = input_hidden_size; }
+
+private:
+ int32_t _axis = 0;
+ int32_t _input_hidden_size = 0;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEBCQGATHER_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchMatMul.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchMatMul.h
new file mode 100644
index 000000000..19999924e
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchMatMul.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCELBATCHMATMUL_H__
+#define __LUCI_IR_CIRCELBATCHMATMUL_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief BATCHMATMUL in Circle
+ */
+class CircleBatchMatMul final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::BATCHMATMUL>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+
+public:
+ bool adj_x(void) const { return _adj_x; }
+ void adj_x(bool arg) { _adj_x = arg; }
+
+ bool adj_y(void) const { return _adj_y; }
+ void adj_y(bool arg) { _adj_y = arg; }
+
+private:
+ bool _adj_x = false;
+ bool _adj_y = false;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCELBATCHMATMUL_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchToSpaceND.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchToSpaceND.h
new file mode 100644
index 000000000..67c0a2102
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchToSpaceND.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEBATCHTOSPACEND_H__
+#define __LUCI_IR_CIRCLEBATCHTOSPACEND_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief BATCH_TO_SPACE_ND in Circle
+ */
+class CircleBatchToSpaceND final
+ : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::BATCH_TO_SPACE_ND>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *block_shape(void) const { return at(1)->node(); }
+ void block_shape(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *crops(void) const { return at(2)->node(); }
+ void crops(loco::Node *node) { at(2)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEBATCHTOSPACEND_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleCast.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleCast.h
new file mode 100644
index 000000000..9a89d0b2b
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleCast.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCELECAST_H__
+#define __LUCI_IR_CIRCELECAST_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief CAST in Circle
+ */
+class CircleCast final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CAST>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+public:
+ loco::DataType in_data_type(void) const { return _in_data_type; }
+ void in_data_type(loco::DataType it) { _in_data_type = it; }
+
+ loco::DataType out_data_type(void) const { return _out_data_type; }
+ void out_data_type(loco::DataType ot) { _out_data_type = ot; }
+
+private:
+ loco::DataType _in_data_type{loco::DataType::FLOAT32};
+ loco::DataType _out_data_type{loco::DataType::FLOAT32};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCELECAST_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleCeil.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleCeil.h
new file mode 100644
index 000000000..8a8715dcf
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleCeil.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_CEIL_H__
+#define __LUCI_IR_CIRCLE_CEIL_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief CEIL in Circle
+ */
+class CircleCeil final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CEIL>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_CEIL_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleConcatenation.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleConcatenation.h
new file mode 100644
index 000000000..dea1a4613
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleConcatenation.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLECONCATENATION_H__
+#define __LUCI_IR_CIRCLECONCATENATION_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+#include "luci/IR/VariadicArityNode.h"
+
+#include <cassert>
+
+namespace luci
+{
+
+/**
+ * @brief CONCATENATION in Circle
+ */
+class CircleConcatenation final
+ : public VariadicArityNode<CircleNodeImpl<CircleOpcode::CONCATENATION>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ CircleConcatenation(uint32_t arity)
+ : VariadicArityNode<CircleNodeImpl<CircleOpcode::CONCATENATION>>(arity)
+ {
+ // TODO Support when arity is 0
+ assert(arity >= 1);
+ }
+
+public:
+ uint32_t numValues(void) const { return arity(); }
+
+public:
+ Node *values(uint32_t index) const
+ {
+ assert(index < numValues());
+ return at(index)->node();
+ }
+ void values(uint32_t index, Node *node)
+ {
+ assert(index < numValues());
+ at(index)->node(node);
+ }
+
+public:
+ int32_t axis(void) const { return _axis; }
+ void axis(int32_t axis) { _axis = axis; }
+
+private:
+ int32_t _axis{0};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLECONCATENATION_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleConst.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleConst.h
new file mode 100644
index 000000000..250282049
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleConst.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLECONST_H__
+#define __LUCI_IR_CIRCLECONST_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+#include <loco/IR/DataTypeTraits.h>
+
+namespace luci
+{
+
+/**
+ * @brief Class to build tensor data
+ * @note This will not be exported as a specific op
+ */
+class CircleConst final : public FixedArityNode<0, CircleNodeImpl<CircleOpcode::CIRCLECONST>>
+{
+public:
+ CircleConst() = default;
+
+public:
+ template <loco::DataType DT> uint32_t size(void) const;
+ template <loco::DataType DT> void size(uint32_t size);
+ template <loco::DataType DT> const typename loco::DataTypeImpl<DT>::Type &at(uint32_t n) const;
+ template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &at(uint32_t n);
+
+ template <loco::DataType DT> const typename loco::DataTypeImpl<DT>::Type &scalar(void) const;
+ template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &scalar(void);
+
+private:
+ std::vector<uint8_t> _data;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLECONST_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleConv2D.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleConv2D.h
new file mode 100644
index 000000000..13657cee4
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleConv2D.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLECONV2D_H__
+#define __LUCI_IR_CIRCLECONV2D_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrPadding.h"
+#include "luci/IR/AttrStride.h"
+#include "luci/IR/AttrDilation.h"
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief CONV_2D in Circle
+ */
+class CircleConv2D final : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::CONV_2D>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>,
+ public LuciNodeMixin<LuciNodeTrait::Bias>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *filter(void) const { return at(1)->node(); }
+ void filter(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *bias(void) const override { return at(2)->node(); }
+ void bias(loco::Node *node) override { at(2)->node(node); }
+
+public:
+ Padding padding() const { return _padding; }
+ void padding(Padding padding) { _padding = padding; }
+
+ const Stride *stride(void) const { return &_stride; }
+ Stride *stride(void) { return &_stride; }
+
+ const Dilation *dilation(void) const { return &_dilation; }
+ Dilation *dilation(void) { return &_dilation; }
+
+private:
+ Padding _padding = Padding::UNDEFINED;
+ Stride _stride;
+ Dilation _dilation;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLECONV2D_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleCos.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleCos.h
new file mode 100644
index 000000000..07ced620a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleCos.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_COS_H__
+#define __LUCI_IR_CIRCLE_COS_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief COS in Circle
+ */
+class CircleCos final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::COS>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_COS_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleCustom.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleCustom.h
new file mode 100644
index 000000000..6c722b766
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleCustom.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLECUSTOM_H__
+#define __LUCI_IR_CIRCLECUSTOM_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/VariadicArityNode.h"
+
+namespace luci
+{
+
+/**
+ * @brief CUSTOM in Circle
+ */
+class CircleCustom final : public VariadicArityNode<CircleNodeImpl<CircleOpcode::CUSTOM>>
+{
+public:
+ CircleCustom(uint32_t arity) : VariadicArityNode<CircleNodeImpl<CircleOpcode::CUSTOM>>(arity)
+ {
+ // TODO Support when arity is 0
+ assert(arity >= 1);
+ }
+
+public:
+ uint32_t numInputs(void) const { return arity(); }
+
+public:
+ Node *inputs(uint32_t index) const { return at(index)->node(); }
+ void inputs(uint32_t index, Node *node) { at(index)->node(node); }
+
+ const std::vector<uint8_t> &custom_options(void) const { return _custom_options; }
+ void custom_options(const std::vector<uint8_t> &custom_options)
+ {
+ _custom_options = custom_options;
+ }
+
+ const std::string &custom_code(void) const { return _custom_code; }
+ void custom_code(const std::string &custom_code) { _custom_code = custom_code; }
+
+private:
+ std::vector<uint8_t> _custom_options;
+ std::string _custom_code;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLECUSTOM_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleCustomOut.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleCustomOut.h
new file mode 100644
index 000000000..36b8e4aed
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleCustomOut.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_CUSTOMOUT_H__
+#define __LUCI_IR_CIRCLE_CUSTOMOUT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief Virtual CIRCLECUSTOMOUT in Circle
+ */
+class CircleCustomOut final
+ : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLECUSTOMOUT>>
+{
+public:
+ CircleCustomOut() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+public:
+ int32_t index(void) const { return _index; }
+ void index(int32_t index) { _index = index; }
+
+private:
+ int32_t _index{-1};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_CUSTOMOUT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthToSpace.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthToSpace.h
new file mode 100644
index 000000000..e19282b97
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthToSpace.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_DEPTHTOSPACE_H__
+#define __LUCI_IR_CIRCLE_DEPTHTOSPACE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief DEPTH_TO_SPACE in Circle
+ */
+class CircleDepthToSpace final
+ : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::DEPTH_TO_SPACE>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+public:
+ int block_size(void) const { return _block_size; }
+ void block_size(int block_size) { _block_size = block_size; }
+
+private:
+ int _block_size{0};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_DEPTHTOSPACE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthwiseConv2D.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthwiseConv2D.h
new file mode 100644
index 000000000..eb058cec1
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthwiseConv2D.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEDEPTHWISECONV2D_H__
+#define __LUCI_IR_CIRCLEDEPTHWISECONV2D_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrDilation.h"
+#include "luci/IR/AttrFilter.h"
+#include "luci/IR/AttrPadding.h"
+#include "luci/IR/AttrStride.h"
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief DEPTHWISE_CONV_2D in Circle
+ */
+class CircleDepthwiseConv2D final
+ : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::DEPTHWISE_CONV_2D>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>,
+ public LuciNodeMixin<LuciNodeTrait::Bias>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *filter(void) const { return at(1)->node(); }
+ void filter(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *bias(void) const override { return at(2)->node(); }
+ void bias(loco::Node *node) override { at(2)->node(node); }
+
+public:
+ Padding padding() const { return _padding; }
+ void padding(Padding padding) { _padding = padding; }
+
+ const Stride *stride(void) const { return &_stride; }
+ Stride *stride(void) { return &_stride; }
+
+ int32_t depthMultiplier(void) const { return _depth_multiplier; }
+ void depthMultiplier(int32_t arg) { _depth_multiplier = arg; }
+
+ const Dilation *dilation(void) const { return &_dilation; }
+ Dilation *dilation(void) { return &_dilation; }
+
+private:
+ Padding _padding = Padding::UNDEFINED;
+ Stride _stride;
+ int32_t _depth_multiplier = 0;
+ Dilation _dilation;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEDEPTHWISECONV2D_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleDequantize.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleDequantize.h
new file mode 100644
index 000000000..847c5dfc5
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleDequantize.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCELEDEQUANTIZE_H__
+#define __LUCI_IR_CIRCELEDEQUANTIZE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief DEQUANTIZE in Circle
+ */
+class CircleDequantize final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::DEQUANTIZE>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCELEDEQUANTIZE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleDiv.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleDiv.h
new file mode 100644
index 000000000..1d4d3a239
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleDiv.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEDIV_H__
+#define __LUCI_IR_CIRCLEDIV_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFilter.h"
+#include "luci/IR/AttrPadding.h"
+#include "luci/IR/AttrStride.h"
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief DIV in Circle
+ */
+class CircleDiv final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::DIV>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ CircleDiv() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEDIV_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleElu.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleElu.h
new file mode 100644
index 000000000..fbb2f3533
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleElu.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEELU_H__
+#define __LUCI_IR_CIRCLEELU_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief ELU in Circle
+ */
+class CircleElu final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::ELU>>
+{
+public:
+ CircleElu() = default;
+
+public:
+ loco::Node *features(void) const { return at(0)->node(); }
+ void features(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEELU_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleEqual.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleEqual.h
new file mode 100644
index 000000000..2087d097a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleEqual.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_EQUAL_H__
+#define __LUCI_IR_CIRCLE_EQUAL_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief EQUAL in Circle
+ */
+class CircleEqual final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::EQUAL>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_EQUAL_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleExp.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleExp.h
new file mode 100644
index 000000000..97aecb30a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleExp.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_EXP_H__
+#define __LUCI_IR_CIRCLE_EXP_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief EXP in Circle
+ */
+class CircleExp final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::EXP>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_EXP_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleExpandDims.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleExpandDims.h
new file mode 100644
index 000000000..f70219614
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleExpandDims.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEEXPAND_DIMS_H__
+#define __LUCI_IR_CIRCLEEXPAND_DIMS_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief EXPAND_DIMS in Circle
+ */
+class CircleExpandDims final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::EXPAND_DIMS>>
+{
+public:
+ CircleExpandDims() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *axis(void) const { return at(1)->node(); }
+ void axis(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEEXPAND_DIMS_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleFill.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleFill.h
new file mode 100644
index 000000000..bfc65274a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleFill.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCEFILL_H__
+#define __LUCI_IR_CIRCEFILL_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief FILL in Circle
+ */
+class CircleFill final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::FILL>>
+{
+public:
+ loco::Node *dims(void) const { return at(0)->node(); }
+ void dims(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *value(void) const { return at(1)->node(); }
+ void value(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCEFILL_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleFloor.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleFloor.h
new file mode 100644
index 000000000..7e10547b6
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleFloor.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_FLOOR_H__
+#define __LUCI_IR_CIRCLE_FLOOR_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief FLOOR in Circle
+ */
+class CircleFloor final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::FLOOR>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_FLOOR_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleFloorDiv.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleFloorDiv.h
new file mode 100644
index 000000000..ba9db010c
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleFloorDiv.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_FLOOR_DIV_H__
+#define __LUCI_IR_CIRCLE_FLOOR_DIV_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief FLOOR_DIV in Circle
+ */
+class CircleFloorDiv final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::FLOOR_DIV>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_FLOOR_DIV_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleFloorMod.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleFloorMod.h
new file mode 100644
index 000000000..4d13717a0
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleFloorMod.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_FLOOR_MOD_H__
+#define __LUCI_IR_CIRCLE_FLOOR_MOD_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief FLOOR_MOD in Circle
+ */
+class CircleFloorMod final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::FLOOR_MOD>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_FLOOR_MOD_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleFullyConnected.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleFullyConnected.h
new file mode 100644
index 000000000..d78f39494
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleFullyConnected.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEFULLYCONNECTED_H__
+#define __LUCI_IR_CIRCLEFULLYCONNECTED_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief FULLY_CONNECTED in Circle
+ */
+class CircleFullyConnected final
+ : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::FULLY_CONNECTED>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>,
+ public LuciNodeMixin<LuciNodeTrait::Bias>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *weights(void) const { return at(1)->node(); }
+ void weights(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *bias(void) const override { return at(2)->node(); }
+ void bias(loco::Node *node) override { at(2)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEFULLYCONNECTED_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleGather.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleGather.h
new file mode 100644
index 000000000..1e8c4982a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleGather.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEGATHER_H__
+#define __LUCI_IR_CIRCLEGATHER_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief GATHER in Circle
+ */
+class CircleGather final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::GATHER>>
+{
+public:
+ loco::Node *params(void) const { return at(0)->node(); }
+ void params(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *indices(void) const { return at(1)->node(); }
+ void indices(loco::Node *node) { at(1)->node(node); }
+
+public:
+ int32_t axis(void) const { return _axis; }
+ void axis(int32_t axis) { _axis = axis; }
+
+private:
+ int32_t _axis = 0;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEGATHER_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleGatherNd.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleGatherNd.h
new file mode 100644
index 000000000..3423a8216
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleGatherNd.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEGATHER_ND_H__
+#define __LUCI_IR_CIRCLEGATHER_ND_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief GATHER_ND in Circle
+ */
+class CircleGatherNd final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::GATHER_ND>>
+{
+public:
+ loco::Node *params(void) const { return at(0)->node(); }
+ void params(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *indices(void) const { return at(1)->node(); }
+ void indices(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEGATHER_ND_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleGreater.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleGreater.h
new file mode 100644
index 000000000..040a4e338
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleGreater.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_GREATER_H__
+#define __LUCI_IR_CIRCLE_GREATER_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief Greater in Circle
+ */
+class CircleGreater final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::GREATER>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_GREATER_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleGreaterEqual.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleGreaterEqual.h
new file mode 100644
index 000000000..82bdab212
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleGreaterEqual.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_GREATEREQUAL_H__
+#define __LUCI_IR_CIRCLE_GREATEREQUAL_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief GREATER EQUAL in Circle
+ */
+class CircleGreaterEqual final
+ : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::GREATER_EQUAL>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_GREATEREQUAL_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleIf.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleIf.h
new file mode 100644
index 000000000..2f9eac211
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleIf.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_IF_H__
+#define __LUCI_IR_CIRCLE_IF_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/VariadicArityNode.h"
+
+#include <cassert>
+
+namespace luci
+{
+
+/**
+ * @brief IF in Circle
+ */
+class CircleIf final : public VariadicArityNode<CircleNodeImpl<CircleOpcode::IF>>
+{
+public:
+ CircleIf(uint32_t arity, uint32_t out)
+ : VariadicArityNode<CircleNodeImpl<CircleOpcode::IF>>(arity + 1), _output_count(out)
+ {
+ assert(arity > 0);
+ assert(out > 0);
+ }
+
+public:
+ uint32_t input_count(void) const { return arity() - 1; }
+ uint32_t output_count(void) const { return _output_count; }
+
+public:
+ Node *cond(void) const { return at(0)->node(); }
+ void cond(Node *node) { at(0)->node(node); }
+
+ Node *input(uint32_t index) const { return at(index + 1)->node(); }
+ void input(uint32_t index, Node *node) { at(index + 1)->node(node); }
+
+public:
+ int32_t then_branch(void) const { return _then_branch; }
+ void then_branch(int32_t then_branch) { _then_branch = then_branch; }
+
+ int32_t else_branch(void) const { return _else_branch; }
+ void else_branch(int32_t else_branch) { _else_branch = else_branch; }
+
+public:
+ loco::Graph *then_graph(void) const { return _then_graph; }
+ void then_graph(loco::Graph *then_graph) { _then_graph = then_graph; }
+
+ loco::Graph *else_graph(void) const { return _else_graph; }
+ void else_graph(loco::Graph *else_graph) { _else_graph = else_graph; }
+
+private:
+ uint32_t _output_count{0};
+ int32_t _then_branch{-1};
+ int32_t _else_branch{-1};
+
+ loco::Graph *_then_graph{nullptr};
+ loco::Graph *_else_graph{nullptr};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_IF_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleIfOut.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleIfOut.h
new file mode 100644
index 000000000..3654e943b
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleIfOut.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_IFOUT_H__
+#define __LUCI_IR_CIRCLE_IFOUT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief Virtual CIRCLEIFOUT in Circle
+ */
+class CircleIfOut final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLEIFOUT>>
+{
+public:
+ CircleIfOut() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+public:
+ int32_t index(void) const { return _index; }
+ void index(int32_t index) { _index = index; }
+
+private:
+ int32_t _index{-1};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_IFOUT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleInput.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleInput.h
new file mode 100644
index 000000000..4a7d36a4e
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleInput.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEINPUT_H__
+#define __LUCI_IR_CIRCLEINPUT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+#include <loco/IR/DataTypeTraits.h>
+#include <loco/IR/GraphInputIndex.h>
+
+namespace luci
+{
+
+/**
+ * @brief CircleNode used for Input of the Graph
+ * @note This will not be exported as a specific op
+ */
+class CircleInput final : public FixedArityNode<0, CircleNodeImpl<CircleOpcode::CIRCLEINPUT>>
+{
+public:
+ CircleInput() = default;
+
+public:
+ void index(const loco::GraphInputIndex &index);
+ loco::GraphInputIndex index(void) const;
+
+ bool indexed(void) const { return _index != -1; }
+
+private:
+ int64_t _index = -1; // Uninitialized
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEINPUT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleInstanceNorm.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleInstanceNorm.h
new file mode 100644
index 000000000..db0faa05e
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleInstanceNorm.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEINSTANCENORM_H__
+#define __LUCI_IR_CIRCLEINSTANCENORM_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief INSTANCE_NORM in Circle
+ */
+class CircleInstanceNorm final
+ : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::INSTANCE_NORM>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ /// @note Currently only support FLOAT32 as input node
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *gamma(void) const { return at(1)->node(); }
+ void gamma(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *beta(void) const { return at(2)->node(); }
+ void beta(loco::Node *node) { at(2)->node(node); }
+
+ float epsilon() const { return _epsilon; }
+ void epsilon(float epsilon) { _epsilon = epsilon; }
+
+private:
+ float _epsilon = 1e-05;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEINSTANCENORM_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleL2Normalize.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleL2Normalize.h
new file mode 100644
index 000000000..efa932d95
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleL2Normalize.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCEL2NORMALIZE_H__
+#define __LUCI_IR_CIRCEL2NORMALIZE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief L2_NORMALIZATION in Circle
+ */
+class CircleL2Normalize final
+ : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::L2_NORMALIZATION>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCEL2NORMALIZE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleL2Pool2D.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleL2Pool2D.h
new file mode 100644
index 000000000..7c76ee5d0
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleL2Pool2D.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_L2_POOL2D_H__
+#define __LUCI_IR_CIRCLE_L2_POOL2D_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFilter.h"
+#include "luci/IR/AttrPadding.h"
+#include "luci/IR/AttrStride.h"
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief L2_POOL_2D in Circle
+ */
+class CircleL2Pool2D final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::L2_POOL_2D>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ CircleL2Pool2D() : _padding(Padding::UNDEFINED) { /* empty */}
+
+public:
+ loco::Node *value(void) const { return at(0)->node(); }
+ void value(loco::Node *node) { at(0)->node(node); }
+
+ Padding padding() const { return _padding; }
+ void padding(Padding padding) { _padding = padding; }
+
+ const Filter *filter(void) const { return &_filter; }
+ Filter *filter(void) { return &_filter; }
+
+ const Stride *stride(void) const { return &_stride; }
+ Stride *stride(void) { return &_stride; }
+
+private:
+ Padding _padding;
+ Stride _stride;
+ Filter _filter;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_L2_POOL2D_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLeakyRelu.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLeakyRelu.h
new file mode 100644
index 000000000..d6ac97fc0
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLeakyRelu.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_LEAKY_RELU_H__
+#define __LUCI_IR_CIRCLE_LEAKY_RELU_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief LEAKY_RELU in Circle
+ */
+class CircleLeakyRelu final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::LEAKY_RELU>>
+{
+public:
+ CircleLeakyRelu() = default;
+
+public:
+ loco::Node *features(void) const { return at(0)->node(); }
+ void features(loco::Node *node) { at(0)->node(node); }
+
+ float alpha() const { return _alpha; }
+ void alpha(float alpha) { _alpha = alpha; }
+
+private:
+ float _alpha = 0.2f;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_LEAKY_RELU_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLess.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLess.h
new file mode 100644
index 000000000..cd6cf1872
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLess.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_LESS_H__
+#define __LUCI_IR_CIRCLE_LESS_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief LESS in Circle
+ */
+class CircleLess final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::LESS>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_LESS_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLessEqual.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLessEqual.h
new file mode 100644
index 000000000..4c7c6a49b
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLessEqual.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_LESSEQUAL_H__
+#define __LUCI_IR_CIRCLE_LESSEQUAL_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief LESS_EQUAL in Circle
+ */
+class CircleLessEqual final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::LESS_EQUAL>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_LESSEQUAL_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLocalResponseNormalization.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLocalResponseNormalization.h
new file mode 100644
index 000000000..8ad2b40fd
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLocalResponseNormalization.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLELOCAL_RESPONSE_NORMALIZATION_H__
+#define __LUCI_IR_CIRCLELOCAL_RESPONSE_NORMALIZATION_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief LOCAL_RESPONSE_NORMALIZATION in Circle
+ */
+class CircleLocalResponseNormalization final
+ : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::LOCAL_RESPONSE_NORMALIZATION>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+public:
+ int32_t radius(void) const { return _radius; }
+ void radius(int32_t radius) { _radius = radius; }
+
+ float bias(void) const { return _bias; }
+ void bias(float bias) { _bias = bias; }
+
+ float alpha(void) const { return _alpha; }
+ void alpha(float alpha) { _alpha = alpha; }
+
+ float beta(void) const { return _beta; }
+ void beta(float beta) { _beta = beta; }
+
+private:
+ int32_t _radius{5};
+ float _bias{1.0f};
+ float _alpha{1.0f};
+ float _beta{0.5f};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLELOCAL_RESPONSE_NORMALIZATION_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLog.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLog.h
new file mode 100644
index 000000000..aeb13fed9
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLog.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_LOG_H__
+#define __LUCI_IR_CIRCLE_LOG_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief LOG in Circle
+ */
+class CircleLog final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::LOG>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_LOG_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogSoftmax.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogSoftmax.h
new file mode 100644
index 000000000..5dfd2c1f9
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogSoftmax.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_LOG_SOFTMAX_H__
+#define __LUCI_IR_CIRCLE_LOG_SOFTMAX_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief LOG_SOFTMAX in Circle
+ */
+class CircleLogSoftmax final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::LOG_SOFTMAX>>
+{
+public:
+ loco::Node *logits(void) const { return at(0)->node(); }
+ void logits(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_LOG_SOFTMAX_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalAnd.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalAnd.h
new file mode 100644
index 000000000..975f6dbc7
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalAnd.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_LOGICALAND_H__
+#define __LUCI_IR_CIRCLE_LOGICALAND_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief LOGICAL_AND in Circle
+ */
+class CircleLogicalAnd final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::LOGICAL_AND>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_LOGICALAND_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalNot.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalNot.h
new file mode 100644
index 000000000..749dbe518
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalNot.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_LOGICALNOT_H__
+#define __LUCI_IR_CIRCLE_LOGICALNOT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief LOGICAL_NOT in Circle
+ */
+class CircleLogicalNot final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::LOGICAL_NOT>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_LOGICALNOT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalOr.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalOr.h
new file mode 100644
index 000000000..570be57af
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalOr.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_LOGICALOR_H__
+#define __LUCI_IR_CIRCLE_LOGICALOR_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief LOGICAL_OR in Circle
+ */
+class CircleLogicalOr final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::LOGICAL_OR>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_LOGICALOR_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogistic.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogistic.h
new file mode 100644
index 000000000..8328cb328
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogistic.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_LOGISTIC_H__
+#define __LUCI_IR_CIRCLE_LOGISTIC_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief LOGISTIC in Circle
+ */
+class CircleLogistic final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::LOGISTIC>>
+{
+public:
+ CircleLogistic() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_LOGISTIC_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMatrixDiag.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMatrixDiag.h
new file mode 100644
index 000000000..dca6538c3
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMatrixDiag.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEMATRIXDIAG_H__
+#define __LUCI_IR_CIRCLEMATRIXDIAG_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief MATRIX_DIAG in Circle
+ */
+class CircleMatrixDiag final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::MATRIX_DIAG>>
+{
+public:
+ loco::Node *diagonal(void) const { return at(0)->node(); }
+ void diagonal(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEMATRIXDIAG_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMatrixSetDiag.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMatrixSetDiag.h
new file mode 100644
index 000000000..c1f5f3023
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMatrixSetDiag.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEMATRIXSETDIAG_H__
+#define __LUCI_IR_CIRCLEMATRIXSETDIAG_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief MATRIX_SET_DIAG in Circle
+ */
+class CircleMatrixSetDiag final
+ : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::MATRIX_SET_DIAG>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *diagonal(void) const { return at(1)->node(); }
+ void diagonal(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEMATRIXSETDIAG_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMaxPool2D.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMaxPool2D.h
new file mode 100644
index 000000000..1eb6532ff
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMaxPool2D.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEMAXPOOL2D_H__
+#define __LUCI_IR_CIRCLEMAXPOOL2D_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFilter.h"
+#include "luci/IR/AttrPadding.h"
+#include "luci/IR/AttrStride.h"
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief MAX_POOL_2D in Circle
+ */
+class CircleMaxPool2D final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::MAX_POOL_2D>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ CircleMaxPool2D() : _padding(Padding::UNDEFINED) { /* empty */}
+
+public:
+ loco::Node *value(void) const { return at(0)->node(); }
+ void value(loco::Node *node) { at(0)->node(node); }
+
+ Padding padding() const { return _padding; }
+ void padding(Padding padding) { _padding = padding; }
+
+ const Filter *filter(void) const { return &_filter; }
+ Filter *filter(void) { return &_filter; }
+
+ const Stride *stride(void) const { return &_stride; }
+ Stride *stride(void) { return &_stride; }
+
+private:
+ Padding _padding;
+ Stride _stride;
+ Filter _filter;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEMAXPOOL2D_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMaximum.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMaximum.h
new file mode 100644
index 000000000..6f789bc14
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMaximum.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEMAXIMUM_H__
+#define __LUCI_IR_CIRCLEMAXIMUM_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief MAXIMUM in Circle
+ */
+class CircleMaximum final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::MAXIMUM>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEMAXIMUM_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMean.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMean.h
new file mode 100644
index 000000000..7f8aeb5aa
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMean.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEMEAN_H__
+#define __LUCI_IR_CIRCLEMEAN_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief MEAN in Circle
+ */
+class CircleMean final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::MEAN>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *reduction_indices(void) const { return at(1)->node(); }
+ void reduction_indices(loco::Node *node) { at(1)->node(node); }
+
+public:
+ bool keep_dims(void) const { return _keep_dims; }
+ void keep_dims(bool keep_dims) { _keep_dims = keep_dims; }
+
+private:
+ bool _keep_dims = false;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEMEAN_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMinimum.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMinimum.h
new file mode 100644
index 000000000..79d5a6f17
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMinimum.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEMINIMUM_H__
+#define __LUCI_IR_CIRCLEMINIMUM_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief MINIMUM in Circle
+ */
+class CircleMinimum final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::MINIMUM>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEMINIMUM_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMirrorPad.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMirrorPad.h
new file mode 100644
index 000000000..68db8f6f3
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMirrorPad.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_MIRRORPAD_H__
+#define __LUCI_IR_CIRCLE_MIRRORPAD_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+#include "luci/IR/AttrMirrorPadMode.h"
+
+namespace luci
+{
+
+/**
+ * @brief MIRROR_PAD in Circle
+ */
+class CircleMirrorPad final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::MIRROR_PAD>>
+{
+public:
+ CircleMirrorPad() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *paddings(void) const { return at(1)->node(); }
+ void paddings(loco::Node *node) { at(1)->node(node); }
+
+public:
+ MirrorPadMode mode(void) const { return _mode; }
+ void mode(MirrorPadMode mode) { _mode = mode; }
+
+private:
+ MirrorPadMode _mode{MirrorPadMode::REFLECT};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_MIRRORPAD_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMul.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMul.h
new file mode 100644
index 000000000..67e897170
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMul.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEMUL_H__
+#define __LUCI_IR_CIRCLEMUL_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief MUL in Circle
+ */
+class CircleMul final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::MUL>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEMUL_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleNeg.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleNeg.h
new file mode 100644
index 000000000..4149ac4a7
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleNeg.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLENEG_H__
+#define __LUCI_IR_CIRCLENEG_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief NEG in Circle
+ */
+class CircleNeg final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::NEG>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLENEG_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV4.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV4.h
new file mode 100644
index 000000000..69f3368c0
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV4.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_NON_MAX_SUPPRESSION_V4_H__
+#define __LUCI_IR_CIRCLE_NON_MAX_SUPPRESSION_V4_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief NON_MAX_SUPPRESSION_V4 in Circle
+ */
+class CircleNonMaxSuppressionV4 final
+ : public FixedArityNode<5, CircleNodeImpl<CircleOpcode::NON_MAX_SUPPRESSION_V4>>
+{
+public:
+ loco::Node *boxes(void) const { return at(0)->node(); }
+ void boxes(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *scores(void) const { return at(1)->node(); }
+ void scores(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *max_output_size(void) const { return at(2)->node(); }
+ void max_output_size(loco::Node *node) { at(2)->node(node); }
+
+ loco::Node *iou_threshold(void) const { return at(3)->node(); }
+ void iou_threshold(loco::Node *node) { at(3)->node(node); }
+
+ loco::Node *score_threshold(void) const { return at(4)->node(); }
+ void score_threshold(loco::Node *node) { at(4)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_NON_MAX_SUPPRESSION_V4_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV4Out.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV4Out.h
new file mode 100644
index 000000000..a24dc3e9c
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV4Out.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_NONMAXSUPPRESSIONV4OUT_H__
+#define __LUCI_IR_CIRCLE_NONMAXSUPPRESSIONV4OUT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief Virtual NONMAXSUPPRESSIONV4OUT in Circle
+ */
+class CircleNonMaxSuppressionV4Out final
+ : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLENONMAXSUPPRESSIONV4OUT>>
+{
+public:
+ CircleNonMaxSuppressionV4Out() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+public:
+ int32_t index(void) const { return _index; }
+ void index(int32_t index) { _index = index; }
+
+private:
+ int32_t _index{-1};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_NONMAXSUPPRESSIONV4OUT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV5.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV5.h
new file mode 100644
index 000000000..52d682147
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV5.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_NON_MAX_SUPPRESSION_V5_H__
+#define __LUCI_IR_CIRCLE_NON_MAX_SUPPRESSION_V5_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief NON_MAX_SUPPRESSION_V5 in Circle
+ */
+class CircleNonMaxSuppressionV5 final
+ : public FixedArityNode<6, CircleNodeImpl<CircleOpcode::NON_MAX_SUPPRESSION_V5>>
+{
+public:
+ loco::Node *boxes(void) const { return at(0)->node(); }
+ void boxes(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *scores(void) const { return at(1)->node(); }
+ void scores(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *max_output_size(void) const { return at(2)->node(); }
+ void max_output_size(loco::Node *node) { at(2)->node(node); }
+
+ loco::Node *iou_threshold(void) const { return at(3)->node(); }
+ void iou_threshold(loco::Node *node) { at(3)->node(node); }
+
+ loco::Node *score_threshold(void) const { return at(4)->node(); }
+ void score_threshold(loco::Node *node) { at(4)->node(node); }
+
+ loco::Node *soft_nms_sigma(void) const { return at(5)->node(); }
+ void soft_nms_sigma(loco::Node *node) { at(5)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_NON_MAX_SUPPRESSION_V5_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV5Out.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV5Out.h
new file mode 100644
index 000000000..0c6989cc7
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV5Out.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_NONMAXSUPPRESSIONV5OUT_H__
+#define __LUCI_IR_CIRCLE_NONMAXSUPPRESSIONV5OUT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief Virtual NONMAXSUPPRESSIONV5OUT in Circle
+ */
+class CircleNonMaxSuppressionV5Out final
+ : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLENONMAXSUPPRESSIONV5OUT>>
+{
+public:
+ CircleNonMaxSuppressionV5Out() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+public:
+ int32_t index(void) const { return _index; }
+ void index(int32_t index) { _index = index; }
+
+private:
+ int32_t _index{-1};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_NONMAXSUPPRESSIONV5OUT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleNotEqual.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleNotEqual.h
new file mode 100644
index 000000000..cca7a5e22
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleNotEqual.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_NOTEQUAL_H__
+#define __LUCI_IR_CIRCLE_NOTEQUAL_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief NOT EQUAL in Circle
+ */
+class CircleNotEqual final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::NOT_EQUAL>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_NOTEQUAL_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleOneHot.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleOneHot.h
new file mode 100644
index 000000000..665e01d48
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleOneHot.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEONEHOT_H__
+#define __LUCI_IR_CIRCLEONEHOT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief ONEHOT in Circle
+ */
+class CircleOneHot final : public FixedArityNode<4, CircleNodeImpl<CircleOpcode::ONE_HOT>>
+{
+public:
+ loco::Node *indices(void) const { return at(0)->node(); }
+ void indices(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *depth(void) const { return at(1)->node(); }
+ void depth(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *on_value(void) const { return at(2)->node(); }
+ void on_value(loco::Node *node) { at(2)->node(node); }
+
+ loco::Node *off_value(void) const { return at(3)->node(); }
+ void off_value(loco::Node *node) { at(3)->node(node); }
+
+public:
+ int32_t axis(void) const { return _axis; }
+ void axis(int32_t axis) { _axis = axis; }
+
+private:
+ int32_t _axis = -1;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEONEHOT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleOutput.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleOutput.h
new file mode 100644
index 000000000..67e55f1a1
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleOutput.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEOUTPUT_H__
+#define __LUCI_IR_CIRCLEOUTPUT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+#include <loco/IR/GraphOutputIndex.h>
+
+namespace luci
+{
+
+/**
+ * @brief CircleNode for Output of the Graph
+ * @note This will not be exported as a specific op
+ */
+class CircleOutput final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLEOUTPUT>>
+{
+public:
+ CircleOutput() = default;
+
+ void index(const loco::GraphOutputIndex &index);
+ loco::GraphOutputIndex index(void) const;
+
+ bool indexed(void) const { return _index != -1; }
+
+public:
+ loco::Node *from(void) const { return at(0)->node(); }
+ void from(loco::Node *node) { at(0)->node(node); }
+
+private:
+ int64_t _index = -1; // Uninitialized
+};
+
+/**
+ * @brief Temporary DummyNode used with dangle CircleNode
+ */
+// TODO remove CircleOutputDummy
+class CircleOutputDummy final
+ : public FixedArityNode<0, CircleNodeImpl<CircleOpcode::CIRCLEOUTPUTDUMMY>>
+{
+public:
+ CircleOutputDummy() = default;
+};
+
+/**
+ * @brief CircleOutputExclude is used to specifying not exported nodes
+ */
+class CircleOutputExclude final
+ : public FixedArityNode<0, CircleNodeImpl<CircleOpcode::CIRCLEOUTPUTEXCLUDE>>
+{
+public:
+ CircleOutputExclude() = default;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEOUTPUT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CirclePRelu.h b/compiler/luci/lang/include/luci/IR/Nodes/CirclePRelu.h
new file mode 100644
index 000000000..693777512
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CirclePRelu.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_PRELU_H__
+#define __LUCI_IR_PRELU_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief PRelu in Circle
+ */
+class CirclePRelu final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::PRELU>>
+{
+public:
+ CirclePRelu() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *alpha(void) const { return at(1)->node(); }
+ void alpha(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_PRELU_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CirclePack.h b/compiler/luci/lang/include/luci/IR/Nodes/CirclePack.h
new file mode 100644
index 000000000..8330b585a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CirclePack.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEPACK_H__
+#define __LUCI_IR_CIRCLEPACK_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/VariadicArityNode.h"
+
+#include <cassert>
+
+namespace luci
+{
+
+/**
+ * @brief PACK in Circle
+ */
+class CirclePack final : public VariadicArityNode<CircleNodeImpl<CircleOpcode::PACK>>
+{
+public:
+ CirclePack(uint32_t arity) : VariadicArityNode<CircleNodeImpl<CircleOpcode::PACK>>(arity)
+ {
+ // TODO Support when arity is 0
+ assert(arity >= 1);
+ }
+
+public:
+ uint32_t values_count(void) const { return arity(); }
+
+public:
+ Node *values(uint32_t index) const
+ {
+ assert(index < values_count());
+ return at(index)->node();
+ }
+ void values(uint32_t index, Node *node)
+ {
+ assert(index < values_count());
+ at(index)->node(node);
+ }
+
+public:
+ int32_t axis(void) const { return _axis; }
+ void axis(int32_t axis) { _axis = axis; }
+
+private:
+ int32_t _axis{0};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEPACK_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CirclePad.h b/compiler/luci/lang/include/luci/IR/Nodes/CirclePad.h
new file mode 100644
index 000000000..31599bda0
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CirclePad.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEPAD_H__
+#define __LUCI_IR_CIRCLEPAD_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief PAD in Circle
+ */
+class CirclePad final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::PAD>>
+{
+public:
+ CirclePad() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *paddings(void) const { return at(1)->node(); }
+ void paddings(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEPAD_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CirclePadV2.h b/compiler/luci/lang/include/luci/IR/Nodes/CirclePadV2.h
new file mode 100644
index 000000000..563cfd9a4
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CirclePadV2.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEPADV2_H__
+#define __LUCI_IR_CIRCLEPADV2_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief PADV2 in Circle
+ */
+class CirclePadV2 final : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::PADV2>>
+{
+public:
+ CirclePadV2() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *paddings(void) const { return at(1)->node(); }
+ void paddings(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *constant_values(void) const { return at(2)->node(); }
+ void constant_values(loco::Node *node) { at(2)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEPADV2_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CirclePow.h b/compiler/luci/lang/include/luci/IR/Nodes/CirclePow.h
new file mode 100644
index 000000000..006e3dd86
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CirclePow.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_POW_H__
+#define __LUCI_IR_POW_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief POW in Circle
+ */
+class CirclePow final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::POW>>
+{
+public:
+ CirclePow() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_POW_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleRange.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleRange.h
new file mode 100644
index 000000000..977a37a52
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleRange.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLERANGE_H__
+#define __LUCI_IR_CIRCLERANGE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief RANGE in Circle
+ */
+class CircleRange final : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::RANGE>>
+{
+public:
+ loco::Node *start(void) const { return at(0)->node(); }
+ void start(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *limit(void) const { return at(1)->node(); }
+ void limit(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *delta(void) const { return at(2)->node(); }
+ void delta(loco::Node *node) { at(2)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLERANGE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleRank.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleRank.h
new file mode 100644
index 000000000..ba6d67f69
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleRank.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLERANK_H__
+#define __LUCI_IR_CIRCLERANK_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief RANK in Circle
+ */
+class CircleRank final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::RANK>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLERANK_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceAny.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceAny.h
new file mode 100644
index 000000000..0456be863
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceAny.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_REDUCE_ANY_H__
+#define __LUCI_IR_CIRCLE_REDUCE_ANY_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief REDUCE_ANY in Circle
+ */
+class CircleReduceAny final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::REDUCE_ANY>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *reduction_indices(void) const { return at(1)->node(); }
+ void reduction_indices(loco::Node *node) { at(1)->node(node); }
+
+public:
+ bool keep_dims(void) const { return _keep_dims; }
+ void keep_dims(bool keep_dims) { _keep_dims = keep_dims; }
+
+private:
+ bool _keep_dims = false;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_REDUCE_ANY_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceMax.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceMax.h
new file mode 100644
index 000000000..925c977e5
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceMax.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_REDUCE_MAX_H__
+#define __LUCI_IR_CIRCLE_REDUCE_MAX_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief REDUCE_MAX in Circle
+ */
+class CircleReduceMax final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::REDUCE_MAX>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *reduction_indices(void) const { return at(1)->node(); }
+ void reduction_indices(loco::Node *node) { at(1)->node(node); }
+
+public:
+ bool keep_dims(void) const { return _keep_dims; }
+ void keep_dims(bool keep_dims) { _keep_dims = keep_dims; }
+
+private:
+ bool _keep_dims = false;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_REDUCE_MAX_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceMin.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceMin.h
new file mode 100644
index 000000000..fd789ae5e
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceMin.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_REDUCE_MIN_H__
+#define __LUCI_IR_CIRCLE_REDUCE_MIN_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief REDUCE_MIN in Circle
+ */
+class CircleReduceMin final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::REDUCE_MIN>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *reduction_indices(void) const { return at(1)->node(); }
+ void reduction_indices(loco::Node *node) { at(1)->node(node); }
+
+public:
+ bool keep_dims(void) const { return _keep_dims; }
+ void keep_dims(bool keep_dims) { _keep_dims = keep_dims; }
+
+private:
+ bool _keep_dims = false;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_REDUCE_MIN_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceProd.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceProd.h
new file mode 100644
index 000000000..b7d226255
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceProd.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_REDUCE_PROD_H__
+#define __LUCI_IR_CIRCLE_REDUCE_PROD_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief REDUCE_PROD in Circle
+ */
+class CircleReduceProd final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::REDUCE_PROD>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *reduction_indices(void) const { return at(1)->node(); }
+ void reduction_indices(loco::Node *node) { at(1)->node(node); }
+
+public:
+ bool keep_dims(void) const { return _keep_dims; }
+ void keep_dims(bool keep_dims) { _keep_dims = keep_dims; }
+
+private:
+ bool _keep_dims = false;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_REDUCE_PROD_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu.h
new file mode 100644
index 000000000..91272d2bf
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLERELU_H__
+#define __LUCI_IR_CIRCLERELU_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief RELU in Circle
+ */
+class CircleRelu final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::RELU>>
+{
+public:
+ CircleRelu() = default;
+
+public:
+ loco::Node *features(void) const { return at(0)->node(); }
+ void features(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLERELU_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu6.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu6.h
new file mode 100644
index 000000000..b4274ded9
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu6.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLERELU6_H__
+#define __LUCI_IR_CIRCLERELU6_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief RELU6 in Circle
+ */
+class CircleRelu6 final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::RELU6>>
+{
+public:
+ CircleRelu6() = default;
+
+public:
+ loco::Node *features(void) const { return at(0)->node(); }
+ void features(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLERELU6_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleReluN1To1.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleReluN1To1.h
new file mode 100644
index 000000000..a5c5710c2
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleReluN1To1.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_RELU_N1_TO_1_H__
+#define __LUCI_IR_CIRCLE_RELU_N1_TO_1_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief RELU_N1_TO_1 in Circle
+ */
+class CircleReluN1To1 final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::RELU_N1_TO_1>>
+{
+public:
+ CircleReluN1To1() = default;
+
+public:
+ loco::Node *features(void) const { return at(0)->node(); }
+ void features(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_RELU_N1_TO_1_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleReshape.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleReshape.h
new file mode 100644
index 000000000..b13144f7e
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleReshape.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLERESHAPE_H__
+#define __LUCI_IR_CIRCLERESHAPE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief RESHAPE in Circle
+ */
+class CircleReshape final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::RESHAPE>>
+{
+public:
+ CircleReshape() = default;
+
+public:
+ loco::Node *tensor(void) const { return at(0)->node(); }
+ void tensor(loco::Node *node) { at(0)->node(node); }
+
+ // NOTE shape is optional and can be CircleConst or any other type
+ // and also can be CircleOutputDummy when reshape option does not exist
+ loco::Node *shape(void) const { return at(1)->node(); }
+ void shape(loco::Node *node) { at(1)->node(node); }
+
+public:
+ class Shape
+ {
+ public:
+ uint32_t rank(void) const { return _shape.size(); }
+ void rank(uint32_t rank) { _shape.resize(rank); }
+
+ int32_t dim(uint32_t n) const { return _shape.at(n); }
+ int32_t &dim(uint32_t n) { return _shape.at(n); }
+
+ private:
+ std::vector<int32_t> _shape;
+ };
+
+ const Shape *newShape(void) const { return &_new_shape; }
+ Shape *newShape(void) { return &_new_shape; }
+
+private:
+ Shape _new_shape;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLERESHAPE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleResizeBilinear.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleResizeBilinear.h
new file mode 100644
index 000000000..3c8223338
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleResizeBilinear.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLERESIZE_BILINEAR_H__
+#define __LUCI_IR_CIRCLERESIZE_BILINEAR_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief RESIZE_BILINEAR in Circle
+ */
+class CircleResizeBilinear final
+ : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::RESIZE_BILINEAR>>
+{
+public:
+ CircleResizeBilinear() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *size(void) const { return at(1)->node(); }
+ void size(loco::Node *node) { at(1)->node(node); }
+
+ bool align_corners() const { return _align_corners; }
+ void align_corners(bool value) { _align_corners = value; }
+
+ bool half_pixel_centers() const { return _half_pixel_centers; }
+ void half_pixel_centers(bool value) { _half_pixel_centers = value; }
+
+private:
+ bool _align_corners = false;
+ bool _half_pixel_centers = false;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLERESIZE_BILINEAR_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleResizeNearestNeighbor.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleResizeNearestNeighbor.h
new file mode 100644
index 000000000..dc32ebee7
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleResizeNearestNeighbor.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLERESSIZE_NEAREST_NEIGHBOR_H__
+#define __LUCI_IR_CIRCLERESSIZE_NEAREST_NEIGHBOR_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief RESIZE_NEAREST_NEIGHBOR in Circle
+ */
+class CircleResizeNearestNeighbor final
+ : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::RESIZE_NEAREST_NEIGHBOR>>
+{
+public:
+ CircleResizeNearestNeighbor() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *size(void) const { return at(1)->node(); }
+ void size(loco::Node *node) { at(1)->node(node); }
+
+ bool align_corners() const { return _align_corners; }
+ void align_corners(bool value) { _align_corners = value; }
+
+private:
+ bool _align_corners = false;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLERESSIZE_NEAREST_NEIGHBOR_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleReverseSequence.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleReverseSequence.h
new file mode 100644
index 000000000..b0766dd3e
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleReverseSequence.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEREVERSESEQUENCE_H__
+#define __LUCI_IR_CIRCLEREVERSESEQUENCE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief REVERSE_SEQUENCE in Circle
+ */
+class CircleReverseSequence final
+ : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::REVERSE_SEQUENCE>>
+{
+public:
+ CircleReverseSequence() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *seq_lengths(void) const { return at(1)->node(); }
+ void seq_lengths(loco::Node *node) { at(1)->node(node); }
+
+public:
+ int seq_axis(void) const { return _seq_axis; }
+ void seq_axis(int seq_axis) { _seq_axis = seq_axis; }
+
+ int batch_axis(void) const { return _batch_axis; }
+ void batch_axis(int batch_axis) { _batch_axis = batch_axis; }
+
+private:
+ int _seq_axis{0};
+ int _batch_axis{0};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEREVERSESEQUENCE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleReverseV2.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleReverseV2.h
new file mode 100644
index 000000000..71d9f65aa
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleReverseV2.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEREVERSE_V2_H__
+#define __LUCI_IR_CIRCLEREVERSE_V2_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief ReverseV2 in Circle
+ */
+class CircleReverseV2 final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::REVERSE_V2>>
+{
+public:
+ loco::Node *tensor(void) const { return at(0)->node(); }
+ void tensor(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *axis(void) const { return at(1)->node(); }
+ void axis(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEREVERSE_V2_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleRound.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleRound.h
new file mode 100644
index 000000000..30296ce9e
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleRound.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEROUND_H__
+#define __LUCI_IR_CIRCLEROUND_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief ROUND in Circle
+ */
+class CircleRound final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::ROUND>>
+{
+public:
+ CircleRound() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEROUND_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleRsqrt.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleRsqrt.h
new file mode 100644
index 000000000..873397bce
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleRsqrt.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLERSQRT_H__
+#define __LUCI_IR_CIRCLERSQRT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief RSQRT in Circle
+ */
+class CircleRsqrt final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::RSQRT>>
+{
+public:
+ CircleRsqrt() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLERSQRT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleScatterNd.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleScatterNd.h
new file mode 100644
index 000000000..9f93a0a80
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleScatterNd.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLESCATTER_ND_H__
+#define __LUCI_IR_CIRCLESCATTER_ND_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SCATTER_ND in Circle
+ */
+class CircleScatterNd final : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::SCATTER_ND>>
+{
+public:
+ loco::Node *indices(void) const { return at(0)->node(); }
+ void indices(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *updates(void) const { return at(1)->node(); }
+ void updates(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *shape(void) const { return at(2)->node(); }
+ void shape(loco::Node *node) { at(2)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLESCATTER_ND_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSegmentSum.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSegmentSum.h
new file mode 100644
index 000000000..416d617b2
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSegmentSum.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_SEGMENT_SUM_H__
+#define __LUCI_IR_CIRCLE_SEGMENT_SUM_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SEGMENT_SUM in Circle
+ */
+class CircleSegmentSum final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::SEGMENT_SUM>>
+{
+public:
+ CircleSegmentSum() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *segment_ids(void) const { return at(1)->node(); }
+ void segment_ids(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_SEGMENT_SUM_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSelect.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSelect.h
new file mode 100644
index 000000000..727647168
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSelect.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_SELECT_H__
+#define __LUCI_IR_CIRCLE_SELECT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SELECT in Circle
+ */
+class CircleSelect final : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::SELECT>>
+{
+public:
+ CircleSelect() = default;
+
+public:
+ loco::Node *condition(void) const { return at(0)->node(); }
+ void condition(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *t(void) const { return at(1)->node(); }
+ void t(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *e(void) const { return at(2)->node(); }
+ void e(loco::Node *node) { at(2)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_SELECT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSelectV2.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSelectV2.h
new file mode 100644
index 000000000..7ac3c0524
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSelectV2.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_SELECT_V2_H__
+#define __LUCI_IR_CIRCLE_SELECT_V2_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SELECT_V2 in Circle
+ */
+class CircleSelectV2 final : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::SELECT_V2>>
+{
+public:
+ CircleSelectV2() = default;
+
+public:
+ loco::Node *condition(void) const { return at(0)->node(); }
+ void condition(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *t(void) const { return at(1)->node(); }
+ void t(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *e(void) const { return at(2)->node(); }
+ void e(loco::Node *node) { at(2)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_SELECT_V2_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleShape.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleShape.h
new file mode 100644
index 000000000..ff20ce684
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleShape.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_SHAPE_H__
+#define __LUCI_IR_CIRCLE_SHAPE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SHAPE in Circle
+ */
+class CircleShape final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::SHAPE>>
+{
+public:
+ CircleShape() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+public:
+ loco::DataType out_type(void) const { return _out_type; }
+ void out_type(loco::DataType ot) { _out_type = ot; }
+
+private:
+ loco::DataType _out_type{loco::DataType::S32};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_SHAPE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSin.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSin.h
new file mode 100644
index 000000000..5624db253
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSin.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_SIN_H__
+#define __LUCI_IR_CIRCLE_SIN_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SIN in Circle
+ */
+class CircleSin final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::SIN>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_SIN_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSlice.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSlice.h
new file mode 100644
index 000000000..a2113643d
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSlice.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_SLICE_H__
+#define __LUCI_IR_SLICE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SLICE in Circle
+ */
+class CircleSlice final : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::SLICE>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *begin(void) const { return at(1)->node(); }
+ void begin(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *size(void) const { return at(2)->node(); }
+ void size(loco::Node *node) { at(2)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_SLICE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSoftmax.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSoftmax.h
new file mode 100644
index 000000000..7166a329b
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSoftmax.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLESOFTMAX_H__
+#define __LUCI_IR_CIRCLESOFTMAX_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SOFTMAX in Circle
+ */
+class CircleSoftmax final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::SOFTMAX>>
+{
+public:
+ loco::Node *logits(void) const { return at(0)->node(); }
+ void logits(loco::Node *node) { at(0)->node(node); }
+
+public:
+ float beta(void) const { return _beta; }
+ void beta(float beta) { _beta = beta; }
+
+private:
+ float _beta{0.0f};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLESOFTMAX_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSpaceToBatchND.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSpaceToBatchND.h
new file mode 100644
index 000000000..042ebffcd
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSpaceToBatchND.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_SPACETOBATCHND_H__
+#define __LUCI_IR_CIRCLE_SPACETOBATCHND_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SPACE_TO_BATCH_ND in Circle
+ */
+class CircleSpaceToBatchND final
+ : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::SPACE_TO_BATCH_ND>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *block_shape(void) const { return at(1)->node(); }
+ void block_shape(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *paddings(void) const { return at(2)->node(); }
+ void paddings(loco::Node *node) { at(2)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_SPACETOBATCHND_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSpaceToDepth.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSpaceToDepth.h
new file mode 100644
index 000000000..420a4cb96
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSpaceToDepth.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_SPACETODEPTH_H__
+#define __LUCI_IR_CIRCLE_SPACETODEPTH_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SPACE_TO_DEPTH in Circle
+ */
+class CircleSpaceToDepth final
+ : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::SPACE_TO_DEPTH>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+public:
+ int block_size(void) const { return _block_size; }
+ void block_size(int block_size) { _block_size = block_size; }
+
+private:
+ int _block_size{0};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_SPACETODEPTH_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSparseToDense.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSparseToDense.h
new file mode 100644
index 000000000..7e80304b0
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSparseToDense.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCELSPARSETODENSE_H__
+#define __LUCI_IR_CIRCELSPARSETODENSE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SPARSE_TO_DENSE in Circle
+ */
+class CircleSparseToDense final
+ : public FixedArityNode<4, CircleNodeImpl<CircleOpcode::SPARSE_TO_DENSE>>
+{
+public:
+ loco::Node *indices(void) const { return at(0)->node(); }
+ void indices(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *output_shape(void) const { return at(1)->node(); }
+ void output_shape(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *values(void) const { return at(2)->node(); }
+ void values(loco::Node *node) { at(2)->node(node); }
+
+ loco::Node *default_value(void) const { return at(3)->node(); }
+ void default_value(loco::Node *node) { at(3)->node(node); }
+
+public:
+ bool validate_indices(void) const { return _validate_indices; }
+ void validate_indices(bool validate_indices) { _validate_indices = validate_indices; }
+
+private:
+ bool _validate_indices{false};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCELSPARSETODENSE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSplit.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSplit.h
new file mode 100644
index 000000000..0eda19501
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSplit.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_SPLIT_H__
+#define __LUCI_IR_CIRCLE_SPLIT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SPLIT in Circle
+ */
+class CircleSplit final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::SPLIT>>
+{
+public:
+ loco::Node *split_dim(void) const { return at(0)->node(); }
+ void split_dim(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *input(void) const { return at(1)->node(); }
+ void input(loco::Node *node) { at(1)->node(node); }
+
+public:
+ // NOTE it is num_split() not num_splits() as we follow TF name
+ int32_t num_split(void) const { return _num_split; }
+ void num_split(int32_t num_split) { _num_split = num_split; }
+
+private:
+ int32_t _num_split{0};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_SPLIT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitOut.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitOut.h
new file mode 100644
index 000000000..6bf4a9fef
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitOut.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_SPLITOUT_H__
+#define __LUCI_IR_CIRCLE_SPLITOUT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief Virtual CIRCLESPLITOUT in Circle
+ */
+class CircleSplitOut final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLESPLITOUT>>
+{
+public:
+ CircleSplitOut() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+public:
+ int32_t index(void) const { return _index; }
+ void index(int32_t index) { _index = index; }
+
+private:
+ int32_t _index{-1};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_SPLITOUT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitV.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitV.h
new file mode 100644
index 000000000..1b7d55534
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitV.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_SPLIT_V_H__
+#define __LUCI_IR_CIRCLE_SPLIT_V_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SPLIT_V in Circle
+ */
+class CircleSplitV final : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::SPLIT_V>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *size_splits(void) const { return at(1)->node(); }
+ void size_splits(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *split_dim(void) const { return at(2)->node(); }
+ void split_dim(loco::Node *node) { at(2)->node(node); }
+
+public:
+ // NOTE it is num_split() not num_splits() as we follow TF name
+ int32_t num_split(void) const { return _num_split; }
+ void num_split(int32_t num_split) { _num_split = num_split; }
+
+private:
+ int32_t _num_split{0};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_SPLIT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitVOut.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitVOut.h
new file mode 100644
index 000000000..d3b2f1e5a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitVOut.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_SPLITVOUT_H__
+#define __LUCI_IR_CIRCLE_SPLITVOUT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief Virtual CIRCLESPLITVOUT in Circle
+ */
+class CircleSplitVOut final
+ : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLESPLITVOUT>>
+{
+public:
+ CircleSplitVOut() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+public:
+ int32_t index(void) const { return _index; }
+ void index(int32_t index) { _index = index; }
+
+private:
+ int32_t _index{-1};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_SPLITVOUT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSqrt.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSqrt.h
new file mode 100644
index 000000000..c96ca8498
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSqrt.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLESQRT_H__
+#define __LUCI_IR_CIRCLESQRT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SQRT in Circle
+ */
+class CircleSqrt final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::SQRT>>
+{
+public:
+ CircleSqrt() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLESQRT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSquare.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSquare.h
new file mode 100644
index 000000000..a29edfe82
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSquare.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLESQUARE_H__
+#define __LUCI_IR_CIRCLESQUARE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SQUARE in Circle
+ */
+class CircleSquare final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::SQUARE>>
+{
+public:
+ CircleSquare() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLESQUARE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSquaredDifference.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSquaredDifference.h
new file mode 100644
index 000000000..b5b39f920
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSquaredDifference.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLESQUAREDIFFERENCE_H__
+#define __LUCI_IR_CIRCLESQUAREDIFFERENCE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SQUARED_DIFFERENCE in Circle
+ */
+class CircleSquaredDifference final
+ : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::SQUARED_DIFFERENCE>>
+{
+public:
+ CircleSquaredDifference() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLESQUAREDIFFERENCE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSqueeze.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSqueeze.h
new file mode 100644
index 000000000..f175f1411
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSqueeze.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLESQUEEZE_H__
+#define __LUCI_IR_CIRCLESQUEEZE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SQUEEZE in Circle
+ */
+class CircleSqueeze final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::SQUEEZE>>
+{
+public:
+ CircleSqueeze() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+public:
+ const std::vector<int32_t> &squeeze_dims() const { return _squeeze_dims; }
+ void squeeze_dims(const std::vector<int32_t> &squeeze_dims) { _squeeze_dims = squeeze_dims; };
+
+private:
+ std::vector<int32_t> _squeeze_dims{};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLESQUEEZE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleStridedSlice.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleStridedSlice.h
new file mode 100644
index 000000000..98799fec1
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleStridedSlice.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_STRIDEDSLICE_H__
+#define __LUCI_IR_STRIDEDSLICE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief STRIDED_SLICE in Circle
+ */
+class CircleStridedSlice final
+ : public FixedArityNode<4, CircleNodeImpl<CircleOpcode::STRIDED_SLICE>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *begin(void) const { return at(1)->node(); }
+ void begin(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *end(void) const { return at(2)->node(); }
+ void end(loco::Node *node) { at(2)->node(node); }
+
+ loco::Node *strides(void) const { return at(3)->node(); }
+ void strides(loco::Node *node) { at(3)->node(node); }
+
+public:
+ int32_t begin_mask() const { return _begin_mask; }
+ void begin_mask(int32_t mask) { _begin_mask = mask; }
+
+ int32_t end_mask() const { return _end_mask; }
+ void end_mask(int32_t mask) { _end_mask = mask; }
+
+ int32_t ellipsis_mask() const { return _ellipsis_mask; }
+ void ellipsis_mask(int32_t mask) { _ellipsis_mask = mask; }
+
+ int32_t new_axis_mask() const { return _new_axis_mask; }
+ void new_axis_mask(int32_t mask) { _new_axis_mask = mask; }
+
+ int32_t shrink_axis_mask() const { return _shrink_axis_mask; }
+ void shrink_axis_mask(int32_t mask) { _shrink_axis_mask = mask; }
+
+private:
+ 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};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_STRIDEDSLICE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSub.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSub.h
new file mode 100644
index 000000000..08208f942
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSub.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLESUB_H__
+#define __LUCI_IR_CIRCLESUB_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SUB in Circle
+ */
+class CircleSub final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::SUB>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ CircleSub() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLESUB_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSum.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSum.h
new file mode 100644
index 000000000..21faa76fe
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSum.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLESUM_H__
+#define __LUCI_IR_CIRCLESUM_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SUM in Circle
+ */
+class CircleSum final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::SUM>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *reduction_indices(void) const { return at(1)->node(); }
+ void reduction_indices(loco::Node *node) { at(1)->node(node); }
+
+public:
+ bool keep_dims(void) const { return _keep_dims; }
+ void keep_dims(bool keep_dims) { _keep_dims = keep_dims; }
+
+private:
+ bool _keep_dims = false;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLESUM_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleTanh.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleTanh.h
new file mode 100644
index 000000000..f7444921f
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleTanh.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLETANH_H__
+#define __LUCI_IR_CIRCLETANH_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief TANH in Circle
+ */
+class CircleTanh final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::TANH>>
+{
+public:
+ CircleTanh() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLETANH_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleTile.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleTile.h
new file mode 100644
index 000000000..96e1f69c6
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleTile.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLETILE_H__
+#define __LUCI_IR_CIRCLETILE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief TILE in Circle
+ */
+class CircleTile final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::TILE>>
+{
+public:
+ CircleTile() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *multiples(void) const { return at(1)->node(); }
+ void multiples(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLETILE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleTopKV2.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleTopKV2.h
new file mode 100644
index 000000000..3b2b5abb7
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleTopKV2.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_TOPK_V2_H__
+#define __LUCI_IR_CIRCLE_TOPK_V2_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief TOPK_V2 in Circle
+ */
+class CircleTopKV2 final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::TOPK_V2>>
+{
+public:
+ CircleTopKV2() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *k(void) const { return at(1)->node(); }
+ void k(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_TOPK_V2_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleTopKV2Out.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleTopKV2Out.h
new file mode 100644
index 000000000..5a6dd0c02
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleTopKV2Out.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_TOPK_V2_OUT_H__
+#define __LUCI_IR_CIRCLE_TOPK_V2_OUT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief Virtual CIRCLETOPKV2OUT in Circle
+ */
+class CircleTopKV2Out final
+ : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLETOPKV2OUT>>
+{
+public:
+ CircleTopKV2Out() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+public:
+ int32_t index(void) const { return _index; }
+ void index(int32_t index) { _index = index; }
+
+private:
+ int32_t _index{-1};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_TOPK_V2_OUT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleTranspose.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleTranspose.h
new file mode 100644
index 000000000..095cd6746
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleTranspose.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLETRANSPOSE_H__
+#define __LUCI_IR_CIRCLETRANSPOSE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief TRANSPOSE in Circle
+ */
+class CircleTranspose final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::TRANSPOSE>>
+{
+public:
+ CircleTranspose() = default;
+
+public:
+ /// @brief Get the input node to transpose
+ loco::Node *a(void) const { return at(0)->node(); }
+
+ /// @brief Set the input node to transpose
+ void a(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *perm(void) const { return at(1)->node(); }
+ void perm(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLETRANSPOSE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h
new file mode 100644
index 000000000..e355102d6
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLETRANSPOSECONV_H__
+#define __LUCI_IR_CIRCLETRANSPOSECONV_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrPadding.h"
+#include "luci/IR/AttrStride.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief TRANSPOSE_CONV in Circle
+ *
+ * @note Argument node function names are from TensorFlow. So referring 'in' and
+ * 'out' acutally means 'out' and 'in' of the this node.
+ */
+class CircleTransposeConv final
+ : public FixedArityNode<4, CircleNodeImpl<CircleOpcode::TRANSPOSE_CONV>>,
+ public LuciNodeMixin<LuciNodeTrait::Bias>
+{
+public:
+ loco::Node *inputSizes(void) const { return at(0)->node(); }
+ void inputSizes(Node *node) { at(0)->node(node); }
+
+ loco::Node *filter(void) const { return at(1)->node(); }
+ void filter(Node *node) { at(1)->node(node); }
+
+ loco::Node *outBackprop(void) const { return at(2)->node(); }
+ void outBackprop(Node *node) { at(2)->node(node); }
+
+ /**
+ * @note "bias" is optional. When this node has no conceptual bias, "bias()"
+ * expected to be `luci::CircleOutputExclude` type.
+ *
+ * <Comment on tflite TRANSPOSE_CONV>
+ *
+ * (Circle node has no dependency on tflite, but just for information on converting)
+ * Before TF v2.3.0, tflite TRANSPOSE_CONV didn't support fused bias as argument.
+ * From TF v2.3.0, tflite TRANSPOSE_CONV supports bias as optional 4th argument.
+ *
+ * Ref: https://github.com/tensorflow/tensorflow/commit/43b8f6e710
+ */
+ loco::Node *bias(void) const override { return at(3)->node(); }
+ void bias(loco::Node *node) override { at(3)->node(node); }
+
+public:
+ const Padding &padding(void) const { return _padding; }
+ void padding(const Padding &padding) { _padding = padding; }
+
+ const Stride *stride(void) const { return &_stride; }
+ Stride *stride(void) { return &_stride; }
+
+private:
+ Padding _padding{Padding::UNDEFINED};
+ Stride _stride;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLETRANSPOSECONV_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleUnidirectionalSequenceLSTM.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleUnidirectionalSequenceLSTM.h
new file mode 100644
index 000000000..4352b045b
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleUnidirectionalSequenceLSTM.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLEUNIDIRECTIONALSEQUENCELSTM_H__
+#define __LUCI_IR_CIRCLEUNIDIRECTIONALSEQUENCELSTM_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief UNIDIRECTIONAL_SEQUENCE_LSTM in Circle
+ */
+class CircleUnidirectionalSequenceLSTM final
+ : public FixedArityNode<24, CircleNodeImpl<CircleOpcode::UNIDIRECTIONAL_SEQUENCE_LSTM>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *input_to_input_weights(void) const { return at(1)->node(); }
+ void input_to_input_weights(loco::Node *node) { at(1)->node(node); }
+ loco::Node *input_to_forget_weights(void) const { return at(2)->node(); }
+ void input_to_forget_weights(loco::Node *node) { at(2)->node(node); }
+ loco::Node *input_to_cell_weights(void) const { return at(3)->node(); }
+ void input_to_cell_weights(loco::Node *node) { at(3)->node(node); }
+ loco::Node *input_to_output_weights(void) const { return at(4)->node(); }
+ void input_to_output_weights(loco::Node *node) { at(4)->node(node); }
+
+ loco::Node *recurrent_to_input_weights(void) const { return at(5)->node(); }
+ void recurrent_to_input_weights(loco::Node *node) { at(5)->node(node); }
+ loco::Node *recurrent_to_forget_weights(void) const { return at(6)->node(); }
+ void recurrent_to_forget_weights(loco::Node *node) { at(6)->node(node); }
+ loco::Node *recurrent_to_cell_weights(void) const { return at(7)->node(); }
+ void recurrent_to_cell_weights(loco::Node *node) { at(7)->node(node); }
+ loco::Node *recurrent_to_output_weights(void) const { return at(8)->node(); }
+ void recurrent_to_output_weights(loco::Node *node) { at(8)->node(node); }
+
+ loco::Node *cell_to_input_weights(void) const { return at(9)->node(); }
+ void cell_to_input_weights(loco::Node *node) { at(9)->node(node); }
+ loco::Node *cell_to_forget_weights(void) const { return at(10)->node(); }
+ void cell_to_forget_weights(loco::Node *node) { at(10)->node(node); }
+ loco::Node *cell_to_output_weights(void) const { return at(11)->node(); }
+ void cell_to_output_weights(loco::Node *node) { at(11)->node(node); }
+
+ loco::Node *input_gate_bias(void) const { return at(12)->node(); }
+ void input_gate_bias(loco::Node *node) { at(12)->node(node); }
+ loco::Node *forget_gate_bias(void) const { return at(13)->node(); }
+ void forget_gate_bias(loco::Node *node) { at(13)->node(node); }
+ loco::Node *cell_gate_bias(void) const { return at(14)->node(); }
+ void cell_gate_bias(loco::Node *node) { at(14)->node(node); }
+ loco::Node *output_gate_bias(void) const { return at(15)->node(); }
+ void output_gate_bias(loco::Node *node) { at(15)->node(node); }
+
+ loco::Node *projection_weights(void) const { return at(16)->node(); }
+ void projection_weights(loco::Node *node) { at(16)->node(node); }
+ loco::Node *projection_bias(void) const { return at(17)->node(); }
+ void projection_bias(loco::Node *node) { at(17)->node(node); }
+
+ loco::Node *activation_state(void) const { return at(18)->node(); }
+ void activation_state(loco::Node *node) { at(18)->node(node); }
+ loco::Node *cell_state(void) const { return at(19)->node(); }
+ void cell_state(loco::Node *node) { at(19)->node(node); }
+
+ loco::Node *input_layer_norm_coefficients(void) const { return at(20)->node(); }
+ void input_layer_norm_coefficients(loco::Node *node) { at(20)->node(node); }
+ loco::Node *forget_layer_norm_coefficients(void) const { return at(21)->node(); }
+ void forget_layer_norm_coefficients(loco::Node *node) { at(21)->node(node); }
+ loco::Node *cell_layer_norm_coefficients(void) const { return at(22)->node(); }
+ void cell_layer_norm_coefficients(loco::Node *node) { at(22)->node(node); }
+ loco::Node *output_layer_norm_coefficients(void) const { return at(23)->node(); }
+ void output_layer_norm_coefficients(loco::Node *node) { at(23)->node(node); }
+
+public:
+ float cell_clip(void) const { return _cell_clip; }
+ void cell_clip(float cell_clip) { _cell_clip = cell_clip; }
+ float proj_clip(void) const { return _proj_clip; }
+ void proj_clip(float proj_clip) { _proj_clip = proj_clip; }
+ bool time_major(void) const { return _time_major; }
+ void time_major(bool time_major) { _time_major = time_major; }
+ bool asymmetric_quantize_inputs(void) const { return _asymmetric_quantize_inputs; }
+ void asymmetric_quantize_inputs(bool asymmetric_quantize_inputs)
+ {
+ _asymmetric_quantize_inputs = asymmetric_quantize_inputs;
+ }
+
+private:
+ float _cell_clip = 0.0f;
+ float _proj_clip = 0.0f;
+ bool _time_major = false;
+ bool _asymmetric_quantize_inputs = false;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEUNIDIRECTIONALSEQUENCELSTM_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleUnique.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleUnique.h
new file mode 100644
index 000000000..719a72362
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleUnique.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCELUNIQUE_H__
+#define __LUCI_IR_CIRCELUNIQUE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief Unique in Circle
+ */
+class CircleUnique final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::UNIQUE>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+public:
+ loco::DataType idx_out_type(void) const { return _idx_out_type; }
+ void output_type(loco::DataType ot) { _idx_out_type = ot; }
+
+private:
+ loco::DataType _idx_out_type{loco::DataType::S32};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCELUNIQUE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleUniqueOut.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleUniqueOut.h
new file mode 100644
index 000000000..f846403e0
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleUniqueOut.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_UNIQUEOUT_H__
+#define __LUCI_IR_CIRCLE_UNIQUEOUT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief Virtual CIRCLEUNIQUEOUT in Circle
+ */
+class CircleUniqueOut final
+ : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLEUNIQUEOUT>>
+{
+public:
+ CircleUniqueOut() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+public:
+ int32_t index(void) const { return _index; }
+ void index(int32_t index) { _index = index; }
+
+private:
+ int32_t _index{-1};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_UNIQUEOUT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleUnpack.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleUnpack.h
new file mode 100644
index 000000000..cb91d7e6a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleUnpack.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_UNPACK_H__
+#define __LUCI_IR_CIRCLE_UNPACK_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief UNPACK in Circle
+ */
+class CircleUnpack final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::UNPACK>>
+{
+public:
+ CircleUnpack() = default;
+
+public:
+ loco::Node *value(void) const { return at(0)->node(); }
+ void value(loco::Node *node) { at(0)->node(node); }
+
+public:
+ int32_t num(void) const { return _num; }
+ void num(int32_t num) { _num = num; }
+
+ int32_t axis(void) const { return _axis; }
+ void axis(int32_t axis) { _axis = axis; }
+
+private:
+ int32_t _num{0};
+ int32_t _axis{0};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_UNPACK_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleUnpackOut.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleUnpackOut.h
new file mode 100644
index 000000000..6f24578a1
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleUnpackOut.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_UNPACKOUT_H__
+#define __LUCI_IR_CIRCLE_UNPACKOUT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief Virtual CIRCLEUNPACKOUT in Circle
+ */
+class CircleUnpackOut final
+ : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLEUNPACKOUT>>
+{
+public:
+ CircleUnpackOut() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+public:
+ int32_t index(void) const { return _index; }
+ void index(int32_t index) { _index = index; }
+
+private:
+ int32_t _index{0};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_UNPACKOUT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleWhere.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleWhere.h
new file mode 100644
index 000000000..51eda3d6e
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleWhere.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_WHERE_H__
+#define __LUCI_IR_CIRCLE_WHERE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+#include <cassert>
+
+namespace luci
+{
+
+/**
+ * @brief WHERE in Circle
+ */
+class CircleWhere final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::WHERE>>
+{
+public:
+ CircleWhere() = default;
+
+public:
+ loco::Node *condition() const { return at(0)->node(); }
+ void condition(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_WHERE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleWhile.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleWhile.h
new file mode 100644
index 000000000..40ec96414
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleWhile.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_WHILE_H__
+#define __LUCI_IR_CIRCLE_WHILE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/VariadicArityNode.h"
+
+#include <cassert>
+
+namespace luci
+{
+
+/**
+ * @brief WHILE in Circle
+ */
+class CircleWhile final : public VariadicArityNode<CircleNodeImpl<CircleOpcode::WHILE>>
+{
+public:
+ CircleWhile(uint32_t arity, uint32_t out)
+ : VariadicArityNode<CircleNodeImpl<CircleOpcode::WHILE>>(arity), _output_count(out)
+ {
+ assert(arity > 0);
+ assert(out > 0);
+
+ // input and output must have the same size
+ assert(arity == out);
+ }
+
+public:
+ uint32_t input_count(void) const { return arity(); }
+ uint32_t output_count(void) const { return _output_count; }
+
+public:
+ Node *input(uint32_t index) const { return at(index)->node(); }
+ void input(uint32_t index, Node *node) { at(index)->node(node); }
+
+public:
+ int32_t cond_branch(void) const { return _cond_branch; }
+ void cond_branch(int32_t cond_branch) { _cond_branch = cond_branch; }
+
+ int32_t body_branch(void) const { return _body_branch; }
+ void body_branch(int32_t body_branch) { _body_branch = body_branch; }
+
+public:
+ loco::Graph *cond_graph(void) const { return _cond_graph; }
+ void cond_graph(loco::Graph *cond_graph) { _cond_graph = cond_graph; }
+
+ loco::Graph *body_graph(void) const { return _body_graph; }
+ void body_graph(loco::Graph *body_graph) { _body_graph = body_graph; }
+
+private:
+ uint32_t _output_count{0};
+ int32_t _cond_branch{-1};
+ int32_t _body_branch{-1};
+
+ loco::Graph *_cond_graph{nullptr};
+ loco::Graph *_body_graph{nullptr};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_WHILE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleWhileOut.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleWhileOut.h
new file mode 100644
index 000000000..cdf617848
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleWhileOut.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_WHILEOUT_H__
+#define __LUCI_IR_CIRCLE_WHILEOUT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief Virtual CIRCLEWHILEOUT in Circle
+ */
+class CircleWhileOut final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLEWHILEOUT>>
+{
+public:
+ CircleWhileOut() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+public:
+ int32_t index(void) const { return _index; }
+ void index(int32_t index) { _index = index; }
+
+private:
+ int32_t _index{-1};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_WHILEOUT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleZerosLike.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleZerosLike.h
new file mode 100644
index 000000000..d3b6d272a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleZerosLike.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_CIRCLE_ZEROS_LIKE_H__
+#define __LUCI_IR_CIRCLE_ZEROS_LIKE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief ZEROS_LIKE in Circle
+ */
+class CircleZerosLike final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::ZEROS_LIKE>>
+{
+public:
+ CircleZerosLike() = default;
+
+public:
+ /// @brief Get the input node
+ loco::Node *input(void) const { return at(0)->node(); }
+
+ /// @brief Set the input node
+ void input(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_ZEROS_LIKE_H__
diff --git a/compiler/luci/lang/include/luci/IR/PropertyShapeStatus.h b/compiler/luci/lang/include/luci/IR/PropertyShapeStatus.h
new file mode 100644
index 000000000..179a8ab3c
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/PropertyShapeStatus.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_PROPERTY_SHAPE_STATUS_H__
+#define __LUCI_IR_PROPERTY_SHAPE_STATUS_H__
+
+namespace luci
+{
+
+/**
+ * @brief ShapeStatus is to remember circle node shape status.
+ * @note This is not an attribute from the file but inner status of a node.
+ * Shape with [] is scalar but sometimes it acts as dynamic shape.
+ */
+enum class ShapeStatus
+{
+ UNDEFINED, // Shape status is undefined
+
+ NOSHAPE, // shape is unknown; to distinguish from scalar
+ VALID, // shape is valid
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_PROPERTY_SHAPE_STATUS_H__
diff --git a/compiler/luci/lang/include/luci/IR/SparsityParam.h b/compiler/luci/lang/include/luci/IR/SparsityParam.h
new file mode 100644
index 000000000..f471e5ef9
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/SparsityParam.h
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_SPARSITYPARAM_H__
+#define __LUCI_IR_SPARSITYPARAM_H__
+
+#include <cstdint>
+#include <stdexcept>
+#include <utility>
+#include <vector>
+
+namespace luci
+{
+
+enum DimensionType
+{
+ DENSE,
+ SPARSE_CSR,
+};
+
+enum SparseIndexVectorType
+{
+ NONE,
+ I32,
+ U16,
+ U8,
+};
+
+class SparseIndexVector
+{
+public:
+ SparseIndexVector() = default;
+ SparseIndexVector(const SparseIndexVectorType &type, const std::vector<int32_t> &sparse_index_vec)
+ : _type{type}
+ {
+ switch (type)
+ {
+ case SparseIndexVectorType::NONE:
+ break;
+ case SparseIndexVectorType::I32:
+ {
+ _vec_ptr = static_cast<void *>(
+ new std::vector<int32_t>(sparse_index_vec.begin(), sparse_index_vec.end()));
+ break;
+ }
+ case SparseIndexVectorType::U16:
+ {
+ auto new_vec = new std::vector<uint16_t>(sparse_index_vec.size());
+ for (uint32_t idx = 0; idx < sparse_index_vec.size(); idx++)
+ {
+ new_vec->at(idx) = static_cast<uint16_t>(sparse_index_vec.at(idx));
+ }
+ _vec_ptr = static_cast<void *>(new_vec);
+ break;
+ }
+ case SparseIndexVectorType::U8:
+ {
+ auto new_vec = new std::vector<uint8_t>(sparse_index_vec.size());
+ for (uint32_t idx = 0; idx < sparse_index_vec.size(); idx++)
+ {
+ new_vec->at(idx) = static_cast<uint8_t>(sparse_index_vec.at(idx));
+ }
+ _vec_ptr = static_cast<void *>(new_vec);
+ break;
+ }
+ default:
+ std::runtime_error("Invalid SparseIndexVectorType");
+ }
+ }
+
+ SparseIndexVector(SparseIndexVectorType type, const void *sparse_index_vec) : _type{type}
+ {
+ switch (type)
+ {
+ case SparseIndexVectorType::NONE:
+ break;
+ case SparseIndexVectorType::I32:
+ {
+ const std::vector<int32_t> *vec =
+ static_cast<const std::vector<int32_t> *>(sparse_index_vec);
+ _vec_ptr = static_cast<void *>(new std::vector<int32_t>(vec->begin(), vec->end()));
+ break;
+ }
+ case SparseIndexVectorType::U16:
+ {
+ const std::vector<uint16_t> *vec =
+ static_cast<const std::vector<uint16_t> *>(sparse_index_vec);
+ _vec_ptr = static_cast<void *>(new std::vector<uint16_t>(vec->begin(), vec->end()));
+ break;
+ }
+ case SparseIndexVectorType::U8:
+ {
+ const std::vector<uint8_t> *vec =
+ static_cast<const std::vector<uint8_t> *>(sparse_index_vec);
+ _vec_ptr = static_cast<void *>(new std::vector<uint8_t>(vec->begin(), vec->end()));
+ break;
+ }
+ default:
+ std::runtime_error("Invalid SparseIndexVectorType");
+ }
+ }
+
+ SparseIndexVector(const SparseIndexVector &sparse_index_vec)
+ : SparseIndexVector(sparse_index_vec._type, sparse_index_vec._vec_ptr)
+ {
+ }
+
+ SparseIndexVector(SparseIndexVector &&sparse_index_vec)
+ : _type{sparse_index_vec._type}, _vec_ptr{std::exchange(sparse_index_vec._vec_ptr, nullptr)}
+ {
+ }
+
+ SparseIndexVector &operator=(const SparseIndexVector &sparse_index_vec)
+ {
+ return *this = SparseIndexVector(sparse_index_vec);
+ }
+
+ SparseIndexVector &operator=(SparseIndexVector &&sparse_index_vector)
+ {
+ std::swap(_vec_ptr, sparse_index_vector._vec_ptr);
+ std::swap(_type, sparse_index_vector._type);
+ return *this;
+ }
+
+ ~SparseIndexVector()
+ {
+ switch (_type)
+ {
+ case SparseIndexVectorType::NONE:
+ break;
+ case SparseIndexVectorType::I32:
+ {
+ auto vec_ptr = static_cast<std::vector<int32_t> *>(_vec_ptr);
+ delete vec_ptr;
+ break;
+ }
+ case SparseIndexVectorType::U16:
+ {
+ auto vec_ptr = static_cast<std::vector<uint16_t> *>(_vec_ptr);
+ delete vec_ptr;
+ break;
+ }
+ case SparseIndexVectorType::U8:
+ {
+ auto vec_ptr = static_cast<std::vector<uint8_t> *>(_vec_ptr);
+ delete vec_ptr;
+ break;
+ }
+ default:
+ break;
+ }
+ _vec_ptr = nullptr;
+ _type = SparseIndexVectorType::NONE;
+ }
+
+public:
+ SparseIndexVectorType type(void) const { return _type; }
+
+public:
+ const std::vector<int32_t> *as_int32_vector(void) const
+ {
+ return _type == SparseIndexVectorType::I32 ? static_cast<const std::vector<int32_t> *>(_vec_ptr)
+ : nullptr;
+ }
+ const std::vector<uint16_t> *as_uint16_vector(void) const
+ {
+ return _type == SparseIndexVectorType::U16
+ ? static_cast<const std::vector<uint16_t> *>(_vec_ptr)
+ : nullptr;
+ }
+ const std::vector<uint8_t> *as_uint8_vector(void) const
+ {
+ return _type == SparseIndexVectorType::U8 ? static_cast<const std::vector<uint8_t> *>(_vec_ptr)
+ : nullptr;
+ }
+
+private:
+ SparseIndexVectorType _type{SparseIndexVectorType::NONE};
+ void *_vec_ptr{nullptr};
+};
+
+class DimMetaData
+{
+public:
+ DimMetaData() = delete;
+ DimMetaData(DimensionType format, int32_t dense_size) : _format{format}, _dense_size{dense_size}
+ {
+ // DO NOTHING
+ }
+ DimMetaData(DimensionType format, int32_t dense_size, const SparseIndexVector &array_segments,
+ const SparseIndexVector &array_indices)
+ : _format{format}, _dense_size{dense_size}, _array_segments{array_segments},
+ _array_indices{array_indices}
+ {
+ // DO NOTHING
+ }
+
+public:
+ DimensionType format(void) const { return _format; }
+ int32_t dense_size(void) const { return _dense_size; }
+ const SparseIndexVector &array_segments(void) const { return _array_segments; }
+ const SparseIndexVector &array_indices(void) const { return _array_indices; }
+
+private:
+ DimensionType _format{DimensionType::DENSE};
+ int32_t _dense_size{0};
+ SparseIndexVector _array_segments;
+ SparseIndexVector _array_indices;
+};
+
+struct SparsityParam
+{
+ std::vector<int32_t> traversal_order;
+ std::vector<int32_t> block_map;
+ std::vector<DimMetaData> dim_metadata;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_SPARSITYPARAM_H__
diff --git a/compiler/luci/lang/include/luci/IR/VariadicArityNode.h b/compiler/luci/lang/include/luci/IR/VariadicArityNode.h
new file mode 100644
index 000000000..e83d90978
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/VariadicArityNode.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_IR_VARIADICARITYNODES_H__
+#define __LUCI_IR_VARIADICARITYNODES_H__
+
+#include <loco/IR/Node.h>
+#include <loco/IR/Use.h>
+
+#include <vector>
+#include <memory>
+#include <cassert>
+
+namespace luci
+{
+
+/**
+ * @brief Nodes with the variadic inputs
+ */
+template <typename Base> class VariadicArityNode : public Base
+{
+public:
+ VariadicArityNode(uint32_t arity)
+ {
+ for (uint32_t n = 0; n < arity; ++n)
+ {
+ _args.push_back(std::make_unique<loco::Use>(this));
+ }
+ };
+
+ virtual ~VariadicArityNode() = default;
+
+public:
+ uint32_t arity(void) const final { return _args.size(); }
+
+ loco::Node *arg(uint32_t n) const final { return _args.at(n)->node(); }
+
+ void drop(void) final
+ {
+ for (uint32_t n = 0; n < _args.size(); ++n)
+ {
+ _args.at(n)->node(nullptr);
+ }
+ }
+
+protected:
+ // This API allows inherited classes to access "_args" field.
+ loco::Use *at(uint32_t n) const { return _args.at(n).get(); }
+
+private:
+ std::vector<std::unique_ptr<loco::Use>> _args;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_VARIADICARITYNODES_H__
diff --git a/compiler/luci/lang/src/Check.h b/compiler/luci/lang/src/Check.h
new file mode 100644
index 000000000..e05ec904a
--- /dev/null
+++ b/compiler/luci/lang/src/Check.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CHECK_H__
+#define __CHECK_H__
+
+#include <stdexcept>
+#include <cassert>
+#include <iostream>
+
+// TODO Add macro for Release version
+
+#define LUCI_ASSERT(condition, msg) \
+ { \
+ if (!(condition)) \
+ { \
+ std::cerr << "[assert failed] " << (msg) << ". " << std::endl; \
+ assert((condition)); \
+ } \
+ }
+
+#endif // __CHECK_H__
diff --git a/compiler/luci/lang/src/CircleDialect.cpp b/compiler/luci/lang/src/CircleDialect.cpp
new file mode 100644
index 000000000..42ca3c917
--- /dev/null
+++ b/compiler/luci/lang/src/CircleDialect.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/Nodes/CircleInput.h"
+#include "luci/IR/Nodes/CircleOutput.h"
+
+#include <loco/IR/Graph.h>
+#include <loco/IR/GraphInputIndex.h>
+#include <loco/IR/GraphOutputIndex.h>
+
+#include "DeadNodeQueryService.h"
+
+#include <cassert>
+#include <memory>
+
+namespace
+{
+
+struct GiiQueryServiceImpl final : public loco::GraphInputIndexQueryService
+{
+ bool associated(const loco::Node *node) const final
+ {
+ if (auto circleinput = dynamic_cast<const luci::CircleInput *>(node))
+ {
+ return circleinput->indexed();
+ }
+ return false;
+ }
+
+ loco::GraphOutputIndex index(const loco::Node *node) const final
+ {
+ assert(associated(node));
+ auto circleinput = loco::must_cast<const luci::CircleInput *>(node);
+ return circleinput->index();
+ }
+};
+
+struct GoiQueryServiceImpl final : public loco::GraphOutputIndexQueryService
+{
+ bool associated(const loco::Node *node) const final
+ {
+ if (auto circleoutput = dynamic_cast<const luci::CircleOutput *>(node))
+ {
+ return circleoutput->indexed();
+ }
+ return false;
+ }
+
+ loco::GraphOutputIndex index(const loco::Node *node) const final
+ {
+ assert(associated(node));
+ auto circleoutput = loco::must_cast<const luci::CircleOutput *>(node);
+ return circleoutput->index();
+ }
+};
+
+} // namespace
+
+namespace luci
+{
+
+CircleDialect::CircleDialect()
+{
+ service<loco::GraphInputIndexQueryService>(std::make_unique<GiiQueryServiceImpl>());
+ service<loco::GraphOutputIndexQueryService>(std::make_unique<GoiQueryServiceImpl>());
+ service<logo::DeadNodeQueryService>(std::make_unique<DeadNodeQueryServiceImpl>());
+}
+
+loco::Dialect *CircleDialect::get(void)
+{
+ static CircleDialect d;
+ return &d;
+}
+
+} // namespace luci
diff --git a/compiler/luci/lang/src/CircleDialect.test.cpp b/compiler/luci/lang/src/CircleDialect.test.cpp
new file mode 100644
index 000000000..a09c105ec
--- /dev/null
+++ b/compiler/luci/lang/src/CircleDialect.test.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodes.h"
+
+#include <loco.h>
+#include <logo/DeadNodeQueryService.h>
+
+#include <gtest/gtest.h>
+
+TEST(CircleDialectTest, get_P)
+{
+ auto d = luci::CircleDialect::get();
+
+ // get() SHOULD return a valid(non-null) pointer
+ ASSERT_NE(d, nullptr);
+ // The return value SHOULD be stable across multiple invocations
+ ASSERT_EQ(luci::CircleDialect::get(), d);
+}
+
+TEST(CircleDialectTest, check_if_dead_node_service)
+{
+ /**
+ * [CircleInput1] [CircleInput2] [CircleInput3]
+ * \ / (dangling input)
+ * \ /
+ * [CircleAdd] [CircleBatchMatMul]
+ * | (dangling node)
+ * |
+ * [CircleOutput1] [CircleOutput2]
+ * (dangling output)
+ */
+ auto g = loco::make_graph();
+
+ auto graph_input1 = g->inputs()->create();
+ auto circle_input1 = g->nodes()->create<luci::CircleInput>();
+ circle_input1->index(graph_input1->index());
+
+ auto graph_input2 = g->inputs()->create();
+ auto circle_input2 = g->nodes()->create<luci::CircleInput>();
+ circle_input2->index(graph_input2->index());
+
+ // dangling output
+ auto graph_input3 = g->inputs()->create();
+ auto dangling_input = g->nodes()->create<luci::CircleInput>();
+ dangling_input->index(graph_input3->index());
+
+ auto active_node = g->nodes()->create<luci::CircleAdd>();
+ active_node->x(circle_input1);
+ active_node->y(circle_input2);
+
+ auto dangling_node = g->nodes()->create<luci::CircleBatchMatMul>();
+
+ auto graph_output1 = g->outputs()->create();
+ auto circle_output1 = g->nodes()->create<luci::CircleOutput>();
+ circle_output1->index(graph_output1->index());
+ circle_output1->from(active_node);
+
+ // dangling output
+ auto graph_output2 = g->outputs()->create();
+ auto circle_output2 = g->nodes()->create<luci::CircleOutput>();
+ circle_output2->index(graph_output2->index());
+
+ auto service = active_node->dialect()->service<logo::DeadNodeQueryService>();
+
+ ASSERT_TRUE(service->isDeadNode(dangling_node));
+ ASSERT_FALSE(service->isDeadNode(dangling_input));
+ ASSERT_FALSE(service->isDeadNode(active_node));
+ ASSERT_FALSE(service->isDeadNode(circle_input1));
+ ASSERT_FALSE(service->isDeadNode(circle_input2));
+ ASSERT_FALSE(service->isDeadNode(circle_output1));
+ ASSERT_FALSE(service->isDeadNode(circle_output2));
+}
diff --git a/compiler/luci/lang/src/CircleNode.cpp b/compiler/luci/lang/src/CircleNode.cpp
new file mode 100644
index 000000000..cc273ba91
--- /dev/null
+++ b/compiler/luci/lang/src/CircleNode.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/CircleNode.h"
+#include "luci/IR/CircleDialect.h"
+
+namespace luci
+{
+
+const loco::Dialect *CircleNode::dialect(void) const { return CircleDialect::get(); }
+
+} // namespace luci
diff --git a/compiler/luci/lang/src/CircleNodeShapeDtype.test.cpp b/compiler/luci/lang/src/CircleNodeShapeDtype.test.cpp
new file mode 100644
index 000000000..61eab4b77
--- /dev/null
+++ b/compiler/luci/lang/src/CircleNodeShapeDtype.test.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/CircleNodes.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleNodeShapeDTypeTest, constructor)
+{
+ luci::CircleAdd node;
+
+ ASSERT_EQ(loco::DataType::Unknown, node.dtype());
+ ASSERT_EQ(0, node.rank());
+}
+
+TEST(CircleNodeShapeDTypeTest, values)
+{
+ luci::CircleAdd node;
+
+ node.dtype(loco::DataType::FLOAT32);
+ ASSERT_EQ(loco::DataType::FLOAT32, node.dtype());
+
+ node.rank(4);
+ ASSERT_EQ(4, node.rank());
+ ASSERT_FALSE(node.dim(0).known());
+
+ node.dim(0) = loco::Dimension(1);
+ ASSERT_TRUE(node.dim(0).known());
+ ASSERT_EQ(1, node.dim(0).value());
+}
+
+TEST(CircleNodeShapeDTypeTest, values_NEG)
+{
+ luci::CircleAdd node;
+
+ node.rank(4);
+ EXPECT_ANY_THROW(node.dim(100).known());
+ EXPECT_ANY_THROW(node.dim(100) = loco::Dimension(1));
+}
diff --git a/compiler/luci/lang/src/CircleNodes.cpp b/compiler/luci/lang/src/CircleNodes.cpp
new file mode 100644
index 000000000..c77c06861
--- /dev/null
+++ b/compiler/luci/lang/src/CircleNodes.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/CircleNodes.h"
+
+#include "Check.h"
+
+#include <loco.h>
+
+namespace luci
+{
+
+void set_new_shape(CircleReshape *node, int32_t *base, uint32_t size)
+{
+ // Check node does not have both of new shape infos
+ LUCI_ASSERT(node->shape() == nullptr, "node already has shape input");
+ LUCI_ASSERT(node->newShape()->rank() == 0, "node already has newShape attribute");
+
+ const loco::DataType S32 = loco::DataType::S32;
+
+ // Set 2nd input as CircleConst
+ auto const_shape_node = node->graph()->nodes()->create<CircleConst>();
+ const_shape_node->rank(1);
+ const_shape_node->dim(0) = size;
+ const_shape_node->dtype(S32);
+ const_shape_node->size<S32>(size);
+ const_shape_node->shape_status(luci::ShapeStatus::VALID);
+ for (uint32_t axis = 0; axis < size; ++axis)
+ const_shape_node->at<S32>(axis) = base[axis];
+ node->shape(const_shape_node);
+
+ // Set newShape attribute
+ node->newShape()->rank(size);
+ for (uint32_t axis = 0; axis < size; ++axis)
+ node->newShape()->dim(axis) = base[axis];
+}
+
+void link(loco::GraphOutput *output, CircleOutput *node) { node->index(output->index()); }
+
+CircleOutput *output_node(loco::Graph *g, const loco::GraphOutputIndex &index)
+{
+ for (uint32_t n = 0; n < g->nodes()->size(); ++n)
+ {
+ if (auto output = dynamic_cast<CircleOutput *>(g->nodes()->at(n)))
+ {
+ if (output->indexed() && output->index() == index)
+ {
+ return output;
+ }
+ }
+ }
+ return nullptr;
+}
+
+void link(loco::GraphInput *input, CircleInput *node) { node->index(input->index()); }
+
+CircleInput *input_node(loco::Graph *g, const loco::GraphInputIndex &index)
+{
+ for (uint32_t n = 0; n < g->nodes()->size(); ++n)
+ {
+ if (auto input = dynamic_cast<CircleInput *>(g->nodes()->at(n)))
+ {
+ if (input->indexed() && input->index() == index)
+ {
+ return input;
+ }
+ }
+ }
+ return nullptr;
+}
+
+} // namespace luci
diff --git a/compiler/luci/lang/src/DeadNodeQueryService.cpp b/compiler/luci/lang/src/DeadNodeQueryService.cpp
new file mode 100644
index 000000000..a22574c94
--- /dev/null
+++ b/compiler/luci/lang/src/DeadNodeQueryService.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DeadNodeQueryService.h"
+
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <loco/IR/Graph.h>
+
+namespace luci
+{
+
+struct VirtualOutputDetector final : public luci::CircleNodeMutableVisitor<bool>
+{
+ bool visit(luci::CircleIfOut *) final { return true; }
+ bool visit(luci::CircleSplitOut *) final { return true; }
+ bool visit(luci::CircleSplitVOut *) final { return true; }
+ bool visit(luci::CircleTopKV2Out *) final { return true; }
+ bool visit(luci::CircleUnpackOut *) final { return true; }
+ bool visit(luci::CircleWhileOut *) final { return true; }
+ // TODO add more nodes that multi output virtual nodes
+
+ // default is false
+ bool visit(luci::CircleNode *) final { return false; }
+};
+
+bool DeadNodeQueryServiceImpl::isDeadNode(loco::Node *node)
+{
+ auto g = node->graph();
+ auto input_nodes_vec = loco::input_nodes(g);
+ auto output_nodes_vec = loco::output_nodes(g);
+
+ auto input_nodes = std::set<loco::Node *>(input_nodes_vec.begin(), input_nodes_vec.end());
+ auto output_nodes = std::set<loco::Node *>(output_nodes_vec.begin(), output_nodes_vec.end());
+ auto active_nodes = loco::active_nodes(output_nodes_vec);
+
+ if (active_nodes.find(node) != active_nodes.end())
+ return false;
+ // input and output nodes are not dead node even if it is not active.
+ if (input_nodes.find(node) != input_nodes.end())
+ return false;
+
+ // if node is one of virtual mulitple outputs, we need to ask the real node
+ if (auto circle_node = dynamic_cast<luci::CircleNode *>(node))
+ {
+ VirtualOutputDetector d;
+ if (circle_node->accept(&d))
+ {
+ assert(node->arity() == 1);
+ loco::Node *real_node = node->arg(0);
+ if (active_nodes.find(real_node) != active_nodes.end())
+ return false;
+ if (input_nodes.find(real_node) != input_nodes.end())
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace luci
diff --git a/compiler/luci/lang/src/DeadNodeQueryService.h b/compiler/luci/lang/src/DeadNodeQueryService.h
new file mode 100644
index 000000000..d10696667
--- /dev/null
+++ b/compiler/luci/lang/src/DeadNodeQueryService.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_LANG_DEADNODEQUERYSERVICE_H__
+#define __LUCI_LANG_DEADNODEQUERYSERVICE_H__
+
+#include <logo/DeadNodeQueryService.h>
+
+#include <loco/IR/Node.h>
+
+namespace luci
+{
+
+struct DeadNodeQueryServiceImpl final : public logo::DeadNodeQueryService
+{
+ bool isDeadNode(loco::Node *node) final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_LANG_DEADNODEQUERYSERVICE_H__
diff --git a/compiler/luci/lang/src/LuciNodeMixins.cpp b/compiler/luci/lang/src/LuciNodeMixins.cpp
new file mode 100644
index 000000000..660cbe1a5
--- /dev/null
+++ b/compiler/luci/lang/src/LuciNodeMixins.cpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This is to validate LuciNodeMixins.h
+#include "luci/IR/LuciNodeMixins.h"
diff --git a/compiler/luci/lang/src/Module.cpp b/compiler/luci/lang/src/Module.cpp
new file mode 100644
index 000000000..80ef61910
--- /dev/null
+++ b/compiler/luci/lang/src/Module.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Module.h"
+
+#include <stdexcept>
+
+namespace luci
+{
+
+void Module::add(std::unique_ptr<loco::Graph> &&g)
+{
+ if (g.get() == nullptr)
+ throw std::invalid_argument("Module: Graph cannot be null");
+
+ _graphs.emplace_back(std::move(g));
+}
+
+loco::Graph *Module::graph(void) const
+{
+ auto &graph = _graphs.at(0);
+ return graph.get();
+}
+
+loco::Graph *Module::graph(size_t idx) const
+{
+ auto &graph = _graphs.at(idx);
+ return graph.get();
+}
+
+std::unique_ptr<Module> make_module(void) { return std::make_unique<Module>(); }
+
+} // namespace luci
diff --git a/compiler/luci/lang/src/Module.test.cpp b/compiler/luci/lang/src/Module.test.cpp
new file mode 100644
index 000000000..a5973e52d
--- /dev/null
+++ b/compiler/luci/lang/src/Module.test.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Module.h"
+
+#include <gtest/gtest.h>
+
+TEST(ModuleTest, consturctor)
+{
+ auto gs = luci::make_module();
+
+ SUCCEED();
+}
+
+TEST(ModuleTest, add)
+{
+ auto m = luci::make_module();
+ auto g = loco::make_graph();
+ auto g_ptr = g.get();
+
+ m->add(std::move(g));
+
+ ASSERT_EQ(g_ptr, m->graph());
+ ASSERT_EQ(g_ptr, m->graph(0));
+}
+
+TEST(ModuleTest, add_more)
+{
+ auto m = luci::make_module();
+ auto g1 = loco::make_graph();
+ auto g2 = loco::make_graph();
+ auto g3 = loco::make_graph();
+ auto g1_ptr = g1.get();
+ auto g2_ptr = g2.get();
+ auto g3_ptr = g3.get();
+
+ m->add(std::move(g1));
+ m->add(std::move(g2));
+ m->add(std::move(g3));
+
+ ASSERT_EQ(3, m->size());
+ ASSERT_EQ(g1_ptr, m->graph());
+ ASSERT_EQ(g1_ptr, m->graph(0));
+ ASSERT_EQ(g2_ptr, m->graph(1));
+ ASSERT_EQ(g3_ptr, m->graph(2));
+}
+
+TEST(ModuleTest, add_nullptr_NEG)
+{
+ auto m = luci::make_module();
+
+ EXPECT_THROW(m->add(nullptr), std::invalid_argument);
+}
+
+TEST(ModuleTest, graph_index_overflow_NEG)
+{
+ auto m = luci::make_module();
+
+ EXPECT_ANY_THROW(m->graph(100));
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleAbs.test.cpp b/compiler/luci/lang/src/Nodes/CircleAbs.test.cpp
new file mode 100644
index 000000000..f97becba8
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleAbs.test.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleAbs.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+TEST(CircleAbsTest, constructor)
+{
+ luci::CircleAbs abs_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), abs_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::ABS, abs_node.opcode());
+
+ ASSERT_EQ(nullptr, abs_node.x());
+}
+
+TEST(CircleAbsTest, common_NEG)
+{
+ luci::CircleAbs abs_node;
+
+ abs_node.name("name");
+ ASSERT_EQ("name", abs_node.name());
+
+ auto q = std::make_unique<luci::CircleQuantParam>();
+ abs_node.quantparam(std::move(q));
+ ASSERT_NE(nullptr, abs_node.quantparam());
+
+ ASSERT_EQ(luci::ShapeStatus::UNDEFINED, abs_node.shape_status());
+ abs_node.shape_status(luci::ShapeStatus::NOSHAPE);
+ ASSERT_NE(luci::ShapeStatus::UNDEFINED, abs_node.shape_status());
+}
+
+TEST(CircleAbsTest, input_NEG)
+{
+ luci::CircleAbs abs_node;
+ luci::CircleAbs node;
+
+ abs_node.x(&node);
+ ASSERT_NE(nullptr, abs_node.x());
+
+ abs_node.x(nullptr);
+ ASSERT_EQ(nullptr, abs_node.x());
+}
+
+TEST(CircleAbsTest, arity_NEG)
+{
+ luci::CircleAbs abs_node;
+
+ ASSERT_NO_THROW(abs_node.arg(0));
+ ASSERT_THROW(abs_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleAbsTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleAbs abs_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(abs_node.accept(&tv), std::exception);
+}
+
+TEST(CircleAbsTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleAbs abs_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(abs_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleAdd.test.cpp b/compiler/luci/lang/src/Nodes/CircleAdd.test.cpp
new file mode 100644
index 000000000..382faa5ef
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleAdd.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleAdd.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleAddTest, constructor_P)
+{
+ luci::CircleAdd add_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), add_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::ADD, add_node.opcode());
+
+ ASSERT_EQ(nullptr, add_node.x());
+ ASSERT_EQ(nullptr, add_node.y());
+}
+
+TEST(CircleAddTest, input_NEG)
+{
+ luci::CircleAdd add_node;
+ luci::CircleAdd node;
+
+ add_node.x(&node);
+ add_node.y(&node);
+ ASSERT_NE(nullptr, add_node.x());
+ ASSERT_NE(nullptr, add_node.y());
+
+ add_node.x(nullptr);
+ add_node.y(nullptr);
+ ASSERT_EQ(nullptr, add_node.x());
+ ASSERT_EQ(nullptr, add_node.y());
+}
+
+TEST(CircleAddTest, arity_NEG)
+{
+ luci::CircleAdd add_node;
+
+ ASSERT_NO_THROW(add_node.arg(1));
+ ASSERT_THROW(add_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleAddTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleAdd add_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(add_node.accept(&tv), std::exception);
+}
+
+TEST(CircleAddTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleAdd add_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(add_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleAddN.test.cpp b/compiler/luci/lang/src/Nodes/CircleAddN.test.cpp
new file mode 100644
index 000000000..399d8cb82
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleAddN.test.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleAddN.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleAddNTest, constructor)
+{
+ luci::CircleAddN add_node(3);
+
+ ASSERT_EQ(luci::CircleDialect::get(), add_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::ADD_N, add_node.opcode());
+
+ ASSERT_EQ(nullptr, add_node.inputs(0));
+ ASSERT_EQ(nullptr, add_node.inputs(1));
+ ASSERT_EQ(nullptr, add_node.inputs(2));
+}
+
+TEST(CircleAddNTest, input_NEG)
+{
+ luci::CircleAddN add_node(3);
+ luci::CircleAddN node(2);
+
+ add_node.inputs(0, &node);
+ add_node.inputs(1, &node);
+ add_node.inputs(2, &node);
+ ASSERT_NE(nullptr, add_node.inputs(0));
+ ASSERT_NE(nullptr, add_node.inputs(1));
+ ASSERT_NE(nullptr, add_node.inputs(2));
+
+ add_node.inputs(0, nullptr);
+ add_node.inputs(1, nullptr);
+ add_node.inputs(2, nullptr);
+ ASSERT_EQ(nullptr, add_node.inputs(0));
+ ASSERT_EQ(nullptr, add_node.inputs(1));
+ ASSERT_EQ(nullptr, add_node.inputs(2));
+}
+
+TEST(CircleAddNTest, arity_NEG)
+{
+ luci::CircleAddN add_node(3);
+ luci::CircleAddN node(2);
+
+ ASSERT_NO_THROW(add_node.inputs(2, &node));
+ ASSERT_NO_THROW(add_node.inputs(2, nullptr));
+ ASSERT_THROW(add_node.inputs(3, &node), std::out_of_range);
+
+ ASSERT_NO_THROW(add_node.arg(2));
+ ASSERT_THROW(add_node.arg(3), std::out_of_range);
+}
+
+TEST(CircleAddNTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleAddN add_node(2);
+
+ TestVisitor tv;
+ ASSERT_THROW(add_node.accept(&tv), std::exception);
+}
+
+TEST(CircleAddNTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleAddN add_node(2);
+
+ TestVisitor tv;
+ ASSERT_THROW(add_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleArgMax.test.cpp b/compiler/luci/lang/src/Nodes/CircleArgMax.test.cpp
new file mode 100644
index 000000000..375a74c20
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleArgMax.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleArgMax.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleArgMaxTest, constructor_P)
+{
+ luci::CircleArgMax argmax_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), argmax_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::ARG_MAX, argmax_node.opcode());
+
+ ASSERT_EQ(nullptr, argmax_node.input());
+ ASSERT_EQ(nullptr, argmax_node.dimension());
+}
+
+TEST(CircleArgMaxTest, input_NEG)
+{
+ luci::CircleArgMax argmax_node;
+ luci::CircleArgMax node;
+
+ argmax_node.input(&node);
+ argmax_node.dimension(&node);
+ ASSERT_NE(nullptr, argmax_node.input());
+ ASSERT_NE(nullptr, argmax_node.dimension());
+
+ argmax_node.input(nullptr);
+ argmax_node.dimension(nullptr);
+ ASSERT_EQ(nullptr, argmax_node.input());
+ ASSERT_EQ(nullptr, argmax_node.dimension());
+}
+
+TEST(CircleArgMaxTest, arity_NEG)
+{
+ luci::CircleArgMax argmax_node;
+
+ ASSERT_NO_THROW(argmax_node.arg(1));
+ ASSERT_THROW(argmax_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleArgMaxTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleArgMax argmax_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(argmax_node.accept(&tv), std::exception);
+}
+
+TEST(CircleArgMaxTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleArgMax argmax_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(argmax_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleArgMin.test.cpp b/compiler/luci/lang/src/Nodes/CircleArgMin.test.cpp
new file mode 100644
index 000000000..6607bf82f
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleArgMin.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleArgMin.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleArgMinTest, constructor_P)
+{
+ luci::CircleArgMin argmin_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), argmin_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::ARG_MIN, argmin_node.opcode());
+
+ ASSERT_EQ(nullptr, argmin_node.input());
+ ASSERT_EQ(nullptr, argmin_node.dimension());
+}
+
+TEST(CircleArgMinTest, input_NEG)
+{
+ luci::CircleArgMin argmin_node;
+ luci::CircleArgMin node;
+
+ argmin_node.input(&node);
+ argmin_node.dimension(&node);
+ ASSERT_NE(nullptr, argmin_node.input());
+ ASSERT_NE(nullptr, argmin_node.dimension());
+
+ argmin_node.input(nullptr);
+ argmin_node.dimension(nullptr);
+ ASSERT_EQ(nullptr, argmin_node.input());
+ ASSERT_EQ(nullptr, argmin_node.dimension());
+}
+
+TEST(CircleArgMinTest, arity_NEG)
+{
+ luci::CircleArgMin argmin_node;
+
+ ASSERT_NO_THROW(argmin_node.arg(1));
+ ASSERT_THROW(argmin_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleArgMinTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleArgMin argmin_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(argmin_node.accept(&tv), std::exception);
+}
+
+TEST(CircleArgMinTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleArgMin argmin_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(argmin_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleAveragePool2D.test.cpp b/compiler/luci/lang/src/Nodes/CircleAveragePool2D.test.cpp
new file mode 100644
index 000000000..fc7265cf0
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleAveragePool2D.test.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleAveragePool2D.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleAveragePool2DTest, constructor_P)
+{
+ luci::CircleAveragePool2D average_pool_2d_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), average_pool_2d_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::AVERAGE_POOL_2D, average_pool_2d_node.opcode());
+
+ ASSERT_EQ(nullptr, average_pool_2d_node.value());
+ ASSERT_EQ(luci::Padding::UNDEFINED, average_pool_2d_node.padding());
+ ASSERT_EQ(1, average_pool_2d_node.filter()->h());
+ ASSERT_EQ(1, average_pool_2d_node.filter()->w());
+ ASSERT_EQ(1, average_pool_2d_node.stride()->h());
+ ASSERT_EQ(1, average_pool_2d_node.stride()->w());
+}
+
+TEST(CircleAveragePool2DTest, input_NEG)
+{
+ luci::CircleAveragePool2D avgpool_node;
+ luci::CircleAveragePool2D node;
+
+ avgpool_node.value(&node);
+ ASSERT_NE(nullptr, avgpool_node.value());
+
+ avgpool_node.value(nullptr);
+ ASSERT_EQ(nullptr, avgpool_node.value());
+
+ avgpool_node.filter()->h(2);
+ avgpool_node.filter()->w(2);
+ avgpool_node.stride()->h(2);
+ avgpool_node.stride()->w(2);
+ ASSERT_NE(1, avgpool_node.filter()->h());
+ ASSERT_NE(1, avgpool_node.filter()->w());
+ ASSERT_NE(1, avgpool_node.stride()->h());
+ ASSERT_NE(1, avgpool_node.stride()->w());
+}
+
+TEST(CircleAveragePool2DTest, arity_NEG)
+{
+ luci::CircleAveragePool2D avgpool_node;
+
+ ASSERT_NO_THROW(avgpool_node.arg(0));
+ ASSERT_THROW(avgpool_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleAveragePool2DTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleAveragePool2D avgpool_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(avgpool_node.accept(&tv), std::exception);
+}
+
+TEST(CircleAveragePool2DTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleAveragePool2D avgpool_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(avgpool_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleBCQFullyConnected.test.cpp b/compiler/luci/lang/src/Nodes/CircleBCQFullyConnected.test.cpp
new file mode 100644
index 000000000..35c9ab95b
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleBCQFullyConnected.test.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleBCQFullyConnected.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleBCQFullyConnectedTest, constructor)
+{
+ luci::CircleBCQFullyConnected bcq_FC_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), bcq_FC_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::BCQ_FULLY_CONNECTED, bcq_FC_node.opcode());
+
+ ASSERT_EQ(nullptr, bcq_FC_node.input());
+ ASSERT_EQ(nullptr, bcq_FC_node.weights_scales());
+ ASSERT_EQ(nullptr, bcq_FC_node.weights_binary());
+ ASSERT_EQ(nullptr, bcq_FC_node.bias());
+ ASSERT_EQ(nullptr, bcq_FC_node.weights_clusters());
+
+ ASSERT_EQ(luci::FusedActFunc::UNDEFINED, bcq_FC_node.fusedActivationFunction());
+ ASSERT_EQ(0, bcq_FC_node.weights_hidden_size());
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleBCQGather.test.cpp b/compiler/luci/lang/src/Nodes/CircleBCQGather.test.cpp
new file mode 100644
index 000000000..c187a9033
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleBCQGather.test.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleBCQGather.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleBCQGatherTest, constructor)
+{
+ luci::CircleBCQGather bcq_gather_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), bcq_gather_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::BCQ_GATHER, bcq_gather_node.opcode());
+
+ ASSERT_EQ(nullptr, bcq_gather_node.input_scales());
+ ASSERT_EQ(nullptr, bcq_gather_node.input_binary());
+ ASSERT_EQ(nullptr, bcq_gather_node.indices());
+ ASSERT_EQ(nullptr, bcq_gather_node.input_clusters());
+
+ ASSERT_EQ(0, bcq_gather_node.axis());
+ ASSERT_EQ(0, bcq_gather_node.input_hidden_size());
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleBatchMatMul.test.cpp b/compiler/luci/lang/src/Nodes/CircleBatchMatMul.test.cpp
new file mode 100644
index 000000000..d7712c8dd
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleBatchMatMul.test.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleBatchMatMul.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleBatchMatMulTest, constructor)
+{
+ luci::CircleBatchMatMul batchmatmul_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), batchmatmul_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::BATCHMATMUL, batchmatmul_node.opcode());
+
+ ASSERT_EQ(nullptr, batchmatmul_node.x());
+ ASSERT_EQ(nullptr, batchmatmul_node.y());
+
+ ASSERT_FALSE(batchmatmul_node.adj_x());
+ ASSERT_FALSE(batchmatmul_node.adj_y());
+}
+
+TEST(CircleBatchMatMulTest, input_NEG)
+{
+ luci::CircleBatchMatMul batchmatmul_node;
+ luci::CircleBatchMatMul node;
+
+ batchmatmul_node.x(&node);
+ batchmatmul_node.y(&node);
+ ASSERT_NE(nullptr, batchmatmul_node.x());
+ ASSERT_NE(nullptr, batchmatmul_node.y());
+
+ batchmatmul_node.x(nullptr);
+ batchmatmul_node.y(nullptr);
+ ASSERT_EQ(nullptr, batchmatmul_node.x());
+ ASSERT_EQ(nullptr, batchmatmul_node.y());
+}
+
+TEST(CircleBatchMatMulTest, arity_NEG)
+{
+ luci::CircleBatchMatMul batchmatmul_node;
+
+ ASSERT_NO_THROW(batchmatmul_node.arg(1));
+ ASSERT_THROW(batchmatmul_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleBatchMatMulTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleBatchMatMul batchmatmul_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(batchmatmul_node.accept(&tv), std::exception);
+}
+
+TEST(CircleBatchMatMulTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleBatchMatMul batchmatmul_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(batchmatmul_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleBatchToSpaceND.test.cpp b/compiler/luci/lang/src/Nodes/CircleBatchToSpaceND.test.cpp
new file mode 100644
index 000000000..0374fe008
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleBatchToSpaceND.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 "luci/IR/Nodes/CircleBatchToSpaceND.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleBatchToSpaceNDTest, constructor)
+{
+ luci::CircleBatchToSpaceND bts_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), bts_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::BATCH_TO_SPACE_ND, bts_node.opcode());
+
+ ASSERT_EQ(nullptr, bts_node.input());
+ ASSERT_EQ(nullptr, bts_node.block_shape());
+ ASSERT_EQ(nullptr, bts_node.crops());
+}
+
+TEST(CircleBatchToSpaceNDTest, input_NEG)
+{
+ luci::CircleBatchToSpaceND bts_node;
+ luci::CircleBatchToSpaceND node;
+
+ bts_node.input(&node);
+ bts_node.block_shape(&node);
+ bts_node.crops(&node);
+ ASSERT_NE(nullptr, bts_node.input());
+ ASSERT_NE(nullptr, bts_node.block_shape());
+ ASSERT_NE(nullptr, bts_node.crops());
+
+ bts_node.input(nullptr);
+ bts_node.block_shape(nullptr);
+ bts_node.crops(nullptr);
+ ASSERT_EQ(nullptr, bts_node.input());
+ ASSERT_EQ(nullptr, bts_node.block_shape());
+ ASSERT_EQ(nullptr, bts_node.crops());
+}
+
+TEST(CircleBatchToSpaceNDTest, arity_NEG)
+{
+ luci::CircleBatchToSpaceND bts_node;
+
+ ASSERT_NO_THROW(bts_node.arg(2));
+ ASSERT_THROW(bts_node.arg(3), std::out_of_range);
+}
+
+TEST(CircleBatchToSpaceNDTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleBatchToSpaceND bts_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(bts_node.accept(&tv), std::exception);
+}
+
+TEST(CircleBatchToSpaceNDTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleBatchToSpaceND bts_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(bts_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleCast.test.cpp b/compiler/luci/lang/src/Nodes/CircleCast.test.cpp
new file mode 100644
index 000000000..b58bf96f9
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleCast.test.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleCast.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleCastTest, constructor)
+{
+ luci::CircleCast cast_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), cast_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::CAST, cast_node.opcode());
+
+ ASSERT_EQ(nullptr, cast_node.x());
+ ASSERT_EQ(loco::DataType::FLOAT32, cast_node.in_data_type());
+ ASSERT_EQ(loco::DataType::FLOAT32, cast_node.out_data_type());
+}
+
+TEST(CircleCastTest, input_NEG)
+{
+ luci::CircleCast cast_node;
+ luci::CircleCast node;
+
+ cast_node.x(&node);
+ ASSERT_NE(nullptr, cast_node.x());
+
+ cast_node.x(nullptr);
+ ASSERT_EQ(nullptr, cast_node.x());
+}
+
+TEST(CircleCastTest, arity_NEG)
+{
+ luci::CircleCast cast_node;
+
+ ASSERT_NO_THROW(cast_node.arg(0));
+ ASSERT_THROW(cast_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleCastTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleCast cast_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(cast_node.accept(&tv), std::exception);
+}
+
+TEST(CircleCastTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleCast cast_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(cast_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleCeil.test.cpp b/compiler/luci/lang/src/Nodes/CircleCeil.test.cpp
new file mode 100644
index 000000000..efac614b2
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleCeil.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleCeil.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleCeilTest, constructor)
+{
+ luci::CircleCeil ceil_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), ceil_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::CEIL, ceil_node.opcode());
+
+ ASSERT_EQ(nullptr, ceil_node.x());
+}
+
+TEST(CircleCeilTest, input_NEG)
+{
+ luci::CircleCeil ceil_node;
+ luci::CircleCeil node;
+
+ ceil_node.x(&node);
+ ASSERT_NE(nullptr, ceil_node.x());
+
+ ceil_node.x(nullptr);
+ ASSERT_EQ(nullptr, ceil_node.x());
+}
+
+TEST(CircleCeilTest, arity_NEG)
+{
+ luci::CircleCeil ceil_node;
+
+ ASSERT_NO_THROW(ceil_node.arg(0));
+ ASSERT_THROW(ceil_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleCeilTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleCeil ceil_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(ceil_node.accept(&tv), std::exception);
+}
+
+TEST(CircleCeilTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleCeil ceil_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(ceil_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleConcatenation.test.cpp b/compiler/luci/lang/src/Nodes/CircleConcatenation.test.cpp
new file mode 100644
index 000000000..9f219a386
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleConcatenation.test.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleConcatenation.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleConcatenationTest, constructor_P)
+{
+ luci::CircleConcatenation concat_node(3);
+
+ ASSERT_EQ(luci::CircleDialect::get(), concat_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::CONCATENATION, concat_node.opcode());
+
+ ASSERT_EQ(3, concat_node.numValues());
+ ASSERT_EQ(nullptr, concat_node.values(0));
+ ASSERT_EQ(nullptr, concat_node.values(1));
+ ASSERT_EQ(nullptr, concat_node.values(2));
+ ASSERT_EQ(luci::FusedActFunc::UNDEFINED, concat_node.fusedActivationFunction());
+}
+
+TEST(CircleConcatenationTest, input_NEG)
+{
+ luci::CircleConcatenation concat_node(2);
+ luci::CircleConcatenation node(2);
+
+ concat_node.values(0, &node);
+ concat_node.values(1, &node);
+ ASSERT_NE(nullptr, concat_node.values(0));
+ ASSERT_NE(nullptr, concat_node.values(1));
+
+ concat_node.values(0, nullptr);
+ concat_node.values(1, nullptr);
+ ASSERT_EQ(nullptr, concat_node.values(0));
+ ASSERT_EQ(nullptr, concat_node.values(1));
+}
+
+TEST(CircleConcatenationTest, arity_NEG)
+{
+ luci::CircleConcatenation concat_node(5);
+
+ ASSERT_NO_THROW(concat_node.arg(4));
+ ASSERT_THROW(concat_node.arg(5), std::out_of_range);
+}
+
+TEST(CircleConcatenationTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleConcatenation concat_node(2);
+
+ TestVisitor tv;
+ ASSERT_THROW(concat_node.accept(&tv), std::exception);
+}
+
+TEST(CircleConcatenationTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleConcatenation concat_node(2);
+
+ TestVisitor tv;
+ ASSERT_THROW(concat_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleConst.cpp b/compiler/luci/lang/src/Nodes/CircleConst.cpp
new file mode 100644
index 000000000..0d02d32dc
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleConst.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleConst.h"
+
+#include <cassert>
+
+namespace luci
+{
+
+template <loco::DataType DT> uint32_t CircleConst::size(void) const
+{
+ assert(dtype() == DT);
+ assert(_data.size() % sizeof(typename loco::DataTypeImpl<DT>::Type) == 0);
+ return _data.size() / sizeof(typename loco::DataTypeImpl<DT>::Type);
+}
+
+template <loco::DataType DT> void CircleConst::size(uint32_t l)
+{
+ assert(dtype() == DT);
+ _data.resize(l * sizeof(typename loco::DataTypeImpl<DT>::Type));
+}
+
+template <loco::DataType DT>
+const typename loco::DataTypeImpl<DT>::Type &CircleConst::at(uint32_t n) const
+{
+ assert(dtype() == DT);
+ assert(n < size<DT>());
+ return *(reinterpret_cast<const typename loco::DataTypeImpl<DT>::Type *>(_data.data()) + n);
+}
+
+template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &CircleConst::at(uint32_t n)
+{
+ assert(dtype() == DT);
+ assert(n < size<DT>());
+ return *(reinterpret_cast<typename loco::DataTypeImpl<DT>::Type *>(_data.data()) + n);
+}
+
+template <loco::DataType DT>
+const typename loco::DataTypeImpl<DT>::Type &CircleConst::scalar(void) const
+{
+ assert(dtype() == DT);
+ return *(reinterpret_cast<const typename loco::DataTypeImpl<DT>::Type *>(_data.data()));
+}
+
+template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &CircleConst::scalar(void)
+{
+ assert(dtype() == DT);
+ return *(reinterpret_cast<typename loco::DataTypeImpl<DT>::Type *>(_data.data()));
+}
+
+#define INSTANTIATE(DT) \
+ template uint32_t CircleConst::size<DT>(void) const; \
+ template void CircleConst::size<DT>(uint32_t); \
+ template const typename loco::DataTypeImpl<DT>::Type &CircleConst::at<DT>(uint32_t) const; \
+ template typename loco::DataTypeImpl<DT>::Type &CircleConst::at<DT>(uint32_t); \
+ template const typename loco::DataTypeImpl<DT>::Type &CircleConst::scalar<DT>(void) const; \
+ template typename loco::DataTypeImpl<DT>::Type &CircleConst::scalar<DT>(void);
+
+INSTANTIATE(loco::DataType::S64);
+INSTANTIATE(loco::DataType::S32);
+INSTANTIATE(loco::DataType::S16);
+INSTANTIATE(loco::DataType::S8);
+INSTANTIATE(loco::DataType::FLOAT32);
+INSTANTIATE(loco::DataType::U8);
+INSTANTIATE(loco::DataType::BOOL);
+
+#undef INSTANTIATE
+
+} // namespace luci
diff --git a/compiler/luci/lang/src/Nodes/CircleConv2D.test.cpp b/compiler/luci/lang/src/Nodes/CircleConv2D.test.cpp
new file mode 100644
index 000000000..7fcc71d6e
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleConv2D.test.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleConv2D.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleConv2Dest, constructor_P)
+{
+ luci::CircleConv2D conv2d_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), conv2d_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::CONV_2D, conv2d_node.opcode());
+
+ ASSERT_EQ(nullptr, conv2d_node.input());
+ ASSERT_EQ(nullptr, conv2d_node.filter());
+ ASSERT_EQ(nullptr, conv2d_node.bias());
+ ASSERT_EQ(luci::Padding::UNDEFINED, conv2d_node.padding());
+ ASSERT_EQ(1, conv2d_node.stride()->h());
+ ASSERT_EQ(1, conv2d_node.stride()->w());
+ ASSERT_EQ(1, conv2d_node.dilation()->h());
+ ASSERT_EQ(1, conv2d_node.dilation()->w());
+ ASSERT_EQ(luci::FusedActFunc::UNDEFINED, conv2d_node.fusedActivationFunction());
+}
+
+TEST(CircleConv2Dest, input_NEG)
+{
+ luci::CircleConv2D conv2d_node;
+ luci::CircleConv2D node;
+
+ conv2d_node.input(&node);
+ conv2d_node.filter(&node);
+ conv2d_node.bias(&node);
+ ASSERT_NE(nullptr, conv2d_node.input());
+ ASSERT_NE(nullptr, conv2d_node.filter());
+ ASSERT_NE(nullptr, conv2d_node.bias());
+
+ conv2d_node.input(nullptr);
+ conv2d_node.filter(nullptr);
+ conv2d_node.bias(nullptr);
+ ASSERT_EQ(nullptr, conv2d_node.input());
+ ASSERT_EQ(nullptr, conv2d_node.filter());
+ ASSERT_EQ(nullptr, conv2d_node.bias());
+
+ conv2d_node.padding(luci::Padding::SAME);
+ ASSERT_NE(luci::Padding::UNDEFINED, conv2d_node.padding());
+
+ conv2d_node.stride()->h(2);
+ conv2d_node.stride()->w(2);
+ ASSERT_EQ(2, conv2d_node.stride()->h());
+ ASSERT_EQ(2, conv2d_node.stride()->w());
+
+ conv2d_node.dilation()->h(2);
+ conv2d_node.dilation()->w(2);
+ ASSERT_EQ(2, conv2d_node.dilation()->h());
+ ASSERT_EQ(2, conv2d_node.dilation()->w());
+
+ conv2d_node.fusedActivationFunction(luci::FusedActFunc::RELU);
+ ASSERT_NE(luci::FusedActFunc::UNDEFINED, conv2d_node.fusedActivationFunction());
+}
+
+TEST(CircleConv2Dest, arity_NEG)
+{
+ luci::CircleConv2D conv2d_node;
+
+ ASSERT_NO_THROW(conv2d_node.arg(2));
+ ASSERT_THROW(conv2d_node.arg(3), std::out_of_range);
+}
+
+TEST(CircleConv2Dest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleConv2D conv2d_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(conv2d_node.accept(&tv), std::exception);
+}
+
+TEST(CircleConv2Dest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleConv2D conv2d_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(conv2d_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleCos.test.cpp b/compiler/luci/lang/src/Nodes/CircleCos.test.cpp
new file mode 100644
index 000000000..55438d37f
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleCos.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleCos.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleCosTest, constructor_P)
+{
+ luci::CircleCos cos_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), cos_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::COS, cos_node.opcode());
+
+ ASSERT_EQ(nullptr, cos_node.x());
+}
+
+TEST(CircleCosTest, input_NEG)
+{
+ luci::CircleCos cos_node;
+ luci::CircleCos node;
+
+ cos_node.x(&node);
+ ASSERT_NE(nullptr, cos_node.x());
+
+ cos_node.x(nullptr);
+ ASSERT_EQ(nullptr, cos_node.x());
+}
+
+TEST(CircleCosTest, arity_NEG)
+{
+ luci::CircleCos cos_node;
+
+ ASSERT_NO_THROW(cos_node.arg(0));
+ ASSERT_THROW(cos_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleCosTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleCos cos_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(cos_node.accept(&tv), std::exception);
+}
+
+TEST(CircleCosTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleCos cos_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(cos_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleCustom.test.cpp b/compiler/luci/lang/src/Nodes/CircleCustom.test.cpp
new file mode 100644
index 000000000..c07268cbf
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleCustom.test.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleCustom.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleCustomTest, constructor)
+{
+ luci::CircleCustom custom_node(2);
+
+ ASSERT_EQ(luci::CircleDialect::get(), custom_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::CUSTOM, custom_node.opcode());
+
+ ASSERT_EQ(2, custom_node.arity());
+ ASSERT_EQ(nullptr, custom_node.arg(0));
+ ASSERT_EQ(nullptr, custom_node.arg(1));
+
+ ASSERT_EQ(2, custom_node.numInputs());
+ ASSERT_EQ(0, custom_node.custom_code().size());
+}
+
+TEST(CircleCustomTest, constructor_NEG)
+{
+ ASSERT_DEBUG_DEATH(luci::CircleCustom{0}, "");
+
+ SUCCEED();
+}
+
+TEST(CircleCustomTest, invalidIndex_NEG)
+{
+ luci::CircleCustom custom_node(2);
+
+ EXPECT_ANY_THROW(custom_node.arg(5));
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleCustomOut.test.cpp b/compiler/luci/lang/src/Nodes/CircleCustomOut.test.cpp
new file mode 100644
index 000000000..8b63f97b1
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleCustomOut.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleCustomOut.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleCustomOutTest, constructor)
+{
+ luci::CircleCustomOut customout_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), customout_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::CIRCLECUSTOMOUT, customout_node.opcode());
+
+ ASSERT_EQ(nullptr, customout_node.input());
+ ASSERT_EQ(-1, customout_node.index());
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleDepthToSpace.test.cpp b/compiler/luci/lang/src/Nodes/CircleDepthToSpace.test.cpp
new file mode 100644
index 000000000..9e3bbb7b7
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleDepthToSpace.test.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleDepthToSpace.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleDepthToSpaceTest, constructor_P)
+{
+ luci::CircleDepthToSpace std_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), std_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::DEPTH_TO_SPACE, std_node.opcode());
+
+ ASSERT_EQ(nullptr, std_node.input());
+ ASSERT_EQ(0, std_node.block_size());
+}
+
+TEST(CircleDepthToSpaceTest, input_NEG)
+{
+ luci::CircleDepthToSpace std_node;
+ luci::CircleDepthToSpace node;
+
+ std_node.input(&node);
+ ASSERT_NE(nullptr, std_node.input());
+
+ std_node.input(nullptr);
+ ASSERT_EQ(nullptr, std_node.input());
+
+ std_node.block_size(2);
+ ASSERT_EQ(2, std_node.block_size());
+}
+
+TEST(CircleDepthToSpaceTest, arity_NEG)
+{
+ luci::CircleDepthToSpace std_node;
+
+ ASSERT_NO_THROW(std_node.arg(0));
+ ASSERT_THROW(std_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleDepthToSpaceTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleDepthToSpace std_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(std_node.accept(&tv), std::exception);
+}
+
+TEST(CircleDepthToSpaceTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleDepthToSpace std_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(std_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleDepthwiseConv2D.test.cpp b/compiler/luci/lang/src/Nodes/CircleDepthwiseConv2D.test.cpp
new file mode 100644
index 000000000..5761775e5
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleDepthwiseConv2D.test.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleDepthwiseConv2D.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleDepthwiseConv2DTest, constructor_P)
+{
+ luci::CircleDepthwiseConv2D dw_conv2d_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), dw_conv2d_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::DEPTHWISE_CONV_2D, dw_conv2d_node.opcode());
+
+ ASSERT_EQ(nullptr, dw_conv2d_node.input());
+ ASSERT_EQ(nullptr, dw_conv2d_node.filter());
+ ASSERT_EQ(nullptr, dw_conv2d_node.bias());
+ ASSERT_EQ(luci::Padding::UNDEFINED, dw_conv2d_node.padding());
+ ASSERT_EQ(1, dw_conv2d_node.stride()->h());
+ ASSERT_EQ(1, dw_conv2d_node.stride()->w());
+ ASSERT_EQ(1, dw_conv2d_node.dilation()->h());
+ ASSERT_EQ(1, dw_conv2d_node.dilation()->w());
+ ASSERT_EQ(0, dw_conv2d_node.depthMultiplier());
+ ASSERT_EQ(luci::FusedActFunc::UNDEFINED, dw_conv2d_node.fusedActivationFunction());
+}
+
+TEST(CircleDepthwiseConv2DTest, input_NEG)
+{
+ luci::CircleDepthwiseConv2D dw_conv2d_node;
+ luci::CircleDepthwiseConv2D node;
+
+ dw_conv2d_node.input(&node);
+ dw_conv2d_node.filter(&node);
+ dw_conv2d_node.bias(&node);
+ ASSERT_NE(nullptr, dw_conv2d_node.input());
+ ASSERT_NE(nullptr, dw_conv2d_node.filter());
+ ASSERT_NE(nullptr, dw_conv2d_node.bias());
+
+ dw_conv2d_node.input(nullptr);
+ dw_conv2d_node.filter(nullptr);
+ dw_conv2d_node.bias(nullptr);
+ ASSERT_EQ(nullptr, dw_conv2d_node.input());
+ ASSERT_EQ(nullptr, dw_conv2d_node.filter());
+ ASSERT_EQ(nullptr, dw_conv2d_node.bias());
+
+ dw_conv2d_node.padding(luci::Padding::SAME);
+ ASSERT_NE(luci::Padding::UNDEFINED, dw_conv2d_node.padding());
+
+ dw_conv2d_node.stride()->h(2);
+ dw_conv2d_node.stride()->w(2);
+ ASSERT_EQ(2, dw_conv2d_node.stride()->h());
+ ASSERT_EQ(2, dw_conv2d_node.stride()->w());
+
+ dw_conv2d_node.dilation()->h(2);
+ dw_conv2d_node.dilation()->w(2);
+ ASSERT_EQ(2, dw_conv2d_node.dilation()->h());
+ ASSERT_EQ(2, dw_conv2d_node.dilation()->w());
+
+ dw_conv2d_node.fusedActivationFunction(luci::FusedActFunc::RELU);
+ ASSERT_NE(luci::FusedActFunc::UNDEFINED, dw_conv2d_node.fusedActivationFunction());
+}
+
+TEST(CircleDepthwiseConv2DTest, arity_NEG)
+{
+ luci::CircleDepthwiseConv2D dw_conv2d_node;
+
+ ASSERT_NO_THROW(dw_conv2d_node.arg(2));
+ ASSERT_THROW(dw_conv2d_node.arg(3), std::out_of_range);
+}
+
+TEST(CircleDepthwiseConv2DTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleDepthwiseConv2D dw_conv2d_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(dw_conv2d_node.accept(&tv), std::exception);
+}
+
+TEST(CircleDepthwiseConv2DTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleDepthwiseConv2D dw_conv2d_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(dw_conv2d_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleDequantize.test.cpp b/compiler/luci/lang/src/Nodes/CircleDequantize.test.cpp
new file mode 100644
index 000000000..c3a132c60
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleDequantize.test.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleDequantize.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+TEST(CircleDequantizeTest, constructor)
+{
+ luci::CircleDequantize dequant_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), dequant_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::DEQUANTIZE, dequant_node.opcode());
+
+ ASSERT_EQ(nullptr, dequant_node.input());
+}
+
+TEST(CircleDequantizeTest, common_NEG)
+{
+ luci::CircleDequantize dequant_node;
+
+ dequant_node.name("name");
+ ASSERT_EQ("name", dequant_node.name());
+
+ auto q = std::make_unique<luci::CircleQuantParam>();
+ dequant_node.quantparam(std::move(q));
+ ASSERT_NE(nullptr, dequant_node.quantparam());
+
+ ASSERT_EQ(luci::ShapeStatus::UNDEFINED, dequant_node.shape_status());
+ dequant_node.shape_status(luci::ShapeStatus::NOSHAPE);
+ ASSERT_NE(luci::ShapeStatus::UNDEFINED, dequant_node.shape_status());
+}
+
+TEST(CircleDequantizeTest, input_NEG)
+{
+ luci::CircleDequantize dequant_node;
+ luci::CircleDequantize node;
+
+ dequant_node.input(&node);
+ ASSERT_NE(nullptr, dequant_node.input());
+
+ dequant_node.input(nullptr);
+ ASSERT_EQ(nullptr, dequant_node.input());
+}
+
+TEST(CircleDequantizeTest, arity_NEG)
+{
+ luci::CircleDequantize dequant_node;
+
+ ASSERT_NO_THROW(dequant_node.arg(0));
+ ASSERT_THROW(dequant_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleDequantizeTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleDequantize dequant_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(dequant_node.accept(&tv), std::exception);
+}
+
+TEST(CircleDequantizeTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleDequantize dequant_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(dequant_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleDiv.test.cpp b/compiler/luci/lang/src/Nodes/CircleDiv.test.cpp
new file mode 100644
index 000000000..d0b632ca9
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleDiv.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleDiv.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleDivTest, constructor_P)
+{
+ luci::CircleDiv div_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), div_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::DIV, div_node.opcode());
+
+ ASSERT_EQ(nullptr, div_node.x());
+ ASSERT_EQ(nullptr, div_node.y());
+}
+
+TEST(CircleDivTest, input_NEG)
+{
+ luci::CircleDiv div_node;
+ luci::CircleDiv node;
+
+ div_node.x(&node);
+ div_node.y(&node);
+ ASSERT_NE(nullptr, div_node.x());
+ ASSERT_NE(nullptr, div_node.y());
+
+ div_node.x(nullptr);
+ div_node.y(nullptr);
+ ASSERT_EQ(nullptr, div_node.x());
+ ASSERT_EQ(nullptr, div_node.y());
+}
+
+TEST(CircleDivTest, arity_NEG)
+{
+ luci::CircleDiv div_node;
+
+ ASSERT_NO_THROW(div_node.arg(1));
+ ASSERT_THROW(div_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleDivTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleDiv div_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(div_node.accept(&tv), std::exception);
+}
+
+TEST(CircleDivTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleDiv div_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(div_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleElu.test.cpp b/compiler/luci/lang/src/Nodes/CircleElu.test.cpp
new file mode 100644
index 000000000..2a044d75b
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleElu.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleElu.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleEluTest, constructor_P)
+{
+ luci::CircleElu elu_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), elu_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::ELU, elu_node.opcode());
+
+ ASSERT_EQ(nullptr, elu_node.features());
+}
+
+TEST(CircleEluTest, input_NEG)
+{
+ luci::CircleElu elu_node;
+ luci::CircleElu node;
+
+ elu_node.features(&node);
+ ASSERT_NE(nullptr, elu_node.features());
+
+ elu_node.features(nullptr);
+ ASSERT_EQ(nullptr, elu_node.features());
+}
+
+TEST(CircleEluTest, arity_NEG)
+{
+ luci::CircleElu elu_node;
+
+ ASSERT_NO_THROW(elu_node.arg(0));
+ ASSERT_THROW(elu_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleEluTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleElu elu_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(elu_node.accept(&tv), std::exception);
+}
+
+TEST(CircleEluTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleElu elu_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(elu_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleEqual.test.cpp b/compiler/luci/lang/src/Nodes/CircleEqual.test.cpp
new file mode 100644
index 000000000..2ae15290d
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleEqual.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleEqual.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleEqualTest, constructor_P)
+{
+ luci::CircleEqual equ_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), equ_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::EQUAL, equ_node.opcode());
+
+ ASSERT_EQ(nullptr, equ_node.x());
+ ASSERT_EQ(nullptr, equ_node.y());
+}
+
+TEST(CircleEqualTest, input_NEG)
+{
+ luci::CircleEqual equ_node;
+ luci::CircleEqual node;
+
+ equ_node.x(&node);
+ equ_node.y(&node);
+ ASSERT_NE(nullptr, equ_node.x());
+ ASSERT_NE(nullptr, equ_node.y());
+
+ equ_node.x(nullptr);
+ equ_node.y(nullptr);
+ ASSERT_EQ(nullptr, equ_node.x());
+ ASSERT_EQ(nullptr, equ_node.y());
+}
+
+TEST(CircleEqualTest, arity_NEG)
+{
+ luci::CircleEqual equ_node;
+
+ ASSERT_NO_THROW(equ_node.arg(1));
+ ASSERT_THROW(equ_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleEqualTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleEqual equ_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(equ_node.accept(&tv), std::exception);
+}
+
+TEST(CircleEqualTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleEqual equ_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(equ_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleExp.test.cpp b/compiler/luci/lang/src/Nodes/CircleExp.test.cpp
new file mode 100644
index 000000000..5ca90e90f
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleExp.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleExp.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleExpTest, constructor)
+{
+ luci::CircleExp exp_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), exp_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::EXP, exp_node.opcode());
+
+ ASSERT_EQ(nullptr, exp_node.x());
+}
+
+TEST(CircleExpTest, input_NEG)
+{
+ luci::CircleExp exp_node;
+ luci::CircleExp node;
+
+ exp_node.x(&node);
+ ASSERT_NE(nullptr, exp_node.x());
+
+ exp_node.x(nullptr);
+ ASSERT_EQ(nullptr, exp_node.x());
+}
+
+TEST(CircleExpTest, arity_NEG)
+{
+ luci::CircleExp exp_node;
+
+ ASSERT_NO_THROW(exp_node.arg(0));
+ ASSERT_THROW(exp_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleExpTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleExp exp_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(exp_node.accept(&tv), std::exception);
+}
+
+TEST(CircleExpTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleExp exp_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(exp_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleExpandDims.test.cpp b/compiler/luci/lang/src/Nodes/CircleExpandDims.test.cpp
new file mode 100644
index 000000000..754ef01f9
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleExpandDims.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleExpandDims.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleExpandDimsTest, constructor_P)
+{
+ luci::CircleExpandDims expand_dims;
+
+ ASSERT_EQ(luci::CircleDialect::get(), expand_dims.dialect());
+ ASSERT_EQ(luci::CircleOpcode::EXPAND_DIMS, expand_dims.opcode());
+
+ ASSERT_EQ(nullptr, expand_dims.input());
+ ASSERT_EQ(nullptr, expand_dims.axis());
+}
+
+TEST(CircleExpandDimsTest, input_NEG)
+{
+ luci::CircleExpandDims expand_dims;
+ luci::CircleExpandDims node;
+
+ expand_dims.input(&node);
+ expand_dims.axis(&node);
+ ASSERT_NE(nullptr, expand_dims.input());
+ ASSERT_NE(nullptr, expand_dims.axis());
+
+ expand_dims.input(nullptr);
+ expand_dims.axis(nullptr);
+ ASSERT_EQ(nullptr, expand_dims.input());
+ ASSERT_EQ(nullptr, expand_dims.axis());
+}
+
+TEST(CircleExpandDimsTest, arity_NEG)
+{
+ luci::CircleExpandDims expand_dims;
+
+ ASSERT_NO_THROW(expand_dims.arg(1));
+ ASSERT_THROW(expand_dims.arg(2), std::out_of_range);
+}
+
+TEST(CircleExpandDimsTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleExpandDims expand_dims;
+
+ TestVisitor tv;
+ ASSERT_THROW(expand_dims.accept(&tv), std::exception);
+}
+
+TEST(CircleExpandDimsTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleExpandDims expand_dims;
+
+ TestVisitor tv;
+ ASSERT_THROW(expand_dims.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleFill.test.cpp b/compiler/luci/lang/src/Nodes/CircleFill.test.cpp
new file mode 100644
index 000000000..4555da1cb
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleFill.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleFill.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleFillTest, constructor_P)
+{
+ luci::CircleFill fill;
+
+ ASSERT_EQ(fill.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(fill.opcode(), luci::CircleOpcode::FILL);
+
+ ASSERT_EQ(nullptr, fill.dims());
+ ASSERT_EQ(nullptr, fill.value());
+}
+
+TEST(CircleFillTest, input_NEG)
+{
+ luci::CircleFill fill_node;
+ luci::CircleFill node;
+
+ fill_node.dims(&node);
+ fill_node.value(&node);
+ ASSERT_NE(nullptr, fill_node.dims());
+ ASSERT_NE(nullptr, fill_node.value());
+
+ fill_node.dims(nullptr);
+ fill_node.value(nullptr);
+ ASSERT_EQ(nullptr, fill_node.dims());
+ ASSERT_EQ(nullptr, fill_node.value());
+}
+
+TEST(CircleFillTest, arity_NEG)
+{
+ luci::CircleFill fill_node;
+
+ ASSERT_NO_THROW(fill_node.arg(1));
+ ASSERT_THROW(fill_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleFillTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleFill fill_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(fill_node.accept(&tv), std::exception);
+}
+
+TEST(CircleFillTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleFill fill_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(fill_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleFloor.test.cpp b/compiler/luci/lang/src/Nodes/CircleFloor.test.cpp
new file mode 100644
index 000000000..38d38a0da
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleFloor.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleFloor.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleFloorTest, constructor)
+{
+ luci::CircleFloor floor_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), floor_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::FLOOR, floor_node.opcode());
+
+ ASSERT_EQ(nullptr, floor_node.x());
+}
+
+TEST(CircleFloorTest, input_NEG)
+{
+ luci::CircleFloor floor_node;
+ luci::CircleFloor node;
+
+ floor_node.x(&node);
+ ASSERT_NE(nullptr, floor_node.x());
+
+ floor_node.x(nullptr);
+ ASSERT_EQ(nullptr, floor_node.x());
+}
+
+TEST(CircleFloorTest, arity_NEG)
+{
+ luci::CircleFloor floor_node;
+
+ ASSERT_NO_THROW(floor_node.arg(0));
+ ASSERT_THROW(floor_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleFloorTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleFloor floor_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(floor_node.accept(&tv), std::exception);
+}
+
+TEST(CircleFloorTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleFloor floor_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(floor_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleFloorDiv.test.cpp b/compiler/luci/lang/src/Nodes/CircleFloorDiv.test.cpp
new file mode 100644
index 000000000..6c52eee73
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleFloorDiv.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleFloorDiv.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleFloorDivTest, constructor_P)
+{
+ luci::CircleFloorDiv floordiv_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), floordiv_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::FLOOR_DIV, floordiv_node.opcode());
+
+ ASSERT_EQ(nullptr, floordiv_node.x());
+ ASSERT_EQ(nullptr, floordiv_node.y());
+}
+
+TEST(CircleFloorDivTest, input_NEG)
+{
+ luci::CircleFloorDiv floordiv_node;
+ luci::CircleFloorDiv node;
+
+ floordiv_node.x(&node);
+ floordiv_node.y(&node);
+ ASSERT_NE(nullptr, floordiv_node.x());
+ ASSERT_NE(nullptr, floordiv_node.y());
+
+ floordiv_node.x(nullptr);
+ floordiv_node.y(nullptr);
+ ASSERT_EQ(nullptr, floordiv_node.x());
+ ASSERT_EQ(nullptr, floordiv_node.y());
+}
+
+TEST(CircleFloorDivTest, arity_NEG)
+{
+ luci::CircleFloorDiv floordiv_node;
+
+ ASSERT_NO_THROW(floordiv_node.arg(1));
+ ASSERT_THROW(floordiv_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleFloorDivTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleFloorDiv floordiv_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(floordiv_node.accept(&tv), std::exception);
+}
+
+TEST(CircleFloorDivTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleFloorDiv floordiv_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(floordiv_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleFloorMod.test.cpp b/compiler/luci/lang/src/Nodes/CircleFloorMod.test.cpp
new file mode 100644
index 000000000..c3fa187f2
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleFloorMod.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleFloorMod.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleFloorModTest, constructor)
+{
+ luci::CircleFloorMod floormod_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), floormod_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::FLOOR_MOD, floormod_node.opcode());
+
+ ASSERT_EQ(nullptr, floormod_node.x());
+ ASSERT_EQ(nullptr, floormod_node.y());
+}
+
+TEST(CircleFloorModTest, input_NEG)
+{
+ luci::CircleFloorMod floormod_node;
+ luci::CircleFloorMod node;
+
+ floormod_node.x(&node);
+ floormod_node.y(&node);
+ ASSERT_NE(nullptr, floormod_node.x());
+ ASSERT_NE(nullptr, floormod_node.y());
+
+ floormod_node.x(nullptr);
+ floormod_node.y(nullptr);
+ ASSERT_EQ(nullptr, floormod_node.x());
+ ASSERT_EQ(nullptr, floormod_node.y());
+}
+
+TEST(CircleFloorModTest, arity_NEG)
+{
+ luci::CircleFloorMod floormod_node;
+
+ ASSERT_NO_THROW(floormod_node.arg(1));
+ ASSERT_THROW(floormod_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleFloorModTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleFloorMod floormod_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(floormod_node.accept(&tv), std::exception);
+}
+
+TEST(CircleFloorModTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleFloorMod floormod_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(floormod_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleFullyConnected.test.cpp b/compiler/luci/lang/src/Nodes/CircleFullyConnected.test.cpp
new file mode 100644
index 000000000..bb0e3c51b
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleFullyConnected.test.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleFullyConnected.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleFullyConnectedTest, constructor)
+{
+ luci::CircleFullyConnected fc_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), fc_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::FULLY_CONNECTED, fc_node.opcode());
+
+ ASSERT_EQ(nullptr, fc_node.input());
+ ASSERT_EQ(nullptr, fc_node.weights());
+ ASSERT_EQ(nullptr, fc_node.bias());
+ ASSERT_EQ(luci::FusedActFunc::UNDEFINED, fc_node.fusedActivationFunction());
+}
+
+TEST(CircleFullyConnectedTest, input_NEG)
+{
+ luci::CircleFullyConnected fc_node;
+ luci::CircleFullyConnected node;
+
+ fc_node.input(&node);
+ fc_node.weights(&node);
+ fc_node.bias(&node);
+ ASSERT_NE(nullptr, fc_node.input());
+ ASSERT_NE(nullptr, fc_node.weights());
+ ASSERT_NE(nullptr, fc_node.bias());
+
+ fc_node.input(nullptr);
+ fc_node.weights(nullptr);
+ fc_node.bias(nullptr);
+ ASSERT_EQ(nullptr, fc_node.input());
+ ASSERT_EQ(nullptr, fc_node.weights());
+ ASSERT_EQ(nullptr, fc_node.bias());
+
+ fc_node.fusedActivationFunction(luci::FusedActFunc::RELU);
+ ASSERT_NE(luci::FusedActFunc::UNDEFINED, fc_node.fusedActivationFunction());
+}
+
+TEST(CircleFullyConnectedTest, arity_NEG)
+{
+ luci::CircleFullyConnected fc_node;
+
+ ASSERT_NO_THROW(fc_node.arg(2));
+ ASSERT_THROW(fc_node.arg(3), std::out_of_range);
+}
+
+TEST(CircleFullyConnectedTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleFullyConnected fc_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(fc_node.accept(&tv), std::exception);
+}
+
+TEST(CircleFullyConnectedTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleFullyConnected fc_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(fc_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleGather.test.cpp b/compiler/luci/lang/src/Nodes/CircleGather.test.cpp
new file mode 100644
index 000000000..5194d6bdd
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleGather.test.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleGather.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleGatherTest, constructor)
+{
+ luci::CircleGather gather_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), gather_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::GATHER, gather_node.opcode());
+
+ ASSERT_EQ(nullptr, gather_node.params());
+ ASSERT_EQ(nullptr, gather_node.indices());
+ ASSERT_EQ(0, gather_node.axis());
+}
+
+TEST(CircleGatherTest, input_NEG)
+{
+ luci::CircleGather gather_node;
+ luci::CircleGather node;
+
+ gather_node.params(&node);
+ gather_node.indices(&node);
+ ASSERT_NE(nullptr, gather_node.params());
+ ASSERT_NE(nullptr, gather_node.indices());
+
+ gather_node.params(nullptr);
+ gather_node.indices(nullptr);
+ ASSERT_EQ(nullptr, gather_node.params());
+ ASSERT_EQ(nullptr, gather_node.indices());
+
+ gather_node.axis(1);
+ ASSERT_NE(0, gather_node.axis());
+}
+
+TEST(CircleGatherTest, arity_NEG)
+{
+ luci::CircleGather gather_node;
+
+ ASSERT_NO_THROW(gather_node.arg(1));
+ ASSERT_THROW(gather_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleGatherTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleGather gather_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(gather_node.accept(&tv), std::exception);
+}
+
+TEST(CircleGatherTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleGather gather_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(gather_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleGatherNd.test.cpp b/compiler/luci/lang/src/Nodes/CircleGatherNd.test.cpp
new file mode 100644
index 000000000..1402e6e45
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleGatherNd.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleGatherNd.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleGatherNdTest, constructor)
+{
+ luci::CircleGatherNd gather_nd_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), gather_nd_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::GATHER_ND, gather_nd_node.opcode());
+
+ ASSERT_EQ(nullptr, gather_nd_node.params());
+ ASSERT_EQ(nullptr, gather_nd_node.indices());
+}
+
+TEST(CircleGatherNdTest, input_NEG)
+{
+ luci::CircleGatherNd gather_nd_node;
+ luci::CircleGatherNd node;
+
+ gather_nd_node.params(&node);
+ gather_nd_node.indices(&node);
+ ASSERT_NE(nullptr, gather_nd_node.params());
+ ASSERT_NE(nullptr, gather_nd_node.indices());
+
+ gather_nd_node.params(nullptr);
+ gather_nd_node.indices(nullptr);
+ ASSERT_EQ(nullptr, gather_nd_node.params());
+ ASSERT_EQ(nullptr, gather_nd_node.indices());
+}
+
+TEST(CircleGatherNdTest, arity_NEG)
+{
+ luci::CircleGatherNd gather_nd_node;
+
+ ASSERT_NO_THROW(gather_nd_node.arg(1));
+ ASSERT_THROW(gather_nd_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleGatherNdTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleGatherNd gather_nd_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(gather_nd_node.accept(&tv), std::exception);
+}
+
+TEST(CircleGatherNdTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleGatherNd gather_nd_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(gather_nd_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleGreater.test.cpp b/compiler/luci/lang/src/Nodes/CircleGreater.test.cpp
new file mode 100644
index 000000000..9a2b5f9f9
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleGreater.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleGreater.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleGreaterTest, constructor_P)
+{
+ luci::CircleGreater greater_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), greater_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::GREATER, greater_node.opcode());
+
+ ASSERT_EQ(nullptr, greater_node.x());
+ ASSERT_EQ(nullptr, greater_node.y());
+}
+
+TEST(CircleGreaterTest, input_NEG)
+{
+ luci::CircleGreater greater_node;
+ luci::CircleGreater node;
+
+ greater_node.x(&node);
+ greater_node.y(&node);
+ ASSERT_NE(nullptr, greater_node.x());
+ ASSERT_NE(nullptr, greater_node.y());
+
+ greater_node.x(nullptr);
+ greater_node.y(nullptr);
+ ASSERT_EQ(nullptr, greater_node.x());
+ ASSERT_EQ(nullptr, greater_node.y());
+}
+
+TEST(CircleGreaterTest, arity_NEG)
+{
+ luci::CircleGreater greater_node;
+
+ ASSERT_NO_THROW(greater_node.arg(1));
+ ASSERT_THROW(greater_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleGreaterTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleGreater greater_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(greater_node.accept(&tv), std::exception);
+}
+
+TEST(CircleGreaterTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleGreater greater_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(greater_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleGreaterEqual.test.cpp b/compiler/luci/lang/src/Nodes/CircleGreaterEqual.test.cpp
new file mode 100644
index 000000000..51c22b707
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleGreaterEqual.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleGreaterEqual.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleGreaterEqualTest, constructor_P)
+{
+ luci::CircleGreaterEqual greater_equal_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), greater_equal_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::GREATER_EQUAL, greater_equal_node.opcode());
+
+ ASSERT_EQ(nullptr, greater_equal_node.x());
+ ASSERT_EQ(nullptr, greater_equal_node.y());
+}
+
+TEST(CircleGreaterEqualTest, input_NEG)
+{
+ luci::CircleGreaterEqual greater_equal_node;
+ luci::CircleGreaterEqual node;
+
+ greater_equal_node.x(&node);
+ greater_equal_node.y(&node);
+ ASSERT_NE(nullptr, greater_equal_node.x());
+ ASSERT_NE(nullptr, greater_equal_node.y());
+
+ greater_equal_node.x(nullptr);
+ greater_equal_node.y(nullptr);
+ ASSERT_EQ(nullptr, greater_equal_node.x());
+ ASSERT_EQ(nullptr, greater_equal_node.y());
+}
+
+TEST(CircleGreaterEqualTest, arity_NEG)
+{
+ luci::CircleGreaterEqual greater_equal_node;
+
+ ASSERT_NO_THROW(greater_equal_node.arg(1));
+ ASSERT_THROW(greater_equal_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleGreaterEqualTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleGreaterEqual greater_equal_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(greater_equal_node.accept(&tv), std::exception);
+}
+
+TEST(CircleGreaterEqualTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleGreaterEqual greater_equal_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(greater_equal_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleIf.test.cpp b/compiler/luci/lang/src/Nodes/CircleIf.test.cpp
new file mode 100644
index 000000000..35f28e9ac
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleIf.test.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleIf.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleIfTest, constructor)
+{
+ luci::CircleIf if_node(2, 2);
+
+ ASSERT_EQ(luci::CircleDialect::get(), if_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::IF, if_node.opcode());
+
+ ASSERT_EQ(2, if_node.input_count());
+ ASSERT_EQ(2, if_node.output_count());
+
+ ASSERT_EQ(nullptr, if_node.input(0));
+ ASSERT_EQ(nullptr, if_node.input(1));
+
+ ASSERT_EQ(-1, if_node.then_branch());
+ ASSERT_EQ(-1, if_node.else_branch());
+}
+
+TEST(CircleIfTestDeath, invalid_arity_NEG)
+{
+ ASSERT_DEBUG_DEATH(luci::CircleIf very_long_name_if_node(0, 1), "");
+
+ SUCCEED();
+}
+
+TEST(CircleIfTestDeath, invalid_output_count_NEG)
+{
+ ASSERT_DEBUG_DEATH(luci::CircleIf if_node(2, 0), "");
+
+ SUCCEED();
+}
+
+TEST(CircleIfTestDeath, invalid_input_get_index_NEG)
+{
+ luci::CircleIf if_node(2, 2);
+
+ EXPECT_ANY_THROW(if_node.input(100));
+}
+
+TEST(CircleIfTestDeath, invalid_input_set_index_NEG)
+{
+ luci::CircleIf if_node(2, 2);
+
+ EXPECT_ANY_THROW(if_node.input(100, nullptr));
+}
+
+TEST(CircleIfTestDeath, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleIf if_node(2, 2);
+
+ TestVisitor tv;
+ ASSERT_THROW(if_node.accept(&tv), std::exception);
+}
+
+TEST(CircleIfTestDeath, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleIf if_node(2, 2);
+
+ TestVisitor tv;
+ ASSERT_THROW(if_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleIfOut.test.cpp b/compiler/luci/lang/src/Nodes/CircleIfOut.test.cpp
new file mode 100644
index 000000000..5154b6b28
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleIfOut.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleIfOut.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleIfOutTest, constructor)
+{
+ luci::CircleIfOut ifout_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), ifout_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::CIRCLEIFOUT, ifout_node.opcode());
+
+ ASSERT_EQ(nullptr, ifout_node.input());
+ ASSERT_EQ(-1, ifout_node.index());
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleInput.cpp b/compiler/luci/lang/src/Nodes/CircleInput.cpp
new file mode 100644
index 000000000..dcf54f3b0
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleInput.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleInput.h"
+
+#include <cassert>
+#include <limits>
+
+namespace luci
+{
+
+void CircleInput::index(const loco::GraphInputIndex &index)
+{
+ // CircleInput internally stores "GraphInputIndex" as int64_t
+ _index = static_cast<int64_t>(index);
+}
+
+loco::GraphInputIndex CircleInput::index(void) const
+{
+ assert(_index >= std::numeric_limits<loco::GraphInputIndex>::min());
+ assert(_index <= std::numeric_limits<loco::GraphInputIndex>::max());
+ return static_cast<loco::GraphInputIndex>(_index);
+}
+
+} // namespace luci
diff --git a/compiler/luci/lang/src/Nodes/CircleInstanceNorm.test.cpp b/compiler/luci/lang/src/Nodes/CircleInstanceNorm.test.cpp
new file mode 100644
index 000000000..88a5b8c6c
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleInstanceNorm.test.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleInstanceNorm.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleInstanceNormTest, constructor)
+{
+ luci::CircleInstanceNorm instance_norm;
+
+ ASSERT_EQ(luci::CircleDialect::get(), instance_norm.dialect());
+ ASSERT_EQ(luci::CircleOpcode::INSTANCE_NORM, instance_norm.opcode());
+
+ ASSERT_EQ(nullptr, instance_norm.input());
+ ASSERT_EQ(nullptr, instance_norm.gamma());
+ ASSERT_EQ(nullptr, instance_norm.beta());
+ ASSERT_FLOAT_EQ(instance_norm.epsilon(), 1e-05);
+ ASSERT_EQ(luci::FusedActFunc::UNDEFINED, instance_norm.fusedActivationFunction());
+}
+
+TEST(CircleInstanceNormTest, input_NEG)
+{
+ luci::CircleInstanceNorm instance_norm;
+ luci::CircleInstanceNorm node;
+
+ instance_norm.input(&node);
+ instance_norm.gamma(&node);
+ instance_norm.beta(&node);
+ ASSERT_NE(nullptr, instance_norm.input());
+ ASSERT_NE(nullptr, instance_norm.gamma());
+ ASSERT_NE(nullptr, instance_norm.beta());
+
+ instance_norm.input(nullptr);
+ instance_norm.gamma(nullptr);
+ instance_norm.beta(nullptr);
+ ASSERT_EQ(nullptr, instance_norm.input());
+ ASSERT_EQ(nullptr, instance_norm.gamma());
+ ASSERT_EQ(nullptr, instance_norm.beta());
+
+ instance_norm.fusedActivationFunction(luci::FusedActFunc::RELU);
+ ASSERT_NE(luci::FusedActFunc::UNDEFINED, instance_norm.fusedActivationFunction());
+}
+
+TEST(CircleInstanceNormTest, arity_NEG)
+{
+ luci::CircleInstanceNorm instance_norm;
+
+ ASSERT_NO_THROW(instance_norm.arg(2));
+ ASSERT_THROW(instance_norm.arg(3), std::out_of_range);
+}
+
+TEST(CircleInstanceNormTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleInstanceNorm instance_norm;
+
+ TestVisitor tv;
+ ASSERT_THROW(instance_norm.accept(&tv), std::exception);
+}
+
+TEST(CircleInstanceNormTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleInstanceNorm instance_norm;
+
+ TestVisitor tv;
+ ASSERT_THROW(instance_norm.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleL2Pool2D.test.cpp b/compiler/luci/lang/src/Nodes/CircleL2Pool2D.test.cpp
new file mode 100644
index 000000000..cb779efa5
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleL2Pool2D.test.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleL2Pool2D.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleL2Pool2DTest, constructor)
+{
+ luci::CircleL2Pool2D l2pool2d_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), l2pool2d_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::L2_POOL_2D, l2pool2d_node.opcode());
+
+ ASSERT_EQ(nullptr, l2pool2d_node.value());
+ ASSERT_EQ(1, l2pool2d_node.filter()->h());
+ ASSERT_EQ(1, l2pool2d_node.filter()->w());
+ ASSERT_EQ(1, l2pool2d_node.stride()->h());
+ ASSERT_EQ(1, l2pool2d_node.stride()->w());
+ ASSERT_EQ(luci::FusedActFunc::UNDEFINED, l2pool2d_node.fusedActivationFunction());
+}
+
+TEST(CircleL2Pool2DTest, input_NEG)
+{
+ luci::CircleL2Pool2D l2pool2d_node;
+ luci::CircleL2Pool2D node;
+
+ l2pool2d_node.value(&node);
+ ASSERT_NE(nullptr, l2pool2d_node.value());
+
+ l2pool2d_node.value(nullptr);
+ ASSERT_EQ(nullptr, l2pool2d_node.value());
+
+ l2pool2d_node.stride()->h(2);
+ l2pool2d_node.stride()->w(2);
+ ASSERT_EQ(2, l2pool2d_node.stride()->h());
+ ASSERT_EQ(2, l2pool2d_node.stride()->w());
+
+ l2pool2d_node.filter()->h(2);
+ l2pool2d_node.filter()->w(2);
+ ASSERT_EQ(2, l2pool2d_node.filter()->h());
+ ASSERT_EQ(2, l2pool2d_node.filter()->w());
+
+ l2pool2d_node.fusedActivationFunction(luci::FusedActFunc::RELU);
+ ASSERT_NE(luci::FusedActFunc::UNDEFINED, l2pool2d_node.fusedActivationFunction());
+}
+
+TEST(CircleL2Pool2DTest, arity_NEG)
+{
+ luci::CircleL2Pool2D l2pool2d_node;
+
+ ASSERT_NO_THROW(l2pool2d_node.arg(0));
+ ASSERT_THROW(l2pool2d_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleL2Pool2DTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleL2Pool2D l2pool2d_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(l2pool2d_node.accept(&tv), std::exception);
+}
+
+TEST(CircleL2Pool2DTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleL2Pool2D l2pool2d_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(l2pool2d_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleLeakyRelu.test.cpp b/compiler/luci/lang/src/Nodes/CircleLeakyRelu.test.cpp
new file mode 100644
index 000000000..bacb444da
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleLeakyRelu.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleLeakyRelu.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleLeakyReluTest, constructor)
+{
+ luci::CircleLeakyRelu relu_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), relu_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::LEAKY_RELU, relu_node.opcode());
+
+ ASSERT_EQ(nullptr, relu_node.features());
+
+ ASSERT_EQ(0.2f, relu_node.alpha());
+}
+
+TEST(CircleLeakyReluTest, input_NEG)
+{
+ luci::CircleLeakyRelu relu_node;
+ luci::CircleLeakyRelu node;
+
+ relu_node.features(&node);
+ ASSERT_NE(nullptr, relu_node.features());
+
+ relu_node.features(nullptr);
+ ASSERT_EQ(nullptr, relu_node.features());
+
+ relu_node.alpha(1.2f);
+ ASSERT_NE(0.2f, relu_node.alpha());
+}
+
+TEST(CircleLeakyReluTest, arity_NEG)
+{
+ luci::CircleLeakyRelu relu_node;
+
+ ASSERT_NO_THROW(relu_node.arg(0));
+ ASSERT_THROW(relu_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleLeakyReluTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleLeakyRelu relu_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(relu_node.accept(&tv), std::exception);
+}
+
+TEST(CircleLeakyReluTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleLeakyRelu relu_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(relu_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleLess.test.cpp b/compiler/luci/lang/src/Nodes/CircleLess.test.cpp
new file mode 100644
index 000000000..ec454dfb5
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleLess.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleLess.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleLessTest, constructor_P)
+{
+ luci::CircleLess less_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), less_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::LESS, less_node.opcode());
+
+ ASSERT_EQ(nullptr, less_node.x());
+ ASSERT_EQ(nullptr, less_node.y());
+}
+
+TEST(CircleLessTest, input_NEG)
+{
+ luci::CircleLess less_node;
+ luci::CircleLess node;
+
+ less_node.x(&node);
+ less_node.y(&node);
+ ASSERT_NE(nullptr, less_node.x());
+ ASSERT_NE(nullptr, less_node.y());
+
+ less_node.x(nullptr);
+ less_node.y(nullptr);
+ ASSERT_EQ(nullptr, less_node.x());
+ ASSERT_EQ(nullptr, less_node.y());
+}
+
+TEST(CircleLessTest, arity_NEG)
+{
+ luci::CircleLess less_node;
+
+ ASSERT_NO_THROW(less_node.arg(1));
+ ASSERT_THROW(less_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleLessTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleLess less_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(less_node.accept(&tv), std::exception);
+}
+
+TEST(CircleLessTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleLess less_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(less_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleLessEqual.test.cpp b/compiler/luci/lang/src/Nodes/CircleLessEqual.test.cpp
new file mode 100644
index 000000000..baa9202ae
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleLessEqual.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleLessEqual.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleLessEqualTest, constructor_P)
+{
+ luci::CircleLessEqual less_equal_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), less_equal_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::LESS_EQUAL, less_equal_node.opcode());
+
+ ASSERT_EQ(nullptr, less_equal_node.x());
+ ASSERT_EQ(nullptr, less_equal_node.y());
+}
+
+TEST(CircleLessEqualTest, input_NEG)
+{
+ luci::CircleLessEqual less_equal_node;
+ luci::CircleLessEqual node;
+
+ less_equal_node.x(&node);
+ less_equal_node.y(&node);
+ ASSERT_NE(nullptr, less_equal_node.x());
+ ASSERT_NE(nullptr, less_equal_node.y());
+
+ less_equal_node.x(nullptr);
+ less_equal_node.y(nullptr);
+ ASSERT_EQ(nullptr, less_equal_node.x());
+ ASSERT_EQ(nullptr, less_equal_node.y());
+}
+
+TEST(CircleLessEqualTest, arity_NEG)
+{
+ luci::CircleLessEqual less_equal_node;
+
+ ASSERT_NO_THROW(less_equal_node.arg(1));
+ ASSERT_THROW(less_equal_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleLessEqualTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleLessEqual less_equal_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(less_equal_node.accept(&tv), std::exception);
+}
+
+TEST(CircleLessEqualTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleLessEqual less_equal_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(less_equal_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleLocalResponseNormalization.test.cpp b/compiler/luci/lang/src/Nodes/CircleLocalResponseNormalization.test.cpp
new file mode 100644
index 000000000..1b1bf67bd
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleLocalResponseNormalization.test.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleLocalResponseNormalization.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleLocalResponseNormalizationTest, constructor_P)
+{
+ luci::CircleLocalResponseNormalization local_response_normalization_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), local_response_normalization_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::LOCAL_RESPONSE_NORMALIZATION,
+ local_response_normalization_node.opcode());
+
+ ASSERT_EQ(nullptr, local_response_normalization_node.input());
+ ASSERT_EQ(5, local_response_normalization_node.radius());
+ ASSERT_EQ(1.0f, local_response_normalization_node.bias());
+ ASSERT_EQ(1.0f, local_response_normalization_node.alpha());
+ ASSERT_EQ(0.5f, local_response_normalization_node.beta());
+}
+
+TEST(CircleLocalResponseNormalizationTest, input_NEG)
+{
+ luci::CircleLocalResponseNormalization local_response_normalization_node;
+ luci::CircleLocalResponseNormalization node;
+
+ local_response_normalization_node.input(&node);
+ ASSERT_NE(nullptr, local_response_normalization_node.input());
+
+ local_response_normalization_node.input(nullptr);
+ ASSERT_EQ(nullptr, local_response_normalization_node.input());
+
+ local_response_normalization_node.radius(100);
+ local_response_normalization_node.bias(100.0f);
+ local_response_normalization_node.alpha(100.0f);
+ local_response_normalization_node.beta(100.0f);
+ ASSERT_NE(5, local_response_normalization_node.radius());
+ ASSERT_NE(1.0f, local_response_normalization_node.bias());
+ ASSERT_NE(1.0f, local_response_normalization_node.alpha());
+ ASSERT_NE(0.5f, local_response_normalization_node.beta());
+}
+
+TEST(CircleLocalResponseNormalizationTest, arity_NEG)
+{
+ luci::CircleLocalResponseNormalization local_response_normalization_node;
+
+ ASSERT_NO_THROW(local_response_normalization_node.arg(0));
+ ASSERT_THROW(local_response_normalization_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleLocalResponseNormalizationTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleLocalResponseNormalization local_response_normalization_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(local_response_normalization_node.accept(&tv), std::exception);
+}
+
+TEST(CircleLocalResponseNormalizationTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleLocalResponseNormalization local_response_normalization_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(local_response_normalization_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleLog.test.cpp b/compiler/luci/lang/src/Nodes/CircleLog.test.cpp
new file mode 100644
index 000000000..0bb9ee76e
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleLog.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleLog.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleLogTest, constructor)
+{
+ luci::CircleLog log_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), log_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::LOG, log_node.opcode());
+
+ ASSERT_EQ(nullptr, log_node.x());
+}
+
+TEST(CircleLogTest, input_NEG)
+{
+ luci::CircleLog log_node;
+ luci::CircleLog node;
+
+ log_node.x(&node);
+ ASSERT_NE(nullptr, log_node.x());
+
+ log_node.x(nullptr);
+ ASSERT_EQ(nullptr, log_node.x());
+}
+
+TEST(CircleLogTest, arity_NEG)
+{
+ luci::CircleLog log_node;
+
+ ASSERT_NO_THROW(log_node.arg(0));
+ ASSERT_THROW(log_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleLogTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleLog log_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(log_node.accept(&tv), std::exception);
+}
+
+TEST(CircleLogTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleLog log_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(log_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleLogSoftmax.test.cpp b/compiler/luci/lang/src/Nodes/CircleLogSoftmax.test.cpp
new file mode 100644
index 000000000..70977ae4f
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleLogSoftmax.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleLogSoftmax.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleLogSoftmaxTest, constructor)
+{
+ luci::CircleLogSoftmax log_softmax_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), log_softmax_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::LOG_SOFTMAX, log_softmax_node.opcode());
+
+ ASSERT_EQ(nullptr, log_softmax_node.logits());
+}
+
+TEST(CircleLogSoftmaxTest, input_NEG)
+{
+ luci::CircleLogSoftmax log_softmax_node;
+ luci::CircleLogSoftmax node;
+
+ log_softmax_node.logits(&node);
+ ASSERT_NE(nullptr, log_softmax_node.logits());
+
+ log_softmax_node.logits(nullptr);
+ ASSERT_EQ(nullptr, log_softmax_node.logits());
+}
+
+TEST(CircleLogSoftmaxTest, arity_NEG)
+{
+ luci::CircleLogSoftmax log_softmax_node;
+
+ ASSERT_NO_THROW(log_softmax_node.arg(0));
+ ASSERT_THROW(log_softmax_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleLogSoftmaxTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleLogSoftmax log_softmax_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(log_softmax_node.accept(&tv), std::exception);
+}
+
+TEST(CircleLogSoftmaxTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleLogSoftmax log_softmax_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(log_softmax_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleLogicalAnd.test.cpp b/compiler/luci/lang/src/Nodes/CircleLogicalAnd.test.cpp
new file mode 100644
index 000000000..db378f022
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleLogicalAnd.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleLogicalAnd.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleLogicalAndTest, constructor_P)
+{
+ luci::CircleLogicalAnd and_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), and_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::LOGICAL_AND, and_node.opcode());
+
+ ASSERT_EQ(nullptr, and_node.x());
+ ASSERT_EQ(nullptr, and_node.y());
+}
+
+TEST(CircleLogicalAndTest, input_NEG)
+{
+ luci::CircleLogicalAnd and_node;
+ luci::CircleLogicalAnd node;
+
+ and_node.x(&node);
+ and_node.y(&node);
+ ASSERT_NE(nullptr, and_node.x());
+ ASSERT_NE(nullptr, and_node.y());
+
+ and_node.x(nullptr);
+ and_node.y(nullptr);
+ ASSERT_EQ(nullptr, and_node.x());
+ ASSERT_EQ(nullptr, and_node.y());
+}
+
+TEST(CircleLogicalAndTest, arity_NEG)
+{
+ luci::CircleLogicalAnd and_node;
+
+ ASSERT_NO_THROW(and_node.arg(1));
+ ASSERT_THROW(and_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleLogicalAndTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleLogicalAnd and_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(and_node.accept(&tv), std::exception);
+}
+
+TEST(CircleLogicalAndTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleLogicalAnd and_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(and_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleLogicalNot.test.cpp b/compiler/luci/lang/src/Nodes/CircleLogicalNot.test.cpp
new file mode 100644
index 000000000..0c2c02938
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleLogicalNot.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleLogicalNot.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleLogicalNotTest, constructor_P)
+{
+ luci::CircleLogicalNot not_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), not_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::LOGICAL_NOT, not_node.opcode());
+
+ ASSERT_EQ(nullptr, not_node.x());
+}
+
+TEST(CircleLogicalNotTest, input_NEG)
+{
+ luci::CircleLogicalNot not_node;
+ luci::CircleLogicalNot node;
+
+ not_node.x(&node);
+ ASSERT_NE(nullptr, not_node.x());
+
+ not_node.x(nullptr);
+ ASSERT_EQ(nullptr, not_node.x());
+}
+
+TEST(CircleLogicalNotTest, arity_NEG)
+{
+ luci::CircleLogicalNot not_node;
+
+ ASSERT_NO_THROW(not_node.arg(0));
+ ASSERT_THROW(not_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleLogicalNotTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleLogicalNot not_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(not_node.accept(&tv), std::exception);
+}
+
+TEST(CircleLogicalNotTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleLogicalNot not_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(not_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleLogicalOr.test.cpp b/compiler/luci/lang/src/Nodes/CircleLogicalOr.test.cpp
new file mode 100644
index 000000000..a08b863c7
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleLogicalOr.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleLogicalOr.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleLogicalOrTest, constructor_P)
+{
+ luci::CircleLogicalOr or_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), or_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::LOGICAL_OR, or_node.opcode());
+
+ ASSERT_EQ(nullptr, or_node.x());
+ ASSERT_EQ(nullptr, or_node.y());
+}
+
+TEST(CircleLogicalOrTest, input_NEG)
+{
+ luci::CircleLogicalOr or_node;
+ luci::CircleLogicalOr node;
+
+ or_node.x(&node);
+ or_node.y(&node);
+ ASSERT_NE(nullptr, or_node.x());
+ ASSERT_NE(nullptr, or_node.y());
+
+ or_node.x(nullptr);
+ or_node.y(nullptr);
+ ASSERT_EQ(nullptr, or_node.x());
+ ASSERT_EQ(nullptr, or_node.y());
+}
+
+TEST(CircleLogicalOrTest, arity_NEG)
+{
+ luci::CircleLogicalOr or_node;
+
+ ASSERT_NO_THROW(or_node.arg(1));
+ ASSERT_THROW(or_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleLogicalOrTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleLogicalOr or_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(or_node.accept(&tv), std::exception);
+}
+
+TEST(CircleLogicalOrTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleLogicalOr or_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(or_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleLogistic.test.cpp b/compiler/luci/lang/src/Nodes/CircleLogistic.test.cpp
new file mode 100644
index 000000000..18efd869d
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleLogistic.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleLogistic.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleLogisticTest, constructor)
+{
+ luci::CircleLogistic logistic_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), logistic_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::LOGISTIC, logistic_node.opcode());
+
+ ASSERT_EQ(nullptr, logistic_node.x());
+}
+
+TEST(CircleLogisticTest, input_NEG)
+{
+ luci::CircleLogistic logistic_node;
+ luci::CircleLogistic node;
+
+ logistic_node.x(&node);
+ ASSERT_NE(nullptr, logistic_node.x());
+
+ logistic_node.x(nullptr);
+ ASSERT_EQ(nullptr, logistic_node.x());
+}
+
+TEST(CircleLogisticTest, arity_NEG)
+{
+ luci::CircleLogistic logistic_node;
+
+ ASSERT_NO_THROW(logistic_node.arg(0));
+ ASSERT_THROW(logistic_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleLogisticTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleLogistic logistic_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(logistic_node.accept(&tv), std::exception);
+}
+
+TEST(CircleLogisticTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleLogistic logistic_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(logistic_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleMatrixDiag.test.cpp b/compiler/luci/lang/src/Nodes/CircleMatrixDiag.test.cpp
new file mode 100644
index 000000000..9209cf1a4
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleMatrixDiag.test.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleMatrixDiag.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleMatrixDiagTest, constructor_P)
+{
+ luci::CircleMatrixDiag matrix_diag_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), matrix_diag_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::MATRIX_DIAG, matrix_diag_node.opcode());
+
+ ASSERT_EQ(nullptr, matrix_diag_node.diagonal());
+}
+
+TEST(CircleMatrixDiagTest, input_NEG)
+{
+ luci::CircleMatrixDiag matrix_diag_node;
+ luci::CircleMatrixDiag node;
+
+ matrix_diag_node.diagonal(&node);
+
+ ASSERT_NE(nullptr, matrix_diag_node.diagonal());
+
+ matrix_diag_node.diagonal(nullptr);
+
+ ASSERT_EQ(nullptr, matrix_diag_node.diagonal());
+}
+
+TEST(CircleMatrixDiagTest, arity_NEG)
+{
+ luci::CircleMatrixDiag matrix_diag_node;
+
+ ASSERT_NO_THROW(matrix_diag_node.arg(0));
+ ASSERT_THROW(matrix_diag_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleMatrixDiagTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleMatrixDiag matrix_diag_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(matrix_diag_node.accept(&tv), std::exception);
+}
+
+TEST(CircleMatrixDiagTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleMatrixDiag matrix_diag_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(matrix_diag_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleMatrixSetDiag.test.cpp b/compiler/luci/lang/src/Nodes/CircleMatrixSetDiag.test.cpp
new file mode 100644
index 000000000..9dea9852e
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleMatrixSetDiag.test.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleMatrixSetDiag.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleMatrixSetDiagTest, constructor_P)
+{
+ luci::CircleMatrixSetDiag matrix_set_diag_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), matrix_set_diag_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::MATRIX_SET_DIAG, matrix_set_diag_node.opcode());
+
+ ASSERT_EQ(nullptr, matrix_set_diag_node.input());
+ ASSERT_EQ(nullptr, matrix_set_diag_node.diagonal());
+}
+
+TEST(CircleMatrixSetDiagTest, input_NEG)
+{
+ luci::CircleMatrixSetDiag matrix_set_diag_node;
+ luci::CircleMatrixSetDiag node;
+
+ matrix_set_diag_node.input(&node);
+ matrix_set_diag_node.diagonal(&node);
+
+ ASSERT_NE(nullptr, matrix_set_diag_node.input());
+ ASSERT_NE(nullptr, matrix_set_diag_node.diagonal());
+
+ matrix_set_diag_node.input(nullptr);
+ matrix_set_diag_node.diagonal(nullptr);
+
+ ASSERT_EQ(nullptr, matrix_set_diag_node.input());
+ ASSERT_EQ(nullptr, matrix_set_diag_node.diagonal());
+}
+
+TEST(CircleMatrixSetDiagTest, arity_NEG)
+{
+ luci::CircleMatrixSetDiag matrix_set_diag_node;
+
+ ASSERT_NO_THROW(matrix_set_diag_node.arg(0));
+ ASSERT_NO_THROW(matrix_set_diag_node.arg(1));
+ ASSERT_THROW(matrix_set_diag_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleMatrixSetDiagTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleMatrixSetDiag matrix_set_diag_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(matrix_set_diag_node.accept(&tv), std::exception);
+}
+
+TEST(CircleMatrixSetDiagTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleMatrixSetDiag matrix_set_diag_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(matrix_set_diag_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleMaxPool2D.test.cpp b/compiler/luci/lang/src/Nodes/CircleMaxPool2D.test.cpp
new file mode 100644
index 000000000..cb6c016e3
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleMaxPool2D.test.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleMaxPool2D.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleMaxPool2DTest, constructor_P)
+{
+ luci::CircleMaxPool2D maxpool2d_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), maxpool2d_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::MAX_POOL_2D, maxpool2d_node.opcode());
+
+ ASSERT_EQ(nullptr, maxpool2d_node.value());
+ ASSERT_EQ(luci::Padding::UNDEFINED, maxpool2d_node.padding());
+ ASSERT_EQ(1, maxpool2d_node.filter()->h());
+ ASSERT_EQ(1, maxpool2d_node.filter()->w());
+ ASSERT_EQ(1, maxpool2d_node.stride()->h());
+ ASSERT_EQ(1, maxpool2d_node.stride()->w());
+}
+
+TEST(CircleMaxPool2DTest, input_NEG)
+{
+ luci::CircleMaxPool2D maxpool2d_node;
+ luci::CircleMaxPool2D node;
+
+ maxpool2d_node.value(&node);
+ ASSERT_NE(nullptr, maxpool2d_node.value());
+
+ maxpool2d_node.value(nullptr);
+ ASSERT_EQ(nullptr, maxpool2d_node.value());
+
+ maxpool2d_node.filter()->h(2);
+ maxpool2d_node.filter()->w(2);
+ maxpool2d_node.stride()->h(2);
+ maxpool2d_node.stride()->w(2);
+ ASSERT_NE(1, maxpool2d_node.filter()->h());
+ ASSERT_NE(1, maxpool2d_node.filter()->w());
+ ASSERT_NE(1, maxpool2d_node.stride()->h());
+ ASSERT_NE(1, maxpool2d_node.stride()->w());
+}
+
+TEST(CircleMaxPool2DTest, arity_NEG)
+{
+ luci::CircleMaxPool2D maxpool2d_node;
+
+ ASSERT_NO_THROW(maxpool2d_node.arg(0));
+ ASSERT_THROW(maxpool2d_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleMaxPool2DTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleMaxPool2D maxpool2d_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(maxpool2d_node.accept(&tv), std::exception);
+}
+
+TEST(CircleMaxPool2DTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleMaxPool2D maxpool2d_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(maxpool2d_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleMaximum.test.cpp b/compiler/luci/lang/src/Nodes/CircleMaximum.test.cpp
new file mode 100644
index 000000000..3fc6f1114
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleMaximum.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleMaximum.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleMaximumTest, constructor_P)
+{
+ luci::CircleMaximum max_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), max_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::MAXIMUM, max_node.opcode());
+
+ ASSERT_EQ(nullptr, max_node.x());
+ ASSERT_EQ(nullptr, max_node.y());
+}
+
+TEST(CircleMaximumTest, input_NEG)
+{
+ luci::CircleMaximum max_node;
+ luci::CircleMaximum node;
+
+ max_node.x(&node);
+ max_node.y(&node);
+ ASSERT_NE(nullptr, max_node.x());
+ ASSERT_NE(nullptr, max_node.y());
+
+ max_node.x(nullptr);
+ max_node.y(nullptr);
+ ASSERT_EQ(nullptr, max_node.x());
+ ASSERT_EQ(nullptr, max_node.y());
+}
+
+TEST(CircleMaximumTest, arity_NEG)
+{
+ luci::CircleMaximum max_node;
+
+ ASSERT_NO_THROW(max_node.arg(1));
+ ASSERT_THROW(max_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleMaximumTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleMaximum max_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(max_node.accept(&tv), std::exception);
+}
+
+TEST(CircleMaximumTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleMaximum max_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(max_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleMean.test.cpp b/compiler/luci/lang/src/Nodes/CircleMean.test.cpp
new file mode 100644
index 000000000..743063968
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleMean.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 "luci/IR/Nodes/CircleMean.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleMeanTest, constructor)
+{
+ luci::CircleMean mean_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), mean_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::MEAN, mean_node.opcode());
+
+ ASSERT_EQ(nullptr, mean_node.input());
+ ASSERT_EQ(nullptr, mean_node.reduction_indices());
+
+ ASSERT_FALSE(mean_node.keep_dims());
+}
+
+TEST(CircleMeanTest, input_NEG)
+{
+ luci::CircleMean mean_node;
+ luci::CircleMean node;
+
+ mean_node.input(&node);
+ mean_node.reduction_indices(&node);
+ ASSERT_NE(nullptr, mean_node.input());
+ ASSERT_NE(nullptr, mean_node.reduction_indices());
+
+ mean_node.input(nullptr);
+ mean_node.reduction_indices(nullptr);
+ ASSERT_EQ(nullptr, mean_node.input());
+ ASSERT_EQ(nullptr, mean_node.reduction_indices());
+
+ mean_node.keep_dims(true);
+ ASSERT_TRUE(mean_node.keep_dims());
+}
+
+TEST(CircleMeanTest, arity_NEG)
+{
+ luci::CircleMean mean_node;
+
+ ASSERT_NO_THROW(mean_node.arg(1));
+ ASSERT_THROW(mean_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleMeanTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleMean mean_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(mean_node.accept(&tv), std::exception);
+}
+
+TEST(CircleMeanTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleMean mean_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(mean_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleMinimum.test.cpp b/compiler/luci/lang/src/Nodes/CircleMinimum.test.cpp
new file mode 100644
index 000000000..19fe69fb7
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleMinimum.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleMinimum.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleMinimumTest, constructor_P)
+{
+ luci::CircleMinimum min_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), min_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::MINIMUM, min_node.opcode());
+
+ ASSERT_EQ(nullptr, min_node.x());
+ ASSERT_EQ(nullptr, min_node.y());
+}
+
+TEST(CircleMinimumTest, input_NEG)
+{
+ luci::CircleMinimum min_node;
+ luci::CircleMinimum node;
+
+ min_node.x(&node);
+ min_node.y(&node);
+ ASSERT_NE(nullptr, min_node.x());
+ ASSERT_NE(nullptr, min_node.y());
+
+ min_node.x(nullptr);
+ min_node.y(nullptr);
+ ASSERT_EQ(nullptr, min_node.x());
+ ASSERT_EQ(nullptr, min_node.y());
+}
+
+TEST(CircleMinimumTest, arity_NEG)
+{
+ luci::CircleMinimum min_node;
+
+ ASSERT_NO_THROW(min_node.arg(1));
+ ASSERT_THROW(min_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleMinimumTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleMinimum min_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(min_node.accept(&tv), std::exception);
+}
+
+TEST(CircleMinimumTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleMinimum min_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(min_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleMirrorPad.test.cpp b/compiler/luci/lang/src/Nodes/CircleMirrorPad.test.cpp
new file mode 100644
index 000000000..9ba6bf58a
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleMirrorPad.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 "luci/IR/Nodes/CircleMirrorPad.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleMirrorPadTest, constructor_P)
+{
+ luci::CircleMirrorPad pad_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), pad_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::MIRROR_PAD, pad_node.opcode());
+
+ ASSERT_EQ(nullptr, pad_node.input());
+ ASSERT_EQ(nullptr, pad_node.paddings());
+
+ ASSERT_EQ(luci::MirrorPadMode::REFLECT, pad_node.mode());
+}
+
+TEST(CircleMirrorPadTest, input_NEG)
+{
+ luci::CircleMirrorPad pad_node;
+ luci::CircleMirrorPad node;
+
+ pad_node.input(&node);
+ pad_node.paddings(&node);
+ ASSERT_NE(nullptr, pad_node.input());
+ ASSERT_NE(nullptr, pad_node.paddings());
+
+ pad_node.input(nullptr);
+ pad_node.paddings(nullptr);
+ ASSERT_EQ(nullptr, pad_node.input());
+ ASSERT_EQ(nullptr, pad_node.paddings());
+
+ pad_node.mode(luci::MirrorPadMode::SYMMETRIC);
+ ASSERT_NE(luci::MirrorPadMode::REFLECT, pad_node.mode());
+}
+
+TEST(CircleMirrorPadTest, arity_NEG)
+{
+ luci::CircleMirrorPad pad_node;
+
+ ASSERT_NO_THROW(pad_node.arg(1));
+ ASSERT_THROW(pad_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleMirrorPadTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleMirrorPad pad_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(pad_node.accept(&tv), std::exception);
+}
+
+TEST(CircleMirrorPadTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleMirrorPad pad_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(pad_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleMul.test.cpp b/compiler/luci/lang/src/Nodes/CircleMul.test.cpp
new file mode 100644
index 000000000..3c26d08ca
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleMul.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleMul.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleMulTest, constructor_P)
+{
+ luci::CircleMul mul_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), mul_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::MUL, mul_node.opcode());
+
+ ASSERT_EQ(nullptr, mul_node.x());
+ ASSERT_EQ(nullptr, mul_node.y());
+}
+
+TEST(CircleMulTest, input_NEG)
+{
+ luci::CircleMul mul_node;
+ luci::CircleMul node;
+
+ mul_node.x(&node);
+ mul_node.y(&node);
+ ASSERT_NE(nullptr, mul_node.x());
+ ASSERT_NE(nullptr, mul_node.y());
+
+ mul_node.x(nullptr);
+ mul_node.y(nullptr);
+ ASSERT_EQ(nullptr, mul_node.x());
+ ASSERT_EQ(nullptr, mul_node.y());
+}
+
+TEST(CircleMulTest, arity_NEG)
+{
+ luci::CircleMul mul_node;
+
+ ASSERT_NO_THROW(mul_node.arg(1));
+ ASSERT_THROW(mul_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleMulTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleMul mul_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(mul_node.accept(&tv), std::exception);
+}
+
+TEST(CircleMulTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleMul mul_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(mul_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleNeg.test.cpp b/compiler/luci/lang/src/Nodes/CircleNeg.test.cpp
new file mode 100644
index 000000000..4bcfa48a6
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleNeg.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleNeg.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleNegTest, constructor)
+{
+ luci::CircleNeg neg_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), neg_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::NEG, neg_node.opcode());
+
+ ASSERT_EQ(nullptr, neg_node.x());
+}
+
+TEST(CircleNegTest, input_NEG)
+{
+ luci::CircleNeg neg_node;
+ luci::CircleNeg node;
+
+ neg_node.x(&node);
+ ASSERT_NE(nullptr, neg_node.x());
+
+ neg_node.x(nullptr);
+ ASSERT_EQ(nullptr, neg_node.x());
+}
+
+TEST(CircleNegTest, arity_NEG)
+{
+ luci::CircleNeg neg_node;
+
+ ASSERT_NO_THROW(neg_node.arg(0));
+ ASSERT_THROW(neg_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleNegTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleNeg neg_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(neg_node.accept(&tv), std::exception);
+}
+
+TEST(CircleNegTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleNeg neg_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(neg_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleNonMaxSuppressionV4.test.cpp b/compiler/luci/lang/src/Nodes/CircleNonMaxSuppressionV4.test.cpp
new file mode 100644
index 000000000..b25ce4d6d
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleNonMaxSuppressionV4.test.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleNonMaxSuppressionV4.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleNonMaxSuppressionV4Test, constructor)
+{
+ luci::CircleNonMaxSuppressionV4 nmsv4_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), nmsv4_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::NON_MAX_SUPPRESSION_V4, nmsv4_node.opcode());
+
+ ASSERT_EQ(nullptr, nmsv4_node.boxes());
+ ASSERT_EQ(nullptr, nmsv4_node.scores());
+ ASSERT_EQ(nullptr, nmsv4_node.max_output_size());
+ ASSERT_EQ(nullptr, nmsv4_node.iou_threshold());
+ ASSERT_EQ(nullptr, nmsv4_node.score_threshold());
+}
+
+TEST(CircleNonMaxSuppressionV4Test, input_NEG)
+{
+ luci::CircleNonMaxSuppressionV4 nmsv4_node;
+ luci::CircleNonMaxSuppressionV4 node;
+
+ nmsv4_node.boxes(&node);
+ nmsv4_node.scores(&node);
+ nmsv4_node.max_output_size(&node);
+ nmsv4_node.iou_threshold(&node);
+ nmsv4_node.score_threshold(&node);
+ ASSERT_NE(nullptr, nmsv4_node.boxes());
+ ASSERT_NE(nullptr, nmsv4_node.scores());
+ ASSERT_NE(nullptr, nmsv4_node.max_output_size());
+ ASSERT_NE(nullptr, nmsv4_node.iou_threshold());
+ ASSERT_NE(nullptr, nmsv4_node.score_threshold());
+
+ nmsv4_node.boxes(nullptr);
+ nmsv4_node.scores(nullptr);
+ nmsv4_node.max_output_size(nullptr);
+ nmsv4_node.iou_threshold(nullptr);
+ nmsv4_node.score_threshold(nullptr);
+ ASSERT_EQ(nullptr, nmsv4_node.boxes());
+ ASSERT_EQ(nullptr, nmsv4_node.scores());
+ ASSERT_EQ(nullptr, nmsv4_node.max_output_size());
+ ASSERT_EQ(nullptr, nmsv4_node.iou_threshold());
+ ASSERT_EQ(nullptr, nmsv4_node.score_threshold());
+}
+
+TEST(CircleNonMaxSuppressionV4Test, arity_NEG)
+{
+ luci::CircleNonMaxSuppressionV4 nmsv4_node;
+
+ ASSERT_NO_THROW(nmsv4_node.arg(4));
+ ASSERT_THROW(nmsv4_node.arg(5), std::out_of_range);
+}
+
+TEST(CircleNonMaxSuppressionV4Test, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleNonMaxSuppressionV4 nmsv4_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(nmsv4_node.accept(&tv), std::exception);
+}
+
+TEST(CircleNonMaxSuppressionV4Test, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleNonMaxSuppressionV4 nmsv4_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(nmsv4_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleNonMaxSuppressionV4Out.test.cpp b/compiler/luci/lang/src/Nodes/CircleNonMaxSuppressionV4Out.test.cpp
new file mode 100644
index 000000000..c6cef4e91
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleNonMaxSuppressionV4Out.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleNonMaxSuppressionV4Out.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleNonMaxSuppressionV4OutTest, constructor)
+{
+ luci::CircleNonMaxSuppressionV4Out vout_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), vout_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::CIRCLENONMAXSUPPRESSIONV4OUT, vout_node.opcode());
+
+ ASSERT_EQ(nullptr, vout_node.input());
+ ASSERT_EQ(-1, vout_node.index());
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleNonMaxSuppressionV5.test.cpp b/compiler/luci/lang/src/Nodes/CircleNonMaxSuppressionV5.test.cpp
new file mode 100644
index 000000000..ceb74e3df
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleNonMaxSuppressionV5.test.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleNonMaxSuppressionV5.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleNonMaxSuppressionV5Test, constructor)
+{
+ luci::CircleNonMaxSuppressionV5 nmsv5_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), nmsv5_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::NON_MAX_SUPPRESSION_V5, nmsv5_node.opcode());
+
+ ASSERT_EQ(nullptr, nmsv5_node.boxes());
+ ASSERT_EQ(nullptr, nmsv5_node.scores());
+ ASSERT_EQ(nullptr, nmsv5_node.max_output_size());
+ ASSERT_EQ(nullptr, nmsv5_node.iou_threshold());
+ ASSERT_EQ(nullptr, nmsv5_node.score_threshold());
+ ASSERT_EQ(nullptr, nmsv5_node.soft_nms_sigma());
+}
+
+TEST(CircleNonMaxSuppressionV5Test, input_NEG)
+{
+ luci::CircleNonMaxSuppressionV5 nmsv5_node;
+ luci::CircleNonMaxSuppressionV5 node;
+
+ nmsv5_node.boxes(&node);
+ nmsv5_node.scores(&node);
+ nmsv5_node.max_output_size(&node);
+ nmsv5_node.iou_threshold(&node);
+ nmsv5_node.score_threshold(&node);
+ nmsv5_node.soft_nms_sigma(&node);
+ ASSERT_NE(nullptr, nmsv5_node.boxes());
+ ASSERT_NE(nullptr, nmsv5_node.scores());
+ ASSERT_NE(nullptr, nmsv5_node.max_output_size());
+ ASSERT_NE(nullptr, nmsv5_node.iou_threshold());
+ ASSERT_NE(nullptr, nmsv5_node.score_threshold());
+ ASSERT_NE(nullptr, nmsv5_node.soft_nms_sigma());
+
+ nmsv5_node.boxes(nullptr);
+ nmsv5_node.scores(nullptr);
+ nmsv5_node.max_output_size(nullptr);
+ nmsv5_node.iou_threshold(nullptr);
+ nmsv5_node.score_threshold(nullptr);
+ nmsv5_node.soft_nms_sigma(nullptr);
+ ASSERT_EQ(nullptr, nmsv5_node.boxes());
+ ASSERT_EQ(nullptr, nmsv5_node.scores());
+ ASSERT_EQ(nullptr, nmsv5_node.max_output_size());
+ ASSERT_EQ(nullptr, nmsv5_node.iou_threshold());
+ ASSERT_EQ(nullptr, nmsv5_node.score_threshold());
+ ASSERT_EQ(nullptr, nmsv5_node.soft_nms_sigma());
+}
+
+TEST(CircleNonMaxSuppressionV5Test, arity_NEG)
+{
+ luci::CircleNonMaxSuppressionV5 nmsv5_node;
+
+ ASSERT_NO_THROW(nmsv5_node.arg(5));
+ ASSERT_THROW(nmsv5_node.arg(6), std::out_of_range);
+}
+
+TEST(CircleNonMaxSuppressionV5Test, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleNonMaxSuppressionV5 nmsv5_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(nmsv5_node.accept(&tv), std::exception);
+}
+
+TEST(CircleNonMaxSuppressionV5Test, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleNonMaxSuppressionV5 nmsv5_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(nmsv5_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleNonMaxSuppressionV5Out.test.cpp b/compiler/luci/lang/src/Nodes/CircleNonMaxSuppressionV5Out.test.cpp
new file mode 100644
index 000000000..7b427ea03
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleNonMaxSuppressionV5Out.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleNonMaxSuppressionV5Out.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleNonMaxSuppressionV5OutTest, constructor)
+{
+ luci::CircleNonMaxSuppressionV5Out vout_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), vout_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::CIRCLENONMAXSUPPRESSIONV5OUT, vout_node.opcode());
+
+ ASSERT_EQ(nullptr, vout_node.input());
+ ASSERT_EQ(-1, vout_node.index());
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleNotEqual.test.cpp b/compiler/luci/lang/src/Nodes/CircleNotEqual.test.cpp
new file mode 100644
index 000000000..e464a7b96
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleNotEqual.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleNotEqual.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleNotEqualTest, constructor_P)
+{
+ luci::CircleNotEqual not_equal_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), not_equal_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::NOT_EQUAL, not_equal_node.opcode());
+
+ ASSERT_EQ(nullptr, not_equal_node.x());
+ ASSERT_EQ(nullptr, not_equal_node.y());
+}
+
+TEST(CircleNotEqualTest, input_NEG)
+{
+ luci::CircleNotEqual not_equal_node;
+ luci::CircleNotEqual node;
+
+ not_equal_node.x(&node);
+ not_equal_node.y(&node);
+ ASSERT_NE(nullptr, not_equal_node.x());
+ ASSERT_NE(nullptr, not_equal_node.y());
+
+ not_equal_node.x(nullptr);
+ not_equal_node.y(nullptr);
+ ASSERT_EQ(nullptr, not_equal_node.x());
+ ASSERT_EQ(nullptr, not_equal_node.y());
+}
+
+TEST(CircleNotEqualTest, arity_NEG)
+{
+ luci::CircleNotEqual not_equal_node;
+
+ ASSERT_NO_THROW(not_equal_node.arg(1));
+ ASSERT_THROW(not_equal_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleNotEqualTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleNotEqual not_equal_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(not_equal_node.accept(&tv), std::exception);
+}
+
+TEST(CircleNotEqualTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleNotEqual not_equal_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(not_equal_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleOneHot.test.cpp b/compiler/luci/lang/src/Nodes/CircleOneHot.test.cpp
new file mode 100644
index 000000000..18e1045cc
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleOneHot.test.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleOneHot.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleOneHotTest, constructor)
+{
+ luci::CircleOneHot one_hot_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), one_hot_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::ONE_HOT, one_hot_node.opcode());
+
+ ASSERT_EQ(nullptr, one_hot_node.indices());
+ ASSERT_EQ(nullptr, one_hot_node.depth());
+ ASSERT_EQ(nullptr, one_hot_node.on_value());
+ ASSERT_EQ(nullptr, one_hot_node.off_value());
+ ASSERT_EQ(-1, one_hot_node.axis());
+}
+
+TEST(CircleOneHotTest, input_NEG)
+{
+ luci::CircleOneHot one_hot_node;
+ luci::CircleOneHot node;
+
+ one_hot_node.indices(&node);
+ one_hot_node.depth(&node);
+ one_hot_node.on_value(&node);
+ one_hot_node.off_value(&node);
+ ASSERT_NE(nullptr, one_hot_node.indices());
+ ASSERT_NE(nullptr, one_hot_node.depth());
+ ASSERT_NE(nullptr, one_hot_node.on_value());
+ ASSERT_NE(nullptr, one_hot_node.off_value());
+
+ one_hot_node.indices(nullptr);
+ one_hot_node.depth(nullptr);
+ one_hot_node.on_value(nullptr);
+ one_hot_node.off_value(nullptr);
+ ASSERT_EQ(nullptr, one_hot_node.indices());
+ ASSERT_EQ(nullptr, one_hot_node.depth());
+ ASSERT_EQ(nullptr, one_hot_node.on_value());
+ ASSERT_EQ(nullptr, one_hot_node.off_value());
+
+ one_hot_node.axis(1);
+ ASSERT_NE(-1, one_hot_node.axis());
+}
+
+TEST(CircleOneHotTest, arity_NEG)
+{
+ luci::CircleOneHot one_hot_node;
+
+ ASSERT_NO_THROW(one_hot_node.arg(3));
+ ASSERT_THROW(one_hot_node.arg(4), std::out_of_range);
+}
+
+TEST(CircleOneHotTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleOneHot one_hot_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(one_hot_node.accept(&tv), std::exception);
+}
+
+TEST(CircleOneHotTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleOneHot one_hot_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(one_hot_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleOutput.cpp b/compiler/luci/lang/src/Nodes/CircleOutput.cpp
new file mode 100644
index 000000000..31380456f
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleOutput.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleOutput.h"
+
+#include <cassert>
+#include <limits>
+
+namespace luci
+{
+
+void CircleOutput::index(const loco::GraphOutputIndex &index)
+{
+ // CircleOutput internally stores "GraphOutputIndex" as int64_t
+ _index = static_cast<int64_t>(index);
+}
+
+loco::GraphOutputIndex CircleOutput::index(void) const
+{
+ assert(_index >= std::numeric_limits<loco::GraphOutputIndex>::min());
+ assert(_index <= std::numeric_limits<loco::GraphOutputIndex>::max());
+ return static_cast<loco::GraphOutputIndex>(_index);
+}
+
+} // namespace luci
diff --git a/compiler/luci/lang/src/Nodes/CirclePRelu.test.cpp b/compiler/luci/lang/src/Nodes/CirclePRelu.test.cpp
new file mode 100644
index 000000000..8355c6d97
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CirclePRelu.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CirclePRelu.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CirclePReluTest, constructor_P)
+{
+ luci::CirclePRelu prelu_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), prelu_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::PRELU, prelu_node.opcode());
+
+ ASSERT_EQ(nullptr, prelu_node.input());
+ ASSERT_EQ(nullptr, prelu_node.alpha());
+}
+
+TEST(CirclePReluTest, input_NEG)
+{
+ luci::CirclePRelu prelu_node;
+ luci::CirclePRelu node;
+
+ prelu_node.input(&node);
+ prelu_node.alpha(&node);
+ ASSERT_NE(nullptr, prelu_node.input());
+ ASSERT_NE(nullptr, prelu_node.alpha());
+
+ prelu_node.input(nullptr);
+ prelu_node.alpha(nullptr);
+ ASSERT_EQ(nullptr, prelu_node.input());
+ ASSERT_EQ(nullptr, prelu_node.alpha());
+}
+
+TEST(CirclePReluTest, arity_NEG)
+{
+ luci::CirclePRelu prelu_node;
+
+ ASSERT_NO_THROW(prelu_node.arg(1));
+ ASSERT_THROW(prelu_node.arg(2), std::out_of_range);
+}
+
+TEST(CirclePReluTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CirclePRelu prelu_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(prelu_node.accept(&tv), std::exception);
+}
+
+TEST(CirclePReluTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CirclePRelu prelu_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(prelu_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CirclePack.test.cpp b/compiler/luci/lang/src/Nodes/CirclePack.test.cpp
new file mode 100644
index 000000000..5e64f0d89
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CirclePack.test.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CirclePack.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CirclePackTest, constructor)
+{
+ luci::CirclePack pack_node(3);
+
+ ASSERT_EQ(luci::CircleDialect::get(), pack_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::PACK, pack_node.opcode());
+
+ ASSERT_EQ(0, pack_node.axis());
+ ASSERT_EQ(3, pack_node.values_count());
+ ASSERT_EQ(nullptr, pack_node.values(0));
+ ASSERT_EQ(nullptr, pack_node.values(1));
+ ASSERT_EQ(nullptr, pack_node.values(2));
+}
+
+TEST(CirclePackTest, input_NEG)
+{
+ luci::CirclePack pack_node(2);
+ luci::CirclePack node(2);
+
+ pack_node.values(0, &node);
+ pack_node.values(1, &node);
+ ASSERT_NE(nullptr, pack_node.values(0));
+ ASSERT_NE(nullptr, pack_node.values(1));
+
+ pack_node.values(0, nullptr);
+ pack_node.values(1, nullptr);
+ ASSERT_EQ(nullptr, pack_node.values(0));
+ ASSERT_EQ(nullptr, pack_node.values(1));
+}
+
+TEST(CirclePackTest, arity_NEG)
+{
+ luci::CirclePack pack_node(5);
+
+ ASSERT_NO_THROW(pack_node.arg(4));
+ ASSERT_THROW(pack_node.arg(5), std::out_of_range);
+}
+
+TEST(CirclePackTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CirclePack pack_node(2);
+
+ TestVisitor tv;
+ ASSERT_THROW(pack_node.accept(&tv), std::exception);
+}
+
+TEST(CirclePackTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CirclePack pack_node(2);
+
+ TestVisitor tv;
+ ASSERT_THROW(pack_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CirclePad.test.cpp b/compiler/luci/lang/src/Nodes/CirclePad.test.cpp
new file mode 100644
index 000000000..12c66b7ea
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CirclePad.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CirclePad.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CirclePadTest, constructor_P)
+{
+ luci::CirclePad pad_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), pad_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::PAD, pad_node.opcode());
+
+ ASSERT_EQ(nullptr, pad_node.input());
+ ASSERT_EQ(nullptr, pad_node.paddings());
+}
+
+TEST(CirclePadTest, input_NEG)
+{
+ luci::CirclePad pad_node;
+ luci::CirclePad node;
+
+ pad_node.input(&node);
+ pad_node.paddings(&node);
+ ASSERT_NE(nullptr, pad_node.input());
+ ASSERT_NE(nullptr, pad_node.paddings());
+
+ pad_node.input(nullptr);
+ pad_node.paddings(nullptr);
+ ASSERT_EQ(nullptr, pad_node.input());
+ ASSERT_EQ(nullptr, pad_node.paddings());
+}
+
+TEST(CirclePadTest, arity_NEG)
+{
+ luci::CirclePad pad_node;
+
+ ASSERT_NO_THROW(pad_node.arg(1));
+ ASSERT_THROW(pad_node.arg(2), std::out_of_range);
+}
+
+TEST(CirclePadTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CirclePad pad_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(pad_node.accept(&tv), std::exception);
+}
+
+TEST(CirclePadTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CirclePad pad_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(pad_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CirclePadV2.test.cpp b/compiler/luci/lang/src/Nodes/CirclePadV2.test.cpp
new file mode 100644
index 000000000..e09d517b2
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CirclePadV2.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 "luci/IR/Nodes/CirclePadV2.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CirclePadV2Test, constructor_P)
+{
+ luci::CirclePadV2 node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::PADV2, node.opcode());
+
+ ASSERT_EQ(nullptr, node.input());
+ ASSERT_EQ(nullptr, node.paddings());
+ ASSERT_EQ(nullptr, node.constant_values());
+}
+
+TEST(CirclePadV2Test, input_NEG)
+{
+ luci::CirclePadV2 pad_node;
+ luci::CirclePadV2 node;
+
+ pad_node.input(&node);
+ pad_node.paddings(&node);
+ pad_node.constant_values(&node);
+ ASSERT_NE(nullptr, pad_node.input());
+ ASSERT_NE(nullptr, pad_node.paddings());
+ ASSERT_NE(nullptr, pad_node.constant_values());
+
+ pad_node.input(nullptr);
+ pad_node.paddings(nullptr);
+ pad_node.constant_values(nullptr);
+ ASSERT_EQ(nullptr, pad_node.input());
+ ASSERT_EQ(nullptr, pad_node.paddings());
+ ASSERT_EQ(nullptr, pad_node.constant_values());
+}
+
+TEST(CirclePadV2Test, arity_NEG)
+{
+ luci::CirclePadV2 pad_node;
+
+ ASSERT_NO_THROW(pad_node.arg(2));
+ ASSERT_THROW(pad_node.arg(3), std::out_of_range);
+}
+
+TEST(CirclePadV2Test, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CirclePadV2 pad_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(pad_node.accept(&tv), std::exception);
+}
+
+TEST(CirclePadV2Test, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CirclePadV2 pad_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(pad_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CirclePow.test.cpp b/compiler/luci/lang/src/Nodes/CirclePow.test.cpp
new file mode 100644
index 000000000..67ba0882b
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CirclePow.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CirclePow.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CirclePowTest, constructor_P)
+{
+ luci::CirclePow pow_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), pow_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::POW, pow_node.opcode());
+
+ ASSERT_EQ(nullptr, pow_node.x());
+ ASSERT_EQ(nullptr, pow_node.y());
+}
+
+TEST(CirclePowTest, input_NEG)
+{
+ luci::CirclePow pow_node;
+ luci::CirclePow node;
+
+ pow_node.x(&node);
+ pow_node.y(&node);
+ ASSERT_NE(nullptr, pow_node.x());
+ ASSERT_NE(nullptr, pow_node.y());
+
+ pow_node.x(nullptr);
+ pow_node.y(nullptr);
+ ASSERT_EQ(nullptr, pow_node.x());
+ ASSERT_EQ(nullptr, pow_node.y());
+}
+
+TEST(CirclePowTest, arity_NEG)
+{
+ luci::CirclePow pow_node;
+
+ ASSERT_NO_THROW(pow_node.arg(1));
+ ASSERT_THROW(pow_node.arg(2), std::out_of_range);
+}
+
+TEST(CirclePowTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CirclePow pow_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(pow_node.accept(&tv), std::exception);
+}
+
+TEST(CirclePowTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CirclePow pow_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(pow_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleRange.test.cpp b/compiler/luci/lang/src/Nodes/CircleRange.test.cpp
new file mode 100644
index 000000000..dd54dfd6e
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleRange.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 "luci/IR/Nodes/CircleRange.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleRangeTest, constructor)
+{
+ luci::CircleRange range_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), range_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::RANGE, range_node.opcode());
+
+ ASSERT_EQ(nullptr, range_node.start());
+ ASSERT_EQ(nullptr, range_node.limit());
+ ASSERT_EQ(nullptr, range_node.delta());
+}
+
+TEST(CircleRangeTest, input_NEG)
+{
+ luci::CircleRange range_node;
+ luci::CircleRange node;
+
+ range_node.start(&node);
+ range_node.limit(&node);
+ range_node.delta(&node);
+ ASSERT_NE(nullptr, range_node.start());
+ ASSERT_NE(nullptr, range_node.limit());
+ ASSERT_NE(nullptr, range_node.delta());
+
+ range_node.start(nullptr);
+ range_node.limit(nullptr);
+ range_node.delta(nullptr);
+ ASSERT_EQ(nullptr, range_node.start());
+ ASSERT_EQ(nullptr, range_node.limit());
+ ASSERT_EQ(nullptr, range_node.delta());
+}
+
+TEST(CircleRangeTest, arity_NEG)
+{
+ luci::CircleRange range_node;
+
+ ASSERT_NO_THROW(range_node.arg(2));
+ ASSERT_THROW(range_node.arg(3), std::out_of_range);
+}
+
+TEST(CircleRangeTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleRange range_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(range_node.accept(&tv), std::exception);
+}
+
+TEST(CircleRangeTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleRange range_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(range_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleRank.test.cpp b/compiler/luci/lang/src/Nodes/CircleRank.test.cpp
new file mode 100644
index 000000000..e64eae235
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleRank.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleRank.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleRankTest, constructor_P)
+{
+ luci::CircleRank rank_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), rank_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::RANK, rank_node.opcode());
+
+ ASSERT_EQ(nullptr, rank_node.input());
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleReduceAny.test.cpp b/compiler/luci/lang/src/Nodes/CircleReduceAny.test.cpp
new file mode 100644
index 000000000..cd5c6b746
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleReduceAny.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 "luci/IR/Nodes/CircleReduceAny.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleReduceAnyTest, constructor)
+{
+ luci::CircleReduceAny reduce_any_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), reduce_any_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::REDUCE_ANY, reduce_any_node.opcode());
+
+ ASSERT_EQ(nullptr, reduce_any_node.input());
+ ASSERT_EQ(nullptr, reduce_any_node.reduction_indices());
+
+ ASSERT_FALSE(reduce_any_node.keep_dims());
+}
+
+TEST(CircleReduceAnyTest, input_NEG)
+{
+ luci::CircleReduceAny reduce_any_node;
+ luci::CircleReduceAny node;
+
+ reduce_any_node.input(&node);
+ reduce_any_node.reduction_indices(&node);
+ ASSERT_NE(nullptr, reduce_any_node.input());
+ ASSERT_NE(nullptr, reduce_any_node.reduction_indices());
+
+ reduce_any_node.input(nullptr);
+ reduce_any_node.reduction_indices(nullptr);
+ ASSERT_EQ(nullptr, reduce_any_node.input());
+ ASSERT_EQ(nullptr, reduce_any_node.reduction_indices());
+
+ reduce_any_node.keep_dims(true);
+ ASSERT_TRUE(reduce_any_node.keep_dims());
+}
+
+TEST(CircleReduceAnyTest, arity_NEG)
+{
+ luci::CircleReduceAny reduce_any_node;
+
+ ASSERT_NO_THROW(reduce_any_node.arg(1));
+ ASSERT_THROW(reduce_any_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleReduceAnyTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleReduceAny reduce_any_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(reduce_any_node.accept(&tv), std::exception);
+}
+
+TEST(CircleReduceAnyTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleReduceAny reduce_any_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(reduce_any_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleReduceMax.test.cpp b/compiler/luci/lang/src/Nodes/CircleReduceMax.test.cpp
new file mode 100644
index 000000000..bdd1818e0
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleReduceMax.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 "luci/IR/Nodes/CircleReduceMax.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleReduceMaxTest, constructor_P)
+{
+ luci::CircleReduceMax reduce_max_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), reduce_max_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::REDUCE_MAX, reduce_max_node.opcode());
+
+ ASSERT_EQ(nullptr, reduce_max_node.input());
+ ASSERT_EQ(nullptr, reduce_max_node.reduction_indices());
+
+ ASSERT_FALSE(reduce_max_node.keep_dims());
+}
+
+TEST(CircleReduceMaxTest, input_NEG)
+{
+ luci::CircleReduceMax reduce_max_node;
+ luci::CircleReduceMax node;
+
+ reduce_max_node.input(&node);
+ reduce_max_node.reduction_indices(&node);
+ ASSERT_NE(nullptr, reduce_max_node.input());
+ ASSERT_NE(nullptr, reduce_max_node.reduction_indices());
+
+ reduce_max_node.input(nullptr);
+ reduce_max_node.reduction_indices(nullptr);
+ ASSERT_EQ(nullptr, reduce_max_node.input());
+ ASSERT_EQ(nullptr, reduce_max_node.reduction_indices());
+
+ reduce_max_node.keep_dims(true);
+ ASSERT_TRUE(reduce_max_node.keep_dims());
+}
+
+TEST(CircleReduceMaxTest, arity_NEG)
+{
+ luci::CircleReduceMax reduce_max_node;
+
+ ASSERT_NO_THROW(reduce_max_node.arg(1));
+ ASSERT_THROW(reduce_max_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleReduceMaxTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleReduceMax reduce_max_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(reduce_max_node.accept(&tv), std::exception);
+}
+
+TEST(CircleReduceMaxTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleReduceMax reduce_max_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(reduce_max_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleReduceMin.test.cpp b/compiler/luci/lang/src/Nodes/CircleReduceMin.test.cpp
new file mode 100644
index 000000000..ba99ae648
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleReduceMin.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 "luci/IR/Nodes/CircleReduceMin.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleReduceMinTest, constructor_P)
+{
+ luci::CircleReduceMin reduce_min_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), reduce_min_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::REDUCE_MIN, reduce_min_node.opcode());
+
+ ASSERT_EQ(nullptr, reduce_min_node.input());
+ ASSERT_EQ(nullptr, reduce_min_node.reduction_indices());
+
+ ASSERT_FALSE(reduce_min_node.keep_dims());
+}
+
+TEST(CircleReduceMinTest, input_NEG)
+{
+ luci::CircleReduceMin reduce_min_node;
+ luci::CircleReduceMin node;
+
+ reduce_min_node.input(&node);
+ reduce_min_node.reduction_indices(&node);
+ ASSERT_NE(nullptr, reduce_min_node.input());
+ ASSERT_NE(nullptr, reduce_min_node.reduction_indices());
+
+ reduce_min_node.input(nullptr);
+ reduce_min_node.reduction_indices(nullptr);
+ ASSERT_EQ(nullptr, reduce_min_node.input());
+ ASSERT_EQ(nullptr, reduce_min_node.reduction_indices());
+
+ reduce_min_node.keep_dims(true);
+ ASSERT_TRUE(reduce_min_node.keep_dims());
+}
+
+TEST(CircleReduceMinTest, arity_NEG)
+{
+ luci::CircleReduceMin reduce_min_node;
+
+ ASSERT_NO_THROW(reduce_min_node.arg(1));
+ ASSERT_THROW(reduce_min_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleReduceMinTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleReduceMin reduce_min_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(reduce_min_node.accept(&tv), std::exception);
+}
+
+TEST(CircleReduceMinTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleReduceMin reduce_min_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(reduce_min_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleReduceProd.test.cpp b/compiler/luci/lang/src/Nodes/CircleReduceProd.test.cpp
new file mode 100644
index 000000000..f60b2905b
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleReduceProd.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 "luci/IR/Nodes/CircleReduceProd.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleReduceProdTest, constructor)
+{
+ luci::CircleReduceProd reduce_prod_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), reduce_prod_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::REDUCE_PROD, reduce_prod_node.opcode());
+
+ ASSERT_EQ(nullptr, reduce_prod_node.input());
+ ASSERT_EQ(nullptr, reduce_prod_node.reduction_indices());
+
+ ASSERT_FALSE(reduce_prod_node.keep_dims());
+}
+
+TEST(CircleReduceProdTest, input_NEG)
+{
+ luci::CircleReduceProd reduce_prod_node;
+ luci::CircleReduceProd node;
+
+ reduce_prod_node.input(&node);
+ reduce_prod_node.reduction_indices(&node);
+ ASSERT_NE(nullptr, reduce_prod_node.input());
+ ASSERT_NE(nullptr, reduce_prod_node.reduction_indices());
+
+ reduce_prod_node.input(nullptr);
+ reduce_prod_node.reduction_indices(nullptr);
+ ASSERT_EQ(nullptr, reduce_prod_node.input());
+ ASSERT_EQ(nullptr, reduce_prod_node.reduction_indices());
+
+ reduce_prod_node.keep_dims(true);
+ ASSERT_TRUE(reduce_prod_node.keep_dims());
+}
+
+TEST(CircleReduceProdTest, arity_NEG)
+{
+ luci::CircleReduceProd reduce_prod_node;
+
+ ASSERT_NO_THROW(reduce_prod_node.arg(1));
+ ASSERT_THROW(reduce_prod_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleReduceProdTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleReduceProd reduce_prod_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(reduce_prod_node.accept(&tv), std::exception);
+}
+
+TEST(CircleReduceProdTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleReduceProd reduce_prod_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(reduce_prod_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleRelu.test.cpp b/compiler/luci/lang/src/Nodes/CircleRelu.test.cpp
new file mode 100644
index 000000000..35796509c
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleRelu.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleRelu.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleReluTest, constructor_P)
+{
+ luci::CircleRelu relu_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), relu_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::RELU, relu_node.opcode());
+
+ ASSERT_EQ(nullptr, relu_node.features());
+}
+
+TEST(CircleReluTest, input_NEG)
+{
+ luci::CircleRelu relu_node;
+ luci::CircleRelu node;
+
+ relu_node.features(&node);
+ ASSERT_NE(nullptr, relu_node.features());
+
+ relu_node.features(nullptr);
+ ASSERT_EQ(nullptr, relu_node.features());
+}
+
+TEST(CircleReluTest, arity_NEG)
+{
+ luci::CircleRelu relu_node;
+
+ ASSERT_NO_THROW(relu_node.arg(0));
+ ASSERT_THROW(relu_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleReluTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleRelu relu_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(relu_node.accept(&tv), std::exception);
+}
+
+TEST(CircleReluTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleRelu relu_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(relu_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleRelu6.test.cpp b/compiler/luci/lang/src/Nodes/CircleRelu6.test.cpp
new file mode 100644
index 000000000..647a5d7ba
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleRelu6.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleRelu6.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleRelu6Test, constructor_P)
+{
+ luci::CircleRelu6 relu6_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), relu6_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::RELU6, relu6_node.opcode());
+
+ ASSERT_EQ(nullptr, relu6_node.features());
+}
+
+TEST(CircleRelu6Test, input_NEG)
+{
+ luci::CircleRelu6 relu6_node;
+ luci::CircleRelu6 node;
+
+ relu6_node.features(&node);
+ ASSERT_NE(nullptr, relu6_node.features());
+
+ relu6_node.features(nullptr);
+ ASSERT_EQ(nullptr, relu6_node.features());
+}
+
+TEST(CircleRelu6Test, arity_NEG)
+{
+ luci::CircleRelu6 relu6_node;
+
+ ASSERT_NO_THROW(relu6_node.arg(0));
+ ASSERT_THROW(relu6_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleRelu6Test, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleRelu6 relu6_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(relu6_node.accept(&tv), std::exception);
+}
+
+TEST(CircleRelu6Test, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleRelu6 relu6_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(relu6_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleReluN1To1.test.cpp b/compiler/luci/lang/src/Nodes/CircleReluN1To1.test.cpp
new file mode 100644
index 000000000..8de84ac42
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleReluN1To1.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleReluN1To1.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleReluN1To1Test, constructor)
+{
+ luci::CircleReluN1To1 relu_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), relu_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::RELU_N1_TO_1, relu_node.opcode());
+
+ ASSERT_EQ(nullptr, relu_node.features());
+}
+
+TEST(CircleReluN1To1Test, input_NEG)
+{
+ luci::CircleReluN1To1 relu_node;
+ luci::CircleReluN1To1 node;
+
+ relu_node.features(&node);
+ ASSERT_NE(nullptr, relu_node.features());
+
+ relu_node.features(nullptr);
+ ASSERT_EQ(nullptr, relu_node.features());
+}
+
+TEST(CircleReluN1To1Test, arity_NEG)
+{
+ luci::CircleReluN1To1 relu_node;
+
+ ASSERT_NO_THROW(relu_node.arg(0));
+ ASSERT_THROW(relu_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleReluN1To1Test, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleReluN1To1 relu_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(relu_node.accept(&tv), std::exception);
+}
+
+TEST(CircleReluN1To1Test, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleReluN1To1 relu_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(relu_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleReshape.test.cpp b/compiler/luci/lang/src/Nodes/CircleReshape.test.cpp
new file mode 100644
index 000000000..236fde28b
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleReshape.test.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleReshape.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleReshapeTest, constructor_P)
+{
+ luci::CircleReshape reshape;
+
+ ASSERT_EQ(luci::CircleDialect::get(), reshape.dialect());
+ ASSERT_EQ(luci::CircleOpcode::RESHAPE, reshape.opcode());
+
+ ASSERT_EQ(nullptr, reshape.tensor());
+ ASSERT_EQ(nullptr, reshape.shape());
+ ASSERT_EQ(0, reshape.newShape()->rank());
+}
+
+TEST(CircleReshapeTest, alloc_new_shape_P)
+{
+ luci::CircleReshape reshape;
+
+ reshape.newShape()->rank(2);
+ ASSERT_EQ(2, reshape.newShape()->rank());
+
+ reshape.newShape()->dim(0) = 0;
+ reshape.newShape()->dim(1) = 1;
+
+ auto &const_reshape = const_cast<const luci::CircleReshape &>(reshape);
+ ASSERT_EQ(0, const_reshape.newShape()->dim(0));
+ ASSERT_EQ(1, const_reshape.newShape()->dim(1));
+}
+
+TEST(CircleReshapeTest, input_NEG)
+{
+ luci::CircleReshape reshape_node;
+ luci::CircleReshape node;
+
+ reshape_node.tensor(&node);
+ reshape_node.shape(&node);
+ ASSERT_NE(nullptr, reshape_node.tensor());
+ ASSERT_NE(nullptr, reshape_node.shape());
+
+ reshape_node.tensor(nullptr);
+ reshape_node.shape(nullptr);
+ ASSERT_EQ(nullptr, reshape_node.tensor());
+ ASSERT_EQ(nullptr, reshape_node.shape());
+}
+
+TEST(CircleReshapeTest, arity_NEG)
+{
+ luci::CircleReshape reshape_node;
+
+ ASSERT_NO_THROW(reshape_node.arg(1));
+ ASSERT_THROW(reshape_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleReshapeTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleReshape reshape_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(reshape_node.accept(&tv), std::exception);
+}
+
+TEST(CircleReshapeTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleReshape reshape_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(reshape_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleResizeBilinear.test.cpp b/compiler/luci/lang/src/Nodes/CircleResizeBilinear.test.cpp
new file mode 100644
index 000000000..a1481a640
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleResizeBilinear.test.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleResizeBilinear.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleResizeBilinearTest, constructor)
+{
+ luci::CircleResizeBilinear resize;
+
+ ASSERT_EQ(luci::CircleDialect::get(), resize.dialect());
+ ASSERT_EQ(luci::CircleOpcode::RESIZE_BILINEAR, resize.opcode());
+
+ ASSERT_EQ(nullptr, resize.input());
+ ASSERT_EQ(nullptr, resize.size());
+ ASSERT_FALSE(resize.align_corners());
+ ASSERT_FALSE(resize.half_pixel_centers());
+}
+
+TEST(CircleResizeBilinearTest, input_NEG)
+{
+ luci::CircleResizeBilinear resize_node;
+ luci::CircleResizeBilinear node;
+
+ resize_node.input(&node);
+ resize_node.size(&node);
+ ASSERT_NE(nullptr, resize_node.input());
+ ASSERT_NE(nullptr, resize_node.size());
+
+ resize_node.input(nullptr);
+ resize_node.size(nullptr);
+ ASSERT_EQ(nullptr, resize_node.input());
+ ASSERT_EQ(nullptr, resize_node.size());
+
+ resize_node.align_corners(true);
+ ASSERT_TRUE(resize_node.align_corners());
+ resize_node.half_pixel_centers(true);
+ ASSERT_TRUE(resize_node.half_pixel_centers());
+}
+
+TEST(CircleResizeBilinearTest, arity_NEG)
+{
+ luci::CircleResizeBilinear resize_node;
+
+ ASSERT_NO_THROW(resize_node.arg(1));
+ ASSERT_THROW(resize_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleResizeBilinearTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleResizeBilinear resize_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(resize_node.accept(&tv), std::exception);
+}
+
+TEST(CircleResizeBilinearTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleResizeBilinear resize_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(resize_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleResizeNearestNeighbor.test.cpp b/compiler/luci/lang/src/Nodes/CircleResizeNearestNeighbor.test.cpp
new file mode 100644
index 000000000..00e0ae9ea
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleResizeNearestNeighbor.test.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleResizeNearestNeighbor.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleResizeNearestNeightborTest, constructor)
+{
+ luci::CircleResizeNearestNeighbor resize;
+
+ ASSERT_EQ(luci::CircleDialect::get(), resize.dialect());
+ ASSERT_EQ(luci::CircleOpcode::RESIZE_NEAREST_NEIGHBOR, resize.opcode());
+
+ ASSERT_EQ(nullptr, resize.input());
+ ASSERT_EQ(nullptr, resize.size());
+ ASSERT_FALSE(resize.align_corners());
+}
+
+TEST(CircleResizeNearestNeightborTest, input_NEG)
+{
+ luci::CircleResizeNearestNeighbor resize_node;
+ luci::CircleResizeNearestNeighbor node;
+
+ resize_node.input(&node);
+ resize_node.size(&node);
+ ASSERT_NE(nullptr, resize_node.input());
+ ASSERT_NE(nullptr, resize_node.size());
+
+ resize_node.input(nullptr);
+ resize_node.size(nullptr);
+ ASSERT_EQ(nullptr, resize_node.input());
+ ASSERT_EQ(nullptr, resize_node.size());
+
+ resize_node.align_corners(true);
+ ASSERT_TRUE(resize_node.align_corners());
+}
+
+TEST(CircleResizeNearestNeightborTest, arity_NEG)
+{
+ luci::CircleResizeNearestNeighbor resize_node;
+
+ ASSERT_NO_THROW(resize_node.arg(1));
+ ASSERT_THROW(resize_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleResizeNearestNeightborTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleResizeNearestNeighbor resize_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(resize_node.accept(&tv), std::exception);
+}
+
+TEST(CircleResizeNearestNeightborTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleResizeNearestNeighbor resize_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(resize_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleReverseSequence.test.cpp b/compiler/luci/lang/src/Nodes/CircleReverseSequence.test.cpp
new file mode 100644
index 000000000..b1cc6d6d6
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleReverseSequence.test.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleReverseSequence.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleReverseSequenceTest, constructor_P)
+{
+ luci::CircleReverseSequence std_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), std_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::REVERSE_SEQUENCE, std_node.opcode());
+
+ ASSERT_EQ(nullptr, std_node.input());
+ ASSERT_EQ(nullptr, std_node.seq_lengths());
+
+ ASSERT_EQ(0, std_node.seq_axis());
+ ASSERT_EQ(0, std_node.batch_axis());
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleReverseV2.test.cpp b/compiler/luci/lang/src/Nodes/CircleReverseV2.test.cpp
new file mode 100644
index 000000000..cc568e81a
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleReverseV2.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleReverseV2.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleReverseV2, constructor_P)
+{
+ luci::CircleReverseV2 std_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), std_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::REVERSE_V2, std_node.opcode());
+
+ ASSERT_EQ(nullptr, std_node.tensor());
+ ASSERT_EQ(nullptr, std_node.axis());
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleRound.test.cpp b/compiler/luci/lang/src/Nodes/CircleRound.test.cpp
new file mode 100644
index 000000000..2f4518daf
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleRound.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleRound.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleRoundTest, constructor_P)
+{
+ luci::CircleRound round_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), round_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::ROUND, round_node.opcode());
+
+ ASSERT_EQ(nullptr, round_node.x());
+}
+
+TEST(CircleRoundTest, input_NEG)
+{
+ luci::CircleRound round_node;
+ luci::CircleRound node;
+
+ round_node.x(&node);
+ ASSERT_NE(nullptr, round_node.x());
+
+ round_node.x(nullptr);
+ ASSERT_EQ(nullptr, round_node.x());
+}
+
+TEST(CircleRoundTest, arity_NEG)
+{
+ luci::CircleRound round_node;
+
+ ASSERT_NO_THROW(round_node.arg(0));
+ ASSERT_THROW(round_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleRoundTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleRound round_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(round_node.accept(&tv), std::exception);
+}
+
+TEST(CircleRoundTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleRound round_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(round_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleRsqrt.test.cpp b/compiler/luci/lang/src/Nodes/CircleRsqrt.test.cpp
new file mode 100644
index 000000000..d038979c1
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleRsqrt.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleRsqrt.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleRsqrtTest, constructor)
+{
+ luci::CircleRsqrt rsqrt_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), rsqrt_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::RSQRT, rsqrt_node.opcode());
+
+ ASSERT_EQ(nullptr, rsqrt_node.x());
+}
+
+TEST(CircleRsqrtTest, input_NEG)
+{
+ luci::CircleRsqrt rsqrt_node;
+ luci::CircleRsqrt node;
+
+ rsqrt_node.x(&node);
+ ASSERT_NE(nullptr, rsqrt_node.x());
+
+ rsqrt_node.x(nullptr);
+ ASSERT_EQ(nullptr, rsqrt_node.x());
+}
+
+TEST(CircleRsqrtTest, arity_NEG)
+{
+ luci::CircleRsqrt rsqrt_node;
+
+ ASSERT_NO_THROW(rsqrt_node.arg(0));
+ ASSERT_THROW(rsqrt_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleRsqrtTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleRsqrt rsqrt_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(rsqrt_node.accept(&tv), std::exception);
+}
+
+TEST(CircleRsqrtTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleRsqrt rsqrt_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(rsqrt_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleScatterNd.test.cpp b/compiler/luci/lang/src/Nodes/CircleScatterNd.test.cpp
new file mode 100644
index 000000000..165f26b44
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleScatterNd.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 "luci/IR/Nodes/CircleScatterNd.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleScatterNdTest, constructor_P)
+{
+ luci::CircleScatterNd scatter_nd_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), scatter_nd_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SCATTER_ND, scatter_nd_node.opcode());
+
+ ASSERT_EQ(nullptr, scatter_nd_node.indices());
+ ASSERT_EQ(nullptr, scatter_nd_node.updates());
+ ASSERT_EQ(nullptr, scatter_nd_node.shape());
+}
+
+TEST(CircleScatterNdTest, input_NEG)
+{
+ luci::CircleScatterNd scatter_nd_node;
+ luci::CircleScatterNd node;
+
+ scatter_nd_node.indices(&node);
+ scatter_nd_node.updates(&node);
+ scatter_nd_node.shape(&node);
+ ASSERT_NE(nullptr, scatter_nd_node.indices());
+ ASSERT_NE(nullptr, scatter_nd_node.updates());
+ ASSERT_NE(nullptr, scatter_nd_node.shape());
+
+ scatter_nd_node.indices(nullptr);
+ scatter_nd_node.updates(nullptr);
+ scatter_nd_node.shape(nullptr);
+ ASSERT_EQ(nullptr, scatter_nd_node.indices());
+ ASSERT_EQ(nullptr, scatter_nd_node.updates());
+ ASSERT_EQ(nullptr, scatter_nd_node.shape());
+}
+
+TEST(CircleScatterNdTest, arity_NEG)
+{
+ luci::CircleScatterNd scatter_nd_node;
+
+ ASSERT_NO_THROW(scatter_nd_node.arg(2));
+ ASSERT_THROW(scatter_nd_node.arg(3), std::out_of_range);
+}
+
+TEST(CircleScatterNdTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleScatterNd scatter_nd_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(scatter_nd_node.accept(&tv), std::exception);
+}
+
+TEST(CircleScatterNdTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleScatterNd scatter_nd_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(scatter_nd_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSegmentSum.test.cpp b/compiler/luci/lang/src/Nodes/CircleSegmentSum.test.cpp
new file mode 100644
index 000000000..90469b7e2
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSegmentSum.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleSegmentSum.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSegmentSumTest, constructor)
+{
+ luci::CircleSegmentSum segment_sum_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), segment_sum_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SEGMENT_SUM, segment_sum_node.opcode());
+
+ ASSERT_EQ(nullptr, segment_sum_node.input());
+ ASSERT_EQ(nullptr, segment_sum_node.segment_ids());
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSelect.test.cpp b/compiler/luci/lang/src/Nodes/CircleSelect.test.cpp
new file mode 100644
index 000000000..7eeb538af
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSelect.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 "luci/IR/Nodes/CircleSelect.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSelectTest, constructor)
+{
+ luci::CircleSelect select_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), select_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SELECT, select_node.opcode());
+
+ ASSERT_EQ(nullptr, select_node.condition());
+ ASSERT_EQ(nullptr, select_node.t());
+ ASSERT_EQ(nullptr, select_node.e());
+}
+
+TEST(CircleSelectTest, input_NEG)
+{
+ luci::CircleSelect select_node;
+ luci::CircleSelect node;
+
+ select_node.condition(&node);
+ select_node.t(&node);
+ select_node.e(&node);
+ ASSERT_NE(nullptr, select_node.condition());
+ ASSERT_NE(nullptr, select_node.t());
+ ASSERT_NE(nullptr, select_node.e());
+
+ select_node.condition(nullptr);
+ select_node.t(nullptr);
+ select_node.e(nullptr);
+ ASSERT_EQ(nullptr, select_node.condition());
+ ASSERT_EQ(nullptr, select_node.t());
+ ASSERT_EQ(nullptr, select_node.e());
+}
+
+TEST(CircleSelectTest, arity_NEG)
+{
+ luci::CircleSelect select_node;
+
+ ASSERT_NO_THROW(select_node.arg(2));
+ ASSERT_THROW(select_node.arg(3), std::out_of_range);
+}
+
+TEST(CircleSelectTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleSelect select_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(select_node.accept(&tv), std::exception);
+}
+
+TEST(CircleSelectTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleSelect select_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(select_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSelectV2.test.cpp b/compiler/luci/lang/src/Nodes/CircleSelectV2.test.cpp
new file mode 100644
index 000000000..eea5fb83f
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSelectV2.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 "luci/IR/Nodes/CircleSelectV2.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSelectV2Test, constructor)
+{
+ luci::CircleSelectV2 select_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), select_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SELECT_V2, select_node.opcode());
+
+ ASSERT_EQ(nullptr, select_node.condition());
+ ASSERT_EQ(nullptr, select_node.t());
+ ASSERT_EQ(nullptr, select_node.e());
+}
+
+TEST(CircleSelectV2Test, input_NEG)
+{
+ luci::CircleSelectV2 select_v2_node;
+ luci::CircleSelectV2 node;
+
+ select_v2_node.condition(&node);
+ select_v2_node.t(&node);
+ select_v2_node.e(&node);
+ ASSERT_NE(nullptr, select_v2_node.condition());
+ ASSERT_NE(nullptr, select_v2_node.t());
+ ASSERT_NE(nullptr, select_v2_node.e());
+
+ select_v2_node.condition(nullptr);
+ select_v2_node.t(nullptr);
+ select_v2_node.e(nullptr);
+ ASSERT_EQ(nullptr, select_v2_node.condition());
+ ASSERT_EQ(nullptr, select_v2_node.t());
+ ASSERT_EQ(nullptr, select_v2_node.e());
+}
+
+TEST(CircleSelectV2Test, arity_NEG)
+{
+ luci::CircleSelectV2 select_v2_node;
+
+ ASSERT_NO_THROW(select_v2_node.arg(2));
+ ASSERT_THROW(select_v2_node.arg(3), std::out_of_range);
+}
+
+TEST(CircleSelectV2Test, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleSelectV2 select_v2_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(select_v2_node.accept(&tv), std::exception);
+}
+
+TEST(CircleSelectV2Test, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleSelectV2 select_v2_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(select_v2_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleShape.test.cpp b/compiler/luci/lang/src/Nodes/CircleShape.test.cpp
new file mode 100644
index 000000000..18271d2b2
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleShape.test.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleShape.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleShapeTest, constructor)
+{
+ luci::CircleShape shape_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), shape_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SHAPE, shape_node.opcode());
+
+ ASSERT_EQ(nullptr, shape_node.input());
+ ASSERT_EQ(loco::DataType::S32, shape_node.out_type());
+}
+
+TEST(CircleShapeTest, input_NEG)
+{
+ luci::CircleShape shape_node;
+ luci::CircleShape node;
+
+ shape_node.input(&node);
+ ASSERT_NE(nullptr, shape_node.input());
+
+ shape_node.input(nullptr);
+ ASSERT_EQ(nullptr, shape_node.input());
+
+ shape_node.out_type(loco::DataType::U8);
+ ASSERT_NE(loco::DataType::S32, shape_node.out_type());
+}
+
+TEST(CircleShapeTest, arity_NEG)
+{
+ luci::CircleShape shape_node;
+
+ ASSERT_NO_THROW(shape_node.arg(0));
+ ASSERT_THROW(shape_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleShapeTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleShape shape_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(shape_node.accept(&tv), std::exception);
+}
+
+TEST(CircleShapeTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleShape shape_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(shape_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSin.test.cpp b/compiler/luci/lang/src/Nodes/CircleSin.test.cpp
new file mode 100644
index 000000000..e01932d4f
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSin.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleSin.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSinTest, constructor)
+{
+ luci::CircleSin sin_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), sin_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SIN, sin_node.opcode());
+
+ ASSERT_EQ(nullptr, sin_node.x());
+}
+
+TEST(CircleSinTest, input_NEG)
+{
+ luci::CircleSin sin_node;
+ luci::CircleSin node;
+
+ sin_node.x(&node);
+ ASSERT_NE(nullptr, sin_node.x());
+
+ sin_node.x(nullptr);
+ ASSERT_EQ(nullptr, sin_node.x());
+}
+
+TEST(CircleSinTest, arity_NEG)
+{
+ luci::CircleSin sin_node;
+
+ ASSERT_NO_THROW(sin_node.arg(0));
+ ASSERT_THROW(sin_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleSinTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleSin sin_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(sin_node.accept(&tv), std::exception);
+}
+
+TEST(CircleSinTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleSin sin_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(sin_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSlice.test.cpp b/compiler/luci/lang/src/Nodes/CircleSlice.test.cpp
new file mode 100644
index 000000000..5563a34b9
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSlice.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 "luci/IR/Nodes/CircleSlice.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSliceTest, constructor)
+{
+ luci::CircleSlice s_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), s_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SLICE, s_node.opcode());
+
+ ASSERT_EQ(nullptr, s_node.input());
+ ASSERT_EQ(nullptr, s_node.begin());
+ ASSERT_EQ(nullptr, s_node.size());
+}
+
+TEST(CircleSliceTest, input_NEG)
+{
+ luci::CircleSlice s_node;
+ luci::CircleSlice node;
+
+ s_node.input(&node);
+ s_node.begin(&node);
+ s_node.size(&node);
+ ASSERT_NE(nullptr, s_node.input());
+ ASSERT_NE(nullptr, s_node.begin());
+ ASSERT_NE(nullptr, s_node.size());
+
+ s_node.input(nullptr);
+ s_node.begin(nullptr);
+ s_node.size(nullptr);
+ ASSERT_EQ(nullptr, s_node.input());
+ ASSERT_EQ(nullptr, s_node.begin());
+ ASSERT_EQ(nullptr, s_node.size());
+}
+
+TEST(CircleSliceTest, arity_NEG)
+{
+ luci::CircleSlice s_node;
+
+ ASSERT_NO_THROW(s_node.arg(2));
+ ASSERT_THROW(s_node.arg(3), std::out_of_range);
+}
+
+TEST(CircleSliceTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleSlice s_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(s_node.accept(&tv), std::exception);
+}
+
+TEST(CircleSliceTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleSlice s_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(s_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSoftmax.test.cpp b/compiler/luci/lang/src/Nodes/CircleSoftmax.test.cpp
new file mode 100644
index 000000000..b15c009f2
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSoftmax.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleSoftmax.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSoftmaxTest, constructor_P)
+{
+ luci::CircleSoftmax softmax_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), softmax_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SOFTMAX, softmax_node.opcode());
+
+ ASSERT_EQ(nullptr, softmax_node.logits());
+}
+
+TEST(CircleSoftmaxTest, input_NEG)
+{
+ luci::CircleSoftmax softmax_node;
+ luci::CircleSoftmax node;
+
+ softmax_node.logits(&node);
+ ASSERT_NE(nullptr, softmax_node.logits());
+
+ softmax_node.logits(nullptr);
+ ASSERT_EQ(nullptr, softmax_node.logits());
+}
+
+TEST(CircleSoftmaxTest, arity_NEG)
+{
+ luci::CircleSoftmax softmax_node;
+
+ ASSERT_NO_THROW(softmax_node.arg(0));
+ ASSERT_THROW(softmax_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleSoftmaxTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleSoftmax softmax_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(softmax_node.accept(&tv), std::exception);
+}
+
+TEST(CircleSoftmaxTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleSoftmax softmax_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(softmax_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSpaceToBatchND.test.cpp b/compiler/luci/lang/src/Nodes/CircleSpaceToBatchND.test.cpp
new file mode 100644
index 000000000..8b4ac8f2b
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSpaceToBatchND.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 "luci/IR/Nodes/CircleSpaceToBatchND.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSpaceToBatchNDTest, constructor)
+{
+ luci::CircleSpaceToBatchND stb_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), stb_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SPACE_TO_BATCH_ND, stb_node.opcode());
+
+ ASSERT_EQ(nullptr, stb_node.input());
+ ASSERT_EQ(nullptr, stb_node.block_shape());
+ ASSERT_EQ(nullptr, stb_node.paddings());
+}
+
+TEST(CircleSpaceToBatchNDTest, input_NEG)
+{
+ luci::CircleSpaceToBatchND stb_node;
+ luci::CircleSpaceToBatchND node;
+
+ stb_node.input(&node);
+ stb_node.block_shape(&node);
+ stb_node.paddings(&node);
+ ASSERT_NE(nullptr, stb_node.input());
+ ASSERT_NE(nullptr, stb_node.block_shape());
+ ASSERT_NE(nullptr, stb_node.paddings());
+
+ stb_node.input(nullptr);
+ stb_node.block_shape(nullptr);
+ stb_node.paddings(nullptr);
+ ASSERT_EQ(nullptr, stb_node.input());
+ ASSERT_EQ(nullptr, stb_node.block_shape());
+ ASSERT_EQ(nullptr, stb_node.paddings());
+}
+
+TEST(CircleSpaceToBatchNDTest, arity_NEG)
+{
+ luci::CircleSpaceToBatchND stb_node;
+
+ ASSERT_NO_THROW(stb_node.arg(2));
+ ASSERT_THROW(stb_node.arg(3), std::out_of_range);
+}
+
+TEST(CircleSpaceToBatchNDTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleSpaceToBatchND stb_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(stb_node.accept(&tv), std::exception);
+}
+
+TEST(CircleSpaceToBatchNDTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleSpaceToBatchND stb_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(stb_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSpaceToDepth.test.cpp b/compiler/luci/lang/src/Nodes/CircleSpaceToDepth.test.cpp
new file mode 100644
index 000000000..d49a2ce85
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSpaceToDepth.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleSpaceToDepth.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSpaceToDepthTest, constructor)
+{
+ luci::CircleSpaceToDepth std_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), std_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SPACE_TO_DEPTH, std_node.opcode());
+
+ ASSERT_EQ(nullptr, std_node.input());
+}
+
+TEST(CircleSpaceToDepthTest, input_NEG)
+{
+ luci::CircleSpaceToDepth std_node;
+ luci::CircleSpaceToDepth node;
+
+ std_node.input(&node);
+ ASSERT_NE(nullptr, std_node.input());
+
+ std_node.input(nullptr);
+ ASSERT_EQ(nullptr, std_node.input());
+}
+
+TEST(CircleSpaceToDepthTest, arity_NEG)
+{
+ luci::CircleSpaceToDepth std_node;
+
+ ASSERT_NO_THROW(std_node.arg(0));
+ ASSERT_THROW(std_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleSpaceToDepthTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleSpaceToDepth std_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(std_node.accept(&tv), std::exception);
+}
+
+TEST(CircleSpaceToDepthTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleSpaceToDepth std_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(std_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSparseToDense.test.cpp b/compiler/luci/lang/src/Nodes/CircleSparseToDense.test.cpp
new file mode 100644
index 000000000..073be6bcb
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSparseToDense.test.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleSparseToDense.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSparseToDenseTest, constructor)
+{
+ luci::CircleSparseToDense stb_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), stb_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SPARSE_TO_DENSE, stb_node.opcode());
+
+ ASSERT_EQ(nullptr, stb_node.indices());
+ ASSERT_EQ(nullptr, stb_node.output_shape());
+ ASSERT_EQ(nullptr, stb_node.values());
+ ASSERT_EQ(nullptr, stb_node.default_value());
+
+ ASSERT_FALSE(stb_node.validate_indices());
+}
+
+TEST(CircleSparseToDenseTest, input_NEG)
+{
+ luci::CircleSparseToDense stb_node;
+ luci::CircleSparseToDense node;
+
+ stb_node.indices(&node);
+ stb_node.output_shape(&node);
+ stb_node.values(&node);
+ stb_node.default_value(&node);
+ ASSERT_NE(nullptr, stb_node.indices());
+ ASSERT_NE(nullptr, stb_node.output_shape());
+ ASSERT_NE(nullptr, stb_node.values());
+ ASSERT_NE(nullptr, stb_node.default_value());
+
+ stb_node.indices(nullptr);
+ stb_node.output_shape(nullptr);
+ stb_node.values(nullptr);
+ stb_node.default_value(nullptr);
+ ASSERT_EQ(nullptr, stb_node.indices());
+ ASSERT_EQ(nullptr, stb_node.output_shape());
+ ASSERT_EQ(nullptr, stb_node.values());
+ ASSERT_EQ(nullptr, stb_node.default_value());
+}
+
+TEST(CircleSparseToDenseTest, arity_NEG)
+{
+ luci::CircleSparseToDense stb_node;
+
+ ASSERT_NO_THROW(stb_node.arg(3));
+ ASSERT_THROW(stb_node.arg(4), std::out_of_range);
+}
+
+TEST(CircleSparseToDenseTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleSparseToDense stb_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(stb_node.accept(&tv), std::exception);
+}
+
+TEST(CircleSparseToDenseTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleSparseToDense stb_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(stb_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSplit.test.cpp b/compiler/luci/lang/src/Nodes/CircleSplit.test.cpp
new file mode 100644
index 000000000..acf8c4410
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSplit.test.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleSplit.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSplitTest, constructor)
+{
+ luci::CircleSplit split_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), split_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SPLIT, split_node.opcode());
+
+ ASSERT_EQ(nullptr, split_node.input());
+ ASSERT_EQ(nullptr, split_node.split_dim());
+ ASSERT_EQ(0, split_node.num_split());
+}
+
+TEST(CircleSplitTest, input_NEG)
+{
+ luci::CircleSplit split_node;
+ luci::CircleSplit node;
+
+ split_node.input(&node);
+ split_node.split_dim(&node);
+ ASSERT_NE(nullptr, split_node.input());
+ ASSERT_NE(nullptr, split_node.split_dim());
+
+ split_node.input(nullptr);
+ split_node.split_dim(nullptr);
+ ASSERT_EQ(nullptr, split_node.input());
+ ASSERT_EQ(nullptr, split_node.split_dim());
+
+ split_node.num_split(100);
+ ASSERT_NE(0, split_node.num_split());
+}
+
+TEST(CircleSplitTest, arity_NEG)
+{
+ luci::CircleSplit split_node;
+
+ ASSERT_NO_THROW(split_node.arg(1));
+ ASSERT_THROW(split_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleSplitTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleSplit split_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(split_node.accept(&tv), std::exception);
+}
+
+TEST(CircleSplitTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleSplit split_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(split_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSplitOut.test.cpp b/compiler/luci/lang/src/Nodes/CircleSplitOut.test.cpp
new file mode 100644
index 000000000..e93715825
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSplitOut.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleSplitOut.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSplitOutTest, constructor)
+{
+ luci::CircleSplitOut vout_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), vout_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::CIRCLESPLITOUT, vout_node.opcode());
+
+ ASSERT_EQ(nullptr, vout_node.input());
+ ASSERT_EQ(-1, vout_node.index());
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSplitV.test.cpp b/compiler/luci/lang/src/Nodes/CircleSplitV.test.cpp
new file mode 100644
index 000000000..1f01608a3
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSplitV.test.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleSplitV.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSplitVTest, constructor)
+{
+ luci::CircleSplitV splitv_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), splitv_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SPLIT_V, splitv_node.opcode());
+
+ ASSERT_EQ(nullptr, splitv_node.input());
+ ASSERT_EQ(nullptr, splitv_node.size_splits());
+ ASSERT_EQ(nullptr, splitv_node.split_dim());
+ ASSERT_EQ(0, splitv_node.num_split());
+}
+
+TEST(CircleSplitVTest, input_NEG)
+{
+ luci::CircleSplitV splitv_node;
+ luci::CircleSplitV node;
+
+ splitv_node.input(&node);
+ splitv_node.size_splits(&node);
+ splitv_node.split_dim(&node);
+ ASSERT_NE(nullptr, splitv_node.input());
+ ASSERT_NE(nullptr, splitv_node.size_splits());
+ ASSERT_NE(nullptr, splitv_node.split_dim());
+
+ splitv_node.input(nullptr);
+ splitv_node.size_splits(nullptr);
+ splitv_node.split_dim(nullptr);
+ ASSERT_EQ(nullptr, splitv_node.input());
+ ASSERT_EQ(nullptr, splitv_node.size_splits());
+ ASSERT_EQ(nullptr, splitv_node.split_dim());
+
+ splitv_node.num_split(100);
+ ASSERT_NE(0, splitv_node.num_split());
+}
+
+TEST(CircleSplitVTest, arity_NEG)
+{
+ luci::CircleSplitV splitv_node;
+
+ ASSERT_NO_THROW(splitv_node.arg(2));
+ ASSERT_THROW(splitv_node.arg(3), std::out_of_range);
+}
+
+TEST(CircleSplitVTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleSplitV splitv_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(splitv_node.accept(&tv), std::exception);
+}
+
+TEST(CircleSplitVTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleSplitV splitv_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(splitv_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSplitVOut.test.cpp b/compiler/luci/lang/src/Nodes/CircleSplitVOut.test.cpp
new file mode 100644
index 000000000..2a4fe3267
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSplitVOut.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleSplitVOut.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSplitVOutTest, constructor)
+{
+ luci::CircleSplitVOut vout_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), vout_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::CIRCLESPLITVOUT, vout_node.opcode());
+
+ ASSERT_EQ(nullptr, vout_node.input());
+ ASSERT_EQ(-1, vout_node.index());
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSqrt.test.cpp b/compiler/luci/lang/src/Nodes/CircleSqrt.test.cpp
new file mode 100644
index 000000000..f4222fd67
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSqrt.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleSqrt.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSqrtTest, constructor_P)
+{
+ luci::CircleSqrt sqrt_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), sqrt_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SQRT, sqrt_node.opcode());
+
+ ASSERT_EQ(nullptr, sqrt_node.x());
+}
+
+TEST(CircleSqrtTest, input_NEG)
+{
+ luci::CircleSqrt sqrt_node;
+ luci::CircleSqrt node;
+
+ sqrt_node.x(&node);
+ ASSERT_NE(nullptr, sqrt_node.x());
+
+ sqrt_node.x(nullptr);
+ ASSERT_EQ(nullptr, sqrt_node.x());
+}
+
+TEST(CircleSqrtTest, arity_NEG)
+{
+ luci::CircleSqrt sqrt_node;
+
+ ASSERT_NO_THROW(sqrt_node.arg(0));
+ ASSERT_THROW(sqrt_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleSqrtTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleSqrt sqrt_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(sqrt_node.accept(&tv), std::exception);
+}
+
+TEST(CircleSqrtTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleSqrt sqrt_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(sqrt_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSquare.test.cpp b/compiler/luci/lang/src/Nodes/CircleSquare.test.cpp
new file mode 100644
index 000000000..3b0a86eed
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSquare.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleSquare.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSquareTest, constructor_P)
+{
+ luci::CircleSquare square_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), square_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SQUARE, square_node.opcode());
+
+ ASSERT_EQ(nullptr, square_node.x());
+}
+
+TEST(CircleSquareTest, input_NEG)
+{
+ luci::CircleSquare square_node;
+ luci::CircleSquare node;
+
+ square_node.x(&node);
+ ASSERT_NE(nullptr, square_node.x());
+
+ square_node.x(nullptr);
+ ASSERT_EQ(nullptr, square_node.x());
+}
+
+TEST(CircleSquareTest, arity_NEG)
+{
+ luci::CircleSquare square_node;
+
+ ASSERT_NO_THROW(square_node.arg(0));
+ ASSERT_THROW(square_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleSquareTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleSquare square_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(square_node.accept(&tv), std::exception);
+}
+
+TEST(CircleSquareTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleSquare square_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(square_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSquaredDifference.test.cpp b/compiler/luci/lang/src/Nodes/CircleSquaredDifference.test.cpp
new file mode 100644
index 000000000..ea632218b
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSquaredDifference.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleSquaredDifference.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSquaredDifferenceTest, constructor_P)
+{
+ luci::CircleSquaredDifference sd_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), sd_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SQUARED_DIFFERENCE, sd_node.opcode());
+
+ ASSERT_EQ(nullptr, sd_node.x());
+ ASSERT_EQ(nullptr, sd_node.y());
+}
+
+TEST(CircleSquaredDifferenceTest, input_NEG)
+{
+ luci::CircleSquaredDifference sd_node;
+ luci::CircleSquaredDifference node;
+
+ sd_node.x(&node);
+ sd_node.y(&node);
+ ASSERT_NE(nullptr, sd_node.x());
+ ASSERT_NE(nullptr, sd_node.y());
+
+ sd_node.x(nullptr);
+ sd_node.y(nullptr);
+ ASSERT_EQ(nullptr, sd_node.x());
+ ASSERT_EQ(nullptr, sd_node.y());
+}
+
+TEST(CircleSquaredDifferenceTest, arity_NEG)
+{
+ luci::CircleSquaredDifference sd_node;
+
+ ASSERT_NO_THROW(sd_node.arg(1));
+ ASSERT_THROW(sd_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleSquaredDifferenceTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleSquaredDifference sd_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(sd_node.accept(&tv), std::exception);
+}
+
+TEST(CircleSquaredDifferenceTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleSquaredDifference sd_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(sd_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSqueeze.test.cpp b/compiler/luci/lang/src/Nodes/CircleSqueeze.test.cpp
new file mode 100644
index 000000000..6dc3d03cd
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSqueeze.test.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleSqueeze.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSqueezeTest, constructor_P)
+{
+ luci::CircleSqueeze squeeze;
+
+ ASSERT_EQ(luci::CircleDialect::get(), squeeze.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SQUEEZE, squeeze.opcode());
+
+ ASSERT_EQ(nullptr, squeeze.input());
+ ASSERT_EQ(0, squeeze.squeeze_dims().size());
+}
+
+TEST(CircleSqueezeTest, squeeze_dims)
+{
+ luci::CircleSqueeze squeeze;
+
+ squeeze.squeeze_dims({1, 2});
+
+ ASSERT_EQ(1, squeeze.squeeze_dims().at(0));
+ ASSERT_EQ(2, squeeze.squeeze_dims().at(1));
+}
+
+TEST(CircleSqueezeTest, input_NEG)
+{
+ luci::CircleSqueeze squeeze_node;
+ luci::CircleSqueeze node;
+
+ squeeze_node.input(&node);
+ ASSERT_NE(nullptr, squeeze_node.input());
+
+ squeeze_node.input(nullptr);
+ ASSERT_EQ(nullptr, squeeze_node.input());
+}
+
+TEST(CircleSqueezeTest, arity_NEG)
+{
+ luci::CircleSqueeze squeeze_node;
+
+ ASSERT_NO_THROW(squeeze_node.arg(0));
+ ASSERT_THROW(squeeze_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleSqueezeTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleSqueeze squeeze_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(squeeze_node.accept(&tv), std::exception);
+}
+
+TEST(CircleSqueezeTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleSqueeze squeeze_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(squeeze_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleStridedSlice.test.cpp b/compiler/luci/lang/src/Nodes/CircleStridedSlice.test.cpp
new file mode 100644
index 000000000..1982e7b38
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleStridedSlice.test.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleStridedSlice.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleStridedSliceTest, constructor)
+{
+ luci::CircleStridedSlice ss_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), ss_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::STRIDED_SLICE, ss_node.opcode());
+
+ ASSERT_EQ(nullptr, ss_node.input());
+ ASSERT_EQ(nullptr, ss_node.begin());
+ ASSERT_EQ(nullptr, ss_node.end());
+ ASSERT_EQ(nullptr, ss_node.strides());
+
+ ASSERT_EQ(0, ss_node.begin_mask());
+ ASSERT_EQ(0, ss_node.end_mask());
+ ASSERT_EQ(0, ss_node.ellipsis_mask());
+ ASSERT_EQ(0, ss_node.new_axis_mask());
+ ASSERT_EQ(0, ss_node.shrink_axis_mask());
+}
+
+TEST(CircleStridedSliceTest, input_NEG)
+{
+ luci::CircleStridedSlice ss_node;
+ luci::CircleStridedSlice node;
+
+ ss_node.input(&node);
+ ss_node.begin(&node);
+ ss_node.end(&node);
+ ss_node.strides(&node);
+ ASSERT_NE(nullptr, ss_node.input());
+ ASSERT_NE(nullptr, ss_node.begin());
+ ASSERT_NE(nullptr, ss_node.end());
+ ASSERT_NE(nullptr, ss_node.strides());
+
+ ss_node.input(nullptr);
+ ss_node.begin(nullptr);
+ ss_node.end(nullptr);
+ ss_node.strides(nullptr);
+ ASSERT_EQ(nullptr, ss_node.input());
+ ASSERT_EQ(nullptr, ss_node.begin());
+ ASSERT_EQ(nullptr, ss_node.end());
+ ASSERT_EQ(nullptr, ss_node.strides());
+
+ ss_node.begin_mask(1);
+ ss_node.end_mask(1);
+ ss_node.ellipsis_mask(1);
+ ss_node.new_axis_mask(1);
+ ss_node.shrink_axis_mask(1);
+ ASSERT_NE(0, ss_node.begin_mask());
+ ASSERT_NE(0, ss_node.end_mask());
+ ASSERT_NE(0, ss_node.ellipsis_mask());
+ ASSERT_NE(0, ss_node.new_axis_mask());
+ ASSERT_NE(0, ss_node.shrink_axis_mask());
+}
+
+TEST(CircleStridedSliceTest, arity_NEG)
+{
+ luci::CircleStridedSlice ss_node;
+
+ ASSERT_NO_THROW(ss_node.arg(3));
+ ASSERT_THROW(ss_node.arg(4), std::out_of_range);
+}
+
+TEST(CircleStridedSliceTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleStridedSlice ss_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(ss_node.accept(&tv), std::exception);
+}
+
+TEST(CircleStridedSliceTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleStridedSlice ss_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(ss_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSub.test.cpp b/compiler/luci/lang/src/Nodes/CircleSub.test.cpp
new file mode 100644
index 000000000..92c674bd0
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSub.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleSub.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSubTest, constructor_P)
+{
+ luci::CircleSub sub_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), sub_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SUB, sub_node.opcode());
+
+ ASSERT_EQ(nullptr, sub_node.x());
+ ASSERT_EQ(nullptr, sub_node.y());
+}
+
+TEST(CircleSubTest, input_NEG)
+{
+ luci::CircleSub sub_node;
+ luci::CircleSub node;
+
+ sub_node.x(&node);
+ sub_node.y(&node);
+ ASSERT_NE(nullptr, sub_node.x());
+ ASSERT_NE(nullptr, sub_node.y());
+
+ sub_node.x(nullptr);
+ sub_node.y(nullptr);
+ ASSERT_EQ(nullptr, sub_node.x());
+ ASSERT_EQ(nullptr, sub_node.y());
+}
+
+TEST(CircleSubTest, arity_NEG)
+{
+ luci::CircleSub sub_node;
+
+ ASSERT_NO_THROW(sub_node.arg(1));
+ ASSERT_THROW(sub_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleSubTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleSub sub_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(sub_node.accept(&tv), std::exception);
+}
+
+TEST(CircleSubTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleSub sub_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(sub_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSum.test.cpp b/compiler/luci/lang/src/Nodes/CircleSum.test.cpp
new file mode 100644
index 000000000..f9d07b200
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSum.test.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleSum.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSumTest, constructor_P)
+{
+ luci::CircleSum sum_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), sum_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::SUM, sum_node.opcode());
+
+ ASSERT_EQ(nullptr, sum_node.input());
+ ASSERT_EQ(nullptr, sum_node.reduction_indices());
+ ASSERT_FALSE(sum_node.keep_dims());
+}
+
+TEST(CircleSumTest, input_NEG)
+{
+ luci::CircleSum sum_node;
+ luci::CircleSum node;
+
+ sum_node.input(&node);
+ sum_node.reduction_indices(&node);
+ ASSERT_NE(nullptr, sum_node.input());
+ ASSERT_NE(nullptr, sum_node.reduction_indices());
+
+ sum_node.input(nullptr);
+ sum_node.reduction_indices(nullptr);
+ ASSERT_EQ(nullptr, sum_node.input());
+ ASSERT_EQ(nullptr, sum_node.reduction_indices());
+
+ sum_node.keep_dims(true);
+ ASSERT_TRUE(sum_node.keep_dims());
+}
+
+TEST(CircleSumTest, arity_NEG)
+{
+ luci::CircleSum sum_node;
+
+ ASSERT_NO_THROW(sum_node.arg(1));
+ ASSERT_THROW(sum_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleSumTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleSum sum_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(sum_node.accept(&tv), std::exception);
+}
+
+TEST(CircleSumTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleSum sum_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(sum_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleTanh.test.cpp b/compiler/luci/lang/src/Nodes/CircleTanh.test.cpp
new file mode 100644
index 000000000..257ecb24d
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleTanh.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleTanh.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleTanhTest, constructor)
+{
+ luci::CircleTanh tanh_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), tanh_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::TANH, tanh_node.opcode());
+
+ ASSERT_EQ(nullptr, tanh_node.x());
+}
+
+TEST(CircleTanhTest, input_NEG)
+{
+ luci::CircleTanh neg_node;
+ luci::CircleTanh node;
+
+ neg_node.x(&node);
+ ASSERT_NE(nullptr, neg_node.x());
+
+ neg_node.x(nullptr);
+ ASSERT_EQ(nullptr, neg_node.x());
+}
+
+TEST(CircleTanhTest, arity_NEG)
+{
+ luci::CircleTanh neg_node;
+
+ ASSERT_NO_THROW(neg_node.arg(0));
+ ASSERT_THROW(neg_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleTanhTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleTanh neg_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(neg_node.accept(&tv), std::exception);
+}
+
+TEST(CircleTanhTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleTanh neg_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(neg_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleTile.test.cpp b/compiler/luci/lang/src/Nodes/CircleTile.test.cpp
new file mode 100644
index 000000000..1695165cc
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleTile.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleTile.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleTileTest, constructor)
+{
+ luci::CircleTile tile_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), tile_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::TILE, tile_node.opcode());
+
+ ASSERT_EQ(nullptr, tile_node.input());
+ ASSERT_EQ(nullptr, tile_node.multiples());
+}
+
+TEST(CircleTileTest, input_NEG)
+{
+ luci::CircleTile tile_node;
+ luci::CircleTile node;
+
+ tile_node.input(&node);
+ tile_node.multiples(&node);
+ ASSERT_NE(nullptr, tile_node.input());
+ ASSERT_NE(nullptr, tile_node.multiples());
+
+ tile_node.input(nullptr);
+ tile_node.multiples(nullptr);
+ ASSERT_EQ(nullptr, tile_node.input());
+ ASSERT_EQ(nullptr, tile_node.multiples());
+}
+
+TEST(CircleTileTest, arity_NEG)
+{
+ luci::CircleTile tile_node;
+
+ ASSERT_NO_THROW(tile_node.arg(1));
+ ASSERT_THROW(tile_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleTileTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleTile tile_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(tile_node.accept(&tv), std::exception);
+}
+
+TEST(CircleTileTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleTile tile_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(tile_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleTopKV2.test.cpp b/compiler/luci/lang/src/Nodes/CircleTopKV2.test.cpp
new file mode 100644
index 000000000..31478d3af
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleTopKV2.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleTopKV2.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleTopKV2Test, constructor)
+{
+ luci::CircleTopKV2 topkv2_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), topkv2_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::TOPK_V2, topkv2_node.opcode());
+
+ ASSERT_EQ(nullptr, topkv2_node.input());
+ ASSERT_EQ(nullptr, topkv2_node.k());
+}
+
+TEST(CircleTopKV2Test, input_NEG)
+{
+ luci::CircleTopKV2 topkv2_node;
+ luci::CircleTopKV2 node;
+
+ topkv2_node.input(&node);
+ topkv2_node.k(&node);
+ ASSERT_NE(nullptr, topkv2_node.input());
+ ASSERT_NE(nullptr, topkv2_node.k());
+
+ topkv2_node.input(nullptr);
+ topkv2_node.k(nullptr);
+ ASSERT_EQ(nullptr, topkv2_node.input());
+ ASSERT_EQ(nullptr, topkv2_node.k());
+}
+
+TEST(CircleTopKV2Test, arity_NEG)
+{
+ luci::CircleTopKV2 topkv2_node;
+
+ ASSERT_NO_THROW(topkv2_node.arg(1));
+ ASSERT_THROW(topkv2_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleTopKV2Test, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleTopKV2 topkv2_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(topkv2_node.accept(&tv), std::exception);
+}
+
+TEST(CircleTopKV2Test, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleTopKV2 topkv2_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(topkv2_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleTopKV2Out.test.cpp b/compiler/luci/lang/src/Nodes/CircleTopKV2Out.test.cpp
new file mode 100644
index 000000000..d0835a27d
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleTopKV2Out.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleTopKV2Out.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleTopKV2OutTest, constructor)
+{
+ luci::CircleTopKV2Out topout_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), topout_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::CIRCLETOPKV2OUT, topout_node.opcode());
+
+ ASSERT_EQ(nullptr, topout_node.input());
+ ASSERT_EQ(-1, topout_node.index());
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleTranspose.test.cpp b/compiler/luci/lang/src/Nodes/CircleTranspose.test.cpp
new file mode 100644
index 000000000..f4db3f37b
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleTranspose.test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleTranspose.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleTransposeTest, constructor_P)
+{
+ luci::CircleTranspose tr_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), tr_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::TRANSPOSE, tr_node.opcode());
+
+ ASSERT_EQ(nullptr, tr_node.a());
+ ASSERT_EQ(nullptr, tr_node.perm());
+}
+
+TEST(CircleTransposeTest, input_NEG)
+{
+ luci::CircleTranspose tr_node;
+ luci::CircleTranspose node;
+
+ tr_node.a(&node);
+ tr_node.perm(&node);
+ ASSERT_NE(nullptr, tr_node.a());
+ ASSERT_NE(nullptr, tr_node.perm());
+
+ tr_node.a(nullptr);
+ tr_node.perm(nullptr);
+ ASSERT_EQ(nullptr, tr_node.a());
+ ASSERT_EQ(nullptr, tr_node.perm());
+}
+
+TEST(CircleTransposeTest, arity_NEG)
+{
+ luci::CircleTranspose tr_node;
+
+ ASSERT_NO_THROW(tr_node.arg(1));
+ ASSERT_THROW(tr_node.arg(2), std::out_of_range);
+}
+
+TEST(CircleTransposeTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleTranspose tr_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(tr_node.accept(&tv), std::exception);
+}
+
+TEST(CircleTransposeTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleTranspose tr_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(tr_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleTransposeConv.test.cpp b/compiler/luci/lang/src/Nodes/CircleTransposeConv.test.cpp
new file mode 100644
index 000000000..3e0db803f
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleTransposeConv.test.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleTransposeConv.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleTransposeConvTest, constructor_P)
+{
+ luci::CircleTransposeConv trc_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), trc_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::TRANSPOSE_CONV, trc_node.opcode());
+
+ ASSERT_EQ(nullptr, trc_node.inputSizes());
+ ASSERT_EQ(nullptr, trc_node.filter());
+ ASSERT_EQ(nullptr, trc_node.outBackprop());
+
+ ASSERT_EQ(luci::Padding::UNDEFINED, trc_node.padding());
+ ASSERT_EQ(1, trc_node.stride()->h());
+ ASSERT_EQ(1, trc_node.stride()->w());
+}
+
+TEST(CircleTransposeConvTest, input_NEG)
+{
+ luci::CircleTransposeConv trc_node;
+ luci::CircleTransposeConv node;
+
+ trc_node.inputSizes(&node);
+ trc_node.filter(&node);
+ trc_node.outBackprop(&node);
+ ASSERT_NE(nullptr, trc_node.inputSizes());
+ ASSERT_NE(nullptr, trc_node.filter());
+ ASSERT_NE(nullptr, trc_node.outBackprop());
+
+ trc_node.inputSizes(nullptr);
+ trc_node.filter(nullptr);
+ trc_node.outBackprop(nullptr);
+ ASSERT_EQ(nullptr, trc_node.inputSizes());
+ ASSERT_EQ(nullptr, trc_node.filter());
+ ASSERT_EQ(nullptr, trc_node.outBackprop());
+
+ trc_node.padding(luci::Padding::SAME);
+ ASSERT_NE(luci::Padding::UNDEFINED, trc_node.padding());
+
+ trc_node.stride()->h(2);
+ trc_node.stride()->w(2);
+ ASSERT_EQ(2, trc_node.stride()->h());
+ ASSERT_EQ(2, trc_node.stride()->w());
+}
+
+TEST(CircleTransposeConvTest, arity_NEG)
+{
+ luci::CircleTransposeConv trc_node;
+
+ ASSERT_NO_THROW(trc_node.arg(3));
+ ASSERT_THROW(trc_node.arg(4), std::out_of_range);
+}
+
+TEST(CircleTransposeConvTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleTransposeConv trc_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(trc_node.accept(&tv), std::exception);
+}
+
+TEST(CircleTransposeConvTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleTransposeConv trc_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(trc_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleUnidirectionalSequenceLSTM.test.cpp b/compiler/luci/lang/src/Nodes/CircleUnidirectionalSequenceLSTM.test.cpp
new file mode 100644
index 000000000..6b00d6f4c
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleUnidirectionalSequenceLSTM.test.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleUnidirectionalSequenceLSTM.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleUnidirectionalSequenceLSTMTest, constructor_P)
+{
+ luci::CircleUnidirectionalSequenceLSTM trc_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), trc_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::UNIDIRECTIONAL_SEQUENCE_LSTM, trc_node.opcode());
+
+ ASSERT_EQ(nullptr, trc_node.input());
+
+ ASSERT_EQ(nullptr, trc_node.input_to_input_weights());
+ ASSERT_EQ(nullptr, trc_node.input_to_forget_weights());
+ ASSERT_EQ(nullptr, trc_node.input_to_cell_weights());
+ ASSERT_EQ(nullptr, trc_node.input_to_output_weights());
+
+ ASSERT_EQ(nullptr, trc_node.recurrent_to_input_weights());
+ ASSERT_EQ(nullptr, trc_node.recurrent_to_forget_weights());
+ ASSERT_EQ(nullptr, trc_node.recurrent_to_cell_weights());
+ ASSERT_EQ(nullptr, trc_node.recurrent_to_output_weights());
+
+ ASSERT_EQ(nullptr, trc_node.cell_to_input_weights());
+ ASSERT_EQ(nullptr, trc_node.cell_to_forget_weights());
+ ASSERT_EQ(nullptr, trc_node.cell_to_output_weights());
+
+ ASSERT_EQ(nullptr, trc_node.input_gate_bias());
+ ASSERT_EQ(nullptr, trc_node.forget_gate_bias());
+ ASSERT_EQ(nullptr, trc_node.cell_gate_bias());
+ ASSERT_EQ(nullptr, trc_node.output_gate_bias());
+
+ ASSERT_EQ(nullptr, trc_node.projection_weights());
+ ASSERT_EQ(nullptr, trc_node.projection_bias());
+
+ ASSERT_EQ(nullptr, trc_node.activation_state());
+ ASSERT_EQ(nullptr, trc_node.cell_state());
+
+ ASSERT_EQ(nullptr, trc_node.input_layer_norm_coefficients());
+ ASSERT_EQ(nullptr, trc_node.forget_layer_norm_coefficients());
+ ASSERT_EQ(nullptr, trc_node.cell_layer_norm_coefficients());
+ ASSERT_EQ(nullptr, trc_node.output_layer_norm_coefficients());
+
+ ASSERT_EQ(luci::FusedActFunc::UNDEFINED, trc_node.fusedActivationFunction());
+ ASSERT_EQ(0.f, trc_node.cell_clip());
+ ASSERT_EQ(0.f, trc_node.proj_clip());
+ ASSERT_EQ(false, trc_node.time_major());
+ ASSERT_EQ(false, trc_node.asymmetric_quantize_inputs());
+}
+
+TEST(CircleUnidirectionalSequenceLSTMTest, arity_NEG)
+{
+ luci::CircleUnidirectionalSequenceLSTM trc_node;
+
+ ASSERT_NO_THROW(trc_node.arg(20));
+ ASSERT_THROW(trc_node.arg(24), std::out_of_range);
+}
+
+TEST(CircleUnidirectionalSequenceLSTMTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleUnidirectionalSequenceLSTM trc_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(trc_node.accept(&tv), std::exception);
+}
+
+TEST(CircleUnidirectionalSequenceLSTMTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleUnidirectionalSequenceLSTM trc_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(trc_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleUnique.test.cpp b/compiler/luci/lang/src/Nodes/CircleUnique.test.cpp
new file mode 100644
index 000000000..517ee97d5
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleUnique.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleUnique.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleUniqueTest, constructor)
+{
+ luci::CircleUnique unique_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), unique_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::UNIQUE, unique_node.opcode());
+
+ ASSERT_EQ(nullptr, unique_node.input());
+}
+
+TEST(CircleUniqueTest, input_NEG)
+{
+ luci::CircleUnique unique_node;
+ luci::CircleUnique node;
+
+ unique_node.input(&node);
+ ASSERT_NE(nullptr, unique_node.input());
+
+ unique_node.input(nullptr);
+ ASSERT_EQ(nullptr, unique_node.input());
+}
+
+TEST(CircleUniqueTest, arity_NEG)
+{
+ luci::CircleUnique unique_node;
+
+ ASSERT_NO_THROW(unique_node.arg(0));
+ ASSERT_THROW(unique_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleUniqueTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleUnique unique_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(unique_node.accept(&tv), std::exception);
+}
+
+TEST(CircleUniqueTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleUnique unique_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(unique_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleUnpack.test.cpp b/compiler/luci/lang/src/Nodes/CircleUnpack.test.cpp
new file mode 100644
index 000000000..4323028e4
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleUnpack.test.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleUnpack.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleUnpackTest, constructor)
+{
+ luci::CircleUnpack unpack_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), unpack_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::UNPACK, unpack_node.opcode());
+
+ ASSERT_EQ(nullptr, unpack_node.value());
+ ASSERT_EQ(0, unpack_node.num());
+ ASSERT_EQ(0, unpack_node.axis());
+}
+
+TEST(CircleUnpackTest, input_NEG)
+{
+ luci::CircleUnpack unpack_node;
+ luci::CircleUnpack node;
+
+ unpack_node.value(&node);
+ ASSERT_NE(nullptr, unpack_node.value());
+
+ unpack_node.value(nullptr);
+ ASSERT_EQ(nullptr, unpack_node.value());
+
+ unpack_node.num(1);
+ unpack_node.axis(1);
+ ASSERT_NE(0, unpack_node.num());
+ ASSERT_NE(0, unpack_node.axis());
+}
+
+TEST(CircleUnpackTest, arity_NEG)
+{
+ luci::CircleUnpack unpack_node;
+
+ ASSERT_NO_THROW(unpack_node.arg(0));
+ ASSERT_THROW(unpack_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleUnpackTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleUnpack unpack_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(unpack_node.accept(&tv), std::exception);
+}
+
+TEST(CircleUnpackTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleUnpack unpack_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(unpack_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleUnpackOut.test.cpp b/compiler/luci/lang/src/Nodes/CircleUnpackOut.test.cpp
new file mode 100644
index 000000000..7b8a41bf7
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleUnpackOut.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleUnpackOut.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleUnpackOutTest, constructor)
+{
+ luci::CircleUnpackOut unpackout_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), unpackout_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::CIRCLEUNPACKOUT, unpackout_node.opcode());
+
+ ASSERT_EQ(nullptr, unpackout_node.input());
+ ASSERT_EQ(0, unpackout_node.index());
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleWhere.test.cpp b/compiler/luci/lang/src/Nodes/CircleWhere.test.cpp
new file mode 100644
index 000000000..287eda460
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleWhere.test.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleWhere.h"
+#include "luci/IR/Nodes/CircleInput.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleWhereTest, constructor_P)
+{
+ luci::CircleWhere where_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), where_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::WHERE, where_node.opcode());
+
+ ASSERT_EQ(1, where_node.arity());
+ ASSERT_EQ(nullptr, where_node.condition());
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleWhile.test.cpp b/compiler/luci/lang/src/Nodes/CircleWhile.test.cpp
new file mode 100644
index 000000000..913686fbd
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleWhile.test.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleWhile.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleWhileTest, constructor)
+{
+ luci::CircleWhile while_node(2, 2);
+
+ ASSERT_EQ(luci::CircleDialect::get(), while_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::WHILE, while_node.opcode());
+
+ ASSERT_EQ(2, while_node.input_count());
+ ASSERT_EQ(2, while_node.output_count());
+
+ ASSERT_EQ(nullptr, while_node.input(0));
+ ASSERT_EQ(nullptr, while_node.input(1));
+
+ ASSERT_EQ(-1, while_node.cond_branch());
+ ASSERT_EQ(-1, while_node.body_branch());
+}
+
+TEST(CircleWhileTestDeath, invalid_arity_NEG)
+{
+ ASSERT_DEBUG_DEATH(luci::CircleWhile very_long_name_while_node(0, 1), "");
+
+ SUCCEED();
+}
+
+TEST(CircleWhileTestDeath, invalid_output_count_NEG)
+{
+ ASSERT_DEBUG_DEATH(luci::CircleWhile while_node(2, 0), "");
+
+ SUCCEED();
+}
+
+TEST(CircleWhileTestDeath, invalid_input_get_index_NEG)
+{
+ luci::CircleWhile while_node(2, 2);
+
+ EXPECT_ANY_THROW(while_node.input(100));
+}
+
+TEST(CircleWhileTestDeath, invalid_input_set_index_NEG)
+{
+ luci::CircleWhile while_node(2, 2);
+
+ EXPECT_ANY_THROW(while_node.input(100, nullptr));
+}
+
+TEST(CircleWhileTestDeath, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleWhile while_node(2, 2);
+
+ TestVisitor tv;
+ ASSERT_THROW(while_node.accept(&tv), std::exception);
+}
+
+TEST(CircleWhileTestDeath, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleWhile while_node(2, 2);
+
+ TestVisitor tv;
+ ASSERT_THROW(while_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleWhileOut.test.cpp b/compiler/luci/lang/src/Nodes/CircleWhileOut.test.cpp
new file mode 100644
index 000000000..1800e4098
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleWhileOut.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleWhileOut.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleWhileOutTest, constructor)
+{
+ luci::CircleWhileOut whileout_node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), whileout_node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::CIRCLEWHILEOUT, whileout_node.opcode());
+
+ ASSERT_EQ(nullptr, whileout_node.input());
+ ASSERT_EQ(-1, whileout_node.index());
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleZerosLike.test.cpp b/compiler/luci/lang/src/Nodes/CircleZerosLike.test.cpp
new file mode 100644
index 000000000..3368c8e3f
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleZerosLike.test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/IR/Nodes/CircleZerosLike.h"
+
+#include "luci/IR/CircleDialect.h"
+#include "luci/IR/CircleNodeVisitor.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleZerosLikeTest, constructor_P)
+{
+ luci::CircleZerosLike node;
+
+ ASSERT_EQ(luci::CircleDialect::get(), node.dialect());
+ ASSERT_EQ(luci::CircleOpcode::ZEROS_LIKE, node.opcode());
+
+ ASSERT_EQ(nullptr, node.input());
+}
+
+TEST(CircleZerosLikeTest, input_NEG)
+{
+ luci::CircleZerosLike zeros_node;
+ luci::CircleZerosLike node;
+
+ zeros_node.input(&node);
+ ASSERT_NE(nullptr, zeros_node.input());
+
+ zeros_node.input(nullptr);
+ ASSERT_EQ(nullptr, zeros_node.input());
+}
+
+TEST(CircleZerosLikeTest, arity_NEG)
+{
+ luci::CircleZerosLike zeros_node;
+
+ ASSERT_NO_THROW(zeros_node.arg(0));
+ ASSERT_THROW(zeros_node.arg(1), std::out_of_range);
+}
+
+TEST(CircleZerosLikeTest, visit_mutable_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeMutableVisitor<void>
+ {
+ };
+
+ luci::CircleZerosLike zeros_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(zeros_node.accept(&tv), std::exception);
+}
+
+TEST(CircleZerosLikeTest, visit_NEG)
+{
+ struct TestVisitor final : public luci::CircleNodeVisitor<void>
+ {
+ };
+
+ luci::CircleZerosLike zeros_node;
+
+ TestVisitor tv;
+ ASSERT_THROW(zeros_node.accept(&tv), std::exception);
+}
diff --git a/compiler/luci/log/CMakeLists.txt b/compiler/luci/log/CMakeLists.txt
new file mode 100644
index 000000000..5e822871b
--- /dev/null
+++ b/compiler/luci/log/CMakeLists.txt
@@ -0,0 +1,10 @@
+# TODO Find how to test logging framework
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+add_library(luci_log SHARED ${SOURCES})
+target_include_directories(luci_log PUBLIC include)
+target_link_libraries(luci_log PUBLIC hermes)
+target_link_libraries(luci_log PRIVATE hermes_std)
+target_link_libraries(luci_log PRIVATE nncc_common)
+target_link_libraries(luci_log PRIVATE luci_env)
+install(TARGETS luci_log DESTINATION lib)
diff --git a/compiler/luci/log/README.md b/compiler/luci/log/README.md
new file mode 100644
index 000000000..512bc96d2
--- /dev/null
+++ b/compiler/luci/log/README.md
@@ -0,0 +1,3 @@
+# luci-log
+
+_luci-log_ is a logging framework for _luci_ compiler framework.
diff --git a/compiler/luci/log/include/luci/Log.h b/compiler/luci/log/include/luci/Log.h
new file mode 100644
index 000000000..e148810d8
--- /dev/null
+++ b/compiler/luci/log/include/luci/Log.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_LOG_H__
+#define __LUCI_LOG_H__
+
+#include <hermes.h>
+
+namespace luci
+{
+
+/**
+ * @brief Logger Implementation
+ */
+class Logger final : public hermes::Source
+{
+public:
+ Logger(hermes::Context *ctx);
+ ~Logger();
+};
+
+/**
+ * @brief Logger Configuration
+ *
+ * Users are able to turn logging on/off via LUCI_LOG environment variable.
+ */
+class LoggerConfig final : public hermes::Config
+{
+public:
+ LoggerConfig();
+
+public:
+ void configure(const hermes::Source *, hermes::Source::Setting &) const final;
+ void configure(const Logger *, hermes::Source::Setting &) const;
+
+private:
+ bool _show_warn = true;
+ bool _show_info = false;
+ int _show_verbose = 0;
+};
+
+} // namespace luci
+
+#include "luci/LoggingContext.h"
+
+/**
+ * HOW TO USE:
+ *
+ * LOGGER(l);
+ *
+ * INFO(l) << "Hello, World" << std::endl;
+ *
+ */
+#define LOGGER(name) ::luci::Logger name{::luci::LoggingContext::get()};
+
+// TODO Support FATAL, ERROR
+#define INFO(name) HERMES_INFO(name)
+#define WARN(name) HERMES_WARN(name)
+#define VERBOSE(name, lv) HERMES_VERBOSE(name, lv)
+
+// WARNING!
+//
+// THE CURRENT IMPLEMENTATION IS NOT THREAD SAFE.
+//
+
+#endif // __LUCI_LOG_H__
diff --git a/compiler/luci/log/include/luci/LoggingContext.h b/compiler/luci/log/include/luci/LoggingContext.h
new file mode 100644
index 000000000..f5091099f
--- /dev/null
+++ b/compiler/luci/log/include/luci/LoggingContext.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_LOGGING_CONTEXT_H__
+#define __LUCI_LOGGING_CONTEXT_H__
+
+#include <hermes.h>
+
+namespace luci
+{
+
+/**
+ * @brief Global logging context
+ */
+struct LoggingContext
+{
+ static hermes::Context *get(void);
+};
+
+} // namespace luci
+
+#endif // __LUCI_LOGGING_CONTEXT_H__
diff --git a/compiler/luci/log/src/Log.cpp b/compiler/luci/log/src/Log.cpp
new file mode 100644
index 000000000..c26bf307b
--- /dev/null
+++ b/compiler/luci/log/src/Log.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Log.h"
+
+#include <luci/UserSettings.h>
+
+#include <cassert>
+#include <cstdlib>
+#include <iostream>
+
+// TODO Extract these lexical conversion routines as a library
+namespace
+{
+
+/**
+ * @brief Convert C-string as a value of type T
+ *
+ * safecast(s, v) returns v if s is nullptr.
+ */
+template <typename T> T safecast(const char *, const T &);
+
+template <> bool safecast<bool>(const char *s, const bool &value)
+{
+ return (s == nullptr) ? value : (std::stoi(s) != 0);
+}
+
+template <> int safecast<int>(const char *s, const int &value)
+{
+ return (s == nullptr) ? value : std::stoi(s);
+}
+
+} // namespace
+
+//
+// Logger
+//
+namespace luci
+{
+
+Logger::Logger(hermes::Context *ctx) { activate(ctx->sources(), ctx->bus()); }
+Logger::~Logger() { deactivate(); }
+
+} // namespace luci
+
+//
+// LoggerConfig
+//
+namespace luci
+{
+
+LoggerConfig::LoggerConfig()
+{
+ auto settings = luci::UserSettings::settings();
+
+ _show_warn = !settings->get(luci::UserSettings::Key::MuteWarnings);
+
+ // Turn on info logging if LUCI_LOG is set as non-zero value
+ _show_info = safecast<bool>(std::getenv("LUCI_LOG"), false);
+
+ // Turn on verbose logging if LUCI_LOG is set to some level
+ // VERBOSE(l, 1) will be visible with LUCI_LOG=2 and VERBOSE(l, 2) with LUCI_LOG=3 and so on
+ _show_verbose = safecast<int>(std::getenv("LUCI_LOG"), 0);
+}
+
+void LoggerConfig::configure(const hermes::Source *source, hermes::Source::Setting &setting) const
+{
+ // Let's ignore hermes::Sources if that is not a moco logger
+ if (auto logger = dynamic_cast<const Logger *>(source))
+ {
+ configure(logger, setting);
+ }
+}
+
+void LoggerConfig::configure(const Logger *, hermes::Source::Setting &setting) const
+{
+ setting.filter(hermes::SeverityCategory::FATAL).reject_all();
+ setting.filter(hermes::SeverityCategory::ERROR).reject_all();
+ setting.filter(hermes::SeverityCategory::WARN).reject_all();
+ setting.filter(hermes::SeverityCategory::INFO).reject_all();
+ setting.filter(hermes::SeverityCategory::VERBOSE).reject_all();
+
+ // TODO enable FATAL and ERROR
+ if (_show_warn)
+ {
+ setting.filter(hermes::SeverityCategory::WARN).accept_all();
+ }
+ if (_show_info)
+ {
+ setting.filter(hermes::SeverityCategory::INFO).accept_all();
+ }
+ if (_show_verbose)
+ {
+ setting.filter(hermes::SeverityCategory::VERBOSE).accept_upto(_show_verbose);
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/log/src/LoggingContext.cpp b/compiler/luci/log/src/LoggingContext.cpp
new file mode 100644
index 000000000..8d7997869
--- /dev/null
+++ b/compiler/luci/log/src/LoggingContext.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/LoggingContext.h"
+#include "luci/Log.h"
+
+#include <hermes/ConsoleReporter.h>
+
+#include <memory>
+
+namespace luci
+{
+
+hermes::Context *LoggingContext::get(void)
+{
+ static hermes::Context *ctx = nullptr;
+
+ if (ctx == nullptr)
+ {
+ ctx = new hermes::Context;
+ ctx->sinks()->append(std::make_unique<hermes::ConsoleReporter>());
+ ctx->config(std::make_unique<LoggerConfig>());
+ }
+
+ return ctx;
+}
+
+} // namespace luci
diff --git a/compiler/luci/logex/CMakeLists.txt b/compiler/luci/logex/CMakeLists.txt
new file mode 100644
index 000000000..fa2ea123c
--- /dev/null
+++ b/compiler/luci/logex/CMakeLists.txt
@@ -0,0 +1,13 @@
+# TODO Find how to test logging-ex utility
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+add_library(luci_logex SHARED ${SOURCES})
+target_include_directories(luci_logex PUBLIC include)
+target_link_libraries(luci_logex PUBLIC loco)
+target_link_libraries(luci_logex PUBLIC locop)
+target_link_libraries(luci_logex PRIVATE luci_log)
+target_link_libraries(luci_logex PRIVATE luci_lang)
+target_link_libraries(luci_logex PRIVATE hermes_std)
+target_link_libraries(luci_logex PRIVATE nncc_common)
+target_link_libraries(luci_logex PRIVATE pepper_str)
+install(TARGETS luci_logex DESTINATION lib)
diff --git a/compiler/luci/logex/README.md b/compiler/luci/logex/README.md
new file mode 100644
index 000000000..03b6baf35
--- /dev/null
+++ b/compiler/luci/logex/README.md
@@ -0,0 +1,3 @@
+# luci-logex
+
+_luci-logex_ is a extended logging utility for _luci_ compiler framework.
diff --git a/compiler/luci/logex/include/luci/FormattedGraph.h b/compiler/luci/logex/include/luci/FormattedGraph.h
new file mode 100644
index 000000000..da4af3bfa
--- /dev/null
+++ b/compiler/luci/logex/include/luci/FormattedGraph.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_FORMATTED_GRAPH_H__
+#define __LUCI_FORMATTED_GRAPH_H__
+
+#include <locop/FormattedGraph.h>
+
+#include <memory>
+
+namespace luci
+{
+
+class NodeSummaryBuilder final : public locop::NodeSummaryBuilder
+{
+public:
+ NodeSummaryBuilder(const locop::SymbolTable *tbl) : _tbl{tbl}
+ {
+ // DO NOTHING
+ }
+
+public:
+ bool build(const loco::Node *node, locop::NodeSummary &s) const final;
+
+private:
+ const locop::SymbolTable *_tbl;
+};
+
+class NodeSummaryBuilderFactory final : public locop::NodeSummaryBuilderFactory
+{
+public:
+ NodeSummaryBuilderFactory() = default;
+
+public:
+ std::unique_ptr<locop::NodeSummaryBuilder> create(const locop::SymbolTable *tlb) const final
+ {
+ return std::make_unique<NodeSummaryBuilder>(tlb);
+ }
+};
+
+} // namespace luci
+
+#endif // __LUCI_FORMATTED_GRAPH_H__
diff --git a/compiler/luci/logex/include/luci/LogHelper.h b/compiler/luci/logex/include/luci/LogHelper.h
new file mode 100644
index 000000000..37cdd735b
--- /dev/null
+++ b/compiler/luci/logex/include/luci/LogHelper.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_LOG_HELPER_H__
+#define __LUCI_LOG_HELPER_H__
+
+#include <locop/FormattedGraph.h>
+#include <loco.h>
+
+#include <memory>
+
+namespace luci
+{
+
+using FormattedGraph = locop::FormattedGraphImpl<locop::Formatter::LinearV1>;
+
+FormattedGraph fmt(loco::Graph *g);
+
+static inline FormattedGraph fmt(const std::unique_ptr<loco::Graph> &g) { return fmt(g.get()); }
+
+} // namespace luci
+
+#endif // __LUCI_LOG_HELPER_H__
diff --git a/compiler/luci/logex/src/FormattedGraph.cpp b/compiler/luci/logex/src/FormattedGraph.cpp
new file mode 100644
index 000000000..b2b9cb72b
--- /dev/null
+++ b/compiler/luci/logex/src/FormattedGraph.cpp
@@ -0,0 +1,1891 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/FormattedGraph.h"
+
+#include <luci/IR/CircleDialect.h>
+#include <luci/IR/CircleNodes.h>
+
+#include <pepper/str.h>
+
+#include <cassert>
+#include <sstream>
+#include <vector>
+
+/**
+ * @brief dump std::vector<int64_t> values to stream
+ */
+std::ostream &operator<<(std::ostream &os, const std::vector<int64_t> &vi64)
+{
+ for (auto vi : vi64)
+ {
+ os << vi << " ";
+ }
+ return os;
+}
+
+// For TF lite
+namespace
+{
+
+const char *to_str(loco::DataType type)
+{
+ switch (type)
+ {
+ case loco::DataType::U8:
+ return "UINT8";
+ case loco::DataType::U16:
+ return "UINT16";
+ case loco::DataType::U32:
+ return "UINT32";
+ case loco::DataType::U64:
+ return "UINT64";
+
+ case loco::DataType::S8:
+ return "INT8";
+ case loco::DataType::S16:
+ return "INT16";
+ case loco::DataType::S32:
+ return "INT32";
+ case loco::DataType::S64:
+ return "INT64";
+
+ case loco::DataType::FLOAT16:
+ return "FLOAT16";
+ case loco::DataType::FLOAT32:
+ return "FLOAT32";
+ case loco::DataType::FLOAT64:
+ return "FLOAT64";
+
+ case loco::DataType::BOOL:
+ return "BOOL";
+
+ default:
+ return "Error";
+ }
+}
+
+const char *to_str(bool value) { return value ? "true" : "false"; }
+
+const char *to_str(luci::FusedActFunc fused)
+{
+ switch (fused)
+ {
+ case luci::FusedActFunc::NONE:
+ return "NONE";
+ case luci::FusedActFunc::RELU:
+ return "RELU";
+ case luci::FusedActFunc::RELU_N1_TO_1:
+ return "RELU_N1_TO_1";
+ case luci::FusedActFunc::RELU6:
+ return "RELU6";
+ case luci::FusedActFunc::TANH:
+ return "TANH";
+ case luci::FusedActFunc::SIGN_BIT:
+ return "SIGN_BIT";
+ default:
+ return "Error";
+ }
+}
+
+const char *to_str(luci::Padding padding)
+{
+ switch (padding)
+ {
+ case luci::Padding::SAME:
+ return "SAME";
+ case luci::Padding::VALID:
+ return "VALID";
+ default:
+ return "Error";
+ }
+}
+
+const char *to_str(luci::MirrorPadMode mode)
+{
+ switch (mode)
+ {
+ case luci::MirrorPadMode::REFLECT:
+ return "REFLECT";
+ case luci::MirrorPadMode::SYMMETRIC:
+ return "SYMMETRIC";
+ default:
+ return "Error";
+ }
+}
+
+std::string to_str(const luci::Stride *stride)
+{
+ return pepper::str(stride->h(), ",", stride->w());
+}
+
+std::string to_str(const luci::Filter *filter)
+{
+ return pepper::str(filter->h(), ",", filter->w());
+}
+
+std::string circle_opname(uint32_t opnum)
+{
+ static const std::string prefix{"circle."};
+
+ switch (static_cast<luci::CircleOpcode>(opnum))
+ {
+#define CIRCLE_NODE(OPCODE, CLASS) \
+ case luci::CircleOpcode::OPCODE: \
+ return prefix + #OPCODE;
+#include <luci/IR/CircleNodes.lst>
+#undef CIRCLE_NODE
+ default:
+ break;
+ };
+
+ return prefix + "Invalid";
+}
+
+// CircleNodeSummaryBuilder with default implementation
+class CircleNodeSummaryBuilderBase : public locop::NodeSummaryBuilder
+{
+public:
+ CircleNodeSummaryBuilderBase(const locop::SymbolTable *tbl) : _tbl{tbl}
+ {
+ // DO NOTHING
+ }
+
+public:
+ bool build(const loco::Node *, locop::NodeSummary &s) const final;
+
+protected:
+#define CIRCLE_NODE(OPCODE, CLASS) \
+ virtual bool summary(const CLASS *, locop::NodeSummary &s) const \
+ { \
+ s.comments().append("Emitted by Default CircleNodeSummaryBuilder"); \
+ s.state(locop::NodeSummary::State::PartiallyKnown); \
+ return true; \
+ }
+#include <luci/IR/CircleNodes.lst>
+#undef CIRCLE_NODE
+
+protected:
+ const locop::SymbolTable *tbl(void) const { return _tbl; }
+
+ // Please do not use _tbl directly and use tbl().
+ // This will be changed to private in near future.
+protected:
+ const locop::SymbolTable *_tbl;
+};
+
+class CircleNodeSummaryBuilder final : public CircleNodeSummaryBuilderBase
+{
+public:
+ CircleNodeSummaryBuilder(const locop::SymbolTable *tbl) : CircleNodeSummaryBuilderBase(tbl)
+ {
+ // DO NOTHING
+ }
+
+private:
+#define IMPLEMENT(CLASS) bool summary(const CLASS *, locop::NodeSummary &) const final;
+ IMPLEMENT(luci::CircleAbs)
+ IMPLEMENT(luci::CircleAdd)
+ IMPLEMENT(luci::CircleAddN)
+ IMPLEMENT(luci::CircleArgMax)
+ IMPLEMENT(luci::CircleArgMin)
+ IMPLEMENT(luci::CircleAveragePool2D)
+ IMPLEMENT(luci::CircleBatchMatMul)
+ IMPLEMENT(luci::CircleBatchToSpaceND)
+ IMPLEMENT(luci::CircleCast)
+ IMPLEMENT(luci::CircleCeil)
+ IMPLEMENT(luci::CircleConcatenation)
+ IMPLEMENT(luci::CircleConst)
+ IMPLEMENT(luci::CircleConv2D)
+ IMPLEMENT(luci::CircleCos)
+ IMPLEMENT(luci::CircleCustom)
+ IMPLEMENT(luci::CircleDepthToSpace)
+ IMPLEMENT(luci::CircleDepthwiseConv2D)
+ IMPLEMENT(luci::CircleDequantize)
+ IMPLEMENT(luci::CircleDiv)
+ IMPLEMENT(luci::CircleElu)
+ IMPLEMENT(luci::CircleExp)
+ IMPLEMENT(luci::CircleExpandDims)
+ IMPLEMENT(luci::CircleFill)
+ IMPLEMENT(luci::CircleFloor)
+ IMPLEMENT(luci::CircleFloorDiv)
+ IMPLEMENT(luci::CircleFloorMod)
+ IMPLEMENT(luci::CircleFullyConnected)
+ IMPLEMENT(luci::CircleGather)
+ IMPLEMENT(luci::CircleGatherNd)
+ IMPLEMENT(luci::CircleGreater)
+ IMPLEMENT(luci::CircleGreaterEqual)
+ IMPLEMENT(luci::CircleIf)
+ IMPLEMENT(luci::CircleL2Normalize)
+ IMPLEMENT(luci::CircleLeakyRelu)
+ IMPLEMENT(luci::CircleLess)
+ IMPLEMENT(luci::CircleLessEqual)
+ IMPLEMENT(luci::CircleLocalResponseNormalization)
+ IMPLEMENT(luci::CircleLog)
+ IMPLEMENT(luci::CircleLogicalAnd)
+ IMPLEMENT(luci::CircleLogicalNot)
+ IMPLEMENT(luci::CircleLogicalOr)
+ IMPLEMENT(luci::CircleLogistic)
+ IMPLEMENT(luci::CircleLogSoftmax)
+ IMPLEMENT(luci::CircleMatrixDiag)
+ IMPLEMENT(luci::CircleMatrixSetDiag)
+ IMPLEMENT(luci::CircleMaximum)
+ IMPLEMENT(luci::CircleMaxPool2D)
+ IMPLEMENT(luci::CircleMean)
+ IMPLEMENT(luci::CircleMinimum)
+ IMPLEMENT(luci::CircleMirrorPad)
+ IMPLEMENT(luci::CircleMul)
+ IMPLEMENT(luci::CircleNeg)
+ IMPLEMENT(luci::CircleNonMaxSuppressionV4)
+ IMPLEMENT(luci::CircleNonMaxSuppressionV5)
+ IMPLEMENT(luci::CircleNotEqual)
+ IMPLEMENT(luci::CircleOneHot)
+ IMPLEMENT(luci::CirclePack)
+ IMPLEMENT(luci::CirclePad)
+ IMPLEMENT(luci::CirclePadV2)
+ IMPLEMENT(luci::CirclePow)
+ IMPLEMENT(luci::CirclePRelu)
+ IMPLEMENT(luci::CircleRange)
+ IMPLEMENT(luci::CircleRank)
+ IMPLEMENT(luci::CircleReduceAny)
+ IMPLEMENT(luci::CircleReduceMax)
+ IMPLEMENT(luci::CircleReduceMin)
+ IMPLEMENT(luci::CircleReduceProd)
+ IMPLEMENT(luci::CircleRelu)
+ IMPLEMENT(luci::CircleRelu6)
+ IMPLEMENT(luci::CircleReluN1To1)
+ IMPLEMENT(luci::CircleReshape)
+ IMPLEMENT(luci::CircleResizeBilinear)
+ IMPLEMENT(luci::CircleResizeNearestNeighbor)
+ IMPLEMENT(luci::CircleReverseSequence)
+ IMPLEMENT(luci::CircleReverseV2)
+ IMPLEMENT(luci::CircleRound)
+ IMPLEMENT(luci::CircleRsqrt)
+ IMPLEMENT(luci::CircleScatterNd)
+ IMPLEMENT(luci::CircleSegmentSum)
+ IMPLEMENT(luci::CircleSelect)
+ IMPLEMENT(luci::CircleSelectV2)
+ IMPLEMENT(luci::CircleShape)
+ IMPLEMENT(luci::CircleSin)
+ IMPLEMENT(luci::CircleSlice)
+ IMPLEMENT(luci::CircleSoftmax)
+ IMPLEMENT(luci::CircleSpaceToBatchND)
+ IMPLEMENT(luci::CircleSpaceToDepth)
+ IMPLEMENT(luci::CircleSparseToDense)
+ IMPLEMENT(luci::CircleSplit)
+ IMPLEMENT(luci::CircleSplitV)
+ IMPLEMENT(luci::CircleSqrt)
+ IMPLEMENT(luci::CircleSquare)
+ IMPLEMENT(luci::CircleSquaredDifference)
+ IMPLEMENT(luci::CircleSqueeze)
+ IMPLEMENT(luci::CircleStridedSlice)
+ IMPLEMENT(luci::CircleSub)
+ IMPLEMENT(luci::CircleSum)
+ IMPLEMENT(luci::CircleTanh)
+ IMPLEMENT(luci::CircleTile)
+ IMPLEMENT(luci::CircleTopKV2)
+ IMPLEMENT(luci::CircleTranspose)
+ IMPLEMENT(luci::CircleTransposeConv)
+ IMPLEMENT(luci::CircleUnidirectionalSequenceLSTM)
+ IMPLEMENT(luci::CircleUnique)
+ IMPLEMENT(luci::CircleUnpack)
+ IMPLEMENT(luci::CircleWhere)
+ IMPLEMENT(luci::CircleWhile)
+ IMPLEMENT(luci::CircleZerosLike)
+ // Circle Only
+ IMPLEMENT(luci::CircleBCQFullyConnected)
+ IMPLEMENT(luci::CircleBCQGather)
+ IMPLEMENT(luci::CircleInstanceNorm)
+ // Virtual nodes
+ IMPLEMENT(luci::CircleInput)
+ IMPLEMENT(luci::CircleOutput)
+ IMPLEMENT(luci::CircleIfOut)
+ IMPLEMENT(luci::CircleNonMaxSuppressionV4Out)
+ IMPLEMENT(luci::CircleNonMaxSuppressionV5Out)
+ IMPLEMENT(luci::CircleSplitOut)
+ IMPLEMENT(luci::CircleSplitVOut)
+ IMPLEMENT(luci::CircleTopKV2Out)
+ IMPLEMENT(luci::CircleUniqueOut)
+ IMPLEMENT(luci::CircleUnpackOut)
+ IMPLEMENT(luci::CircleWhileOut)
+#undef IMPLEMENT
+};
+
+template <class CIRCLENODE>
+bool use_x(const locop::SymbolTable *tbl, const CIRCLENODE *node, locop::NodeSummary &s)
+{
+ s.args().append("x", tbl->lookup(node->x()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+template <class CIRCLENODE>
+bool use_input(const locop::SymbolTable *tbl, const CIRCLENODE *node, locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+template <class CIRCLENODE>
+bool use_features(const locop::SymbolTable *tbl, const CIRCLENODE *node, locop::NodeSummary &s)
+{
+ s.args().append("features", tbl->lookup(node->features()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+template <class CIRCLENODE>
+bool use_xy(const locop::SymbolTable *tbl, const CIRCLENODE *node, locop::NodeSummary &s)
+{
+ s.args().append("x", tbl->lookup(node->x()));
+ s.args().append("y", tbl->lookup(node->y()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+template <class CIRCLENODE>
+bool use_xy_act(const locop::SymbolTable *tbl, const CIRCLENODE *node, locop::NodeSummary &s)
+{
+ assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED);
+
+ s.args().append("x", tbl->lookup(node->x()));
+ s.args().append("y", tbl->lookup(node->y()));
+ s.args().append("fused_activation_function", to_str(node->fusedActivationFunction()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+template <class CIRCLENODE>
+bool use_reducer(const locop::SymbolTable *tbl, const CIRCLENODE *node, locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("reduction_indices", tbl->lookup(node->reduction_indices()));
+ s.args().append("keep_dims", node->keep_dims() ? "true" : "false");
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+template <class CIRCLENODE>
+bool use_ido(const locop::SymbolTable *tbl, const CIRCLENODE *node, locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("dimension", tbl->lookup(node->dimension()));
+ s.args().append("output_type", to_str(node->output_type()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleAddN *node,
+ locop::NodeSummary &s)
+{
+ for (uint32_t i = 0; i < node->arity(); ++i)
+ s.args().append("inputs", tbl->lookup(node->inputs(i)));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleAveragePool2D *node,
+ locop::NodeSummary &s)
+{
+ assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED);
+
+ s.args().append("value", tbl->lookup(node->value()));
+ s.args().append("filter(h,w)", to_str(node->filter()));
+ s.args().append("stride(h,w)", to_str(node->stride()));
+ s.args().append("padding", to_str(node->padding()));
+ s.args().append("fused", to_str(node->fusedActivationFunction()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleBatchMatMul *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("x", tbl->lookup(node->x()));
+ s.args().append("y", tbl->lookup(node->y()));
+ s.args().append("adj_x", to_str(node->adj_x()));
+ s.args().append("adj_y", to_str(node->adj_y()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleBatchToSpaceND *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("block_shape", tbl->lookup(node->block_shape()));
+ s.args().append("crops", tbl->lookup(node->crops()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleCast *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("x", tbl->lookup(node->x()));
+ s.args().append("in_data_type", to_str(node->in_data_type()));
+ s.args().append("out_data_type", to_str(node->out_data_type()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleConcatenation *node,
+ locop::NodeSummary &s)
+{
+ assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED);
+
+ for (uint32_t i = 0; i < node->numValues(); ++i)
+ s.args().append("values", tbl->lookup(node->values(i)));
+ s.args().append("axis", pepper::str(node->axis()));
+ s.args().append("fused", to_str(node->fusedActivationFunction()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleConv2D *node,
+ locop::NodeSummary &s)
+{
+ assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED);
+ assert(node->padding() != luci::Padding::UNDEFINED);
+
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("filter", tbl->lookup(node->filter()));
+ s.args().append("bias", tbl->lookup(node->bias()));
+ s.args().append("stride(h,w)", to_str(node->stride()));
+ s.args().append("dilation(h,w)", to_str(node->dilation()));
+ s.args().append("padding", to_str(node->padding()));
+ s.args().append("fused", to_str(node->fusedActivationFunction()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleCustom *node,
+ locop::NodeSummary &s)
+{
+ for (uint32_t i = 0; i < node->numInputs(); i++)
+ {
+ s.args().append("input" + std::to_string(i), tbl->lookup(node->inputs(i)));
+ }
+ s.args().append("custom_code", node->custom_code());
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleDepthToSpace *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("block_size", std::to_string(node->block_size()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleDepthwiseConv2D *node,
+ locop::NodeSummary &s)
+{
+ assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED);
+ assert(node->padding() != luci::Padding::UNDEFINED);
+
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("filter", tbl->lookup(node->filter()));
+ s.args().append("bias", tbl->lookup(node->bias()));
+ s.args().append("stride(h,w)", to_str(node->stride()));
+ s.args().append("dilation(h,w)", to_str(node->dilation()));
+ s.args().append("padding", to_str(node->padding()));
+ s.args().append("depthMultiplier", std::to_string(node->depthMultiplier()));
+ s.args().append("fused", to_str(node->fusedActivationFunction()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleExpandDims *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("axis", tbl->lookup(node->axis()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleFill *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("dims", tbl->lookup(node->dims()));
+ s.args().append("value", tbl->lookup(node->value()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleFullyConnected *node,
+ locop::NodeSummary &s)
+{
+ assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED);
+
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("weights", tbl->lookup(node->weights()));
+ s.args().append("bias", tbl->lookup(node->bias()));
+ s.args().append("fused", to_str(node->fusedActivationFunction()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleGather *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("params", tbl->lookup(node->params()));
+ s.args().append("indices", tbl->lookup(node->indices()));
+ s.args().append("axis", pepper::str(node->axis()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleGatherNd *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("params", tbl->lookup(node->params()));
+ s.args().append("indices", tbl->lookup(node->indices()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleIf *node, locop::NodeSummary &s)
+{
+ s.args().append("cond", tbl->lookup(node->cond()));
+ for (uint32_t i = 0; i < node->input_count(); ++i)
+ s.args().append("input", tbl->lookup(node->input(i)));
+
+ if (node->then_graph() != nullptr)
+ s.args().append("then_graph", node->then_graph()->name());
+ else
+ s.args().append("then_branch", pepper::str(node->then_branch()));
+
+ if (node->else_graph() != nullptr)
+ s.args().append("else_graph", node->else_graph()->name());
+ else
+ s.args().append("else_branch", pepper::str(node->else_branch()));
+
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleL2Normalize *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("x", tbl->lookup(node->x()));
+ s.args().append("fused_activation_function", to_str(node->fusedActivationFunction()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleLeakyRelu *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("features", tbl->lookup(node->features()));
+ s.args().append("alpha", std::to_string(node->alpha()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleLocalResponseNormalization *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("radius", pepper::str(node->radius()));
+ s.args().append("bias", pepper::str(node->bias()));
+ s.args().append("alpha", pepper::str(node->alpha()));
+ s.args().append("beta", pepper::str(node->beta()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleLogSoftmax *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("logits", tbl->lookup(node->logits()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleMatrixDiag *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("diagonal", tbl->lookup(node->diagonal()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleMatrixSetDiag *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("diagonal", tbl->lookup(node->diagonal()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleMaxPool2D *node,
+ locop::NodeSummary &s)
+{
+ assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED);
+
+ s.args().append("value", tbl->lookup(node->value()));
+ s.args().append("filter(h,w)", to_str(node->filter()));
+ s.args().append("stride(h,w)", to_str(node->stride()));
+ s.args().append("padding", to_str(node->padding()));
+ s.args().append("fused", to_str(node->fusedActivationFunction()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleMirrorPad *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("paddings", tbl->lookup(node->paddings()));
+ s.args().append("mode", to_str(node->mode()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleNonMaxSuppressionV4 *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("boxes", tbl->lookup(node->boxes()));
+ s.args().append("scores", tbl->lookup(node->scores()));
+ s.args().append("max_output_size", tbl->lookup(node->max_output_size()));
+ s.args().append("iou_threshold", tbl->lookup(node->iou_threshold()));
+ s.args().append("score_threshold", tbl->lookup(node->score_threshold()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleNonMaxSuppressionV5 *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("boxes", tbl->lookup(node->boxes()));
+ s.args().append("scores", tbl->lookup(node->scores()));
+ s.args().append("max_output_size", tbl->lookup(node->max_output_size()));
+ s.args().append("iou_threshold", tbl->lookup(node->iou_threshold()));
+ s.args().append("score_threshold", tbl->lookup(node->score_threshold()));
+ s.args().append("soft_nms_sigma", tbl->lookup(node->soft_nms_sigma()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleOneHot *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("indices", tbl->lookup(node->indices()));
+ s.args().append("depth", tbl->lookup(node->depth()));
+ s.args().append("on_value", tbl->lookup(node->on_value()));
+ s.args().append("off_value", tbl->lookup(node->off_value()));
+ s.args().append("axis", pepper::str(node->axis()));
+
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CirclePack *node,
+ locop::NodeSummary &s)
+{
+ for (uint32_t i = 0; i < node->values_count(); ++i)
+ s.args().append("values", tbl->lookup(node->values(i)));
+ s.args().append("values_count", pepper::str(node->values_count()));
+ s.args().append("axis", pepper::str(node->axis()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CirclePad *node, locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("paddings", tbl->lookup(node->paddings()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CirclePadV2 *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("paddings", tbl->lookup(node->paddings()));
+ s.args().append("constant_values", tbl->lookup(node->constant_values()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CirclePRelu *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("alpha", tbl->lookup(node->alpha()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleRange *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("start", tbl->lookup(node->start()));
+ s.args().append("limit", tbl->lookup(node->limit()));
+ s.args().append("delta", tbl->lookup(node->delta()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleReshape *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("tensor", tbl->lookup(node->tensor()));
+ s.args().append("shape", tbl->lookup(node->shape()));
+ // TODO Show newShape info
+ s.state(locop::NodeSummary::State::PartiallyKnown);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleResizeBilinear *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("size", tbl->lookup(node->size()));
+ s.args().append("align_corners", node->align_corners() ? "true" : "false");
+ s.args().append("half_pixel_centers", node->half_pixel_centers() ? "true" : "false");
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleResizeNearestNeighbor *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("size", tbl->lookup(node->size()));
+ s.args().append("align_corners", node->align_corners() ? "true" : "false");
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleReverseSequence *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("seq_lengths", tbl->lookup(node->seq_lengths()));
+ s.args().append("seq_axis", std::to_string(node->seq_axis()));
+ s.args().append("batch_axis", std::to_string(node->batch_axis()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleReverseV2 *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("tensor", tbl->lookup(node->tensor()));
+ s.args().append("axis", tbl->lookup(node->axis()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleScatterNd *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("indices", tbl->lookup(node->indices()));
+ s.args().append("updates", tbl->lookup(node->updates()));
+ s.args().append("shape", tbl->lookup(node->shape()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleSegmentSum *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("segment_ids", tbl->lookup(node->segment_ids()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleSelect *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("condition", tbl->lookup(node->condition()));
+ s.args().append("t", tbl->lookup(node->t()));
+ s.args().append("e", tbl->lookup(node->e()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleSelectV2 *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("condition", tbl->lookup(node->condition()));
+ s.args().append("t", tbl->lookup(node->t()));
+ s.args().append("e", tbl->lookup(node->e()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleShape *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("out_type", to_str(node->out_type()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleSlice *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("begin", tbl->lookup(node->begin()));
+ s.args().append("size", tbl->lookup(node->size()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleSoftmax *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("logits", tbl->lookup(node->logits()));
+ s.args().append("beta", pepper::str(node->beta()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleSpaceToBatchND *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("block_shape", tbl->lookup(node->block_shape()));
+ s.args().append("paddings", tbl->lookup(node->paddings()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleSpaceToDepth *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("block_size", pepper::str(node->block_size()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleSparseToDense *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("indices", tbl->lookup(node->indices()));
+ s.args().append("output_shape", tbl->lookup(node->output_shape()));
+ s.args().append("values", tbl->lookup(node->values()));
+ s.args().append("default_value", tbl->lookup(node->default_value()));
+ s.args().append("Validate_indices", pepper::str(node->validate_indices()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleSplit *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("split_dim", tbl->lookup(node->split_dim()));
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("num_split", pepper::str(node->num_split()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleSplitV *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("size_splits", tbl->lookup(node->size_splits()));
+ s.args().append("split_dim", tbl->lookup(node->split_dim()));
+ s.args().append("num_split", pepper::str(node->num_split()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleSqueeze *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+
+ std::stringstream ss{"("};
+ for (size_t i = 0; i < node->squeeze_dims().size(); ++i)
+ {
+ if (i != 0)
+ ss << ", ";
+ ss << node->squeeze_dims()[i];
+ }
+ ss << ")";
+ s.args().append("squeeze_dims", ss.str());
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleStridedSlice *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("begin", tbl->lookup(node->begin()));
+ s.args().append("end", tbl->lookup(node->end()));
+ s.args().append("strides", tbl->lookup(node->strides()));
+ s.args().append("begin_mask", pepper::str(node->begin_mask()));
+ s.args().append("end_mask", pepper::str(node->end_mask()));
+ s.args().append("ellipsis_mask", pepper::str(node->ellipsis_mask()));
+ s.args().append("new_axis_mask", pepper::str(node->new_axis_mask()));
+ s.args().append("shrink_axis_mask", pepper::str(node->shrink_axis_mask()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleTile *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("multiples", tbl->lookup(node->multiples()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleTopKV2 *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("k", tbl->lookup(node->k()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleTranspose *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("a", tbl->lookup(node->a()));
+ s.args().append("perm", tbl->lookup(node->perm()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleTransposeConv *node,
+ locop::NodeSummary &s)
+{
+ assert(node->padding() != luci::Padding::UNDEFINED);
+
+ s.args().append("inputSizes", tbl->lookup(node->inputSizes()));
+ s.args().append("filter", tbl->lookup(node->filter()));
+ s.args().append("outBackprop", tbl->lookup(node->outBackprop()));
+ s.args().append("bias", tbl->lookup(node->bias()));
+ s.args().append("stride(h,w)", to_str(node->stride()));
+ s.args().append("padding", to_str(node->padding()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleUnidirectionalSequenceLSTM *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+
+ s.args().append("input_to_input_weights", tbl->lookup(node->input_to_input_weights()));
+ s.args().append("input_to_forget_weights", tbl->lookup(node->input_to_forget_weights()));
+ s.args().append("input_to_cell_weights", tbl->lookup(node->input_to_cell_weights()));
+ s.args().append("input_to_output_weights", tbl->lookup(node->input_to_output_weights()));
+
+ s.args().append("recurrent_to_input_weights", tbl->lookup(node->recurrent_to_input_weights()));
+ s.args().append("recurrent_to_forget_weights", tbl->lookup(node->recurrent_to_forget_weights()));
+ s.args().append("recurrent_to_cell_weights", tbl->lookup(node->recurrent_to_cell_weights()));
+ s.args().append("recurrent_to_output_weights", tbl->lookup(node->recurrent_to_output_weights()));
+
+ s.args().append("cell_to_input_weights", tbl->lookup(node->cell_to_input_weights()));
+ s.args().append("cell_to_forget_weights", tbl->lookup(node->cell_to_forget_weights()));
+ s.args().append("cell_to_output_weights", tbl->lookup(node->cell_to_output_weights()));
+
+ s.args().append("input_gate_bias", tbl->lookup(node->input_gate_bias()));
+ s.args().append("forget_gate_bias", tbl->lookup(node->forget_gate_bias()));
+ s.args().append("cell_gate_bias", tbl->lookup(node->cell_gate_bias()));
+ s.args().append("output_gate_bias", tbl->lookup(node->output_gate_bias()));
+
+ s.args().append("projection_weights", tbl->lookup(node->projection_weights()));
+ s.args().append("projection_bias", tbl->lookup(node->projection_bias()));
+
+ s.args().append("activation_state", tbl->lookup(node->activation_state()));
+ s.args().append("cell_state", tbl->lookup(node->cell_state()));
+
+ s.args().append("input_layer_norm_coefficients",
+ tbl->lookup(node->input_layer_norm_coefficients()));
+ s.args().append("forget_layer_norm_coefficients",
+ tbl->lookup(node->forget_layer_norm_coefficients()));
+ s.args().append("cell_layer_norm_coefficients",
+ tbl->lookup(node->cell_layer_norm_coefficients()));
+ s.args().append("output_layer_norm_coefficients",
+ tbl->lookup(node->output_layer_norm_coefficients()));
+
+ s.args().append("cell_clip", to_str(node->cell_clip()));
+ s.args().append("proj_clip", to_str(node->proj_clip()));
+ s.args().append("time_major", to_str(node->time_major()));
+ s.args().append("asymmetric_quantize_inputs", to_str(node->asymmetric_quantize_inputs()));
+
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleUnique *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("idx_out_type", to_str(node->idx_out_type()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleUnpack *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("value", tbl->lookup(node->value()));
+ s.args().append("num", pepper::str(node->num()));
+ s.args().append("axis", pepper::str(node->axis()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleWhere *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("condition", tbl->lookup(node->condition()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleWhile *node,
+ locop::NodeSummary &s)
+{
+ for (uint32_t i = 0; i < node->input_count(); ++i)
+ s.args().append("input", tbl->lookup(node->input(i)));
+
+ if (node->cond_graph() != nullptr)
+ s.args().append("cond_graph", node->cond_graph()->name());
+ else
+ s.args().append("cond_branch", pepper::str(node->cond_branch()));
+
+ if (node->body_graph() != nullptr)
+ s.args().append("body_graph", node->body_graph()->name());
+ else
+ s.args().append("body_branch", pepper::str(node->body_branch()));
+
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleTopKV2Out *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("topkv2", tbl->lookup(node->input()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleUniqueOut *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("unique", tbl->lookup(node->input()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleUnpackOut *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("unpack", tbl->lookup(node->input()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleWhileOut *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("while", tbl->lookup(node->input()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleOutput *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("from", tbl->lookup(node->from()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleBCQFullyConnected *node,
+ locop::NodeSummary &s)
+{
+ assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED);
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("weights_scales", tbl->lookup(node->weights_scales()));
+ s.args().append("weights_binary", tbl->lookup(node->weights_binary()));
+ s.args().append("bias", tbl->lookup(node->bias()));
+ s.args().append("weights_clusters", tbl->lookup(node->weights_clusters()));
+ s.args().append("fused", to_str(node->fusedActivationFunction()));
+ s.args().append("weights_hidden_size", pepper::str(node->weights_hidden_size()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleBCQGather *node,
+ locop::NodeSummary &s)
+{
+ s.args().append("input_scales", tbl->lookup(node->input_scales()));
+ s.args().append("input_binary", tbl->lookup(node->input_binary()));
+ s.args().append("indices", tbl->lookup(node->indices()));
+ s.args().append("input_clusters", tbl->lookup(node->input_clusters()));
+ s.args().append("axis", pepper::str(node->axis()));
+ s.args().append("input_hidden_size", pepper::str(node->input_hidden_size()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool summary_node(const locop::SymbolTable *tbl, const luci::CircleInstanceNorm *node,
+ locop::NodeSummary &s)
+{
+ auto fused = node->fusedActivationFunction();
+ assert(fused != luci::FusedActFunc::UNDEFINED);
+
+ s.args().append("input", tbl->lookup(node->input()));
+ s.args().append("gamma", tbl->lookup(node->gamma()));
+ s.args().append("beta", tbl->lookup(node->beta()));
+ s.args().append("epsilon", pepper::str(node->epsilon()));
+ s.args().append("fused_activation_function", to_str(fused));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilderBase::build(const loco::Node *node, locop::NodeSummary &s) const
+{
+ if (node->dialect() != luci::CircleDialect::get())
+ return false;
+
+ auto ptr_to_str = [](const void *ptr) {
+ std::stringstream ss;
+ ss << ptr;
+ return ss.str();
+ };
+
+#define CIRCLE_NODE(OPCODE, CLASS) \
+ if (dynamic_cast<const CLASS *>(node)) \
+ { \
+ s.opname(circle_opname(node->opnum())); \
+ s.comments().append("Mem = " + ptr_to_str(node)); \
+ return summary(dynamic_cast<const CLASS *>(node), s); \
+ }
+#include <luci/IR/CircleNodes.lst>
+#undef CIRCLE_NODE
+
+ return false;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleAbs *node, locop::NodeSummary &s) const
+{
+ return use_x(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleAdd *node, locop::NodeSummary &s) const
+{
+ return use_xy_act(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleAddN *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleArgMax *node, locop::NodeSummary &s) const
+{
+ return use_ido(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleArgMin *node, locop::NodeSummary &s) const
+{
+ return use_ido(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleAveragePool2D *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleBatchMatMul *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleBatchToSpaceND *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleCast *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleCeil *node, locop::NodeSummary &s) const
+{
+ return use_x(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleConcatenation *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleConst *, locop::NodeSummary &s) const
+{
+ s.state(locop::NodeSummary::State::PartiallyKnown);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleConv2D *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleCos *node, locop::NodeSummary &s) const
+{
+ return use_x(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleCustom *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleDepthToSpace *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleDepthwiseConv2D *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleDequantize *node,
+ locop::NodeSummary &s) const
+{
+ return use_input(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleDiv *node, locop::NodeSummary &s) const
+{
+ return use_xy(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleElu *node, locop::NodeSummary &s) const
+{
+ return use_features(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleExp *node, locop::NodeSummary &s) const
+{
+ return use_x(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleExpandDims *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleFloor *node, locop::NodeSummary &s) const
+{
+ return use_x(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleFloorDiv *node,
+ locop::NodeSummary &s) const
+{
+ return use_xy(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleFloorMod *node,
+ locop::NodeSummary &s) const
+{
+ return use_xy(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleFill *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleFullyConnected *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleGather *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleGatherNd *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleGreater *node, locop::NodeSummary &s) const
+{
+ return use_xy(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleGreaterEqual *node,
+ locop::NodeSummary &s) const
+{
+ return use_xy(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleIf *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleL2Normalize *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleLess *node, locop::NodeSummary &s) const
+{
+ return use_xy(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleLessEqual *node,
+ locop::NodeSummary &s) const
+{
+ return use_xy(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleLeakyRelu *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleLocalResponseNormalization *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleLog *node, locop::NodeSummary &s) const
+{
+ return use_x(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleLogicalAnd *node,
+ locop::NodeSummary &s) const
+{
+ return use_xy(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleLogicalNot *node,
+ locop::NodeSummary &s) const
+{
+ return use_x(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleLogicalOr *node,
+ locop::NodeSummary &s) const
+{
+ return use_xy(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleLogistic *node,
+ locop::NodeSummary &s) const
+{
+ return use_x(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleLogSoftmax *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleMatrixDiag *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleMatrixSetDiag *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleMaximum *node, locop::NodeSummary &s) const
+{
+ return use_xy(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleMaxPool2D *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleMean *node, locop::NodeSummary &s) const
+{
+ return use_reducer(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleMinimum *node, locop::NodeSummary &s) const
+{
+ return use_xy(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleMirrorPad *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleMul *node, locop::NodeSummary &s) const
+{
+ return use_xy_act(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleNeg *node, locop::NodeSummary &s) const
+{
+ return use_x(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleNonMaxSuppressionV4 *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleNonMaxSuppressionV5 *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleNotEqual *node,
+ locop::NodeSummary &s) const
+{
+ return use_xy(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleOneHot *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CirclePack *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CirclePad *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CirclePadV2 *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CirclePow *node, locop::NodeSummary &s) const
+{
+ return use_xy(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CirclePRelu *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleRange *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleRank *node, locop::NodeSummary &s) const
+{
+ return use_input(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleReduceAny *node,
+ locop::NodeSummary &s) const
+{
+ return use_reducer(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleReduceMax *node,
+ locop::NodeSummary &s) const
+{
+ return use_reducer(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleReduceMin *node,
+ locop::NodeSummary &s) const
+{
+ return use_reducer(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleReduceProd *node,
+ locop::NodeSummary &s) const
+{
+ return use_reducer(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleRelu *node, locop::NodeSummary &s) const
+{
+ return use_features(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleRelu6 *node, locop::NodeSummary &s) const
+{
+ return use_features(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleReluN1To1 *node,
+ locop::NodeSummary &s) const
+{
+ return use_features(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleReshape *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleResizeBilinear *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleResizeNearestNeighbor *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleReverseSequence *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleReverseV2 *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleRound *node, locop::NodeSummary &s) const
+{
+ return use_x(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleRsqrt *node, locop::NodeSummary &s) const
+{
+ return use_x(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleScatterNd *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSegmentSum *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSelect *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSelectV2 *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleShape *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSin *node, locop::NodeSummary &s) const
+{
+ return use_x(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSlice *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSoftmax *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSpaceToBatchND *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSpaceToDepth *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSparseToDense *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSplit *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSplitV *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSqrt *node, locop::NodeSummary &s) const
+{
+ return use_x(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSquare *node, locop::NodeSummary &s) const
+{
+ return use_x(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSquaredDifference *node,
+ locop::NodeSummary &s) const
+{
+ return use_xy(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSqueeze *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleStridedSlice *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSub *node, locop::NodeSummary &s) const
+{
+ return use_xy(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSum *node, locop::NodeSummary &s) const
+{
+ return use_reducer(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleTanh *node, locop::NodeSummary &s) const
+{
+ return use_x(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleTile *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleTopKV2 *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleTranspose *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleTransposeConv *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleUnidirectionalSequenceLSTM *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleUnique *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleUnpack *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleWhere *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleWhile *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleZerosLike *node,
+ locop::NodeSummary &s) const
+{
+ return use_input(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSplitOut *node,
+ locop::NodeSummary &s) const
+{
+ return use_input(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSplitVOut *node,
+ locop::NodeSummary &s) const
+{
+ return use_input(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleTopKV2Out *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleUniqueOut *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleUnpackOut *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleIfOut *node, locop::NodeSummary &s) const
+{
+ return use_input(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleNonMaxSuppressionV4Out *node,
+ locop::NodeSummary &s) const
+{
+ return use_input(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleNonMaxSuppressionV5Out *node,
+ locop::NodeSummary &s) const
+{
+ return use_input(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleWhileOut *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleInput *, locop::NodeSummary &s) const
+{
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleOutput *node, locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleBCQFullyConnected *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleBCQGather *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleInstanceNorm *node,
+ locop::NodeSummary &s) const
+{
+ return summary_node(tbl(), node, s);
+}
+
+} // namespace
+
+namespace luci
+{
+
+bool NodeSummaryBuilder::build(const loco::Node *node, locop::NodeSummary &s) const
+{
+ if (locop::CanonicalNodeSummaryBuilder(_tbl).build(node, s))
+ {
+ return true;
+ }
+
+ if (CircleNodeSummaryBuilder(_tbl).build(node, s))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace luci
diff --git a/compiler/luci/logex/src/LogHelper.cpp b/compiler/luci/logex/src/LogHelper.cpp
new file mode 100644
index 000000000..caf659906
--- /dev/null
+++ b/compiler/luci/logex/src/LogHelper.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/LogHelper.h"
+#include "luci/FormattedGraph.h"
+
+namespace luci
+{
+
+FormattedGraph fmt(loco::Graph *g)
+{
+ auto node_summary_builder = std::make_unique<NodeSummaryBuilderFactory>();
+ return std::move(locop::fmt<locop::LinearV1>(g).with(std::move(node_summary_builder)));
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/CMakeLists.txt b/compiler/luci/pass/CMakeLists.txt
new file mode 100644
index 000000000..2c5fb3407
--- /dev/null
+++ b/compiler/luci/pass/CMakeLists.txt
@@ -0,0 +1,29 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(luci_pass SHARED ${SOURCES})
+target_include_directories(luci_pass PRIVATE src)
+target_include_directories(luci_pass PUBLIC include)
+target_link_libraries(luci_pass PUBLIC loco)
+target_link_libraries(luci_pass PUBLIC logo_core)
+target_link_libraries(luci_pass PRIVATE logo)
+target_link_libraries(luci_pass PRIVATE luci_lang)
+target_link_libraries(luci_pass PRIVATE luci_log)
+target_link_libraries(luci_pass PRIVATE luci_service)
+target_link_libraries(luci_pass PRIVATE luci_logex)
+target_link_libraries(luci_pass PRIVATE nncc_common)
+target_link_libraries(luci_pass PRIVATE oops)
+install(TARGETS luci_pass DESTINATION lib)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(luci_pass_test ${TESTS})
+target_include_directories(luci_pass_test PRIVATE src)
+target_link_libraries(luci_pass_test luci_pass)
+target_link_libraries(luci_pass_test luci_lang)
+#target_link_libraries(luci_pass_test oops)
diff --git a/compiler/luci/pass/README.md b/compiler/luci/pass/README.md
new file mode 100644
index 000000000..9b6cdebd3
--- /dev/null
+++ b/compiler/luci/pass/README.md
@@ -0,0 +1,3 @@
+# luci-pass
+
+_luci-pass_ provides Circle Dialect transformation passes
diff --git a/compiler/luci/pass/include/luci/CircleOptimizer.h b/compiler/luci/pass/include/luci/CircleOptimizer.h
new file mode 100644
index 000000000..db5bdb501
--- /dev/null
+++ b/compiler/luci/pass/include/luci/CircleOptimizer.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_CIRCLE_OPTIMIZER_H__
+#define __LUCI_CIRCLE_OPTIMIZER_H__
+
+#include <loco.h>
+
+#include <string>
+#include <vector>
+
+namespace luci
+{
+
+class CircleOptimizer final
+{
+public:
+ struct Options
+ {
+ enum Algorithm
+ {
+ FuseAddWithTConv,
+ FuseBatchNormWithTConv,
+ FuseBCQ,
+ FuseInstanceNorm,
+ ResolveCustomOpAdd,
+ ResolveCustomOpBatchMatMul,
+ ResolveCustomOpMatMul,
+ QuantizeDequantizeWeights,
+ QuantizeWithMinMax,
+ Requantize,
+ FoldDequantize,
+ SparsifyTensorPass,
+ FusePreActivationBatchNorm,
+ MakeBatchNormGammaPositive,
+ FuseActivationFunction,
+ };
+
+ enum AlgorithmParameters
+ {
+ // quantize
+ Quantize_input_dtype,
+ Quantize_output_dtype,
+ Quantize_granularity, // layer-wise or channel-wise
+
+ // sparsify
+ Sparsify_tensor_name,
+ Sparsify_traversal_order,
+ Sparsify_format,
+ Sparsify_block_size,
+ Sparsify_block_map,
+ };
+
+ virtual ~Options() = default;
+
+ virtual void enable(Algorithm) = 0;
+ virtual bool query(Algorithm) = 0;
+ virtual void param(AlgorithmParameters, const std::string &) = 0;
+ virtual const std::string param(AlgorithmParameters) const = 0;
+ };
+
+public:
+ // TODO maybe caller can provide Options as ctor parameters
+ Options *options(void);
+
+public:
+ void optimize(loco::Graph *) const;
+
+ void quantize(loco::Graph *) const;
+
+ void sparsify(loco::Graph *) const;
+
+private:
+ std::unique_ptr<Options> _options;
+};
+
+} // namespace luci
+
+#endif // __LUCI_CIRCLE_OPTIMIZER_H__
diff --git a/compiler/luci/pass/include/luci/Pass/FoldDequantizePass.h b/compiler/luci/pass/include/luci/Pass/FoldDequantizePass.h
new file mode 100644
index 000000000..07610d3e1
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/FoldDequantizePass.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_FOLD_DEQUANTIZE_PASS_H__
+#define __LUCI_FOLD_DEQUANTIZE_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace luci
+{
+
+/**
+ * @brief Class to fold Dequantize, which can be folded by constant inputs
+ *
+ */
+struct FoldDequantizePass final : public logo::Pass
+{
+ const char *name(void) const final { return "luci::FOLD_DEQUANTIZE"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_FOLD_DEQUANTIZE_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/FuseActivationFunctionPass.h b/compiler/luci/pass/include/luci/Pass/FuseActivationFunctionPass.h
new file mode 100644
index 000000000..5d05fcffa
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/FuseActivationFunctionPass.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_FUSE_ACTIVATION_FUNCTION_PASS_H__
+#define __LUCI_FUSE_ACTIVATION_FUNCTION_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace luci
+{
+
+/**
+ * @brief Class to fuse activation functions into preceding operators
+ */
+struct FuseActivationFunctionPass final : public logo::Pass
+{
+ const char *name(void) const final { return "luci::FuseActivationFunctionPass"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_FUSE_ACTIVATION_FUNCTION_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/FuseAddWithTConvPass.h b/compiler/luci/pass/include/luci/Pass/FuseAddWithTConvPass.h
new file mode 100644
index 000000000..89b120397
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/FuseAddWithTConvPass.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_FUSE_ADD_WITH_TCONV_PASS_H__
+#define __LUCI_FUSE_ADD_WITH_TCONV_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace luci
+{
+
+/**
+ * @brief Class to fuse Add into CircleTransposeConv
+ */
+struct FuseAddWithTConvPass final : public logo::Pass
+{
+ const char *name(void) const final { return "luci::FuseAddWithTConvPass"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_FUSE_ADD_WITH_TCONV_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/FuseBCQPass.h b/compiler/luci/pass/include/luci/Pass/FuseBCQPass.h
new file mode 100644
index 000000000..4404a9fc9
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/FuseBCQPass.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_FUSE_BCQ_PASS_H__
+#define __LUCI_FUSE_BCQ_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace luci
+{
+
+/**
+ * @brief Class to fuse certain pattern of subgraph into CircleBCQFullyConnected or CircleBCQGather
+ *
+ */
+struct FuseBCQPass final : public logo::Pass
+{
+ const char *name(void) const final { return "luci::FuseBCQPass"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_FUSE_BCQ_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/FuseBatchNormWithTConv.h b/compiler/luci/pass/include/luci/Pass/FuseBatchNormWithTConv.h
new file mode 100644
index 000000000..d3e930a36
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/FuseBatchNormWithTConv.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_FUSE_BATCH_NORM_WITH_TCONV_PASS_H__
+#define __LUCI_FUSE_BATCH_NORM_WITH_TCONV_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace luci
+{
+
+/**
+ * @brief Class to fuse Batch Normalization into CircleTransposeConv
+ */
+struct FuseBatchNormWithTConvPass final : public logo::Pass
+{
+ const char *name(void) const final { return "luci::FuseBatchNormWithTConvPass"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_FUSE_BATCH_NORM_WITH_TCONV_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/FuseInstanceNormPass.h b/compiler/luci/pass/include/luci/Pass/FuseInstanceNormPass.h
new file mode 100644
index 000000000..800a5f789
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/FuseInstanceNormPass.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_FUSE_INSTANCE_NORM_PASS_H__
+#define __LUCI_FUSE_INSTANCE_NORM_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace luci
+{
+
+/**
+ * @brief Class to fuse certain pattern of subgraph into CircleInstanceNorm
+ * with auxiliary nodes
+ *
+ * For detailed subgraph pattern to be fused, please check its implementation.
+ */
+struct FuseInstanceNormPass final : public logo::Pass
+{
+ const char *name(void) const final { return "luci::FuseInstanceNormPass"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_FUSE_INSTANCE_NORM_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/FusePreActivationBatchNormPass.h b/compiler/luci/pass/include/luci/Pass/FusePreActivationBatchNormPass.h
new file mode 100644
index 000000000..bd6aec7a2
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/FusePreActivationBatchNormPass.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_FUSE_PRE_ACTIVATION_BATCH_NORM_PASS_H__
+#define __LUCI_FUSE_PRE_ACTIVATION_BATCH_NORM_PASS_H__
+
+#include <logo/Pass.h>
+#include <luci/IR/CircleNodes.h>
+
+namespace luci
+{
+
+/**
+ * @brief Class to fuse batch normalization of pre-activation
+ */
+struct FusePreActivationBatchNormPass final : public logo::Pass
+{
+ const char *name(void) const final { return "luci::FusePreActivationBatchNormPass"; }
+
+ bool run(loco::Graph *g) final;
+
+ std::vector<luci::CircleMul *> _mul_list;
+ std::vector<luci::CircleAdd *> _add_list;
+ std::vector<luci::CircleSub *> _sub_list; // inserted during fusion
+};
+
+} // namespace luci
+
+#endif // __LUCI_FUSE_PRE_ACTIVATION_BATCH_NORM_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/MakeBatchNormGammaPositivePass.h b/compiler/luci/pass/include/luci/Pass/MakeBatchNormGammaPositivePass.h
new file mode 100644
index 000000000..f00a68b45
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/MakeBatchNormGammaPositivePass.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_MAKE_BATCH_NORM_GAMMA_POSITIVE_PASS_H__
+#define __LUCI_MAKE_BATCH_NORM_GAMMA_POSITIVE_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace luci
+{
+
+/**
+ * @brief Class to make negative gamma of batchnorm to a small positive value (1e-10)
+ * This pass can change the execution result of the model.
+ * So, use it only when the impact is known to be acceptable.
+ */
+struct MakeBatchNormGammaPositivePass final : public logo::Pass
+{
+ const char *name(void) const final { return "luci::MakeBatchNormGammaPositivePass"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_MAKE_BATCH_NORM_GAMMA_POSITIVE_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/QuantizationParameters.h b/compiler/luci/pass/include/luci/Pass/QuantizationParameters.h
new file mode 100644
index 000000000..5c9cd427f
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/QuantizationParameters.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_QUANTIZATION_PARAMETERS_H__
+#define __LUCI_QUANTIZATION_PARAMETERS_H__
+
+namespace luci
+{
+
+enum QuantizationGranularity
+{
+ LayerWise = 0,
+ ChannelWise = 1,
+};
+
+} // namespace luci
+
+#endif // __LUCI_QUANTIZATION_PARAMETERS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/QuantizeDequantizeWeightsPass.h b/compiler/luci/pass/include/luci/Pass/QuantizeDequantizeWeightsPass.h
new file mode 100644
index 000000000..713b88f9d
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/QuantizeDequantizeWeightsPass.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_QUANTIZE_DEQUANTIZE_WEIGHTS_PASS_H__
+#define __LUCI_QUANTIZE_DEQUANTIZE_WEIGHTS_PASS_H__
+
+#include <loco.h>
+
+#include <logo/Pass.h>
+
+#include <luci/Pass/QuantizationParameters.h>
+
+namespace luci
+{
+
+/**
+ * @brief Pass to quantize weights
+ */
+class QuantizeDequantizeWeightsPass : public logo::Pass
+{
+public:
+ QuantizeDequantizeWeightsPass(loco::DataType input_dtype, loco::DataType output_dtype,
+ QuantizationGranularity granularity)
+ : _input_dtype{input_dtype}, _output_dtype{output_dtype}, _granularity{granularity}
+ {
+ // DO NOTHING
+ }
+ virtual const char *name(void) const { return "luci::QuantizeDequantizeWeightsPass"; }
+
+public:
+ bool run(loco::Graph *graph);
+
+private:
+ loco::DataType _input_dtype;
+ loco::DataType _output_dtype;
+ QuantizationGranularity _granularity;
+};
+
+} // namespace luci
+
+#endif //__LUCI_QUANTIZE_DEQUANTIZE_WEIGHTS_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/QuantizeWithMinMaxPass.h b/compiler/luci/pass/include/luci/Pass/QuantizeWithMinMaxPass.h
new file mode 100644
index 000000000..bb0d0ff40
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/QuantizeWithMinMaxPass.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_QUANTIZE_WITH_MINMAX_PASS_H__
+#define __LUCI_QUANTIZE_WITH_MINMAX_PASS_H__
+
+#include <loco.h>
+
+#include <logo/Pass.h>
+
+#include <luci/Pass/QuantizationParameters.h>
+
+namespace luci
+{
+
+/**
+ * @brief Pass to quantize activation, weights, and bias
+ */
+class QuantizeWithMinMaxPass : public logo::Pass
+{
+public:
+ QuantizeWithMinMaxPass(loco::DataType input_dtype, loco::DataType output_dtype,
+ QuantizationGranularity granularity)
+ : _input_dtype{input_dtype}, _output_dtype{output_dtype}, _granularity{granularity}
+ {
+ // DO NOTHING
+ }
+ virtual const char *name(void) const { return "luci::QuantizeWithMinMaxPass"; }
+
+public:
+ bool run(loco::Graph *graph);
+
+private:
+ loco::DataType _input_dtype;
+ loco::DataType _output_dtype;
+ QuantizationGranularity _granularity;
+};
+
+} // namespace luci
+
+#endif //__LUCI_QUANTIZE_WITH_MINMAX_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/RequantizePass.h b/compiler/luci/pass/include/luci/Pass/RequantizePass.h
new file mode 100644
index 000000000..2442b24ea
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/RequantizePass.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_REQUANTIZE_PASS_H__
+#define __LUCI_REQUANTIZE_PASS_H__
+
+#include <loco.h>
+
+#include <logo/Pass.h>
+
+#include <luci/Pass/QuantizationParameters.h>
+
+namespace luci
+{
+
+/**
+ * @brief Pass to quantize weights
+ */
+class RequantizePass : public logo::Pass
+{
+public:
+ RequantizePass(loco::DataType input_dtype, loco::DataType output_dtype)
+ : _input_dtype{input_dtype}, _output_dtype{output_dtype}
+ {
+ // DO NOTHING
+ }
+ virtual const char *name(void) const { return "luci::RequantizePass"; }
+
+public:
+ bool run(loco::Graph *graph);
+
+private:
+ loco::DataType _input_dtype;
+ loco::DataType _output_dtype;
+};
+
+} // namespace luci
+
+#endif //__LUCI_REQUANTIZE_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/ResolveCustomOpAddPass.h b/compiler/luci/pass/include/luci/Pass/ResolveCustomOpAddPass.h
new file mode 100644
index 000000000..35a335028
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/ResolveCustomOpAddPass.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_RESOLVE_CUSTOM_OP_ADD_PASS_H__
+#define __LUCI_RESOLVE_CUSTOM_OP_ADD_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace luci
+{
+
+/**
+ * @brief Class to resolve certain custom op of subgraph into add op in circle schema.
+ */
+struct ResolveCustomOpAddPass final : public logo::Pass
+{
+ const char *name(void) const final { return "luci::ResolveCustomOpAddPass"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_RESOLVE_CUSTOM_OP_ADD_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/ResolveCustomOpBatchMatMulPass.h b/compiler/luci/pass/include/luci/Pass/ResolveCustomOpBatchMatMulPass.h
new file mode 100644
index 000000000..7c48c8d16
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/ResolveCustomOpBatchMatMulPass.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_RESOLVE_CUSTOM_OP_BATCHMATMUL_PASS_H__
+#define __LUCI_RESOLVE_CUSTOM_OP_BATCHMATMUL_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace luci
+{
+
+/**
+ * @brief Class to resolve certain custom op of subgraph into batchmatmul op in circle schema.
+ */
+struct ResolveCustomOpBatchMatMulPass final : public logo::Pass
+{
+ const char *name(void) const final { return "luci::ResolveCustomOpBatchMatMulPass"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_RESOLVE_CUSTOM_OP_BATCHMATMUL_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/ResolveCustomOpMatMulPass.h b/compiler/luci/pass/include/luci/Pass/ResolveCustomOpMatMulPass.h
new file mode 100644
index 000000000..701deba91
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/ResolveCustomOpMatMulPass.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_RESOLVE_CUSTOM_OP_MATMUL_PASS_H__
+#define __LUCI_RESOLVE_CUSTOM_OP_MATMUL_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace luci
+{
+
+/**
+ * @brief Class to resolve certain custom op of subgraph into matmul op in circle schema.
+ */
+struct ResolveCustomOpMatMulPass final : public logo::Pass
+{
+ const char *name(void) const final { return "luci::ResolveCustomOpMatMulPass"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_RESOLVE_CUSTOM_OP_MATMUL_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/ShapeInferencePass.h b/compiler/luci/pass/include/luci/Pass/ShapeInferencePass.h
new file mode 100644
index 000000000..86bb2ab42
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/ShapeInferencePass.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_SHAPE_INFERENCE_PASS_H__
+#define __LUCI_SHAPE_INFERENCE_PASS_H__
+
+#include <loco.h>
+
+#include <logo/Pass.h>
+
+namespace luci
+{
+
+/**
+ * @brief Pass to infer shape of nodes
+ */
+class ShapeInferencePass : public logo::Pass
+{
+public:
+ virtual const char *name(void) const { return "luci::ShapeInferencePass"; }
+
+public:
+ bool run(loco::Graph *graph);
+};
+
+} // namespace luci
+
+#endif //__LUCI_SHAPE_INFERENCE_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/SparsifyTensorPass.h b/compiler/luci/pass/include/luci/Pass/SparsifyTensorPass.h
new file mode 100644
index 000000000..41f43bf88
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/SparsifyTensorPass.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_SPARSIFY_TENSOR_PASS_H__
+#define __LUCI_SPARSIFY_TENSOR_PASS_H__
+
+#include <logo/Pass.h>
+
+#include <luci/IR/SparsityParam.h>
+
+namespace luci
+{
+
+class CircleConst;
+
+/**
+ * @brief Pass to sparsify tensor
+ */
+struct SparsifyTensorPass final : public logo::Pass
+{
+public:
+ SparsifyTensorPass(const std::string &tensor_name, const std::vector<int32_t> &traversal_order,
+ const std::vector<DimensionType> &format,
+ const std::vector<int32_t> &block_size, const std::vector<int32_t> &block_map)
+ : _tensor_name{tensor_name}, _traversal_order{traversal_order}, _format{format},
+ _block_size{block_size}, _block_map{block_map}
+ {
+ // DO NOTHING
+ }
+
+public:
+ const char *name(void) const final { return "luci::SparsifyTensorPass"; }
+
+ bool run(loco::Graph *g) final;
+
+ template <loco::DataType DT> void sparsify_tensor(luci::CircleConst *cop);
+
+private:
+ // Tensor name that the pass will sparsify
+ std::string _tensor_name;
+ std::vector<int32_t> _traversal_order;
+ std::vector<DimensionType> _format;
+ std::vector<int32_t> _block_size;
+ std::vector<int32_t> _block_map;
+};
+
+extern template void
+SparsifyTensorPass::sparsify_tensor<loco::DataType::S32>(luci::CircleConst *cop);
+extern template void
+SparsifyTensorPass::sparsify_tensor<loco::DataType::S8>(luci::CircleConst *cop);
+extern template void
+SparsifyTensorPass::sparsify_tensor<loco::DataType::FLOAT32>(luci::CircleConst *cop);
+
+} // namespace luci
+
+#endif // __LUCI_SPARSIFY_TENSOR_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/TypeInferencePass.h b/compiler/luci/pass/include/luci/Pass/TypeInferencePass.h
new file mode 100644
index 000000000..c607ac63f
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/TypeInferencePass.h
@@ -0,0 +1,42 @@
+
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_TYPE_INFERENCE_PASS_H__
+#define __LUCI_TYPE_INFERENCE_PASS_H__
+
+#include <loco.h>
+
+#include <logo/Pass.h>
+
+namespace luci
+{
+
+/**
+ * @brief Pass to infer type of nodes
+ */
+class TypeInferencePass : public logo::Pass
+{
+public:
+ virtual const char *name(void) const { return "luci::TypeInferencePass"; }
+
+public:
+ bool run(loco::Graph *graph);
+};
+
+} // namespace luci
+
+#endif //__LUCI_TYPE_INFERENCE_PASS_H__
diff --git a/compiler/luci/pass/src/CircleOptimizer.cpp b/compiler/luci/pass/src/CircleOptimizer.cpp
new file mode 100644
index 000000000..34f647301
--- /dev/null
+++ b/compiler/luci/pass/src/CircleOptimizer.cpp
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/CircleOptimizer.h"
+
+#include "luci/Pass/FoldDequantizePass.h"
+#include "luci/Pass/FuseActivationFunctionPass.h"
+#include "luci/Pass/FuseAddWithTConvPass.h"
+#include "luci/Pass/FuseBatchNormWithTConv.h"
+#include "luci/Pass/FuseBCQPass.h"
+#include "luci/Pass/FuseInstanceNormPass.h"
+#include "luci/Pass/FusePreActivationBatchNormPass.h"
+#include "luci/Pass/MakeBatchNormGammaPositivePass.h"
+#include "luci/Pass/ResolveCustomOpAddPass.h"
+#include "luci/Pass/ResolveCustomOpBatchMatMulPass.h"
+#include "luci/Pass/ResolveCustomOpMatMulPass.h"
+#include "luci/Pass/RequantizePass.h"
+#include "luci/Pass/QuantizeWithMinMaxPass.h"
+#include "luci/Pass/QuantizeDequantizeWeightsPass.h"
+#include "luci/Pass/SparsifyTensorPass.h"
+// TODO add more passes
+
+#include "luci/Pass/ShapeInferencePass.h"
+#include "luci/Pass/TypeInferencePass.h"
+
+// logo passes
+#include <logo/RemoveDeadNodeWithQueryPass.h>
+
+#include "ProgressReporter.h"
+#include "CircleOptimizerUtils.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <logo/Phase.h>
+
+#include <memory>
+#include <sstream>
+
+namespace
+{
+
+std::vector<int> parseIntFromCommadelimitedStr(std::string str)
+{
+ std::vector<int> ret;
+ std::istringstream is(str);
+ for (uint32_t i; is >> i;)
+ {
+ assert(i != ',');
+ ret.push_back(i);
+ if (is.peek() == ',')
+ is.ignore();
+ }
+ return ret;
+}
+
+using namespace luci;
+
+class OptimizeOptionsImpl final : public luci::CircleOptimizer::Options
+{
+public:
+ void enable(Algorithm) final;
+ void param(AlgorithmParameters, const std::string &) final;
+ const std::string param(AlgorithmParameters) const final;
+ bool query(Algorithm) final;
+
+private:
+ std::vector<Algorithm> _algorithms;
+ std::map<AlgorithmParameters, const std::string> _algorithm_params;
+};
+
+void OptimizeOptionsImpl::enable(Algorithm algo) { _algorithms.push_back(algo); }
+
+void OptimizeOptionsImpl::param(AlgorithmParameters param, const std::string &str)
+{
+ _algorithm_params.insert(std::pair<AlgorithmParameters, const std::string>(param, str));
+}
+
+const std::string OptimizeOptionsImpl::param(AlgorithmParameters param) const
+{
+ auto param_str = _algorithm_params.find(param);
+ if (param_str != _algorithm_params.end())
+ {
+ return param_str->second;
+ }
+ else
+ {
+ return std::string();
+ }
+}
+
+bool OptimizeOptionsImpl::query(Algorithm algo)
+{
+ std::vector<Algorithm>::iterator it = std::find(_algorithms.begin(), _algorithms.end(), algo);
+ if (it == _algorithms.end())
+ return false;
+
+ return true;
+}
+
+} // namespace
+
+namespace luci
+{
+
+CircleOptimizer::Options *CircleOptimizer::options(void)
+{
+ if (_options == nullptr)
+ {
+ _options = std::make_unique<OptimizeOptionsImpl>();
+ }
+
+ return _options.get();
+}
+
+void CircleOptimizer::optimize(loco::Graph *g) const
+{
+ logo::Phase phase;
+
+ /* TRANSFORM DECLARATION BEGIN */
+ if (_options->query(Options::Algorithm::ResolveCustomOpAdd))
+ {
+ phase.emplace_back(std::make_unique<luci::ResolveCustomOpAddPass>());
+ }
+ if (_options->query(Options::Algorithm::ResolveCustomOpBatchMatMul))
+ {
+ phase.emplace_back(std::make_unique<luci::ResolveCustomOpBatchMatMulPass>());
+ }
+ if (_options->query(Options::Algorithm::ResolveCustomOpMatMul))
+ {
+ phase.emplace_back(std::make_unique<luci::ResolveCustomOpMatMulPass>());
+ }
+ if (_options->query(Options::Algorithm::FuseInstanceNorm))
+ {
+ phase.emplace_back(std::make_unique<FuseInstanceNormPass>());
+ }
+ if (_options->query(Options::Algorithm::FuseBCQ))
+ {
+ phase.emplace_back(std::make_unique<FuseBCQPass>());
+ }
+ if (_options->query(Options::Algorithm::FuseBatchNormWithTConv))
+ {
+ phase.emplace_back(std::make_unique<FuseBatchNormWithTConvPass>());
+ }
+ if (_options->query(Options::Algorithm::FuseAddWithTConv))
+ {
+ phase.emplace_back(std::make_unique<FuseAddWithTConvPass>());
+ }
+ if (_options->query(Options::Algorithm::FuseActivationFunction))
+ {
+ phase.emplace_back(std::make_unique<FuseActivationFunctionPass>());
+ }
+ if (_options->query(Options::Algorithm::FoldDequantize))
+ {
+ phase.emplace_back(std::make_unique<luci::FoldDequantizePass>());
+ }
+ if (_options->query(Options::Algorithm::FusePreActivationBatchNorm))
+ {
+ phase.emplace_back(std::make_unique<luci::FusePreActivationBatchNormPass>());
+ }
+ if (_options->query(Options::Algorithm::MakeBatchNormGammaPositive))
+ {
+ phase.emplace_back(std::make_unique<luci::MakeBatchNormGammaPositivePass>());
+ }
+
+ // Shape inference is needed for added nodes doing above transformations
+ phase.emplace_back(std::make_unique<luci::ShapeInferencePass>());
+ phase.emplace_back(std::make_unique<luci::TypeInferencePass>());
+ phase.emplace_back(std::make_unique<logo::RemoveDeadNodeWithQueryPass>());
+ /* TRANSFORM DECLARATION END */
+
+ ProgressReporter prog(g, logo::PhaseStrategy::Saturate);
+ logo::PhaseRunner<logo::PhaseStrategy::Saturate> phase_runner{g};
+ phase_runner.attach(&prog);
+ phase_runner.run(phase);
+}
+
+void CircleOptimizer::quantize(loco::Graph *g) const
+{
+ // Fake quantization of weights
+ if (_options->query(Options::Algorithm::QuantizeDequantizeWeights))
+ {
+ static const std::vector<std::string> fakeq_supported_input_dtype{"float32"};
+ static const std::vector<std::string> fakeq_supported_output_dtype{"uint8", "int16"};
+ static const std::vector<std::string> fakeq_supported_granularity{"layer", "channel"};
+
+ auto input_dtype = _options->param(Options::AlgorithmParameters::Quantize_input_dtype);
+ auto output_dtype = _options->param(Options::AlgorithmParameters::Quantize_output_dtype);
+ auto granularity = _options->param(Options::AlgorithmParameters::Quantize_granularity);
+
+ if (!in_array(to_lower_case(input_dtype), fakeq_supported_input_dtype))
+ throw std::runtime_error("Unsupported input type. List of supported input type: " +
+ to_string(fakeq_supported_input_dtype));
+
+ if (!in_array(to_lower_case(output_dtype), fakeq_supported_output_dtype))
+ throw std::runtime_error("Unsupported output type. List of supported output type: " +
+ to_string(fakeq_supported_output_dtype));
+
+ if (!in_array(to_lower_case(granularity), fakeq_supported_granularity))
+ throw std::runtime_error("Unsupported granularity. List of supported granularity: " +
+ to_string(fakeq_supported_granularity));
+
+ if (str_to_granularity(granularity) == QuantizationGranularity::LayerWise &&
+ str_to_dtype(output_dtype) != loco::DataType::U8)
+ throw std::runtime_error("Layer-wise quantization only supports uint8 dtype.");
+
+ // Clear existing quantparams before doing fake quantization
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ auto circle_node = loco::must_cast<luci::CircleNode *>(node);
+ if (circle_node->quantparam() != nullptr)
+ circle_node->quantparam(nullptr);
+ }
+
+ luci::QuantizeDequantizeWeightsPass fake_quantizer(
+ str_to_dtype(input_dtype), str_to_dtype(output_dtype), str_to_granularity(granularity));
+ fake_quantizer.run(g);
+ }
+
+ // Actual quantization of weights, bias, and activation
+ if (_options->query(Options::Algorithm::QuantizeWithMinMax))
+ {
+ static const std::vector<std::string> qwmm_supported_input_dtype{"float32"};
+ static const std::vector<std::string> qwmm_supported_output_dtype{"uint8", "int16"};
+ static const std::vector<std::string> qwmm_supported_granularity{"layer", "channel"};
+
+ auto input_dtype = _options->param(Options::AlgorithmParameters::Quantize_input_dtype);
+ auto output_dtype = _options->param(Options::AlgorithmParameters::Quantize_output_dtype);
+ auto granularity = _options->param(Options::AlgorithmParameters::Quantize_granularity);
+
+ if (!in_array(to_lower_case(input_dtype), qwmm_supported_input_dtype))
+ throw std::runtime_error("Unsupported input type. List of supported input types: " +
+ to_string(qwmm_supported_input_dtype));
+
+ if (!in_array(to_lower_case(output_dtype), qwmm_supported_output_dtype))
+ throw std::runtime_error("Unsupported output type. List of supported output types: " +
+ to_string(qwmm_supported_output_dtype));
+
+ if (!in_array(to_lower_case(granularity), qwmm_supported_granularity))
+ throw std::runtime_error("Unsupported granularity. List of supported granularity: " +
+ to_string(qwmm_supported_granularity));
+
+ if (str_to_granularity(granularity) == QuantizationGranularity::LayerWise &&
+ str_to_dtype(output_dtype) != loco::DataType::U8)
+ throw std::runtime_error("Layer-wise quantization only supports uint8 dtype.");
+
+ luci::QuantizeWithMinMaxPass quantizer(str_to_dtype(input_dtype), str_to_dtype(output_dtype),
+ str_to_granularity(granularity));
+ quantizer.run(g);
+ }
+
+ // Requantize
+ if (_options->query(Options::Algorithm::Requantize))
+ {
+ static const std::vector<std::string> rq_supported_input_dtype{"int8"};
+ static const std::vector<std::string> rq_supported_output_dtype{"uint8"};
+
+ auto input_dtype = _options->param(Options::AlgorithmParameters::Quantize_input_dtype);
+ auto output_dtype = _options->param(Options::AlgorithmParameters::Quantize_output_dtype);
+
+ if (!in_array(to_lower_case(input_dtype), rq_supported_input_dtype))
+ throw std::runtime_error("Unsupported input type. List of supported input types: " +
+ to_string(rq_supported_input_dtype));
+
+ if (!in_array(to_lower_case(output_dtype), rq_supported_output_dtype))
+ throw std::runtime_error("Unsupported output type. List of supported output types: " +
+ to_string(rq_supported_output_dtype));
+
+ luci::RequantizePass requantizer(str_to_dtype(input_dtype), str_to_dtype(output_dtype));
+ requantizer.run(g);
+ }
+
+ logo::Phase phase;
+
+ // Do Shape/Type inference
+ phase.emplace_back(std::make_unique<luci::ShapeInferencePass>());
+ phase.emplace_back(std::make_unique<luci::TypeInferencePass>());
+
+ ProgressReporter prog(g, logo::PhaseStrategy::Saturate);
+ logo::PhaseRunner<logo::PhaseStrategy::Saturate> phase_runner{g};
+ phase_runner.attach(&prog);
+ phase_runner.run(phase);
+}
+
+void CircleOptimizer::sparsify(loco::Graph *g) const
+{
+ if (_options->query(Options::Algorithm::SparsifyTensorPass))
+ {
+ std::string tensor_name = _options->param(Options::AlgorithmParameters::Sparsify_tensor_name);
+ std::string str_tarversal_order =
+ _options->param(Options::AlgorithmParameters::Sparsify_traversal_order);
+ std::string str_format = _options->param(Options::AlgorithmParameters::Sparsify_format);
+ std::string str_block_size = _options->param(Options::AlgorithmParameters::Sparsify_block_size);
+ std::string str_block_map = _options->param(Options::AlgorithmParameters::Sparsify_block_map);
+
+ // traversal order
+ std::vector<int32_t> traversal_order = parseIntFromCommadelimitedStr(str_tarversal_order);
+ // format
+ std::vector<DimensionType> format;
+ std::istringstream is(str_format);
+ for (char c; is >> c;)
+ {
+ assert(c != ',');
+ if (c == 'd')
+ format.push_back(DimensionType::DENSE);
+ else if (c == 's')
+ format.push_back(DimensionType::SPARSE_CSR);
+ if (is.peek() == ',')
+ is.ignore();
+ }
+ // block size
+ std::vector<int32_t> block_size = parseIntFromCommadelimitedStr(str_block_size);
+ // block map
+ std::vector<int32_t> block_map = parseIntFromCommadelimitedStr(str_block_map);
+
+ luci::SparsifyTensorPass sparsifier{tensor_name, traversal_order, format, block_size,
+ block_map};
+ sparsifier.run(g);
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/CircleOptimizerUtils.cpp b/compiler/luci/pass/src/CircleOptimizerUtils.cpp
new file mode 100644
index 000000000..ffc372392
--- /dev/null
+++ b/compiler/luci/pass/src/CircleOptimizerUtils.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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 "CircleOptimizerUtils.h"
+
+namespace luci
+{
+
+bool in_array(const std::string &str, const std::vector<std::string> &array)
+{
+ return std::find(array.begin(), array.end(), str) != array.end();
+}
+
+std::string to_string(const std::vector<std::string> &strings)
+{
+ assert(!strings.empty());
+
+ std::string res;
+ for (unsigned int i = 0; i < strings.size() - 1; i++)
+ res += strings[i] + ", ";
+
+ res += strings[strings.size() - 1];
+ return res;
+}
+
+std::string to_lower_case(std::string s)
+{
+ std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); });
+ return s;
+}
+
+loco::DataType str_to_dtype(const std::string &str)
+{
+ if (to_lower_case(str).compare("uint8") == 0)
+ return loco::DataType::U8;
+ if (to_lower_case(str).compare("uint16") == 0)
+ return loco::DataType::U16;
+ if (to_lower_case(str).compare("uint32") == 0)
+ return loco::DataType::U32;
+ if (to_lower_case(str).compare("uint64") == 0)
+ return loco::DataType::U64;
+
+ if (to_lower_case(str).compare("int8") == 0)
+ return loco::DataType::S8;
+ if (to_lower_case(str).compare("int16") == 0)
+ return loco::DataType::S16;
+ if (to_lower_case(str).compare("int32") == 0)
+ return loco::DataType::S32;
+ if (to_lower_case(str).compare("int64") == 0)
+ return loco::DataType::S64;
+
+ if (to_lower_case(str).compare("float16") == 0)
+ return loco::DataType::FLOAT16;
+ if (to_lower_case(str).compare("float32") == 0)
+ return loco::DataType::FLOAT32;
+ if (to_lower_case(str).compare("float64") == 0)
+ return loco::DataType::FLOAT64;
+
+ if (to_lower_case(str).compare("bool") == 0)
+ return loco::DataType::BOOL;
+
+ return loco::DataType::Unknown;
+}
+
+QuantizationGranularity str_to_granularity(const std::string &str)
+{
+ if (to_lower_case(str).compare("layer") == 0)
+ return QuantizationGranularity::LayerWise;
+
+ if (to_lower_case(str).compare("channel") == 0)
+ return QuantizationGranularity::ChannelWise;
+
+ throw std::runtime_error("Quantization granularity must be either 'layer' or 'channel'");
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/CircleOptimizerUtils.h b/compiler/luci/pass/src/CircleOptimizerUtils.h
new file mode 100644
index 000000000..7e577a05f
--- /dev/null
+++ b/compiler/luci/pass/src/CircleOptimizerUtils.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_CIRCLE_OPTIMIZER_UTILS_H__
+#define __LUCI_CIRCLE_OPTIMIZER_UTILS_H__
+
+#include "luci/Pass/QuantizeDequantizeWeightsPass.h"
+#include "luci/Pass/QuantizeWithMinMaxPass.h"
+
+#include <loco.h>
+
+#include <algorithm>
+
+namespace luci
+{
+
+bool in_array(const std::string &, const std::vector<std::string> &);
+
+std::string to_string(const std::vector<std::string> &);
+
+std::string to_lower_case(std::string);
+
+loco::DataType str_to_dtype(const std::string &);
+
+QuantizationGranularity str_to_granularity(const std::string &);
+
+} // namespace luci
+
+#endif // __LUCI_CIRCLE_OPTIMIZER_UTILS_H__
diff --git a/compiler/luci/pass/src/FoldDequantizePass.cpp b/compiler/luci/pass/src/FoldDequantizePass.cpp
new file mode 100644
index 000000000..01c04f478
--- /dev/null
+++ b/compiler/luci/pass/src/FoldDequantizePass.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Pass/FoldDequantizePass.h"
+
+#include <luci/IR/CircleNodes.h>
+
+#include <loco/Service/TypeInference.h>
+
+namespace
+{
+
+bool is_hybrid_kernel_supported(loco::Node *node)
+{
+ if (dynamic_cast<luci::CircleFullyConnected *>(node) != nullptr)
+ return true;
+
+ return false;
+}
+
+bool is_foldable_const(luci::CircleConst *node)
+{
+ if (node->quantparam() == nullptr)
+ return false;
+
+ if (node->dtype() == loco::DataType::S8)
+ return true;
+ if (node->dtype() == loco::DataType::U8)
+ return true;
+
+ return false;
+}
+
+luci::CircleConst *dequantized_const_node(luci::CircleConst *const_node)
+{
+ if (const_node->quantparam() == nullptr)
+ {
+ throw std::runtime_error("Given constant node has no quantization parameter");
+ }
+
+ auto g = const_node->graph();
+ auto new_const_node = g->nodes()->create<luci::CircleConst>();
+
+ new_const_node->dtype(loco::DataType::FLOAT32);
+ new_const_node->rank(const_node->rank());
+ uint32_t dim_size = 1;
+ for (uint32_t i = 0; i < new_const_node->rank(); ++i)
+ {
+ new_const_node->dim(i) = const_node->dim(i);
+ dim_size *= const_node->dim(i).value();
+ }
+ new_const_node->size<loco::DataType::FLOAT32>(dim_size);
+ new_const_node->shape_status(luci::ShapeStatus::VALID);
+
+ const int32_t q_dim = const_node->quantparam()->quantized_dimension;
+ const int32_t q_dim_value = const_node->dim(q_dim).value();
+
+ int32_t right_count = q_dim_value;
+ for (uint32_t i = q_dim + 1; i < const_node->rank(); ++i)
+ right_count *= const_node->dim(i).value();
+
+ if (const_node->dtype() == loco::DataType::S8)
+ {
+ for (uint32_t i = 0; i < const_node->size<loco::DataType::S8>(); ++i)
+ {
+ uint32_t qd = (i % right_count) / (right_count / q_dim_value);
+ if (qd >= const_node->quantparam()->zerop.size())
+ qd = 0;
+
+ new_const_node->at<loco::DataType::FLOAT32>(i) =
+ (float)(const_node->at<loco::DataType::S8>(i) - const_node->quantparam()->zerop.at(qd)) *
+ const_node->quantparam()->scale.at(qd);
+ }
+ }
+ else
+ {
+ for (uint32_t i = 0; i < const_node->size<loco::DataType::U8>(); ++i)
+ {
+ uint32_t qd = (i % right_count) / (right_count / q_dim_value);
+ if (qd >= const_node->quantparam()->zerop.size())
+ qd = 0;
+
+ new_const_node->at<loco::DataType::FLOAT32>(i) =
+ (float)((int)const_node->at<loco::DataType::U8>(i) -
+ const_node->quantparam()->zerop.at(qd)) *
+ const_node->quantparam()->scale.at(qd);
+ }
+ }
+
+ return new_const_node;
+}
+
+bool replace_const_node(loco::Node *node, luci::CircleConst *const_node)
+{
+ if (auto gather = dynamic_cast<luci::CircleGather *>(node))
+ {
+ gather->params(dequantized_const_node(const_node));
+ gather->dtype(loco::DataType::FLOAT32);
+ return true;
+ }
+ else
+ {
+ // TODO Support more ops
+ return false;
+ }
+}
+
+} // namespace
+
+namespace luci
+{
+
+/**
+ *
+ * Folding pattern 1 - When input of Dequantize is foldable constant
+ *
+ * [Before]
+ * quantized_const_input ---------- Dequantize ---------- Op ---
+ * +-- Op1_with_quant_input ---
+ * +-- Op2_with_quant_input ---
+ *
+ * [After]
+ * dequantized_const_input -------------------------------- Op ---
+ *
+ * quantized_const_input ----- Op1_with_quant_input ---
+ * +-- Op2_with_quant_input ---
+ *
+ *
+ * Folding pattern 2 - When input of Dequantize uses quantized output value
+ *
+ * [Before]
+ * quantized_const_input ----- Gather ----- Dequantize --- Op ---
+ * +-- Op1_with_quant_input ---
+ * +-- Op2_with_quant_input ---
+ *
+ * [After]
+ * dequantized_const_input ------Gather -------------------- Op ---
+ *
+ * quantized_const_input ----- Op1_with_quant_input ---
+ * +-- Op2_with_quant_input ---
+ *
+ *
+ */
+bool FoldDequantizePass::run(loco::Graph *g)
+{
+ bool changed = false;
+
+ for (auto node : loco::all_nodes(g))
+ {
+ if (auto circle_dequant = dynamic_cast<luci::CircleDequantize *>(node))
+ {
+ if (auto const_input = dynamic_cast<luci::CircleConst *>(circle_dequant->input()))
+ {
+ // Pattern 1 - When input of Dequantize is foldable constant
+ if (is_foldable_const(const_input))
+ {
+ loco::replace(circle_dequant).with(dequantized_const_node(const_input));
+ changed = true;
+ }
+ }
+ }
+ else if (auto const_node = dynamic_cast<luci::CircleConst *>(node))
+ {
+ if (is_foldable_const(const_node))
+ {
+ for (auto const_node_user : loco::succs(const_node))
+ {
+ // If user is hybrid kernel supported operation, do not dequantize
+ if (is_hybrid_kernel_supported(const_node_user))
+ continue;
+
+ auto users = loco::succs(const_node_user);
+ if (users.size() > 1)
+ continue;
+
+ // Pattern 2 - When input of Dequantize uses quantized output value
+ if (auto dequant = dynamic_cast<luci::CircleDequantize *>(*users.begin()))
+ {
+ if (replace_const_node(const_node_user, const_node))
+ {
+ loco::replace(dequant).with(const_node_user);
+ changed = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return changed;
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/FuseActivationFunctionPass.cpp b/compiler/luci/pass/src/FuseActivationFunctionPass.cpp
new file mode 100644
index 000000000..844541d2d
--- /dev/null
+++ b/compiler/luci/pass/src/FuseActivationFunctionPass.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Pass/FuseActivationFunctionPass.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/CircleOpcode.h>
+
+namespace luci
+{
+
+bool fuse_activation_function(luci::CircleNode *node)
+{
+ auto preds = loco::preds(node);
+ assert(preds.size() == 1);
+
+ auto pred_node = static_cast<luci::CircleNode *>(*preds.begin());
+ if (loco::succs(pred_node).size() != 1)
+ return false;
+
+ auto node_with_fused_act =
+ dynamic_cast<luci::LuciNodeMixin<luci::LuciNodeTrait::FusedActFunc> *>(pred_node);
+ if (node_with_fused_act == nullptr)
+ return false;
+
+ auto fused_act = node_with_fused_act->fusedActivationFunction();
+
+ luci::FusedActFunc target_func = luci::FusedActFunc::UNDEFINED;
+
+ auto opcode = node->opcode();
+ if (opcode == luci::CircleOpcode::RELU)
+ {
+ if (fused_act == luci::FusedActFunc::NONE || fused_act == luci::FusedActFunc::RELU)
+ target_func = luci::FusedActFunc::RELU;
+ else if (fused_act == luci::FusedActFunc::RELU6)
+ target_func = luci::FusedActFunc::RELU6;
+ else
+ return false;
+ }
+ else if (opcode == luci::CircleOpcode::RELU6)
+ {
+ if (fused_act == luci::FusedActFunc::NONE || fused_act == luci::FusedActFunc::RELU ||
+ fused_act == luci::FusedActFunc::RELU6)
+ target_func = luci::FusedActFunc::RELU6;
+ else
+ return false;
+ }
+ else if (opcode == luci::CircleOpcode::RELU_N1_TO_1)
+ {
+ if (fused_act == luci::FusedActFunc::NONE || fused_act == luci::FusedActFunc::RELU_N1_TO_1)
+ target_func = luci::FusedActFunc::RELU_N1_TO_1;
+ else
+ return false;
+ }
+ else if (opcode == luci::CircleOpcode::TANH)
+ {
+ if (fused_act == luci::FusedActFunc::NONE)
+ target_func = luci::FusedActFunc::TANH;
+ else
+ return false;
+ }
+ else
+ return false;
+
+ node_with_fused_act->fusedActivationFunction(target_func);
+ loco::replace(node).with(pred_node);
+
+ node->drop();
+
+ return true;
+}
+
+bool FuseActivationFunctionPass::run(loco::Graph *g)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ auto circle_node = static_cast<luci::CircleNode *>(node);
+ auto opcode = circle_node->opcode();
+ if (opcode == luci::CircleOpcode::RELU || opcode == luci::CircleOpcode::RELU6 ||
+ opcode == luci::CircleOpcode::RELU_N1_TO_1 || opcode == luci::CircleOpcode::TANH)
+ {
+ if (fuse_activation_function(circle_node))
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/FuseActivationFunctionPass.test.cpp b/compiler/luci/pass/src/FuseActivationFunctionPass.test.cpp
new file mode 100644
index 000000000..226a303a1
--- /dev/null
+++ b/compiler/luci/pass/src/FuseActivationFunctionPass.test.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FuseActivationFunctionPassInternal.h"
+
+#include <luci/IR/CircleNodes.h>
+
+#include <gtest/gtest.h>
+
+namespace
+{
+
+/**
+ * Simple graph for test
+ *
+ * BEFORE
+ *
+ * [Conv1]
+ * |
+ * [Activation func]
+ * |
+ * [Conv2]
+ *
+ * AFTER
+ *
+ * [Conv1 + Activation func]
+ * |
+ * [Conv2]
+ *
+ */
+class SimpleGraph
+{
+public:
+ SimpleGraph()
+ {
+ conv1 = g.nodes()->create<luci::CircleConv2D>();
+ conv2 = g.nodes()->create<luci::CircleConv2D>();
+ relu = g.nodes()->create<luci::CircleRelu>();
+
+ conv1->fusedActivationFunction(luci::FusedActFunc::NONE);
+
+ relu->features(conv1);
+ conv2->input(relu);
+ }
+
+public:
+ loco::Graph g;
+ luci::CircleConv2D *conv1;
+ luci::CircleConv2D *conv2;
+ luci::CircleRelu *relu;
+};
+
+} // namespace
+
+TEST(FusePreActivationBatchNorm, fuse_activation_function)
+{
+ SimpleGraph g;
+
+ EXPECT_TRUE(luci::fuse_activation_function(g.relu));
+
+ EXPECT_EQ(g.conv1, g.conv2->input());
+}
+
+TEST(FusePreActivationBatchNorm, fuse_activation_function_dup_relu)
+{
+ SimpleGraph g;
+ g.conv1->fusedActivationFunction(luci::FusedActFunc::RELU);
+
+ EXPECT_TRUE(luci::fuse_activation_function(g.relu));
+
+ EXPECT_EQ(g.conv1, g.conv2->input());
+}
+
+TEST(FusePreActivationBatchNorm, fuse_activation_function_NEG)
+{
+ SimpleGraph g;
+ g.conv2->input(g.conv1);
+
+ // Conv1 has multiple successors
+ EXPECT_FALSE(luci::fuse_activation_function(g.relu));
+
+ g.conv2->input(g.relu);
+ g.conv1->fusedActivationFunction(luci::FusedActFunc::TANH);
+
+ // Conv1 already has activation function
+ EXPECT_FALSE(luci::fuse_activation_function(g.relu));
+}
diff --git a/compiler/luci/pass/src/FuseActivationFunctionPassInternal.h b/compiler/luci/pass/src/FuseActivationFunctionPassInternal.h
new file mode 100644
index 000000000..0cfb9d507
--- /dev/null
+++ b/compiler/luci/pass/src/FuseActivationFunctionPassInternal.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_CIRCLE_FUSE_ACTIVATION_FUNCTION_PASS_INTERNAL_H__
+#define __LUCI_CIRCLE_FUSE_ACTIVATION_FUNCTION_PASS_INTERNAL_H__
+
+#include <luci/IR/CircleNodes.h>
+
+namespace luci
+{
+
+// Fuse activation function with preceding Op
+/// @return true if success
+bool fuse_activation_function(luci::CircleNode *node);
+
+} // namespace luci
+
+#endif // __LUCI_CIRCLE_FUSE_ACTIVATION_FUNCTION_PASS_INTERNAL_H__
diff --git a/compiler/luci/pass/src/FuseAddWithTConvPass.cpp b/compiler/luci/pass/src/FuseAddWithTConvPass.cpp
new file mode 100644
index 000000000..bd7805f6a
--- /dev/null
+++ b/compiler/luci/pass/src/FuseAddWithTConvPass.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Pass/FuseAddWithTConvPass.h"
+
+#include <luci/IR/CircleNodes.h>
+
+namespace
+{
+/**
+ * Fuse add to TCONV if possible
+ *
+ * BEFORE
+ *
+ * [CircleTransposeConv]
+ * |
+ * [add]
+ * AFTER
+ *
+ * [CircleTransposeConv]
+ */
+bool fuse_add_with_tconv(luci::CircleTransposeConv *tconv)
+{
+ // check whether it has bias or not. This optimization works only if it doesn't.
+ auto bias = dynamic_cast<luci::CircleOutputExclude *>(tconv->bias());
+ if (not bias)
+ return false;
+
+ // get weight of tconv
+ auto filter = dynamic_cast<luci::CircleConst *>(tconv->filter());
+ if (not filter)
+ return false;
+ if (filter->dtype() != loco::DataType::FLOAT32)
+ return false;
+
+ // get add node
+ auto tconv_output = loco::succs(tconv);
+ assert(tconv_output.size() == 1);
+ auto add = dynamic_cast<luci::CircleAdd *>(*tconv_output.begin());
+ if (not add)
+ return false;
+ if (add->dtype() != loco::DataType::FLOAT32)
+ return false;
+ if (add->fusedActivationFunction() != luci::FusedActFunc::NONE &&
+ add->fusedActivationFunction() != luci::FusedActFunc::RELU6)
+ return false;
+
+ // get addition
+ luci::CircleConst *addition = nullptr;
+ if (add->x() == tconv)
+ addition = dynamic_cast<luci::CircleConst *>(add->y());
+ else
+ addition = dynamic_cast<luci::CircleConst *>(add->x());
+
+ if (not addition)
+ return false;
+
+ // addition dim(0) == tconv filter channel dim
+ if (addition->rank() != 1)
+ return false;
+ auto addition_dim = addition->dim(0).value();
+ auto filter_channel_dim = filter->dim(0).value();
+ if (filter_channel_dim != addition_dim)
+ return false;
+
+ // fuse addition with transposed conv
+ tconv->bias(addition);
+
+ if (add->fusedActivationFunction() == luci::FusedActFunc::RELU6)
+ {
+ // separate relu op from add op
+ auto relu = add->graph()->nodes()->create<luci::CircleRelu6>();
+ relu->features(tconv);
+
+ // remove add node
+ replace(add).with(relu);
+ }
+ else
+ {
+ replace(add).with(tconv);
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace luci
+{
+
+bool FuseAddWithTConvPass::run(loco::Graph *g)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ auto tconv = dynamic_cast<luci::CircleTransposeConv *>(node);
+ if (not tconv)
+ continue;
+
+ if (fuse_add_with_tconv(tconv))
+ changed = true;
+ }
+
+ return changed;
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/FuseBCQPass.cpp b/compiler/luci/pass/src/FuseBCQPass.cpp
new file mode 100644
index 000000000..ebf28779b
--- /dev/null
+++ b/compiler/luci/pass/src/FuseBCQPass.cpp
@@ -0,0 +1,506 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Pass/FuseBCQPass.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/Log.h>
+
+#include <cassert>
+#include <set>
+
+namespace
+{
+
+// V means the version of BCQ.
+template <int32_t V> class BCQFuser;
+
+template <> class BCQFuser<1>
+{
+public:
+ BCQFuser<1>(int32_t original_output_cnt, int32_t bundle_cnt)
+ : _original_output_cnt{original_output_cnt}, _bundle_cnt{bundle_cnt}
+ {
+ // Do nothing
+ }
+
+public:
+ bool fuseBCQ(loco::Graph *g)
+ {
+
+ const auto output_nodes = loco::output_nodes(g);
+ for (auto node : output_nodes)
+ {
+ auto output_node = loco::must_cast<luci::CircleOutput *>(node);
+
+ /**
+ * First output of model is metadata for BCQ. Please refer to following example.
+ *
+ * When original_output_cnt is 2,
+ * BCQ_METADATA, original_output_1, original_output_2, BCQ_INFO_1, ...
+ */
+ if ((int)output_node->index() > _original_output_cnt)
+ {
+ const auto prefix = (output_node->index() - (_original_output_cnt + 1)) / (_bundle_cnt);
+ const MetadataType metadata_type = static_cast<MetadataType>(
+ (output_node->index() - (_original_output_cnt + 1)) % (_bundle_cnt));
+ const auto circle_node = loco::must_cast<luci::CircleNode *>(output_node->from());
+ add_BCQ_info_node(prefix, metadata_type, circle_node);
+ }
+ }
+
+ if (!is_bcqinfo_valid())
+ return false;
+
+ for (auto f : _fusable_op)
+ {
+ auto prefix = f.first;
+ luci::CircleNode *node = f.second;
+
+ if (!is_valid_prefix(prefix))
+ continue;
+
+ // Fuse Gather to BCQGather
+ if (auto gather = dynamic_cast<luci::CircleGather *>(node))
+ {
+ if (auto params = dynamic_cast<luci::CircleConst *>(gather->params()))
+ {
+ auto bcq_gather = g->nodes()->create<luci::CircleBCQGather>();
+
+ bcq_gather->op_version(1);
+ bcq_gather->input_scales(_alpha[prefix]);
+ bcq_gather->input_binary(_packed_binary_code[prefix]);
+ bcq_gather->indices(gather->indices());
+ bcq_gather->input_clusters(packed_clusters(g, prefix));
+
+ if (_do_w_x[prefix]->at<loco::DataType::BOOL>(0))
+ {
+ bcq_gather->input_hidden_size(params->dim(1).value());
+ bcq_gather->axis(gather->axis());
+ loco::replace(gather).with(bcq_gather);
+ }
+ else
+ {
+ bcq_gather->input_hidden_size(params->dim(0).value());
+ const auto axis_transpose = (gather->axis() == 0) ? 1 : 0;
+ bcq_gather->axis(axis_transpose);
+
+ const auto indices_rank =
+ loco::must_cast<luci::CircleNode *>(gather->indices())->rank();
+
+ auto perm = g->nodes()->create<luci::CircleConst>();
+ perm->dtype(loco::DataType::S32);
+ perm->size<loco::DataType::S32>(1 + indices_rank);
+ perm->rank(1);
+ perm->dim(0) = 1 + indices_rank;
+ for (uint32_t idx = 0; idx < indices_rank; ++idx)
+ perm->at<loco::DataType::S32>(idx) = idx + 1;
+ perm->at<loco::DataType::S32>(indices_rank) = 0;
+ perm->shape_status(luci::ShapeStatus::VALID);
+
+ auto output_transpose = g->nodes()->create<luci::CircleTranspose>();
+ output_transpose->a(bcq_gather);
+ output_transpose->perm(perm);
+
+ loco::replace(gather).with(output_transpose);
+ }
+
+ return true;
+ }
+ }
+
+ // Einsum is unpacked to FullyConnected, Pack and Reshape
+ if (auto reshape = dynamic_cast<luci::CircleReshape *>(node))
+ {
+ node = dynamic_cast<luci::CircleNode *>(reshape->tensor());
+ }
+ if (auto pack = dynamic_cast<luci::CirclePack *>(node))
+ {
+ if (pack->values_count() == 1 && pack->rank() == 3)
+ {
+ node = dynamic_cast<luci::CircleNode *>(pack->values(0));
+ }
+ }
+
+ // Fuse FullyConnected to BCQFullyConnected
+ if (auto fully_connected = dynamic_cast<luci::CircleFullyConnected *>(node))
+ {
+ if (auto weights = dynamic_cast<luci::CircleConst *>(fully_connected->weights()))
+ {
+ auto bcq_fc = g->nodes()->create<luci::CircleBCQFullyConnected>();
+
+ bcq_fc->op_version(1);
+ bcq_fc->weights_scales(_alpha[prefix]);
+ bcq_fc->weights_binary(_packed_binary_code[prefix]);
+ bcq_fc->bias(fully_connected->bias());
+ bcq_fc->weights_clusters(packed_clusters(g, prefix));
+ bcq_fc->fusedActivationFunction(fully_connected->fusedActivationFunction());
+
+ loco::Node *bcq_input = fully_connected->input();
+
+ // If input of BCQFullyConnected has more than rank 2, we should reshape it as rank 2
+ const auto original_input = loco::must_cast<luci::CircleNode *>(fully_connected->input());
+ if (original_input->shape_status() == luci::ShapeStatus::VALID &&
+ original_input->rank() > 2)
+ {
+ auto new_shape = g->nodes()->create<luci::CircleConst>();
+ new_shape->dtype(loco::DataType::S32);
+ new_shape->size<loco::DataType::S32>(2);
+ new_shape->rank(1);
+ new_shape->dim(0) = 2;
+
+ auto batch_size = 1;
+ for (uint32_t i = 0; i < original_input->rank() - 1; ++i)
+ batch_size *= original_input->dim(i).value();
+
+ new_shape->at<loco::DataType::S32>(0) = batch_size;
+ new_shape->at<loco::DataType::S32>(1) =
+ original_input->dim(original_input->rank() - 1).value();
+ new_shape->shape_status(luci::ShapeStatus::VALID);
+
+ auto reshape = g->nodes()->create<luci::CircleReshape>();
+ reshape->tensor(original_input);
+ reshape->shape(new_shape);
+
+ bcq_input = reshape;
+ }
+
+ // If x_w formation, we should insert Transpose in front and back of BCQFullyConnected
+ if (_do_w_x[prefix]->at<loco::DataType::BOOL>(0))
+ {
+ bcq_fc->weights_hidden_size(weights->dim(0).value());
+ bcq_fc->input(bcq_input);
+ loco::replace(fully_connected).with(bcq_fc);
+ }
+ else
+ {
+ bcq_fc->weights_hidden_size(weights->dim(1).value());
+
+ auto perm = g->nodes()->create<luci::CircleConst>();
+ perm->dtype(loco::DataType::S32);
+ perm->size<loco::DataType::S32>(2);
+ perm->rank(1);
+ perm->dim(0) = 2;
+ perm->at<loco::DataType::S32>(0) = 1;
+ perm->at<loco::DataType::S32>(1) = 0;
+ perm->shape_status(luci::ShapeStatus::VALID);
+
+ auto input_transpose = g->nodes()->create<luci::CircleTranspose>();
+ input_transpose->a(bcq_input);
+ input_transpose->perm(perm);
+
+ bcq_fc->input(input_transpose);
+
+ auto output_transpose = g->nodes()->create<luci::CircleTranspose>();
+ output_transpose->a(bcq_fc);
+ output_transpose->perm(perm);
+
+ loco::replace(fully_connected).with(output_transpose);
+ }
+
+ return true;
+ }
+ else
+ {
+ // TODO Is there any case that input() is constant, instead of weights()?
+ }
+ }
+ }
+
+ return false;
+ }
+
+private:
+ enum MetadataType
+ {
+ DO_W_X,
+ ALPHA,
+ BINARY_CODE,
+ NUM_OF_CLUSTERS,
+ SIZE_OF_CLUSTERS,
+ QBITS_OF_CLUSTERS,
+ FUSABLE_OP,
+ DEQUANT_WEIGHT,
+ };
+
+ void add_BCQ_info_node(int32_t prefix, MetadataType metadata_type, luci::CircleNode *node)
+ {
+ if (metadata_type == MetadataType::FUSABLE_OP)
+ {
+ _fusable_op[prefix] = node;
+ return;
+ }
+
+ luci::CircleConst *const_node;
+
+ // Converter in TensorFlow v1.x sometimes generate Reshape op
+ if (auto reshape = dynamic_cast<luci::CircleReshape *>(node))
+ const_node = loco::must_cast<luci::CircleConst *>(reshape->tensor());
+ else
+ const_node = loco::must_cast<luci::CircleConst *>(node);
+
+ if (metadata_type == MetadataType::DO_W_X)
+ _do_w_x[prefix] = const_node;
+ else if (metadata_type == MetadataType::ALPHA)
+ _alpha[prefix] = const_node;
+ else if (metadata_type == MetadataType::BINARY_CODE)
+ _packed_binary_code[prefix] = const_node;
+ else if (metadata_type == MetadataType::NUM_OF_CLUSTERS)
+ _number_of_clusters[prefix] = const_node;
+ else if (metadata_type == MetadataType::SIZE_OF_CLUSTERS)
+ _size_of_clusters[prefix] = const_node;
+ else if (metadata_type == MetadataType::QBITS_OF_CLUSTERS)
+ _qbits_of_clusters[prefix] = const_node;
+ else
+ _dequant_weight[prefix] = const_node;
+ }
+
+ bool is_bcqinfo_valid()
+ {
+ LOGGER(l);
+
+ for (auto n : _do_w_x)
+ {
+ // do_w_x should be BOOL type
+ if (n.second->dtype() != loco::DataType::BOOL)
+ {
+ WARN(l) << "FuseBCQPass : do_w_x has wrong type" << std::endl;
+ return false;
+ }
+ }
+
+ for (auto n : _alpha)
+ {
+ // alpha should be FLOAT32 type
+ if (n.second->dtype() != loco::DataType::FLOAT32)
+ {
+ WARN(l) << "FuseBCQPass : alpha has wrong type" << std::endl;
+ return false;
+ }
+ }
+
+ for (auto n : _packed_binary_code)
+ {
+ // packed_binary_code should be INT32 type
+ if (n.second->dtype() != loco::DataType::S32)
+ {
+ WARN(l) << "FuseBCQPass : packed_binary_code has wrong type" << std::endl;
+ return false;
+ }
+ }
+
+ for (auto n : _number_of_clusters)
+ {
+ // number_of_clusters should be INT32 type
+ if (n.second->dtype() != loco::DataType::S32)
+ {
+ WARN(l) << "FuseBCQPass : number_of_clusters has wrong type" << std::endl;
+ return false;
+ }
+ }
+
+ for (auto n : _size_of_clusters)
+ {
+ // size_of_clusters should be INT32 type
+ if (n.second->dtype() != loco::DataType::S32)
+ {
+ WARN(l) << "FuseBCQPass : size_of_clusters has wrong type" << std::endl;
+ return false;
+ }
+ }
+
+ for (auto n : _qbits_of_clusters)
+ {
+ // qbits_of_clusters should be INT32 type
+ if (n.second->dtype() != loco::DataType::S32)
+ {
+ WARN(l) << "FuseBCQPass : qbits_of_clusters has wrong type" << std::endl;
+ return false;
+ }
+ }
+
+ // As dequant_weight is not used for fusing, skip validation.
+
+ return true;
+ }
+
+ bool is_valid_prefix(int32_t prefix)
+ {
+ LOGGER(l);
+
+ if (_do_w_x.find(prefix) == _do_w_x.end())
+ {
+ WARN(l) << "do_w_x is not found" << std::endl;
+ return false;
+ }
+
+ if (_alpha.find(prefix) == _alpha.end())
+ {
+ WARN(l) << "alpha is not found" << std::endl;
+ return false;
+ }
+
+ if (_packed_binary_code.find(prefix) == _packed_binary_code.end())
+ {
+ WARN(l) << "packed_binary_code is not found" << std::endl;
+ return false;
+ }
+
+ if (_number_of_clusters.find(prefix) == _number_of_clusters.end())
+ {
+ WARN(l) << "number_of_clusters is not found" << std::endl;
+ return false;
+ }
+
+ if (_size_of_clusters.find(prefix) == _size_of_clusters.end())
+ {
+ WARN(l) << "size_of_clusters is not found" << std::endl;
+ return false;
+ }
+
+ if (_qbits_of_clusters.find(prefix) == _qbits_of_clusters.end())
+ {
+ WARN(l) << "qbits_of_clusters is not found" << std::endl;
+ return false;
+ }
+
+ // As dequant_weight is not used for fusing, skip validation.
+
+ return true;
+ }
+
+private:
+ luci::CircleConst *packed_clusters(loco::Graph *graph, int32_t prefix)
+ {
+ auto qbits_of_clusters = _qbits_of_clusters[prefix];
+ auto size_of_clusters = _size_of_clusters[prefix];
+ const auto number_of_clusters = _number_of_clusters[prefix]->at<loco::DataType::S32>(0);
+
+ auto packed_clusters = graph->nodes()->create<luci::CircleConst>();
+ packed_clusters->dtype(loco::DataType::S32);
+ packed_clusters->size<loco::DataType::S32>(number_of_clusters * 2);
+ packed_clusters->rank(2);
+ packed_clusters->dim(0) = number_of_clusters;
+ packed_clusters->dim(1) = 2;
+ packed_clusters->shape_status(luci::ShapeStatus::VALID);
+
+ for (int i = 0; i < number_of_clusters; ++i)
+ {
+ packed_clusters->at<loco::DataType::S32>(i * 2) =
+ qbits_of_clusters->at<loco::DataType::S32>(i);
+ packed_clusters->at<loco::DataType::S32>(i * 2 + 1) =
+ size_of_clusters->at<loco::DataType::S32>(i);
+ }
+
+ return packed_clusters;
+ }
+
+private:
+ std::map<int32_t, luci::CircleConst *> _do_w_x;
+ std::map<int32_t, luci::CircleConst *> _alpha;
+ std::map<int32_t, luci::CircleConst *> _packed_binary_code;
+ std::map<int32_t, luci::CircleConst *> _number_of_clusters;
+ std::map<int32_t, luci::CircleConst *> _size_of_clusters;
+ std::map<int32_t, luci::CircleConst *> _qbits_of_clusters;
+ std::map<int32_t, luci::CircleConst *> _dequant_weight;
+ std::map<int32_t, luci::CircleNode *> _fusable_op;
+
+private:
+ int32_t _original_output_cnt = 0;
+ int32_t _bundle_cnt = 0;
+};
+
+} // namespace
+
+namespace luci
+{
+
+bool FuseBCQPass::run(loco::Graph *g)
+{
+ bool changed = false;
+
+ const int32_t start_magicnum = -2e9 + 27;
+ const int32_t end_magicnum = 2e9 - 27;
+
+ luci::CircleConst *metadata_node = nullptr;
+ for (auto node : loco::output_nodes(g))
+ {
+ auto output_node = loco::must_cast<luci::CircleOutput *>(node);
+
+ // Metadata node should be first output
+ if (output_node->index() != 0)
+ continue;
+
+ // Metadata should be constant and dtype should be S32
+ auto const_node = dynamic_cast<luci::CircleConst *>(output_node->from());
+ if (const_node == nullptr || const_node->dtype() != loco::DataType::S32)
+ continue;
+
+ // Metadata has at least four elements
+ const auto element_cnt = const_node->size<loco::DataType::S32>();
+ if (element_cnt < 4)
+ continue;
+
+ // Metadata has magic numbers at first and at last
+ const auto start_value = const_node->at<loco::DataType::S32>(0);
+ const auto end_value = const_node->at<loco::DataType::S32>(element_cnt - 1);
+ if (start_value == start_magicnum && end_value == end_magicnum)
+ {
+ metadata_node = const_node;
+ break;
+ }
+ }
+
+ if (metadata_node != nullptr)
+ {
+ const auto bcq_version = metadata_node->at<loco::DataType::S32>(1);
+ const auto original_output_cnt = metadata_node->at<loco::DataType::S32>(2);
+
+ if (bcq_version == 1)
+ {
+ const auto bundle_cnt = metadata_node->at<loco::DataType::S32>(3);
+
+ BCQFuser<1> fuser{original_output_cnt, bundle_cnt};
+ if (fuser.fuseBCQ(g))
+ changed = true;
+ }
+ else
+ {
+ LOGGER(l);
+ WARN(l) << "Not supported BCQ version is found." << std::endl;
+ }
+
+ // Remove all of BCQ information nodes iff there is no change
+ if (changed == false)
+ {
+ for (auto node : loco::output_nodes(g))
+ {
+ auto output_node = loco::must_cast<luci::CircleOutput *>(node);
+ if (output_node->index() == 0 || (int)output_node->index() > original_output_cnt)
+ {
+ auto noOp = g->nodes()->create<luci::CircleOutputExclude>();
+ noOp->dtype(loco::DataType::FLOAT32); // TODO Remove this setting
+ output_node->from(noOp);
+ changed = true;
+ }
+ }
+ }
+ }
+
+ return changed;
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/FuseBatchNormWithTConv.cpp b/compiler/luci/pass/src/FuseBatchNormWithTConv.cpp
new file mode 100644
index 000000000..95ccd8176
--- /dev/null
+++ b/compiler/luci/pass/src/FuseBatchNormWithTConv.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Pass/FuseBatchNormWithTConv.h"
+
+#include <luci/IR/CircleNodes.h>
+
+namespace
+{
+/**
+ * NOTE TF's fusedBatchNorm is converted to mul and add of Circle.
+ *
+ * BEFORE
+ *
+ * [CircleTransposeConv]
+ * |
+ * [mul]
+ * |
+ * [add]
+ * AFTER
+ *
+ * [CircleTransposeConv]
+ */
+bool fused_batch_norm_with_tconv(luci::CircleTransposeConv *tconv)
+{
+ // check whether it has bias or not. This optimization works only if it doesn't.
+ auto bias = dynamic_cast<luci::CircleOutputExclude *>(tconv->bias());
+ if (not bias)
+ return false;
+
+ // get weight of tconv
+ auto filter = dynamic_cast<luci::CircleConst *>(tconv->filter());
+ if (not filter)
+ return false;
+ if (filter->dtype() != loco::DataType::FLOAT32)
+ return false;
+
+ // get mul node
+ auto tconv_output = loco::succs(tconv);
+ assert(tconv_output.size() == 1);
+ auto mul = dynamic_cast<luci::CircleMul *>(*tconv_output.begin());
+ if (not mul)
+ return false;
+ if (mul->dtype() != loco::DataType::FLOAT32)
+ return false;
+
+ // get add node
+ auto mul_output = loco::succs(mul);
+ assert(mul_output.size() == 1);
+ auto add = dynamic_cast<luci::CircleAdd *>(*mul_output.begin());
+ if (not add)
+ return false;
+ if (add->dtype() != loco::DataType::FLOAT32)
+ return false;
+ if (add->fusedActivationFunction() != luci::FusedActFunc::NONE &&
+ add->fusedActivationFunction() != luci::FusedActFunc::RELU6)
+ return false;
+
+ // get scale of batchnorm
+ auto scale = dynamic_cast<luci::CircleConst *>(mul->y());
+ if (not scale)
+ return false;
+
+ // scale dim(0) == tconv filter channel dim
+ if (filter->rank() != 4)
+ return false;
+ auto filter_out_dim = filter->dim(0).value();
+ if (scale->rank() != 1)
+ return false;
+ auto scale_dim = scale->dim(0).value();
+ if (filter_out_dim != scale_dim)
+ return false;
+
+ // get shift of batchnorm
+ auto shift = dynamic_cast<luci::CircleConst *>(add->y());
+ if (not shift)
+ return false;
+
+ // shift dim(0) == tconv filter channel dim
+ if (shift->rank() != 1)
+ return false;
+ auto shift_dim = shift->dim(0).value();
+ if (filter_out_dim != shift_dim)
+ return false;
+
+ // filter weight = filter weight * mul(scale) + add(shift)
+ uint32_t filter_height_dim = filter->dim(1).value();
+ uint32_t filter_width_dim = filter->dim(2).value();
+ uint32_t filter_in_dim = filter->dim(3).value();
+ for (uint32_t c = 0; c < filter_out_dim; c++)
+ {
+ for (uint32_t h = 0; h < filter_height_dim; h++)
+ {
+ for (uint32_t w = 0; w < filter_width_dim; w++)
+ {
+ for (uint32_t b = 0; b < filter_in_dim; b++)
+ {
+ uint32_t offset = c * filter_height_dim * filter_width_dim * filter_in_dim +
+ h * filter_width_dim * filter_in_dim + w * filter_in_dim + b;
+ filter->at<loco::DataType::FLOAT32>(offset) *= scale->at<loco::DataType::FLOAT32>(c);
+ }
+ }
+ }
+ }
+
+ // fuse shift with transposed conv
+ tconv->bias(shift);
+
+ if (add->fusedActivationFunction() == luci::FusedActFunc::RELU6)
+ {
+ // separate relu op from add op
+ auto relu = add->graph()->nodes()->create<luci::CircleRelu6>();
+ relu->features(tconv);
+
+ // remove mul node
+ replace(add).with(relu);
+ }
+ else
+ {
+ replace(add).with(tconv);
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace luci
+{
+
+bool FuseBatchNormWithTConvPass::run(loco::Graph *g)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ auto tconv = dynamic_cast<luci::CircleTransposeConv *>(node);
+ if (not tconv)
+ continue;
+
+ changed |= fused_batch_norm_with_tconv(tconv);
+ }
+
+ return changed;
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/FuseInstanceNormPass.cpp b/compiler/luci/pass/src/FuseInstanceNormPass.cpp
new file mode 100644
index 000000000..237152f98
--- /dev/null
+++ b/compiler/luci/pass/src/FuseInstanceNormPass.cpp
@@ -0,0 +1,727 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Pass/FuseInstanceNormPass.h"
+#include "FuseInstanceNormPassInternal.h"
+
+#include <luci/IR/CircleNodes.h>
+
+#include <loco/Service/ShapeInference.h>
+
+#include <cassert>
+#include <set>
+
+// Helper to find commutative node's arguments
+namespace
+{
+
+/**
+ * INTRODUCTION
+ * Binary operation f(x,y) is 'commutative' when
+ * f(x,y) == f(y,x) holds for all x, y.
+ * For examples, ADD, MUL and SQUARED_DIFFERENCE are commutative.
+ * These helpers make it easy to find commutative arguemnts of commtative node.
+ *
+ * HOW TO USE
+ * COMM_NODE *node;
+ * ARG_TYPE_1 *arg1;
+ * ARG_TYPE_2 *arg2;
+ *
+ * bool ok = fill(&arg1, &arg2).with_commutative_args_of(node);
+ *
+ * Result
+ * If 'node's commutative argument types are actually {ARG_TYPE_1, ARG_TYPE_2}
+ * (as a set), 'arg1' and 'arg2' set as actual 'node's arguemnts with matching
+ * type, and return value 'ok' is true.
+ * Otherwise, 'arg1' and 'arg2' not changed, 'ok' is false.
+ */
+
+template <class ARG_TYPE_1, class ARG_TYPE_2> class NodeFiller final
+{
+public:
+ NodeFiller(ARG_TYPE_1 **arg_1, ARG_TYPE_2 **arg_2) : _arg_1(arg_1), _arg_2(arg_2)
+ {
+ // DO NOTHING
+ }
+
+ /**
+ * @return true When 'node's argument types are 'ARG_TYPE_1' and 'ARG_TYPE_2'
+ * In such case, it assign '_arg_1' and '_arg_2' to actual arguments
+ *
+ * @return false When 'node's argument types are NOT matched with 'ARG_TYPE_*'
+ * In such case, it does not amend '_arg_1' and '_arg_2'
+ *
+ * @require COMM_NODE has member x() and y()
+ */
+ template <class COMM_NODE> bool with_commutative_args_of(const COMM_NODE *node);
+
+private:
+ ARG_TYPE_1 **_arg_1;
+ ARG_TYPE_2 **_arg_2;
+};
+
+template <class ARG_TYPE_1, class ARG_TYPE_2>
+inline NodeFiller<ARG_TYPE_1, ARG_TYPE_2> fill(ARG_TYPE_1 **arg_1, ARG_TYPE_2 **arg_2)
+{
+ return NodeFiller<ARG_TYPE_1, ARG_TYPE_2>{arg_1, arg_2};
+}
+
+template <class ARG_TYPE_1, class ARG_TYPE_2>
+template <class COMM_NODE>
+bool NodeFiller<ARG_TYPE_1, ARG_TYPE_2>::with_commutative_args_of(const COMM_NODE *node)
+{
+ // Case 1) X == ARG_TYPE_1 / Y == ARG_TYPE_2
+ {
+ auto x = dynamic_cast<ARG_TYPE_1 *>(node->x());
+ auto y = dynamic_cast<ARG_TYPE_2 *>(node->y());
+
+ if (x && y)
+ {
+ *_arg_1 = x;
+ *_arg_2 = y;
+ return true;
+ }
+ }
+
+ // Case 2) X == ARG_TYPE_2 / Y == ARG_TYPE_1
+ {
+ auto x = dynamic_cast<ARG_TYPE_2 *>(node->x());
+ auto y = dynamic_cast<ARG_TYPE_1 *>(node->y());
+
+ if (x && y)
+ {
+ *_arg_1 = y;
+ *_arg_2 = x;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace
+
+// Helper to check detail
+
+/// @return true When node has shape of '1 x .. x 1 x depth'
+bool is_1D_with_dummy_dim(luci::CircleConst *node, uint32_t depth)
+{
+ auto rank = node->rank();
+ uint32_t axis;
+ for (axis = 0; axis < rank - 1; ++axis)
+ {
+ if (node->dim(axis).value() != 1)
+ return false;
+ }
+ return node->dim(axis).value() == depth;
+}
+
+/// @return true if node shape consists of ones, except the one before the last dim: 1,...1,depth,1
+bool is_quasi_1D_with_dummy_dim(luci::CircleConst *node, uint32_t depth)
+{
+ auto rank = node->rank();
+ // minimal accepted shape is [1 x depth x 1]
+ if (rank < 3)
+ return false;
+ const auto depth_axis = rank - 2;
+ for (uint32_t axis = 0; axis < rank; ++axis)
+ {
+ if (axis != depth_axis && node->dim(axis).value() != 1)
+ return false;
+ }
+ return node->dim(depth_axis).value() == depth;
+}
+
+bool is_instance_mean_v0(luci::CircleMean *mean)
+{
+ //
+ // CHECK 1) input is rank 4
+ //
+ auto input = mean->input();
+ if (not loco::shape_known(input))
+ return false;
+ auto input_shape = loco::shape_get(input).as<loco::TensorShape>();
+ if (input_shape.rank() != 4)
+ return false;
+
+ //
+ // CHECK 2) 'reduction indices' is CircleConst of value [1,2], that is HW of NHWC
+ //
+ // TODO Support equivalent case, like [-3,-2]
+ // TODO Support non-Const case?
+ // TODO What if input is NCHW format in Circle?
+ auto red_indices = dynamic_cast<luci::CircleConst *>(mean->reduction_indices());
+ if (not red_indices)
+ return false;
+ if (red_indices->rank() != 1)
+ return false;
+ std::set<int32_t> red_indices_set;
+ {
+ // TODO Currently only support S32, support other types
+ assert(red_indices->dtype() == loco::DataType::S32);
+ for (uint32_t i = 0; i < red_indices->dim(0).value(); ++i)
+ red_indices_set.insert(red_indices->at<loco::DataType::S32>(i));
+ }
+ if (red_indices_set.size() != 2)
+ return false;
+ if (red_indices_set.find(1) == red_indices_set.end())
+ return false;
+ if (red_indices_set.find(2) == red_indices_set.end())
+ return false;
+
+ //
+ // CHECK 3) keep_dims == true (?)
+ //
+ // We only have case of 'keep_dims == true' so far, but it might be okay with 'keep_dims == false'
+ // TODO Check this fact, and if true, return true regardless of keep_dims
+ return mean->keep_dims();
+}
+
+bool is_instance_mean_v1(luci::CircleMean *mean)
+{
+ //
+ // CHECK 1) input is rank 5 (NHWCX)
+ //
+ auto input = mean->input();
+ if (not loco::shape_known(input))
+ return false;
+ auto input_shape = loco::shape_get(input).as<loco::TensorShape>();
+ if (input_shape.rank() != 5)
+ return false;
+
+ //
+ // CHECK 2) 'reduction indices' is CircleConst of value [1,2,4], that is HWX of NHWCX input shape
+ //
+ // TODO Support equivalent case, like [-3,-2]
+ // TODO Support non-Const case?
+ // TODO What if input is NCHW format in Circle?
+ auto red_indices = dynamic_cast<luci::CircleConst *>(mean->reduction_indices());
+ if (not red_indices)
+ return false;
+ if (red_indices->rank() != 1)
+ return false;
+ std::set<int32_t> red_indices_set;
+
+ // TODO Currently only support S32, support other types
+ if (red_indices->dtype() != loco::DataType::S32)
+ return false;
+ for (uint32_t i = 0; i < red_indices->dim(0).value(); ++i)
+ red_indices_set.insert(red_indices->at<loco::DataType::S32>(i));
+
+ if (red_indices_set.size() != 3)
+ return false;
+ if (red_indices_set.find(1) == red_indices_set.end())
+ return false;
+ if (red_indices_set.find(2) == red_indices_set.end())
+ return false;
+ if (red_indices_set.find(4) == red_indices_set.end())
+ return false;
+
+ //
+ // CHECK 3) keep_dims == true (?)
+ //
+ // We only have case of 'keep_dims == true' so far, but it might be okay with 'keep_dims == false'
+ // TODO Check this fact, and if true, return true regardless of keep_dims
+ return mean->keep_dims();
+}
+
+/// @return true When node has the shape of 1D channel_size
+bool is_1D_float32_const(const luci::CircleConst *node, uint32_t channel_size)
+{
+ if (node->rank() != 1)
+ return false;
+
+ if (node->dim(0).value() != channel_size)
+ return false;
+
+ if (node->dtype() != loco::DataType::FLOAT32)
+ return false;
+
+ if (node->size<loco::DataType::FLOAT32>() != channel_size)
+ return false;
+
+ return true;
+}
+
+// Helper to fuse Instance Norm
+namespace
+{
+
+/**
+ * SUBGRAPH PATTERN
+ *
+ * - Below diagram shows Instance Norm pattern to fuse.
+ * - Execution dependency order is top to the bottom.
+ * - Node name is matched with variable name of InstanceNormPattern class.
+ * - Usually, first word of node name (variable name) is node type. For e.g.
+ * variable 'mean_as_variance' is pointer to TFLMean.
+ * - (Item in parenthesis) means actually exist, but not having a name and
+ * not a variable of InstanceNormPattern class.
+ *
+ * TODO support other semantically same patterns for instance norm
+ *
+ * [In]
+ * |
+ * V
+ * +----------- ifm -----+ (reduction indicies)
+ * | | | |
+ * | | V V
+ * | | mean_of_ifm ----------------+
+ * | V | |
+ * | sqdiff <--+ (reduction indicies) |
+ * | | | |
+ * | V | |
+ * | mean_as_variance <---+ const_as_epsilon |
+ * | | | |
+ * | V | |
+ * | add_as_variance <--------+ |
+ * | | |
+ * | V |
+ * | rsqrt const_as_gamma |
+ * | | | |
+ * | V | |
+ * | mul_gamma <--+ |
+ * | | | |
+ * V V V |
+ * mul_as_scaled_ifm mul_as_scaled_mean <-------------+
+ * | |
+ * | const_as_beta |
+ * | | V
+ * | +------> sub
+ * V |
+ * add_as_terminal <----------+
+ * |
+ * V
+ * [Out]
+ *-------------------------------------------------------------------
+ * [In]
+ * |
+ * V
+ * ifm
+ * |
+ * V
+ * +---------reshape_of_ifm ----+ (reduction indicies)
+ * | | | |
+ * | | V V
+ * | | mean_of_reshape -------------+
+ * | V | |
+ * | sqdiff <--+ (reduction indicies) |
+ * | | | |
+ * | V | |
+ * | mean_as_variance <---+ const_as_epsilon |
+ * | | | |
+ * | V | |
+ * | add_as_variance <--------+ |
+ * | | |
+ * | V |
+ * | rsqrt const_as_gamma |
+ * | | | |
+ * | V | |
+ * | mul_gamma <--+ |
+ * | | | |
+ * V V V |
+ * mul_as_scaled_reshape mul_as_scaled_mean <-----------+
+ * | |
+ * | const_as_beta |
+ * | | V
+ * | +------> sub
+ * V |
+ * add_as_terminal <----------+
+ * |
+ * V
+ * reshape_as_terminal
+ * |
+ * V
+ * [Out]
+ *-------------------------------------------------------------------
+ * [In]
+ * |
+ * V
+ * +----+-------------- ifm
+ * | | (reduction |
+ * | | indicies) |
+ * | | | |
+ * | V V |
+ * | mean_of_ifm |
+ * | | V
+ * | +-------> sqdiff (reduction indicies)
+ * V | | |
+ * sub <----+ V |
+ * | mean_as_variance <---+ const_as_epsilon
+ * | | |
+ * | V |
+ * | add_as_variance <------------+
+ * | | (0.5)
+ * | V |
+ * | pow <---+
+ * | |
+ * V |
+ * div <------------------+ const_as_gamma
+ * | |
+ * V |
+ * mul_gamma <----------------------+
+ * | const_as_beta
+ * V |
+ * add_as_terminal <--------+
+ * |
+ * V
+ * [Out]
+ */
+class InstanceNormPattern final
+{
+public:
+ enum PatternVersion
+ {
+ Version_0,
+ Version_1,
+ Version_2,
+ };
+
+ InstanceNormPattern(luci::CircleAdd *candidate, PatternVersion pv)
+ {
+ assert(candidate);
+ add_as_terminal = candidate;
+ _pv = pv;
+ }
+
+public:
+ bool matched();
+ bool matched() const { return _matched; }
+
+ PatternVersion version() const { return _pv; }
+
+public:
+ // Context
+ loco::Node *ifm = nullptr;
+ luci::CircleReshape *reshape_of_ifm = nullptr;
+ luci::CircleMean *mean_of_ifm = nullptr;
+ luci::CircleMean *mean_of_reshape = nullptr;
+ luci::CircleSquaredDifference *sqdiff = nullptr;
+ luci::CircleMean *mean_as_variance = nullptr;
+ luci::CircleConst *const_as_epsilon = nullptr;
+ luci::CircleAdd *add_as_variance = nullptr;
+ luci::CircleRsqrt *rsqrt = nullptr;
+ luci::CircleConst *const_as_gamma = nullptr;
+ luci::CircleMul *mul_gamma = nullptr;
+ luci::CircleMul *mul_as_scaled_ifm = nullptr;
+ luci::CircleMul *mul_as_scaled_mean = nullptr;
+ luci::CircleMul *mul_as_scaled_reshape = nullptr;
+ luci::CircleConst *const_as_beta = nullptr;
+ luci::CircleSub *sub = nullptr;
+ luci::CircleAdd *add_as_terminal = nullptr;
+ luci::CirclePow *pow = nullptr;
+ luci::CircleDiv *div = nullptr;
+
+private:
+ bool _matched = false;
+ PatternVersion _pv;
+};
+
+bool InstanceNormPattern::matched()
+{
+ if (_matched)
+ return true;
+
+#define CHECK_OR_FALSE(condition) \
+ if (not(condition)) \
+ return false;
+
+ // Check order is DFS
+
+ // Version 2 is quite different from Version 0 and 1.
+ // So it is handled in the separate if statement
+ if (_pv == PatternVersion::Version_2)
+ {
+ CHECK_OR_FALSE(fill(&mul_gamma, &const_as_beta).with_commutative_args_of(add_as_terminal));
+ CHECK_OR_FALSE(fill(&div, &const_as_gamma).with_commutative_args_of(mul_gamma));
+
+ sub = dynamic_cast<luci::CircleSub *>(div->x());
+ CHECK_OR_FALSE(sub);
+
+ ifm = sub->x();
+ CHECK_OR_FALSE(ifm);
+
+ luci::CircleNode *ifm_node = loco::must_cast<luci::CircleNode *>(ifm);
+ CHECK_OR_FALSE(ifm_node->rank() == 4);
+ uint32_t ifm_channel_depth = ifm_node->dim(3).value();
+
+ mean_of_ifm = dynamic_cast<luci::CircleMean *>(sub->y());
+ CHECK_OR_FALSE(mean_of_ifm);
+
+ CHECK_OR_FALSE(ifm == mean_of_ifm->input());
+
+ pow = dynamic_cast<luci::CirclePow *>(div->y());
+ CHECK_OR_FALSE(pow);
+
+ add_as_variance = dynamic_cast<luci::CircleAdd *>(pow->x());
+ CHECK_OR_FALSE(add_as_variance);
+
+ luci::CircleConst *zero_point_five = dynamic_cast<luci::CircleConst *>(pow->y());
+ CHECK_OR_FALSE(zero_point_five);
+ CHECK_OR_FALSE(zero_point_five->dtype() == loco::DataType::FLOAT32);
+ // TODO Support regarding broadcast
+ CHECK_OR_FALSE(zero_point_five->size<loco::DataType::FLOAT32>() == 1);
+ CHECK_OR_FALSE(zero_point_five->at<loco::DataType::FLOAT32>(0) == 0.5);
+
+ CHECK_OR_FALSE(
+ fill(&mean_as_variance, &const_as_epsilon).with_commutative_args_of(add_as_variance));
+ CHECK_OR_FALSE(const_as_epsilon->dtype() == loco::DataType::FLOAT32);
+ // TODO Support regarding broadcast
+ CHECK_OR_FALSE(const_as_epsilon->size<loco::DataType::FLOAT32>() == 1);
+
+ CHECK_OR_FALSE(is_instance_mean_v0(mean_as_variance));
+
+ sqdiff = dynamic_cast<luci::CircleSquaredDifference *>(mean_as_variance->input());
+ CHECK_OR_FALSE(sqdiff);
+
+ loco::Node *ifm_should_be = nullptr;
+ luci::CircleMean *mean_of_ifm_should_be = nullptr;
+ CHECK_OR_FALSE(fill(&ifm_should_be, &mean_of_ifm_should_be).with_commutative_args_of(sqdiff));
+ CHECK_OR_FALSE(ifm == ifm_should_be);
+ CHECK_OR_FALSE(mean_of_ifm == mean_of_ifm_should_be);
+
+ // Check for channel size
+ CHECK_OR_FALSE(is_1D_float32_const(const_as_gamma, ifm_channel_depth));
+ CHECK_OR_FALSE(is_1D_float32_const(const_as_beta, ifm_channel_depth));
+
+ _matched = true;
+ return true;
+ }
+
+ if (_pv == PatternVersion::Version_0)
+ {
+ CHECK_OR_FALSE(fill(&mul_as_scaled_ifm, &sub).with_commutative_args_of(add_as_terminal));
+ CHECK_OR_FALSE(fill(&ifm, &mul_gamma).with_commutative_args_of(mul_as_scaled_ifm));
+ }
+ if (_pv == PatternVersion::Version_1)
+ {
+ CHECK_OR_FALSE(fill(&mul_as_scaled_reshape, &sub).with_commutative_args_of(add_as_terminal));
+ CHECK_OR_FALSE(
+ fill(&reshape_of_ifm, &mul_gamma).with_commutative_args_of(mul_as_scaled_reshape));
+ ifm = reshape_of_ifm->tensor();
+ }
+
+ CHECK_OR_FALSE(loco::shape_known(ifm));
+ auto ifm_shape = loco::shape_get(ifm);
+ CHECK_OR_FALSE(ifm_shape.domain() == loco::Domain::Tensor);
+ auto ifm_tensor_shape = ifm_shape.as<loco::TensorShape>();
+ CHECK_OR_FALSE(ifm_tensor_shape.rank() == 4);
+ uint32_t ifm_channel_depth = ifm_tensor_shape.dim(3).value();
+
+ CHECK_OR_FALSE(fill(&rsqrt, &const_as_gamma).with_commutative_args_of(mul_gamma));
+
+ if (_pv == PatternVersion::Version_0)
+ {
+ CHECK_OR_FALSE(is_1D_with_dummy_dim(const_as_gamma, ifm_channel_depth));
+ }
+ if (_pv == PatternVersion::Version_1)
+ {
+ CHECK_OR_FALSE(is_quasi_1D_with_dummy_dim(const_as_gamma, ifm_channel_depth));
+ }
+
+ add_as_variance = dynamic_cast<luci::CircleAdd *>(rsqrt->x());
+ CHECK_OR_FALSE(add_as_variance);
+
+ CHECK_OR_FALSE(
+ fill(&mean_as_variance, &const_as_epsilon).with_commutative_args_of(add_as_variance));
+
+ CHECK_OR_FALSE(const_as_epsilon->dtype() == loco::DataType::FLOAT32);
+ // TODO Support regarding broadcast
+ CHECK_OR_FALSE(const_as_epsilon->size<loco::DataType::FLOAT32>() == 1);
+
+ if (_pv == PatternVersion::Version_0)
+ {
+ CHECK_OR_FALSE(is_instance_mean_v0(mean_as_variance));
+ }
+ if (_pv == PatternVersion::Version_1)
+ {
+ CHECK_OR_FALSE(is_instance_mean_v1(mean_as_variance));
+ }
+
+ sqdiff = dynamic_cast<luci::CircleSquaredDifference *>(mean_as_variance->input());
+ CHECK_OR_FALSE(sqdiff);
+
+ if (_pv == PatternVersion::Version_0)
+ {
+ loco::Node *ifm_should_be = nullptr;
+ CHECK_OR_FALSE(fill(&ifm_should_be, &mean_of_ifm).with_commutative_args_of(sqdiff));
+ CHECK_OR_FALSE(ifm == ifm_should_be);
+ CHECK_OR_FALSE(is_instance_mean_v0(mean_of_ifm));
+ CHECK_OR_FALSE(ifm == mean_of_ifm->input());
+ }
+ if (_pv == PatternVersion::Version_1)
+ {
+ loco::Node *reshape_should_be = nullptr;
+ CHECK_OR_FALSE(fill(&reshape_should_be, &mean_of_reshape).with_commutative_args_of(sqdiff));
+ CHECK_OR_FALSE(reshape_of_ifm == reshape_should_be);
+ CHECK_OR_FALSE(is_instance_mean_v1(mean_of_reshape));
+ CHECK_OR_FALSE(reshape_of_ifm == mean_of_reshape->input());
+ }
+
+ const_as_beta = dynamic_cast<luci::CircleConst *>(sub->x());
+ CHECK_OR_FALSE(const_as_beta);
+
+ if (_pv == PatternVersion::Version_0)
+ {
+ CHECK_OR_FALSE(is_1D_with_dummy_dim(const_as_beta, ifm_channel_depth));
+ }
+ if (_pv == PatternVersion::Version_1)
+ {
+ CHECK_OR_FALSE(is_quasi_1D_with_dummy_dim(const_as_beta, ifm_channel_depth));
+ }
+
+ mul_as_scaled_mean = dynamic_cast<luci::CircleMul *>(sub->y());
+ CHECK_OR_FALSE(mul_as_scaled_mean);
+
+ luci::CircleMul *mul_gamma_should_be = nullptr;
+ luci::CircleMean *mean_of_ifm_should_be = nullptr;
+ luci::CircleMean *mean_of_reshape_should_be = nullptr;
+
+ if (_pv == PatternVersion::Version_0)
+ {
+ CHECK_OR_FALSE(fill(&mul_gamma_should_be, &mean_of_ifm_should_be)
+ .with_commutative_args_of(mul_as_scaled_mean));
+ CHECK_OR_FALSE(mul_gamma == mul_gamma_should_be);
+ CHECK_OR_FALSE(mean_of_ifm == mean_of_ifm_should_be);
+ }
+ if (_pv == PatternVersion::Version_1)
+ {
+ CHECK_OR_FALSE(fill(&mul_gamma_should_be, &mean_of_reshape_should_be)
+ .with_commutative_args_of(mul_as_scaled_mean));
+ CHECK_OR_FALSE(mul_gamma == mul_gamma_should_be);
+ CHECK_OR_FALSE(mean_of_reshape == mean_of_reshape_should_be);
+ }
+
+#undef CHECK_OR_FALSE
+ _matched = true;
+ return true;
+}
+
+/**
+ * Instance norm pattern would be fused like following diagram:
+ *
+ * [In] --------------------------- CircleInstanceNorm --- [Out]
+ * / /
+ * const_as_gamma --- TFLReshape --- /
+ * /
+ * const_as_beta ---- TFLReshape ---
+ *
+ * Note
+ * - 'const_as_gamma' and 'const_as_beta' are from original graph
+ * - Value of 'const_as_epsilon' would be copied to CircleInstanceNorm's attribute
+ * - TFLReshape is added as CircleInstanceNorm only accept 1D tensor
+ * - 'CircleConst --- TFLReshape' is expected to be fused in constant folding for Reshape
+ */
+void fuse_instance_norm(const InstanceNormPattern &p)
+{
+ assert(p.matched());
+
+ auto graph = p.add_as_terminal->graph();
+
+ // Special case for version 2 (no need to reshape)
+ if (p.version() == InstanceNormPattern::Version_2)
+ {
+ // Make Instance Norm to replace
+ auto instance_norm = graph->nodes()->create<luci::CircleInstanceNorm>();
+ instance_norm->input(p.ifm);
+ instance_norm->gamma(p.const_as_gamma);
+ instance_norm->beta(p.const_as_beta);
+ float epsilon = p.const_as_epsilon->at<loco::DataType::FLOAT32>(0);
+ instance_norm->epsilon(epsilon);
+ instance_norm->fusedActivationFunction(p.add_as_terminal->fusedActivationFunction());
+
+ replace(p.add_as_terminal).with(instance_norm);
+
+ return;
+ }
+
+ // Make reshape for gamma & beta
+ auto reshape_gamma = graph->nodes()->create<luci::CircleReshape>();
+ auto reshape_beta = graph->nodes()->create<luci::CircleReshape>();
+ {
+ auto ifm_shape = loco::shape_get(p.ifm).as<loco::TensorShape>();
+ uint32_t ifm_channel_depth = ifm_shape.dim(3).value();
+
+ int32_t new_shape[1] = {static_cast<int32_t>(ifm_channel_depth)};
+
+ reshape_gamma->tensor(p.const_as_gamma);
+ reshape_beta->tensor(p.const_as_beta);
+
+ luci::set_new_shape(reshape_gamma, new_shape, 1);
+ luci::set_new_shape(reshape_beta, new_shape, 1);
+ }
+
+ // Make Instance Norm to replace
+ auto instance_norm = graph->nodes()->create<luci::CircleInstanceNorm>();
+ instance_norm->input(p.ifm);
+ instance_norm->gamma(reshape_gamma);
+ instance_norm->beta(reshape_beta);
+ float epsilon = p.const_as_epsilon->at<loco::DataType::FLOAT32>(0);
+ instance_norm->epsilon(epsilon);
+ instance_norm->fusedActivationFunction(p.add_as_terminal->fusedActivationFunction());
+
+ replace(p.add_as_terminal).with(instance_norm);
+}
+
+} // namespace
+
+namespace luci
+{
+
+bool FuseInstanceNormPass::run(loco::Graph *g)
+{
+ bool changed = false;
+ luci::CircleAdd *add;
+ InstanceNormPattern::PatternVersion pv;
+
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ auto reshape = dynamic_cast<luci::CircleReshape *>(node);
+ if (not reshape)
+ {
+ add = dynamic_cast<luci::CircleAdd *>(node);
+ if (not add)
+ continue;
+ pv = InstanceNormPattern::PatternVersion::Version_0;
+
+ auto x = loco::must_cast<luci::CircleNode *>(add->x());
+ auto y = loco::must_cast<luci::CircleNode *>(add->y());
+ if ((x->opcode() == luci::CircleOpcode::MUL &&
+ y->opcode() == luci::CircleOpcode::CIRCLECONST) ||
+ (x->opcode() == luci::CircleOpcode::CIRCLECONST &&
+ y->opcode() == luci::CircleOpcode::MUL))
+ pv = InstanceNormPattern::PatternVersion::Version_2;
+ }
+ else
+ {
+ add = dynamic_cast<luci::CircleAdd *>(reshape->tensor());
+ if (not add)
+ continue;
+ pv = InstanceNormPattern::PatternVersion::Version_1;
+ }
+
+ InstanceNormPattern pattern(add, pv);
+ if (not pattern.matched())
+ continue;
+
+ fuse_instance_norm(pattern);
+ changed = true;
+ }
+
+ return changed;
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/FuseInstanceNormPass.test.cpp b/compiler/luci/pass/src/FuseInstanceNormPass.test.cpp
new file mode 100644
index 000000000..3037f3def
--- /dev/null
+++ b/compiler/luci/pass/src/FuseInstanceNormPass.test.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 "FuseInstanceNormPassInternal.h"
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+namespace
+{
+
+void setShape(luci::CircleNode &node, const std::vector<int> &v)
+{
+ node.rank(v.size());
+ for (int i = 0; i < v.size(); ++i)
+ {
+ node.dim(i) = v[i];
+ }
+}
+
+} // namespace
+
+TEST(FuseInstanceNormPass, is_quasi_1D_with_dummy_dim)
+{
+ luci::CircleConst const_node;
+
+ setShape(const_node, {});
+ EXPECT_FALSE(is_quasi_1D_with_dummy_dim(&const_node, 8));
+
+ setShape(const_node, {1});
+ EXPECT_FALSE(is_quasi_1D_with_dummy_dim(&const_node, 8));
+
+ setShape(const_node, {8});
+ EXPECT_FALSE(is_quasi_1D_with_dummy_dim(&const_node, 8));
+
+ setShape(const_node, {1, 2, 1, 8, 1});
+ EXPECT_FALSE(is_quasi_1D_with_dummy_dim(&const_node, 8));
+
+ setShape(const_node, {8, 3});
+ EXPECT_FALSE(is_quasi_1D_with_dummy_dim(&const_node, 8));
+
+ setShape(const_node, {8, 1});
+ EXPECT_FALSE(is_quasi_1D_with_dummy_dim(&const_node, 8));
+
+ setShape(const_node, {1, 8, 1});
+ EXPECT_TRUE(is_quasi_1D_with_dummy_dim(&const_node, 8));
+
+ setShape(const_node, {1, 1, 1, 8, 1});
+ EXPECT_TRUE(is_quasi_1D_with_dummy_dim(&const_node, 8));
+}
diff --git a/compiler/luci/pass/src/FuseInstanceNormPassInternal.h b/compiler/luci/pass/src/FuseInstanceNormPassInternal.h
new file mode 100644
index 000000000..32b638ba5
--- /dev/null
+++ b/compiler/luci/pass/src/FuseInstanceNormPassInternal.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_CIRCLE_FUSE_INSTANCE_NORM_PASS_INTERNAL_H__
+#define __LUCI_CIRCLE_FUSE_INSTANCE_NORM_PASS_INTERNAL_H__
+
+#include <luci/IR/CircleNodes.h>
+
+/// @return true When node has shape of '1 x .. x 1 x depth'
+bool is_1D_with_dummy_dim(luci::CircleConst *node, uint32_t depth);
+
+/// @return true When node has shape of '1 x .. x depth x 1'
+bool is_quasi_1D_with_dummy_dim(luci::CircleConst *node, uint32_t depth);
+
+#endif // __LUCI_CIRCLE_FUSE_INSTANCE_NORM_PASS_INTERNAL_H__
diff --git a/compiler/luci/pass/src/FusePreActivationBatchNormPass.cpp b/compiler/luci/pass/src/FusePreActivationBatchNormPass.cpp
new file mode 100644
index 000000000..bcde5fac4
--- /dev/null
+++ b/compiler/luci/pass/src/FusePreActivationBatchNormPass.cpp
@@ -0,0 +1,681 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Pass/FusePreActivationBatchNormPass.h"
+#include "FusePreActivationBatchNormPassInternal.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/Log.h>
+
+namespace
+{
+
+// Check if all elements are non-negative
+bool is_non_negative(const luci::CircleConst *node)
+{
+ assert(node->dtype() == loco::DataType::FLOAT32);
+
+ uint32_t size = node->size<loco::DataType::FLOAT32>();
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ if (node->at<loco::DataType::FLOAT32>(i) < 0)
+ return false;
+ }
+ return true;
+}
+
+// Check if mul is batchnorm mul
+bool is_batchnorm_mul(const luci::CircleMul *mul, luci::CircleNode *&pred_node,
+ luci::CircleConst *&gamma)
+{
+ auto x = dynamic_cast<luci::CircleConst *>(mul->x());
+ auto y = dynamic_cast<luci::CircleConst *>(mul->y());
+
+ luci::CircleNode *pred = nullptr;
+ luci::CircleConst *constant = nullptr;
+
+ if (x != nullptr && y == nullptr)
+ {
+ pred = loco::must_cast<luci::CircleNode *>(mul->y());
+ constant = x;
+ }
+ else if (x == nullptr && y != nullptr)
+ {
+ pred = loco::must_cast<luci::CircleNode *>(mul->x());
+ constant = y;
+ }
+ else
+ {
+ return false;
+ }
+
+ if (constant->rank() != 1)
+ return false;
+
+ auto channel_dim = constant->dim(0);
+ if (!(channel_dim == mul->dim(mul->rank() - 1)))
+ return false;
+
+ pred_node = pred;
+ gamma = constant;
+ return true;
+}
+
+// Check if add is batchnorm add
+bool is_batchnorm_add(const luci::CircleAdd *add, luci::CircleMul *&mul, luci::CircleConst *&beta)
+{
+ auto x = loco::must_cast<luci::CircleNode *>(add->x());
+ auto y = loco::must_cast<luci::CircleNode *>(add->y());
+
+ luci::CircleMul *pred = nullptr;
+ luci::CircleConst *constant = nullptr;
+
+ if (add->fusedActivationFunction() != luci::FusedActFunc::RELU)
+ return false;
+
+ if (x->opcode() == luci::CircleOpcode::CIRCLECONST && y->opcode() == luci::CircleOpcode::MUL)
+ {
+ pred = loco::must_cast<luci::CircleMul *>(y);
+ constant = loco::must_cast<luci::CircleConst *>(x);
+ }
+ else if (x->opcode() == luci::CircleOpcode::MUL && y->opcode() == luci::CircleOpcode::CIRCLECONST)
+ {
+ pred = loco::must_cast<luci::CircleMul *>(x);
+ constant = loco::must_cast<luci::CircleConst *>(y);
+ }
+ else
+ {
+ return false;
+ }
+
+ if (constant->rank() != 1)
+ return false;
+
+ auto channel_dim = constant->dim(0);
+ // Assumption: Layout is channel-last
+ if (!(channel_dim == add->dim(add->rank() - 1)))
+ return false;
+
+ mul = pred;
+ beta = constant;
+ return true;
+}
+
+const luci::CircleConv2D *get_forward_conv2d(const luci::CircleNode *node, uint32_t channel_size)
+{
+ auto opcode = node->opcode();
+ if (opcode == luci::CircleOpcode::CONV_2D)
+ {
+ auto conv = loco::must_cast<const luci::CircleConv2D *>(node);
+ auto filter = dynamic_cast<luci::CircleConst *>(conv->filter());
+ if (filter == nullptr)
+ return nullptr;
+
+ if (filter->rank() != 4)
+ return nullptr;
+
+ if (filter->dim(3).value() != channel_size)
+ return nullptr;
+
+ if (loco::succs(filter).size() != 1)
+ return nullptr;
+
+ return conv;
+ }
+ // MUL can be fused with CONV across MEAN
+ // i.e., MUL-MEAN-CONV -> MEAN-CONV
+ // This is for handling the last part of ResNetV2
+ else if (opcode == luci::CircleOpcode::MEAN)
+ {
+ auto mean = loco::must_cast<const luci::CircleMean *>(node);
+ auto axis = mean->reduction_indices();
+ auto axis_const = dynamic_cast<luci::CircleConst *>(axis);
+ if (not axis_const)
+ return nullptr;
+
+ assert(axis_const->dtype() == loco::DataType::S32);
+ auto axis_size = axis_const->size<loco::DataType::S32>();
+ for (uint32_t i = 0; i < axis_size; ++i)
+ {
+ // Reduction axis must not be the channel index
+ // Assumption: Layout is channel-last
+ if (axis_const->at<loco::DataType::S32>(i) == static_cast<int32_t>(node->rank() - 1))
+ return nullptr;
+ }
+
+ auto succ = loco::succs(node);
+ if (succ.size() != 1)
+ return nullptr;
+
+ auto succ_node = loco::must_cast<luci::CircleNode *>(*succ.begin());
+
+ return get_forward_conv2d(succ_node, channel_size);
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+void update_conv_weights_with_gamma(const luci::CircleConv2D *conv, const luci::CircleConst *gamma)
+{
+ assert(conv != nullptr);
+ assert(gamma != nullptr);
+ auto filter = loco::must_cast<luci::CircleConst *>(conv->filter());
+
+ uint32_t filter_out_dim = filter->dim(0).value();
+ uint32_t filter_height_dim = filter->dim(1).value();
+ uint32_t filter_width_dim = filter->dim(2).value();
+ uint32_t filter_in_dim = filter->dim(3).value();
+ for (uint32_t o = 0; o < filter_out_dim; o++)
+ {
+ for (uint32_t h = 0; h < filter_height_dim; h++)
+ {
+ for (uint32_t w = 0; w < filter_width_dim; w++)
+ {
+ for (uint32_t i = 0; i < filter_in_dim; i++)
+ {
+ uint32_t offset = o * filter_height_dim * filter_width_dim * filter_in_dim +
+ h * filter_width_dim * filter_in_dim + w * filter_in_dim + i;
+ filter->at<loco::DataType::FLOAT32>(offset) *= gamma->at<loco::DataType::FLOAT32>(i);
+ }
+ }
+ }
+ }
+}
+
+// Find CONV_2D that can be fused with ADD
+luci::CircleConv2D *get_backward_conv2d(luci::CircleNode *node, uint32_t channel_size)
+{
+ // Stop searching when meeting a node used by multiple nodes
+ if (loco::succs(node).size() != 1)
+ return nullptr;
+
+ auto opcode = node->opcode();
+ if (opcode == luci::CircleOpcode::CONV_2D)
+ {
+ auto conv = loco::must_cast<luci::CircleConv2D *>(node);
+ auto filter = dynamic_cast<luci::CircleConst *>(conv->filter());
+
+ if (filter == nullptr)
+ return nullptr;
+
+ if (filter->rank() != 4)
+ return nullptr;
+
+ if (filter->dim(0).value() != channel_size)
+ return nullptr;
+
+ if (loco::succs(filter).size() != 1)
+ return nullptr;
+
+ return conv;
+ }
+ else if (opcode == luci::CircleOpcode::MAX_POOL_2D || opcode == luci::CircleOpcode::PAD ||
+ opcode == luci::CircleOpcode::ADD)
+ {
+ auto preds = loco::preds(node);
+ for (auto pred : preds)
+ {
+ auto pred_conv = get_backward_conv2d(loco::must_cast<luci::CircleNode *>(pred), channel_size);
+ if (pred_conv != nullptr)
+ return pred_conv;
+ }
+ return nullptr;
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+bool update_conv_bias_with_beta(luci::CircleConv2D *conv, const luci::CircleConst *beta,
+ bool add_beta)
+{
+ assert(beta->rank() == 1);
+ auto size = beta->dim(0).value();
+ auto bias = dynamic_cast<luci::CircleConst *>(conv->bias());
+
+ if (bias == nullptr)
+ {
+ bias = conv->graph()->nodes()->create<luci::CircleConst>();
+ bias->dtype(loco::DataType::FLOAT32);
+ bias->rank(1);
+ bias->dim(0).set(size);
+ bias->size<loco::DataType::FLOAT32>(size);
+ conv->bias(bias);
+ }
+ else
+ {
+ if (bias->rank() != 1)
+ return false;
+
+ if (loco::succs(bias).size() != 1)
+ return false;
+
+ if (size != bias->dim(0).value())
+ return false;
+ }
+
+ for (uint32_t i = 0; i < size; i++)
+ {
+ if (add_beta)
+ bias->at<loco::DataType::FLOAT32>(i) += beta->at<loco::DataType::FLOAT32>(i);
+ else
+ bias->at<loco::DataType::FLOAT32>(i) -= beta->at<loco::DataType::FLOAT32>(i);
+ }
+ return true;
+}
+
+luci::CircleSub *insert_sub(luci::CircleNode *pred, luci::CircleConst *beta)
+{
+ auto sub = pred->graph()->nodes()->create<luci::CircleSub>();
+ sub->dtype(loco::DataType::FLOAT32);
+ sub->rank(pred->rank());
+ for (uint32_t i = 0; i < sub->rank(); i++)
+ {
+ sub->dim(i).set(pred->dim(i).value());
+ }
+ sub->fusedActivationFunction(luci::FusedActFunc::NONE);
+
+ loco::replace(pred).with(sub);
+
+ sub->x(pred);
+ sub->y(beta);
+
+ return sub;
+}
+
+luci::CircleAdd *get_forward_add(luci::CircleNode *node)
+{
+ auto opcode = node->opcode();
+ if (opcode == luci::CircleOpcode::ADD)
+ {
+ auto add = loco::must_cast<luci::CircleAdd *>(node);
+ return add;
+ }
+ else if (opcode == luci::CircleOpcode::MAX_POOL_2D)
+ {
+ auto succ = loco::succs(node);
+ if (succ.size() != 1)
+ return nullptr;
+
+ auto succ_node = loco::must_cast<luci::CircleNode *>(*succ.begin());
+ return get_forward_add(succ_node);
+ }
+
+ return nullptr;
+}
+
+} // namespace
+
+namespace luci
+{
+
+/**
+ * Fuse SUB with CONV
+ *
+ * BEFORE
+ *
+ * beta [Sub]
+ * |
+ * [Passable Op] [Conv] bias
+ * \ /
+ * [Add]
+ *
+ * AFTER
+ *
+ * [Passable Op] [Conv] bias - beta
+ * \ /
+ * [Add]
+ */
+bool fuse_sub_with_conv(luci::CircleSub *sub)
+{
+ luci::CircleAdd *add = nullptr;
+ luci::CircleConv2D *conv = nullptr;
+ auto succs = loco::succs(sub);
+ if (succs.size() != 1)
+ return false;
+
+ add = get_forward_add(loco::must_cast<luci::CircleNode *>(*succs.begin()));
+ if (add == nullptr)
+ return false;
+
+ conv = dynamic_cast<luci::CircleConv2D *>(add->x());
+ if (conv == nullptr)
+ conv = dynamic_cast<luci::CircleConv2D *>(add->y());
+
+ if (conv == nullptr)
+ return false;
+
+ auto beta = loco::must_cast<luci::CircleConst *>(sub->y());
+ assert(beta != nullptr);
+ if (!update_conv_bias_with_beta(conv, beta, false))
+ return false;
+
+ auto pred = sub->x();
+ loco::replace(sub).with(pred);
+
+ sub->drop();
+
+ return true;
+}
+
+/**
+ * Fuse ADD with the preceding CONV
+ *
+ * BEFORE
+ *
+ * [Conv] bias
+ * |
+ * [Passable Op] (None, Max pool, Pad, etc)
+ * |
+ * [Add] beta
+ *
+ * AFTER
+ *
+ * [Conv] bias + beta
+ * |
+ * [Passable Op]
+ *
+ * A special case where SUB is newly inserted
+ *
+ * BEFORE
+ *
+ * [Conv] bias
+ * \ /
+ * [Add]
+ * / \
+ * [Add] beta
+ *
+ * AFTER
+ *
+ * [Conv] bias + beta
+ * \ /
+ * [Add]
+ * / \
+ * beta [Sub]
+ */
+bool fuse_add_with_conv(luci::CircleAdd *add, std::vector<luci::CircleSub *> &sub_list)
+{
+ auto x = dynamic_cast<luci::CircleConst *>(add->x());
+ auto y = dynamic_cast<luci::CircleConst *>(add->y());
+
+ luci::CircleNode *pred = nullptr;
+ luci::CircleConst *beta = nullptr;
+
+ if (x != nullptr && y == nullptr)
+ {
+ pred = loco::must_cast<luci::CircleNode *>(add->y());
+ beta = x;
+ }
+ else if (x == nullptr && y != nullptr)
+ {
+ pred = loco::must_cast<luci::CircleNode *>(add->x());
+ beta = y;
+ }
+ else
+ {
+ return false;
+ }
+
+ assert(beta->rank() == 1);
+
+ auto channel_size = beta->dim(0).value();
+ auto conv = get_backward_conv2d(pred, channel_size);
+
+ if (conv != nullptr)
+ {
+ if (!update_conv_bias_with_beta(conv, beta, true))
+ return false;
+
+ loco::replace(add).with(pred);
+ add->drop();
+
+ return true;
+ }
+ // A special case shown at the residual blocks of ResNetV2
+ // TODO: Handle this with get_backward_conv2d
+ else if (pred->opcode() == luci::CircleOpcode::ADD)
+ {
+ auto pred_add = loco::must_cast<luci::CircleAdd *>(pred);
+ conv = get_backward_conv2d(loco::must_cast<luci::CircleNode *>(pred_add->y()), channel_size);
+ if (conv == nullptr)
+ conv = get_backward_conv2d(loco::must_cast<luci::CircleNode *>(pred_add->x()), channel_size);
+
+ if (conv == nullptr)
+ return false;
+
+ if (!update_conv_bias_with_beta(conv, beta, true))
+ return false;
+
+ auto relu = *loco::succs(add).begin();
+ auto relu_node = loco::must_cast<luci::CircleRelu *>(relu);
+ assert(relu_node != nullptr);
+
+ loco::replace(add).with(pred);
+
+ add->drop();
+
+ sub_list.push_back(insert_sub(pred, beta));
+
+ relu_node->features(pred);
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Fuse MUL with the next CONV
+ *
+ * BEFORE
+ *
+ * [Mul] gamma
+ * |
+ * [Relu]
+ * / \
+ * W1 [Conv] [Conv] W2
+ *
+ * AFTER
+ *
+ * [Relu]
+ * / \
+ * gamma X W1 [Conv] [Conv] gamma X W2
+ */
+bool fuse_mul_with_conv(luci::CircleMul *mul)
+{
+ luci::CircleNode *pred_node = nullptr;
+ luci::CircleConst *gamma = nullptr;
+
+ if (!is_batchnorm_mul(mul, pred_node, gamma))
+ return false;
+
+ auto mul_succ = loco::succs(mul);
+ assert(mul_succ.size() == 1);
+
+ auto relu = loco::must_cast<luci::CircleRelu *>(*mul_succ.begin());
+
+ auto channel_size = gamma->dim(0).value();
+
+ bool fusable = true;
+ auto relu_succ = loco::succs(relu);
+ for (auto s : relu_succ)
+ {
+ auto conv = get_forward_conv2d(loco::must_cast<luci::CircleNode *>(s), channel_size);
+ if (conv == nullptr)
+ fusable = false;
+ }
+
+ if (fusable)
+ {
+ for (auto s : relu_succ)
+ {
+ // Find the next CONV
+ auto conv = get_forward_conv2d(loco::must_cast<luci::CircleNode *>(s), channel_size);
+
+ // Update CONV weights
+ update_conv_weights_with_gamma(conv, gamma);
+ }
+
+ loco::replace(mul).with(pred_node);
+ relu->features(pred_node);
+
+ mul->drop();
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Swap MUL/ADD if they are from batch normalization
+ *
+ * BEFORE
+ * [Mul] gamma
+ * |
+ * [Add + Relu] beta
+ *
+ * AFTER
+ * [Add] beta/gamma
+ * |
+ * [Mul] gamma
+ * |
+ * [Relu]
+ */
+bool swap_mul_add(luci::CircleAdd *add, std::vector<luci::CircleMul *> &mul_list,
+ std::vector<luci::CircleAdd *> &add_list)
+{
+ luci::CircleNode *pred_node = nullptr;
+ luci::CircleMul *mul = nullptr;
+ luci::CircleConst *beta = nullptr;
+ luci::CircleConst *gamma = nullptr;
+
+ if (!is_batchnorm_add(add, mul, beta))
+ return false;
+
+ if (loco::succs(mul).size() != 1)
+ return false;
+
+ if (!is_batchnorm_mul(mul, pred_node, gamma))
+ return false;
+
+ if (beta->dtype() != loco::DataType::FLOAT32 || gamma->dtype() != loco::DataType::FLOAT32)
+ throw std::runtime_error("FusePreActivationBatchNormPass only supports Float32 model");
+
+ if (!is_non_negative(gamma))
+ return false;
+
+ // Insert Relu at the bottom
+ auto relu = add->graph()->nodes()->create<luci::CircleRelu>();
+ relu->features(mul);
+ loco::replace(add).with(relu);
+
+ // Replace beta <- beta / gamma
+ if (add->x() == beta)
+ {
+ add->y(pred_node);
+ }
+ else
+ {
+ add->x(pred_node);
+ }
+ add->fusedActivationFunction(luci::FusedActFunc::NONE);
+ uint32_t size = beta->size<loco::DataType::FLOAT32>();
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ auto b = beta->at<loco::DataType::FLOAT32>(i);
+ auto g = gamma->at<loco::DataType::FLOAT32>(i);
+ if (b == g)
+ {
+ beta->at<loco::DataType::FLOAT32>(i) = 1;
+ }
+ else
+ {
+ // If g is 0, we use a small value (empirically determined)
+ if (g == 0)
+ g = 1e-10;
+ beta->at<loco::DataType::FLOAT32>(i) = b / g;
+ }
+ }
+
+ if (mul->x() == gamma)
+ {
+ mul->y(add);
+ }
+ else
+ {
+ mul->x(add);
+ }
+
+ mul_list.push_back(mul);
+ add_list.push_back(add);
+
+ return true;
+}
+
+bool FusePreActivationBatchNormPass::run(loco::Graph *g)
+{
+ LOGGER(l);
+ bool changed = false;
+
+ // Step 1. Swap MUL <-> ADD
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ auto add = dynamic_cast<luci::CircleAdd *>(node);
+ if (add == nullptr)
+ continue;
+
+ if (swap_mul_add(add, _mul_list, _add_list))
+ changed = true;
+ }
+
+ INFO(l) << "[FusePreActivationBatchNorm] Target pre-activations: " << _mul_list.size()
+ << std::endl;
+
+ // Valid pattern was not detected. Fast exit.
+ if (!changed)
+ return false;
+
+ // Step 2. Fuse MUL with the next CONV
+ for (auto const &mul : _mul_list)
+ {
+ if (fuse_mul_with_conv(mul))
+ INFO(l) << "[FusePreActivationBatchNorm] Fused MUL: " << mul->name() << std::endl;
+ }
+
+ // Step 3. Fuse ADD with the preceding CONV and insert SUB
+ for (auto const &add : _add_list)
+ {
+ if (fuse_add_with_conv(add, _sub_list))
+ INFO(l) << "[FusePreActivationBatchNorm] Fused ADD: " << add->name() << std::endl;
+ }
+
+ INFO(l) << "[FusePreActivationBatchNorm] " << _sub_list.size() << " SUB were added." << std::endl;
+
+ // Step 4. Fuse SUB to CONV (SUB -> ADD <- CONV pattern)
+ for (auto const &sub : _sub_list)
+ {
+ if (fuse_sub_with_conv(sub))
+ INFO(l) << "[FusePreActivationBatchNorm] Fused SUB: " << sub->name() << std::endl;
+ }
+
+ return changed;
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/FusePreActivationBatchNormPass.test.cpp b/compiler/luci/pass/src/FusePreActivationBatchNormPass.test.cpp
new file mode 100644
index 000000000..a79b5bd5d
--- /dev/null
+++ b/compiler/luci/pass/src/FusePreActivationBatchNormPass.test.cpp
@@ -0,0 +1,381 @@
+/*
+ * 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 "FusePreActivationBatchNormPassInternal.h"
+
+#include <luci/IR/CircleNodes.h>
+
+#include <math.h>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+namespace
+{
+
+/**
+ * Simple graph for test
+ *
+ * BEFORE
+ *
+ * [Conv] W + bias
+ * \ [Conv]
+ * \ /
+ * [Add]
+ * / \
+ * / [Mul] gamma
+ * | |
+ * | [Add+Relu] beta
+ * | |
+ * | [Conv] W + bias
+ * \ /
+ * [Add]
+ *
+ * AFTER
+ *
+ * [Conv] W + (bias + beta/gamma)
+ * \ [Conv]
+ * \ /
+ * [Add]
+ * / \
+ * | [Relu]
+ * | |
+ * | [Conv] (gamma * W) + (bias - beta/gamma)
+ * \ /
+ * [Add]
+ *
+ */
+class SimpleGraph
+{
+public:
+ SimpleGraph()
+ {
+ pred_conv = g.nodes()->create<luci::CircleConv2D>();
+ pred_conv_filter = g.nodes()->create<luci::CircleConst>();
+ pred_conv_bias = g.nodes()->create<luci::CircleConst>();
+ pred_conv2 = g.nodes()->create<luci::CircleConv2D>();
+ pred_conv2_filter = g.nodes()->create<luci::CircleConst>();
+ pred_conv2_bias = g.nodes()->create<luci::CircleConst>();
+ pred_add = g.nodes()->create<luci::CircleAdd>();
+ mul = g.nodes()->create<luci::CircleMul>();
+ mul_gamma = g.nodes()->create<luci::CircleConst>();
+ add = g.nodes()->create<luci::CircleAdd>();
+ add_beta = g.nodes()->create<luci::CircleConst>();
+ conv = g.nodes()->create<luci::CircleConv2D>();
+ conv_filter = g.nodes()->create<luci::CircleConst>();
+ conv_bias = g.nodes()->create<luci::CircleConst>();
+ succ_add = g.nodes()->create<luci::CircleAdd>();
+
+ pred_conv->dtype(loco::DataType::FLOAT32);
+ pred_conv_filter->dtype(loco::DataType::FLOAT32);
+ pred_conv_bias->dtype(loco::DataType::FLOAT32);
+ pred_conv2->dtype(loco::DataType::FLOAT32);
+ pred_conv2_filter->dtype(loco::DataType::FLOAT32);
+ pred_conv2_bias->dtype(loco::DataType::FLOAT32);
+ pred_add->dtype(loco::DataType::FLOAT32);
+ mul->dtype(loco::DataType::FLOAT32);
+ mul_gamma->dtype(loco::DataType::FLOAT32);
+ add->dtype(loco::DataType::FLOAT32);
+ add->fusedActivationFunction(luci::FusedActFunc::RELU);
+ add_beta->dtype(loco::DataType::FLOAT32);
+ conv->dtype(loco::DataType::FLOAT32);
+ conv_filter->dtype(loco::DataType::FLOAT32);
+ conv_bias->dtype(loco::DataType::FLOAT32);
+ succ_add->dtype(loco::DataType::FLOAT32);
+
+ pred_conv->shape({1, 4, 4, 16});
+ pred_conv_filter->shape({16, 1, 1, 16});
+ pred_conv_bias->shape({16});
+ pred_conv2->shape({1, 4, 4, 16});
+ pred_conv2_filter->shape({16, 1, 1, 16});
+ pred_conv2_bias->shape({16});
+ pred_add->shape({1, 4, 4, 16});
+ mul->shape({1, 4, 4, 16});
+ mul_gamma->shape({16});
+ add->shape({1, 4, 4, 16});
+ add_beta->shape({16});
+ conv->shape({1, 4, 4, 16});
+ conv_filter->shape({16, 1, 1, 16});
+ conv_bias->shape({16});
+ succ_add->shape({1, 4, 4, 16});
+
+ pred_conv->filter(pred_conv_filter);
+ pred_conv->bias(pred_conv_bias);
+ pred_conv2->filter(pred_conv2_filter);
+ pred_conv2->bias(pred_conv2_bias);
+ pred_add->x(pred_conv);
+ pred_add->y(pred_conv2);
+ mul->x(pred_add);
+ mul->y(mul_gamma);
+ add->x(mul);
+ add->y(add_beta);
+ conv->input(add);
+ conv->filter(conv_filter);
+ conv->bias(conv_bias);
+ succ_add->x(pred_add);
+ succ_add->y(conv);
+
+ uint32_t channel_size = 16;
+ uint32_t out_size = 16;
+ add_beta->size<loco::DataType::FLOAT32>(channel_size);
+ mul_gamma->size<loco::DataType::FLOAT32>(channel_size);
+ conv_filter->size<loco::DataType::FLOAT32>(channel_size * out_size);
+ conv_bias->size<loco::DataType::FLOAT32>(out_size);
+ pred_conv_bias->size<loco::DataType::FLOAT32>(channel_size);
+ pred_conv2_bias->size<loco::DataType::FLOAT32>(channel_size);
+ for (uint32_t i = 0; i < channel_size; i++)
+ {
+ add_beta->at<loco::DataType::FLOAT32>(i) = i;
+ mul_gamma->at<loco::DataType::FLOAT32>(i) = i;
+ pred_conv_bias->at<loco::DataType::FLOAT32>(i) = i;
+ pred_conv2_bias->at<loco::DataType::FLOAT32>(i) = i;
+ conv_bias->at<loco::DataType::FLOAT32>(i) = i;
+ for (uint32_t j = 0; j < out_size; j++)
+ {
+ conv_filter->at<loco::DataType::FLOAT32>(i * out_size + j) = i * out_size + j;
+ }
+ }
+ }
+
+public:
+ loco::Graph g;
+ luci::CircleConv2D *pred_conv = nullptr;
+ luci::CircleConst *pred_conv_filter = nullptr;
+ luci::CircleConst *pred_conv_bias = nullptr;
+ luci::CircleConv2D *pred_conv2 = nullptr;
+ luci::CircleConst *pred_conv2_filter = nullptr;
+ luci::CircleConst *pred_conv2_bias = nullptr;
+ luci::CircleAdd *pred_add = nullptr;
+ luci::CircleMul *mul = nullptr;
+ luci::CircleConst *mul_gamma = nullptr;
+ luci::CircleAdd *add = nullptr;
+ luci::CircleConst *add_beta = nullptr;
+ luci::CircleConv2D *conv = nullptr;
+ luci::CircleConst *conv_filter = nullptr;
+ luci::CircleConst *conv_bias = nullptr;
+ luci::CircleAdd *succ_add = nullptr;
+};
+
+} // namespace
+
+TEST(FusePreActivationBatchNorm, swap_mul_add)
+{
+ SimpleGraph g;
+ int channel_size = 16;
+ std::vector<luci::CircleMul *> mul_list;
+ std::vector<luci::CircleAdd *> add_list;
+
+ EXPECT_TRUE(luci::swap_mul_add(g.add, mul_list, add_list));
+ EXPECT_EQ(1, mul_list.size());
+ EXPECT_EQ(1, add_list.size());
+ EXPECT_EQ(g.mul, mul_list[0]);
+ EXPECT_EQ(g.add, add_list[0]);
+
+ for (uint32_t i = 0; i < channel_size; ++i)
+ {
+ float beta = g.add_beta->at<loco::DataType::FLOAT32>(i);
+ float gamma = g.mul_gamma->at<loco::DataType::FLOAT32>(i);
+ EXPECT_FLOAT_EQ(1.0, beta);
+ EXPECT_FLOAT_EQ(i, gamma);
+ }
+
+ auto relu = static_cast<luci::CircleRelu *>(g.conv->input());
+ EXPECT_TRUE(relu != nullptr);
+
+ EXPECT_EQ(g.mul, relu->features());
+ EXPECT_EQ(g.add, g.mul->x());
+ EXPECT_EQ(luci::FusedActFunc::NONE, g.add->fusedActivationFunction());
+ EXPECT_EQ(g.pred_add, g.add->x());
+}
+
+TEST(FusePreActivationBatchNorm, swap_mul_add_NEG)
+{
+ SimpleGraph g;
+ std::vector<luci::CircleMul *> mul_list;
+ std::vector<luci::CircleAdd *> add_list;
+
+ // Add does not have fused activation
+ g.add->fusedActivationFunction(luci::FusedActFunc::NONE);
+ EXPECT_FALSE(luci::swap_mul_add(g.add, mul_list, add_list));
+ EXPECT_EQ(0, mul_list.size());
+ EXPECT_EQ(0, add_list.size());
+ g.add->fusedActivationFunction(luci::FusedActFunc::RELU);
+
+ // Add is element-wise
+ g.add_beta->shape({1, 4, 4, 16});
+ EXPECT_FALSE(luci::swap_mul_add(g.add, mul_list, add_list));
+ EXPECT_EQ(0, mul_list.size());
+ EXPECT_EQ(0, add_list.size());
+ g.add_beta->shape({16});
+
+ // Mul is element-wise
+ g.mul_gamma->shape({1, 4, 4, 16});
+ EXPECT_FALSE(luci::swap_mul_add(g.add, mul_list, add_list));
+ EXPECT_EQ(0, mul_list.size());
+ EXPECT_EQ(0, add_list.size());
+ g.mul_gamma->shape({16});
+
+ // Negative gamma
+ g.mul_gamma->at<loco::DataType::FLOAT32>(0) = -10;
+ EXPECT_FALSE(luci::swap_mul_add(g.add, mul_list, add_list));
+ EXPECT_EQ(0, mul_list.size());
+ EXPECT_EQ(0, add_list.size());
+}
+
+TEST(FusePreActivationBatchNorm, fuse_mul_with_conv)
+{
+ SimpleGraph g;
+ int channel_size = 16;
+ int out_size = 16;
+ std::vector<luci::CircleMul *> mul_list;
+ std::vector<luci::CircleAdd *> add_list;
+
+ EXPECT_TRUE(luci::swap_mul_add(g.add, mul_list, add_list));
+
+ EXPECT_TRUE(luci::fuse_mul_with_conv(g.mul));
+ for (uint32_t o = 0; o < out_size; o++)
+ {
+ for (uint32_t c = 0; c < channel_size; c++)
+ {
+ auto val = g.conv_filter->at<loco::DataType::FLOAT32>(o * channel_size + c);
+ auto gamma = g.mul_gamma->at<loco::DataType::FLOAT32>(c);
+ EXPECT_FLOAT_EQ((o * channel_size + c) * gamma, val);
+ }
+ }
+
+ auto relu = static_cast<luci::CircleRelu *>(g.conv->input());
+ EXPECT_EQ(g.add, relu->features());
+}
+
+TEST(FusePreActivationBatchNorm, fuse_mul_with_conv_NEG)
+{
+ SimpleGraph g;
+ std::vector<luci::CircleMul *> mul_list;
+ std::vector<luci::CircleAdd *> add_list;
+
+ EXPECT_TRUE(luci::swap_mul_add(g.add, mul_list, add_list));
+
+ // Non-conv layer uses the output of relu
+ auto relu = static_cast<luci::CircleRelu *>(g.conv->input());
+ auto fc = g.g.nodes()->create<luci::CircleFullyConnected>();
+ fc->input(relu);
+ EXPECT_FALSE(luci::fuse_mul_with_conv(g.mul));
+}
+
+TEST(FusePreActivationBatchNorm, fuse_add_with_conv)
+{
+ SimpleGraph g;
+ int channel_size = 16;
+ std::vector<luci::CircleMul *> mul_list;
+ std::vector<luci::CircleAdd *> add_list;
+ std::vector<luci::CircleSub *> sub_list;
+
+ EXPECT_TRUE(luci::swap_mul_add(g.add, mul_list, add_list));
+ EXPECT_TRUE(luci::fuse_mul_with_conv(g.mul));
+ EXPECT_TRUE(luci::fuse_add_with_conv(g.add, sub_list));
+
+ for (uint32_t c = 0; c < channel_size; c++)
+ {
+ auto bias = g.pred_conv2_bias->at<loco::DataType::FLOAT32>(c);
+ EXPECT_FLOAT_EQ(c + 1.0, bias);
+ }
+
+ auto relu = static_cast<luci::CircleRelu *>(g.conv->input());
+ EXPECT_EQ(relu, g.conv->input());
+ EXPECT_EQ(g.pred_add, relu->features());
+ EXPECT_EQ(g.pred_conv, g.pred_add->x());
+ EXPECT_EQ(g.pred_conv2, g.pred_add->y());
+
+ auto sub = static_cast<luci::CircleSub *>(sub_list[0]);
+ EXPECT_EQ(sub, g.succ_add->x());
+ EXPECT_EQ(g.pred_add, sub->x());
+ for (uint32_t c = 0; c < channel_size; c++)
+ {
+ auto beta = static_cast<luci::CircleConst *>(sub->y());
+ EXPECT_FLOAT_EQ(1.0, beta->at<loco::DataType::FLOAT32>(c));
+ }
+}
+
+TEST(FusePreActivationBatchNorm, fuse_add_with_conv_NEG)
+{
+ SimpleGraph g;
+ int channel_size = 16;
+ std::vector<luci::CircleMul *> mul_list;
+ std::vector<luci::CircleAdd *> add_list;
+ std::vector<luci::CircleSub *> sub_list;
+
+ EXPECT_TRUE(luci::swap_mul_add(g.add, mul_list, add_list));
+ EXPECT_TRUE(luci::fuse_mul_with_conv(g.mul));
+
+ // No conv layer to fuse add
+ auto fc1 = g.g.nodes()->create<luci::CircleFullyConnected>();
+ auto fc2 = g.g.nodes()->create<luci::CircleFullyConnected>();
+ g.pred_add->x(fc1);
+ g.pred_add->y(fc2);
+ EXPECT_FALSE(luci::fuse_add_with_conv(g.add, sub_list));
+ EXPECT_EQ(0, sub_list.size());
+}
+
+TEST(FusePreActivationBatchNorm, fuse_sub_with_conv)
+{
+ SimpleGraph g;
+ int channel_size = 16;
+ std::vector<luci::CircleMul *> mul_list;
+ std::vector<luci::CircleAdd *> add_list;
+ std::vector<luci::CircleSub *> sub_list;
+
+ EXPECT_TRUE(luci::swap_mul_add(g.add, mul_list, add_list));
+ EXPECT_TRUE(luci::fuse_mul_with_conv(g.mul));
+ EXPECT_TRUE(luci::fuse_add_with_conv(g.add, sub_list));
+ EXPECT_TRUE(luci::fuse_sub_with_conv(sub_list[0]));
+
+ for (uint32_t c = 0; c < channel_size; c++)
+ {
+ auto bias = g.conv_bias->at<loco::DataType::FLOAT32>(c);
+ EXPECT_FLOAT_EQ(c - 1.0, bias);
+ }
+
+ EXPECT_EQ(g.pred_add, g.succ_add->x());
+ EXPECT_EQ(g.conv, g.succ_add->y());
+}
+
+TEST(FusePreActivationBatchNorm, fuse_sub_with_conv_NEG)
+{
+ SimpleGraph g;
+ int channel_size = 16;
+ std::vector<luci::CircleMul *> mul_list;
+ std::vector<luci::CircleAdd *> add_list;
+ std::vector<luci::CircleSub *> sub_list;
+
+ EXPECT_TRUE(luci::swap_mul_add(g.add, mul_list, add_list));
+ EXPECT_TRUE(luci::fuse_mul_with_conv(g.mul));
+ EXPECT_TRUE(luci::fuse_add_with_conv(g.add, sub_list));
+
+ // No suitable pattern (relu was inserted between add and conv)
+ auto relu = g.g.nodes()->create<luci::CircleRelu>();
+ relu->features(g.conv);
+ g.succ_add->y(relu);
+ EXPECT_FALSE(luci::fuse_sub_with_conv(sub_list[0]));
+ g.succ_add->y(g.conv);
+ relu->drop();
+
+ // No suitable pattern (add was replaced with mul)
+ auto mul = g.g.nodes()->create<luci::CircleMul>();
+ mul->x(sub_list[0]);
+ mul->y(g.conv);
+ g.succ_add->drop();
+ EXPECT_FALSE(luci::fuse_sub_with_conv(sub_list[0]));
+}
diff --git a/compiler/luci/pass/src/FusePreActivationBatchNormPassInternal.h b/compiler/luci/pass/src/FusePreActivationBatchNormPassInternal.h
new file mode 100644
index 000000000..5c24d8ba6
--- /dev/null
+++ b/compiler/luci/pass/src/FusePreActivationBatchNormPassInternal.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_CIRCLE_FUSE_PRE_ACTIVATION_BATCH_NORM_PASS_INTERNAL_H__
+#define __LUCI_CIRCLE_FUSE_PRE_ACTIVATION_BATCH_NORM_PASS_INTERNAL_H__
+
+#include <luci/IR/CircleNodes.h>
+
+namespace luci
+{
+
+// Swap MUL/ADD if they are from batch normalization
+/// @return true if success
+bool swap_mul_add(luci::CircleAdd *add, std::vector<luci::CircleMul *> &mul_list,
+ std::vector<luci::CircleAdd *> &add_list);
+
+// Fuse MUL with the next CONV if possible
+/// @return true if success
+bool fuse_mul_with_conv(luci::CircleMul *mul);
+
+// Fuse ADD with the preceding CONV if possible
+/// @return true if success
+bool fuse_add_with_conv(luci::CircleAdd *mul, std::vector<luci::CircleSub *> &sub_list);
+
+// Fuse SUB with CONV if possible
+/// @return true if success
+bool fuse_sub_with_conv(luci::CircleSub *sub);
+
+} // namespace luci
+
+#endif // __LUCI_CIRCLE_FUSE_PRE_ACTIVATION_BATCH_NORM_PASS_INTERNAL_H__
diff --git a/compiler/luci/pass/src/MakeBatchNormGammaPositivePass.cpp b/compiler/luci/pass/src/MakeBatchNormGammaPositivePass.cpp
new file mode 100644
index 000000000..281d1b081
--- /dev/null
+++ b/compiler/luci/pass/src/MakeBatchNormGammaPositivePass.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Pass/MakeBatchNormGammaPositivePass.h"
+
+#include <luci/IR/CircleNodes.h>
+
+namespace
+{
+
+// Update negative gamma to positive (1e-10)
+bool negative_gamma_to_positive(luci::CircleConst *gamma)
+{
+ assert(gamma->dtype() == loco::DataType::FLOAT32);
+
+ bool changed = false;
+ uint32_t size = gamma->size<loco::DataType::FLOAT32>();
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ if (gamma->at<loco::DataType::FLOAT32>(i) < 0)
+ {
+ gamma->at<loco::DataType::FLOAT32>(i) = 1e-10;
+ changed = true;
+ }
+ }
+ return changed;
+}
+
+// Check if add is batchnorm add
+bool is_batchnorm_add(const luci::CircleAdd *add)
+{
+ auto x = dynamic_cast<luci::CircleConst *>(add->x());
+ auto y = dynamic_cast<luci::CircleConst *>(add->y());
+
+ luci::CircleConst *constant = nullptr;
+
+ if (x != nullptr && y == nullptr)
+ constant = x;
+ else if (x == nullptr && y != nullptr)
+ constant = y;
+ else
+ return false;
+
+ if (constant->rank() != 1)
+ return false;
+
+ // Only support Relu
+ if (add->fusedActivationFunction() != luci::FusedActFunc::RELU)
+ return false;
+
+ auto channel_dim = constant->dim(0);
+ if (!(channel_dim == add->dim(add->rank() - 1)))
+ return false;
+
+ return true;
+}
+
+// Check if mul is batchnorm mul
+bool is_batchnorm_mul(const luci::CircleMul *mul, luci::CircleConst *&gamma)
+{
+ auto x = dynamic_cast<luci::CircleConst *>(mul->x());
+ auto y = dynamic_cast<luci::CircleConst *>(mul->y());
+
+ luci::CircleConst *constant = nullptr;
+
+ if (x != nullptr && y == nullptr)
+ constant = x;
+ else if (x == nullptr && y != nullptr)
+ constant = y;
+ else
+ return false;
+
+ if (constant->rank() != 1)
+ return false;
+
+ auto channel_dim = constant->dim(0);
+ if (!(channel_dim == mul->dim(mul->rank() - 1)))
+ return false;
+
+ // Check successor is batchnorm add
+ auto succs = loco::succs(mul);
+ if (succs.size() != 1)
+ return false;
+
+ auto add = dynamic_cast<luci::CircleAdd *>(*succs.begin());
+ if (add == nullptr)
+ return false;
+
+ if (!is_batchnorm_add(add))
+ return false;
+
+ gamma = constant;
+ return true;
+}
+
+} // namespace
+
+namespace luci
+{
+
+bool MakeBatchNormGammaPositivePass::run(loco::Graph *g)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ auto mul = dynamic_cast<luci::CircleMul *>(node);
+ if (mul == nullptr)
+ continue;
+
+ luci::CircleConst *gamma;
+ if (is_batchnorm_mul(mul, gamma))
+ changed = negative_gamma_to_positive(gamma);
+ }
+ return changed;
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/ProgressReporter.cpp b/compiler/luci/pass/src/ProgressReporter.cpp
new file mode 100644
index 000000000..dcf47aba6
--- /dev/null
+++ b/compiler/luci/pass/src/ProgressReporter.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ProgressReporter.h"
+
+#include <luci/Log.h>
+#include <luci/LogHelper.h>
+
+#include <logo/Phase.h>
+#include <logo/Pass.h>
+
+#include <cassert>
+
+namespace
+{
+
+char to_char(bool b) { return b ? 'Y' : 'N'; }
+
+const char *to_str(logo::PhaseStrategy s)
+{
+ switch (s)
+ {
+ case logo::PhaseStrategy::Saturate:
+ return "Saturate";
+ case logo::PhaseStrategy::Restart:
+ return "Restart";
+ }
+ assert(false);
+ return "";
+}
+
+} // namespace
+
+namespace luci
+{
+
+void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseBegin> *)
+{
+ LOGGER(prime);
+
+ INFO(prime) << "==============================================================";
+ INFO(prime) << "PhaseRunner<" << to_str(strategy()) << ">";
+ INFO(prime) << "Initial graph";
+ INFO(prime) << luci::fmt(graph());
+}
+
+void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseEnd> *)
+{
+ LOGGER(prime);
+
+ INFO(prime) << "PhaseRunner<" << to_str(strategy()) << "> - done";
+}
+
+void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassBegin> *info)
+{
+ LOGGER(prime);
+
+ INFO(prime) << "--------------------------------------------------------------";
+ INFO(prime) << "Before " << logo::pass_name(info->pass());
+}
+
+void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassEnd> *info)
+{
+ LOGGER(prime);
+
+ INFO(prime) << "After " << logo::pass_name(info->pass())
+ << " (changed: " << to_char(info->changed()) << ")";
+ INFO(prime) << luci::fmt(graph());
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/ProgressReporter.h b/compiler/luci/pass/src/ProgressReporter.h
new file mode 100644
index 000000000..bd2ba9849
--- /dev/null
+++ b/compiler/luci/pass/src/ProgressReporter.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_PROGRESSREPORTER_H__
+#define __LUCI_PROGRESSREPORTER_H__
+
+#include <logo/Phase.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+class ProgressReporter : public logo::PhaseEventListener
+{
+public:
+ ProgressReporter(loco::Graph *graph, logo::PhaseStrategy strategy)
+ : _graph{graph}, _strategy{strategy}
+ {
+ // DO NOTHING
+ }
+
+public:
+ void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseBegin> *) override;
+ void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseEnd> *) override;
+ void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassBegin> *) override;
+ void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassEnd> *) override;
+
+public:
+ loco::Graph *graph(void) const { return _graph; }
+ logo::PhaseStrategy strategy(void) const { return _strategy; }
+
+private:
+ loco::Graph *_graph;
+ logo::PhaseStrategy _strategy;
+};
+
+} // namespace luci
+
+#endif // __LUCI_PROGRESSREPORTER_H__
diff --git a/compiler/luci/pass/src/PropagateConcatenationQparam.test.cpp b/compiler/luci/pass/src/PropagateConcatenationQparam.test.cpp
new file mode 100644
index 000000000..0f8d562e9
--- /dev/null
+++ b/compiler/luci/pass/src/PropagateConcatenationQparam.test.cpp
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "QuantizationUtils.h"
+
+#include <luci/IR/CircleQuantParam.h>
+
+#include <math.h>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+namespace
+{
+
+void addQuantParam(luci::CircleNode &node, const std::vector<float> &scale,
+ const std::vector<int64_t> &zp)
+{
+ assert(node.quantparam() == nullptr);
+
+ auto quantparam = std::make_unique<luci::CircleQuantParam>();
+ quantparam->scale = scale;
+ quantparam->zerop = zp;
+ node.quantparam(std::move(quantparam));
+}
+
+int32_t quantize(float f, luci::CircleQuantParam *qparam)
+{
+ float scale = qparam->scale[0];
+ int64_t zp = qparam->zerop[0];
+
+ return std::round(f / scale) + zp;
+}
+
+class SimpleConcatGraph
+{
+public:
+ SimpleConcatGraph(loco::DataType quant_type)
+ {
+ concat_node.dtype(quant_type);
+ concat_node.fusedActivationFunction(luci::FusedActFunc::NONE);
+ input_1.dtype(quant_type);
+ input_2.dtype(quant_type);
+
+ concat_node.values(0, &input_1);
+ concat_node.values(1, &input_2);
+
+ if (quant_type == loco::DataType::U8)
+ {
+ addQuantParam(concat_node, {3.14}, {77});
+ addQuantParam(input_1, {1.0}, {1});
+ addQuantParam(input_2, {2.0}, {2});
+ }
+ else if (quant_type == loco::DataType::S16)
+ {
+ addQuantParam(concat_node, {3.14}, {0});
+ addQuantParam(input_1, {1.0}, {0});
+ addQuantParam(input_2, {2.0}, {0});
+ }
+ else
+ {
+ throw std::runtime_error("Unsupported quantization type");
+ }
+ }
+
+ ~SimpleConcatGraph()
+ {
+ concat_node.values(0, nullptr);
+ concat_node.values(1, nullptr);
+ }
+
+public:
+ luci::CircleConcatenation concat_node{2};
+ luci::CircleConv2D input_1;
+ luci::CircleConv2D input_2;
+};
+
+class SubsequentConcatGraph
+{
+public:
+ SubsequentConcatGraph(loco::DataType quant_type)
+ {
+ concat_node.dtype(quant_type);
+ concat_node.fusedActivationFunction(luci::FusedActFunc::NONE);
+ input_1.dtype(quant_type);
+ input_2.dtype(quant_type);
+
+ concat_node.values(0, &input_1);
+ concat_node.values(1, &input_2);
+
+ if (quant_type == loco::DataType::U8)
+ {
+ addQuantParam(concat_node, {3.14}, {77});
+ addQuantParam(input_1, {1.0}, {1});
+ addQuantParam(input_2, {2.0}, {2});
+ }
+ else if (quant_type == loco::DataType::S16)
+ {
+ addQuantParam(concat_node, {3.14}, {0});
+ addQuantParam(input_1, {1.0}, {0});
+ addQuantParam(input_2, {2.0}, {0});
+ }
+ else
+ {
+ throw std::runtime_error("Unsupported quantization type");
+ }
+ }
+
+ ~SubsequentConcatGraph()
+ {
+ concat_node.values(0, nullptr);
+ concat_node.values(1, nullptr);
+ }
+
+public:
+ luci::CircleConcatenation concat_node{2};
+ luci::CircleConcatenation input_1{2};
+ luci::CircleConv2D input_2;
+};
+
+class ConstInputConcatGraph
+{
+public:
+ ConstInputConcatGraph(loco::DataType quant_type)
+ {
+ concat_node.dtype(quant_type);
+ concat_node.fusedActivationFunction(luci::FusedActFunc::NONE);
+ input_1.dtype(loco::DataType::FLOAT32);
+ input_1.size<loco::DataType::FLOAT32>(5);
+ for (int i = 0; i < 5; i++)
+ {
+ // Set data {-2, -1, 0, 1, 2}
+ input_1.at<loco::DataType::FLOAT32>(i) = i - 2.0;
+ }
+
+ input_2.dtype(quant_type);
+
+ concat_node.values(0, &input_1);
+ concat_node.values(1, &input_2);
+
+ if (quant_type == loco::DataType::U8)
+ {
+ addQuantParam(concat_node, {0.1}, {10});
+ addQuantParam(input_2, {2.0}, {2});
+ }
+ else if (quant_type == loco::DataType::S16)
+ {
+ addQuantParam(concat_node, {0.1}, {0});
+ addQuantParam(input_2, {2.0}, {0});
+ }
+ else
+ {
+ throw std::runtime_error("Unsupported quantization type");
+ }
+ }
+
+ ~ConstInputConcatGraph()
+ {
+ concat_node.values(0, nullptr);
+ concat_node.values(1, nullptr);
+ }
+
+public:
+ luci::CircleConcatenation concat_node{2};
+ luci::CircleConst input_1;
+ luci::CircleConv2D input_2;
+};
+
+} // namespace
+
+TEST(PropagateConcatenationQparam, propagate_concat_quantparam_u8)
+{
+ // Check cases where qparam of concat_node is propagated
+ // (1) normal case: qparam is propagated to input_1 and input_2
+ // (2) input used by other Op: input_1 is an input of input_2. qparam is propagated only to
+ // input_2
+ // (3) subsequent concat: input_1 is concat. qparam is propagated only to input_2
+ // (4) const input: input_1 is const. constant values are quantized
+
+ // normal case: qparam of concat_node is propagated to input_1 and input_2
+ SimpleConcatGraph g(loco::DataType::U8);
+ luci::propagate_concat_quantparam(&g.concat_node, loco::DataType::U8);
+ EXPECT_FLOAT_EQ(3.14, g.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(77, g.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(3.14, g.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(77, g.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(3.14, g.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(77, g.input_2.quantparam()->zerop[0]);
+
+ // input_1 is an input of input_2. qparam is propagated only to input_2
+ SimpleConcatGraph g2(loco::DataType::U8);
+ g2.input_2.input(&g2.input_1);
+ luci::propagate_concat_quantparam(&g2.concat_node, loco::DataType::U8);
+ EXPECT_FLOAT_EQ(3.14, g2.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(77, g2.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(1.0, g2.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(1, g2.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(3.14, g2.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(77, g2.input_2.quantparam()->zerop[0]);
+
+ // input_1 is concat. qparam is propagated only to input_2
+ SubsequentConcatGraph sg(loco::DataType::U8);
+ luci::propagate_concat_quantparam(&sg.concat_node, loco::DataType::U8);
+ EXPECT_FLOAT_EQ(3.14, sg.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(77, sg.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(1.0, sg.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(1, sg.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(3.14, sg.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(77, sg.input_2.quantparam()->zerop[0]);
+
+ // input_1 is const. const values are quantized with the qparam of concat
+ ConstInputConcatGraph cg(loco::DataType::U8);
+ luci::propagate_concat_quantparam(&cg.concat_node, loco::DataType::U8);
+ EXPECT_FLOAT_EQ(0.1, cg.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(10, cg.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(0.1, cg.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(10, cg.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(0.1, cg.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(10, cg.input_2.quantparam()->zerop[0]);
+ EXPECT_EQ(loco::DataType::U8, cg.input_1.dtype());
+ EXPECT_EQ(0, cg.input_1.at<loco::DataType::U8>(0));
+ EXPECT_EQ(0, cg.input_1.at<loco::DataType::U8>(1));
+ EXPECT_EQ(10, cg.input_1.at<loco::DataType::U8>(2));
+ EXPECT_EQ(20, cg.input_1.at<loco::DataType::U8>(3));
+ EXPECT_EQ(30, cg.input_1.at<loco::DataType::U8>(4));
+}
+
+TEST(PropagateConcatenationQparam, propagate_concat_quantparam_u8_NEG)
+{
+ // Check negative cases where qparam is not propagated
+ // (1) concat has fused activation function
+ // (2) concat has fused activation function and input is const
+
+ SimpleConcatGraph g(loco::DataType::U8);
+
+ // concat has fused activation function
+ g.concat_node.fusedActivationFunction(luci::FusedActFunc::RELU);
+ luci::propagate_concat_quantparam(&g.concat_node, loco::DataType::U8);
+ EXPECT_FLOAT_EQ(3.14, g.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(77, g.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(1.0, g.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(1, g.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(2.0, g.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(2, g.input_2.quantparam()->zerop[0]);
+ g.concat_node.fusedActivationFunction(luci::FusedActFunc::NONE);
+
+ // concat has fused activation function and input_1 is const.
+ // const values are quantized using its min/max
+ ConstInputConcatGraph cg(loco::DataType::U8);
+ cg.concat_node.fusedActivationFunction(luci::FusedActFunc::RELU);
+ luci::propagate_concat_quantparam(&cg.concat_node, loco::DataType::U8);
+ EXPECT_FLOAT_EQ(0.1, cg.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(10, cg.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(0.015686275, cg.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(128, cg.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(2.0, cg.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(2, cg.input_2.quantparam()->zerop[0]);
+ EXPECT_EQ(loco::DataType::U8, cg.input_1.dtype());
+ EXPECT_EQ(quantize(-2, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::U8>(0));
+ EXPECT_EQ(quantize(-1, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::U8>(1));
+ EXPECT_EQ(quantize(0, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::U8>(2));
+ EXPECT_EQ(quantize(1, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::U8>(3));
+ EXPECT_EQ(quantize(2, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::U8>(4));
+}
+
+TEST(PropagateConcatenationQparam, propagate_concat_quantparam_i16)
+{
+ // Check cases where qparam of concat_node is propagated
+ // (1) normal case: qparam is propagated to input_1 and input_2
+ // (2) input used by other Op: input_1 is an input of input_2. qparam is propagated only to
+ // input_2
+ // (3) subsequent concat: input_1 is concat. qparam is propagated only to input_2
+ // (4) const input: input_1 is const. constant values are quantized
+
+ // normal case: qparam of concat_node is propagated to input_1 and input_2
+ SimpleConcatGraph g(loco::DataType::S16);
+ luci::propagate_concat_quantparam(&g.concat_node, loco::DataType::S16);
+ EXPECT_FLOAT_EQ(3.14, g.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(0, g.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(3.14, g.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(0, g.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(3.14, g.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(0, g.input_2.quantparam()->zerop[0]);
+
+ // input_1 is an input of input_2. qparam is propagated only to input_2
+ SimpleConcatGraph g2(loco::DataType::S16);
+ g2.input_2.input(&g2.input_1);
+ luci::propagate_concat_quantparam(&g2.concat_node, loco::DataType::S16);
+ EXPECT_FLOAT_EQ(3.14, g2.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(0, g2.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(1.0, g2.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(0, g2.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(3.14, g2.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(0, g2.input_2.quantparam()->zerop[0]);
+
+ // input_1 is concat. qparam is propagated only to input_2
+ SubsequentConcatGraph sg(loco::DataType::S16);
+ luci::propagate_concat_quantparam(&sg.concat_node, loco::DataType::S16);
+ EXPECT_FLOAT_EQ(3.14, sg.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(0, sg.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(1.0, sg.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(0, sg.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(3.14, sg.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(0, sg.input_2.quantparam()->zerop[0]);
+
+ // input_1 is const. const values are quantized with the qparam of concat
+ ConstInputConcatGraph cg(loco::DataType::S16);
+ luci::propagate_concat_quantparam(&cg.concat_node, loco::DataType::S16);
+ EXPECT_FLOAT_EQ(0.1, cg.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(0, cg.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(0.1, cg.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(0, cg.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(0.1, cg.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(0, cg.input_2.quantparam()->zerop[0]);
+ EXPECT_EQ(loco::DataType::S16, cg.input_1.dtype());
+ EXPECT_EQ(-20, cg.input_1.at<loco::DataType::S16>(0));
+ EXPECT_EQ(-10, cg.input_1.at<loco::DataType::S16>(1));
+ EXPECT_EQ(0, cg.input_1.at<loco::DataType::S16>(2));
+ EXPECT_EQ(10, cg.input_1.at<loco::DataType::S16>(3));
+ EXPECT_EQ(20, cg.input_1.at<loco::DataType::S16>(4));
+}
+
+TEST(PropagateConcatenationQparam, propagate_concat_quantparam_i16_NEG)
+{
+ // Check negative cases where qparam is not propagated
+ // (1) concat has fused activation function
+ // (2) concat has fused activation function and input is const
+
+ SimpleConcatGraph g(loco::DataType::S16);
+
+ // concat has fused activation function
+ g.concat_node.fusedActivationFunction(luci::FusedActFunc::RELU);
+ luci::propagate_concat_quantparam(&g.concat_node, loco::DataType::S16);
+ EXPECT_FLOAT_EQ(3.14, g.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(0, g.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(1.0, g.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(0, g.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(2.0, g.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(0, g.input_2.quantparam()->zerop[0]);
+ g.concat_node.fusedActivationFunction(luci::FusedActFunc::NONE);
+
+ // concat has fused activation function and input_1 is const.
+ // const values are quantized using its min/max
+ ConstInputConcatGraph cg(loco::DataType::S16);
+ cg.concat_node.fusedActivationFunction(luci::FusedActFunc::RELU);
+ luci::propagate_concat_quantparam(&cg.concat_node, loco::DataType::S16);
+ EXPECT_FLOAT_EQ(0.1, cg.concat_node.quantparam()->scale[0]);
+ EXPECT_EQ(0, cg.concat_node.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(0.000061037, cg.input_1.quantparam()->scale[0]);
+ EXPECT_EQ(0, cg.input_1.quantparam()->zerop[0]);
+ EXPECT_FLOAT_EQ(2.0, cg.input_2.quantparam()->scale[0]);
+ EXPECT_EQ(0, cg.input_2.quantparam()->zerop[0]);
+ EXPECT_EQ(loco::DataType::S16, cg.input_1.dtype());
+ EXPECT_EQ(quantize(-2, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::S16>(0));
+ EXPECT_EQ(quantize(-1, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::S16>(1));
+ EXPECT_EQ(quantize(0, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::S16>(2));
+ EXPECT_EQ(quantize(1, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::S16>(3));
+ EXPECT_EQ(quantize(2, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::S16>(4));
+}
diff --git a/compiler/luci/pass/src/QuantizationUtils.cpp b/compiler/luci/pass/src/QuantizationUtils.cpp
new file mode 100644
index 000000000..fa0141114
--- /dev/null
+++ b/compiler/luci/pass/src/QuantizationUtils.cpp
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "QuantizationUtils.h"
+
+#include <luci/Log.h>
+
+#include <iostream>
+#include <cmath>
+
+namespace luci
+{
+
+bool is_quantized(const CircleNode *node)
+{
+ return node->quantparam() != nullptr &&
+ (node->dtype() == loco::DataType::U8 || // activation, weight (uint8 quant)
+ node->dtype() == loco::DataType::S16 || // activation, weight (int16 quant)
+ node->dtype() == loco::DataType::S32 || // bias (uint8 quant)
+ node->dtype() == loco::DataType::S64); // bias (int16 quant)
+}
+
+// Check if node is weights of conv2d, depthwise_conv2d, or fully_connected layer
+bool is_weights(CircleNode *node)
+{
+ auto circle_const = dynamic_cast<CircleConst *>(node);
+ if (circle_const == nullptr)
+ return false;
+
+ auto succs = loco::succs(node);
+
+ // Node is weights if it is the weights of all of its successors
+ for (auto out : succs)
+ {
+ bool is_weights = false;
+
+ auto conv = dynamic_cast<CircleConv2D *>(out);
+ if (conv != nullptr && conv->filter() == circle_const)
+ is_weights = true;
+
+ auto dw_conv = dynamic_cast<CircleDepthwiseConv2D *>(out);
+ if (dw_conv != nullptr && dw_conv->filter() == circle_const)
+ is_weights = true;
+
+ auto t_conv = dynamic_cast<CircleTransposeConv *>(out);
+ if (t_conv != nullptr && t_conv->filter() == circle_const && circle_const->rank() == 4)
+ is_weights = true;
+
+ auto fc = dynamic_cast<CircleFullyConnected *>(out);
+ if (fc != nullptr && fc->weights() == circle_const)
+ is_weights = true;
+
+ if (!is_weights)
+ return false;
+ }
+
+ return true;
+}
+
+uint8_t fp32_to_uint8_cast(float f)
+{
+ assert(std::numeric_limits<uint8_t>::min() <= f);
+ assert(f <= std::numeric_limits<uint8_t>::max());
+ return static_cast<uint8_t>(f);
+}
+
+// Per-layer quantization of weights (const tensor) using given min/max values
+void asymmetric_wquant_with_minmax_per_layer(CircleConst *node, float min, float max,
+ float &scaling_factor, int64_t &zp, float &nudged_min,
+ float &nudged_max)
+{
+ const int32_t kMinScale = 0;
+ const int32_t kMaxScale = 255;
+
+ uint32_t size = node->size<loco::DataType::FLOAT32>();
+ compute_asym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max);
+ const float scaling_factor_inv = 1.0 / scaling_factor;
+ std::vector<int32_t> quantized_values(size);
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ // clipping
+ auto data = node->at<loco::DataType::FLOAT32>(i);
+ data = data < nudged_min ? nudged_min : data;
+ data = data > nudged_max ? nudged_max : data;
+ quantized_values[i] =
+ static_cast<int32_t>(std::round((data - nudged_min) * scaling_factor_inv));
+ }
+
+ node->dtype(loco::DataType::U8); // change the type of tensor
+ node->size<loco::DataType::U8>(size); // resize tensor
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ node->at<loco::DataType::U8>(i) = std::min(kMaxScale, std::max(kMinScale, quantized_values[i]));
+ }
+}
+
+// Per-layer quantization of weights (const tensor) using given min/max values
+void symmetric_wquant_with_minmax_per_layer(CircleConst *node, float min, float max,
+ float &scaling_factor, int64_t &zp, float &nudged_min,
+ float &nudged_max)
+{
+ const int32_t kMaxScale = std::numeric_limits<int16_t>::max();
+ const int32_t kMinScale = -kMaxScale;
+
+ uint32_t size = node->size<loco::DataType::FLOAT32>();
+ compute_sym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max);
+ const float scaling_factor_inv = 1.0 / scaling_factor;
+ std::vector<int32_t> quantized_values(size);
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ // clipping
+ auto data = node->at<loco::DataType::FLOAT32>(i);
+ data = data < nudged_min ? nudged_min : data;
+ data = data > nudged_max ? nudged_max : data;
+ quantized_values[i] = static_cast<int32_t>(std::round(data * scaling_factor_inv));
+ }
+
+ node->dtype(loco::DataType::S16); // change the type of tensor
+ node->size<loco::DataType::S16>(size); // resize tensor
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ node->at<loco::DataType::S16>(i) =
+ std::min(kMaxScale, std::max(kMinScale, quantized_values[i]));
+ }
+}
+
+void compute_sym_scale_zp(float min, float max, float &scaling_factor, int64_t &zp,
+ float &nudged_min, float &nudged_max)
+{
+ assert(min != max);
+
+ const int32_t kMaxScale = std::numeric_limits<int16_t>::max();
+ const int32_t kMinScale = -kMaxScale;
+ const double qmin_double = kMinScale;
+ const double qmax_double = kMaxScale;
+ const double rmin = std::fmin(0, min);
+ const double rmax = std::fmax(0, max);
+ double scale_factor_from_min_side{0};
+ double scale_factor_from_max_side{0};
+
+ if ((qmin_double * rmin) > 0)
+ scale_factor_from_min_side = rmin / qmin_double;
+
+ if ((qmax_double * rmax) > 0)
+ scale_factor_from_max_side = rmax / qmax_double;
+
+ scaling_factor = scale_factor_from_min_side > scale_factor_from_max_side
+ ? scale_factor_from_min_side
+ : scale_factor_from_max_side;
+ zp = 0;
+ nudged_min = static_cast<float>(qmin_double * scaling_factor);
+ nudged_max = static_cast<float>(qmax_double * scaling_factor);
+}
+
+void compute_asym_scale_zp(float min, float max, float &scaling_factor, int64_t &zp,
+ float &nudged_min, float &nudged_max)
+{
+ LOGGER(l);
+
+ assert(min <= max);
+ const int32_t kMinScale = 0;
+ const int32_t kMaxScale = 255;
+ const double qmin_double = kMinScale;
+ const double qmax_double = kMaxScale;
+ const double rmin = std::fmin(0, min);
+ const double rmax = std::fmax(0, max);
+
+ double scale = (rmax - rmin) / (qmax_double - qmin_double);
+ double zero_point_double = 0;
+ uint8_t nudged_zero_point = 0;
+ if (scale == 0)
+ {
+ WARN(l) << "The minimum and maximum values are the same." << std::endl;
+ if (min >= 0 && max >= 0)
+ zero_point_double = kMinScale;
+ else
+ zero_point_double = kMaxScale;
+ }
+ else
+ zero_point_double = qmin_double - rmin / scale;
+ if (min >= 0)
+ {
+ assert(min >= 0 && max >= 0);
+ nudged_zero_point = kMinScale;
+ scale = max / (qmax_double - qmin_double);
+ if (min > 0 && max > 0)
+ WARN(l) << "The minimum and maximum values are all positive." << std::endl;
+ }
+ else if (max < 0)
+ {
+ assert(min < 0 && max < 0);
+ nudged_zero_point = kMaxScale;
+ scale = -min / (qmax_double - qmin_double);
+ WARN(l) << "The minimum and maximum values are all negative." << std::endl;
+ }
+ else
+ {
+ assert(min < 0 && max >= 0);
+ nudged_zero_point = fp32_to_uint8_cast(std::round(zero_point_double));
+ }
+
+ // protect scale from being very low due to overflow
+ if (scale < 1e-5)
+ {
+ scale = 1e-5;
+ nudged_zero_point = fp32_to_uint8_cast(std::round(qmin_double - rmin / scale));
+ }
+
+ nudged_min = static_cast<float>((qmin_double - nudged_zero_point) * scale);
+ nudged_max = static_cast<float>((qmax_double - nudged_zero_point) * scale);
+
+ scaling_factor = scale;
+ zp = nudged_zero_point;
+}
+
+bool get_channel_dim_index(CircleConst *node, loco::TensorShape &dimension, int &channel_dim_index)
+{
+ auto succs = loco::succs(node);
+
+ // opcode is initialized to CIRCLEINPUT, because
+ // CIRCLEINPUT should never be the successor of any node
+ // (this is checked w/ the assert in the loop body)
+ luci::CircleOpcode opcode = luci::CircleOpcode::CIRCLEINPUT;
+ for (auto out : succs)
+ {
+ const auto circle_node = static_cast<CircleNode *>(out);
+ assert(circle_node->opcode() != luci::CircleOpcode::CIRCLEINPUT);
+
+ if (opcode == luci::CircleOpcode::CIRCLEINPUT)
+ {
+ opcode = circle_node->opcode();
+ }
+ else
+ {
+ // Node is used by multiple layers with different opcodes
+ // We do not care such cases
+ if (opcode != circle_node->opcode())
+ return false;
+ }
+ }
+
+ for (auto out : succs)
+ {
+ auto conv = dynamic_cast<CircleConv2D *>(out);
+ auto dw_conv = dynamic_cast<CircleDepthwiseConv2D *>(out);
+ auto tw_conv = dynamic_cast<CircleTransposeConv *>(out);
+ auto fc = dynamic_cast<CircleFullyConnected *>(out);
+
+ // Refer to https://github.com/Samsung/ONE/pull/2448.
+ if ((conv != nullptr && conv->filter() == node) ||
+ (tw_conv != nullptr && tw_conv->filter() == node)) // OHWI
+ {
+ assert(node->rank() == 4);
+ dimension.dim(0).set(node->dim(0).value());
+ dimension.dim(1).set(node->dim(1).value());
+ dimension.dim(2).set(node->dim(2).value());
+ dimension.dim(3).set(node->dim(3).value());
+ channel_dim_index = 0; // Set channel_dim_index based on "O"
+ return true;
+ }
+ else if (dw_conv != nullptr && dw_conv->filter() == node) // IHWC
+ {
+ assert(node->rank() == 4);
+ dimension.dim(0).set(node->dim(0).value());
+ dimension.dim(1).set(node->dim(1).value());
+ dimension.dim(2).set(node->dim(2).value());
+ dimension.dim(3).set(node->dim(3).value());
+ channel_dim_index = 3; // Set channel_dim_index based on "C"
+ return true;
+ }
+ else if (fc != nullptr && fc->weights() == node) // OI
+ {
+ assert(node->rank() == 2);
+ dimension.dim(0).set(node->dim(0).value());
+ dimension.dim(1).set(1); // Set FC layer like CONV
+ dimension.dim(2).set(1);
+ dimension.dim(3).set(node->dim(1).value());
+ channel_dim_index = 0; // Set channel_dim_index based on "O"
+ return true;
+ }
+ else
+ {
+ // node does not support channle-wise quantization
+ assert(false);
+ }
+ }
+
+ return false;
+}
+
+uint32_t cal_offset(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];
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/QuantizationUtils.h b/compiler/luci/pass/src/QuantizationUtils.h
new file mode 100644
index 000000000..22a5cf1ee
--- /dev/null
+++ b/compiler/luci/pass/src/QuantizationUtils.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_QUANTIZATION_UTILS_H__
+#define __LUCI_QUANTIZATION_UTILS_H__
+
+#include <luci/IR/CircleNodes.h>
+#include <loco/IR/TensorShape.h>
+
+namespace luci
+{
+
+void compute_sym_scale_zp(float min, float max, float &scaling_factor, int64_t &zp,
+ float &nudged_min, float &nudged_max);
+
+void compute_asym_scale_zp(float min, float max, float &scaling_factor, int64_t &zp,
+ float &nudged_min, float &nudged_max);
+
+void asymmetric_wquant_with_minmax_per_layer(CircleConst *node, float min, float max,
+ float &scaling_factor, int64_t &zp, float &nudged_min,
+ float &nudged_max);
+
+void symmetric_wquant_with_minmax_per_layer(CircleConst *node, float min, float max,
+ float &scaling_factor, int64_t &zp, float &nudged_min,
+ float &nudged_max);
+
+bool get_channel_dim_index(CircleConst *node, loco::TensorShape &dimension, int &channel_dim_index);
+
+uint32_t cal_offset(loco::TensorShape &dimension, uint32_t *indices);
+
+void propagate_concat_quantparam(luci::CircleConcatenation *concat, loco::DataType quant_type);
+
+bool is_weights(CircleNode *node);
+
+bool is_quantized(const CircleNode *node);
+
+} // namespace luci
+
+#endif // __LUCI_QUANTIZATION_UTILS_H__
diff --git a/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp b/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp
new file mode 100644
index 000000000..e10c4bb4d
--- /dev/null
+++ b/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp
@@ -0,0 +1,426 @@
+/*
+ * 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 "luci/Pass/QuantizeDequantizeWeightsPass.h"
+#include "QuantizationUtils.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/CircleNodeVisitor.h>
+#include <luci/Log.h>
+#include <loco/IR/TensorShape.h>
+
+#include <iostream>
+#include <cmath>
+
+namespace luci
+{
+
+namespace
+{
+
+void cal_minmax_per_channel(CircleConst *node, std::vector<float> &min, std::vector<float> &max)
+{
+ loco::TensorShape dimension;
+ dimension.rank(4);
+ uint32_t indices[4] = {
+ 0,
+ };
+ int channel_dim_index{0};
+ int size{0};
+
+ if (!get_channel_dim_index(node, dimension, channel_dim_index))
+ {
+ assert(false);
+ return;
+ }
+ size = dimension.dim(channel_dim_index).value();
+
+ std::vector<bool> has_min_max_value(size, false);
+ min.resize(size);
+ max.resize(size);
+ 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]++)
+ {
+ int channel_idx = indices[channel_dim_index];
+ auto data = node->at<loco::DataType::FLOAT32>(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;
+ }
+ }
+ }
+ }
+ }
+}
+
+void sym_wquant_per_channel(CircleConst *node, std::vector<float> &min, std::vector<float> &max,
+ std::vector<float> &scaling_factor, std::vector<int64_t> &zp,
+ std::vector<float> &nudged_min, std::vector<float> &nudged_max)
+{
+ assert(node->dtype() == loco::DataType::FLOAT32);
+ const int32_t kMaxScale = std::numeric_limits<int16_t>::max();
+ const int32_t kMinScale = -kMaxScale;
+
+ uint32_t size = node->size<loco::DataType::FLOAT32>();
+ std::vector<int32_t> quantized_values(size);
+
+ 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]);
+ }
+
+ loco::TensorShape dimension;
+ dimension.rank(4);
+ uint32_t indices[4] = {
+ 0,
+ };
+ int channel_dim_index{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]++)
+ {
+ int channel_idx = indices[channel_dim_index];
+ const float scaling_factor_inv = 1.0 / scaling_factor[channel_idx];
+ auto data = node->at<loco::DataType::FLOAT32>(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<int32_t>(std::round(data * scaling_factor_inv));
+ }
+ }
+ }
+ }
+
+ node->dtype(loco::DataType::S16); // change the type of tensor
+ node->size<loco::DataType::S16>(size); // resize tensor
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ node->at<loco::DataType::S16>(i) =
+ std::min(kMaxScale, std::max(kMinScale, quantized_values[i]));
+ }
+}
+
+void sym_wdequant_per_channel(CircleConst *node, std::vector<float> &scaling_factor)
+{
+ assert(node->dtype() == loco::DataType::S16);
+ uint32_t size = node->size<loco::DataType::S16>();
+ std::vector<float> dequantized_values(size);
+
+ loco::TensorShape dimension;
+ dimension.rank(4);
+ uint32_t indices[4] = {
+ 0,
+ };
+ int channel_dim_index{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]++)
+ {
+ int channel_idx = indices[channel_dim_index];
+ auto data = node->at<loco::DataType::S16>(cal_offset(dimension, indices));
+ dequantized_values[cal_offset(dimension, indices)] =
+ static_cast<float>(data) * scaling_factor[channel_idx];
+ }
+ }
+ }
+ }
+
+ node->dtype(loco::DataType::FLOAT32); // change the type of tensor
+ node->size<loco::DataType::FLOAT32>(size); // resize tensor
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ node->at<loco::DataType::FLOAT32>(i) = dequantized_values[i];
+ }
+}
+
+void asymmetric_wquant_per_channel(CircleConst *node, std::vector<float> &min,
+ std::vector<float> &max, std::vector<float> &scaling_factor,
+ std::vector<int64_t> &zp, std::vector<float> &nudged_min,
+ std::vector<float> &nudged_max)
+{
+ assert(node->dtype() == loco::DataType::FLOAT32);
+
+ const int32_t kMinScale = 0;
+ const int32_t kMaxScale = 255;
+
+ uint32_t size = node->size<loco::DataType::FLOAT32>();
+ std::vector<int32_t> quantized_values(size);
+
+ for (size_t i = 0; i < min.size(); ++i)
+ {
+ compute_asym_scale_zp(min[i], max[i], scaling_factor[i], zp[i], nudged_min[i], nudged_max[i]);
+ }
+
+ loco::TensorShape dimension;
+ dimension.rank(4);
+ uint32_t indices[4] = {
+ 0,
+ };
+ int channel_dim_index{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]++)
+ {
+ int channel_idx = indices[channel_dim_index];
+ const float scaling_factor_inv = 1.0 / scaling_factor[channel_idx];
+ auto data = node->at<loco::DataType::FLOAT32>(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<int32_t>(
+ std::round((data - nudged_min[channel_idx]) * scaling_factor_inv));
+ }
+ }
+ }
+ }
+
+ node->dtype(loco::DataType::U8); // change the type of tensor
+ node->size<loco::DataType::U8>(size); // resize tensor
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ node->at<loco::DataType::U8>(i) = std::min(kMaxScale, std::max(kMinScale, quantized_values[i]));
+ }
+}
+
+void asymmetric_wdequant_per_channel(CircleConst *node, std::vector<float> &scaling_factor,
+ std::vector<float> &nudged_min)
+{
+ assert(node->dtype() == loco::DataType::U8);
+ uint32_t size = node->size<loco::DataType::U8>();
+ std::vector<float> dequantized_values(size);
+
+ loco::TensorShape dimension;
+ dimension.rank(4);
+ uint32_t indices[4] = {
+ 0,
+ };
+ int channel_dim_index{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]++)
+ {
+ int channel_idx = indices[channel_dim_index];
+ auto data = node->at<loco::DataType::U8>(cal_offset(dimension, indices));
+ dequantized_values[cal_offset(dimension, indices)] =
+ static_cast<float>(data) * scaling_factor[channel_idx] + nudged_min[channel_idx];
+ }
+ }
+ }
+ }
+
+ node->dtype(loco::DataType::FLOAT32); // change the type of tensor
+ node->size<loco::DataType::FLOAT32>(size); // resize tensor
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ node->at<loco::DataType::FLOAT32>(i) = dequantized_values[i];
+ }
+}
+
+void asymmetric_wdequant_with_minmax_per_layer(CircleConst *node, float scaling_factor,
+ float nudged_min)
+{
+ uint32_t size = node->size<loco::DataType::U8>();
+ std::vector<float> dequantized_values(size);
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ auto data = node->at<loco::DataType::U8>(i);
+ dequantized_values[i] = static_cast<float>(data) * scaling_factor + nudged_min;
+ }
+
+ node->dtype(loco::DataType::FLOAT32); // change the type of tensor
+ node->size<loco::DataType::FLOAT32>(size); // resize tensor
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ node->at<loco::DataType::FLOAT32>(i) = dequantized_values[i];
+ }
+}
+
+/**
+ * @brief QuantizeDequantizeWeights quantizes and dequantizes tensors for weights
+ * @details Find min/max values on the fly, quantize the model, and dequantize the model
+ */
+struct QuantizeDequantizeWeights final : public luci::CircleNodeMutableVisitor<bool>
+{
+ QuantizeDequantizeWeights(loco::DataType input, loco::DataType output,
+ QuantizationGranularity granularity)
+ : input_type(input), output_type(output), granularity(granularity)
+ {
+ }
+
+ loco::DataType input_type;
+ loco::DataType output_type;
+ QuantizationGranularity granularity;
+
+ // Quantize and dequantize input tensors of each node
+ bool visit(luci::CircleNode *node)
+ {
+ assert(output_type == loco::DataType::U8 || output_type == loco::DataType::S16);
+ LOGGER(l);
+ INFO(l) << "QuantizeDequantizeWeights 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<luci::CircleNode *>(input_node);
+
+ // Check if this is already quantized
+ if (is_quantized(circle_node))
+ continue;
+
+ if (is_weights(circle_node))
+ {
+ auto circle_const = loco::must_cast<luci::CircleConst *>(circle_node);
+
+ // Find min/max per channel-wise
+ if (granularity == QuantizationGranularity::ChannelWise)
+ {
+ std::vector<float> min;
+ std::vector<float> max;
+
+ cal_minmax_per_channel(circle_const, min, max);
+
+ std::vector<float> nudged_min(min.size());
+ std::vector<float> nudged_max(min.size());
+ std::vector<float> scaling_factor(min.size());
+ std::vector<int64_t> zp(min.size());
+
+ if (output_type == loco::DataType::U8)
+ {
+ asymmetric_wquant_per_channel(circle_const, min, max, scaling_factor, zp, nudged_min,
+ nudged_max);
+ asymmetric_wdequant_per_channel(circle_const, scaling_factor, nudged_min);
+ }
+ else
+ {
+ sym_wquant_per_channel(circle_const, min, max, scaling_factor, zp, nudged_min,
+ nudged_max);
+ sym_wdequant_per_channel(circle_const, scaling_factor);
+ }
+
+ auto quantparam = std::make_unique<CircleQuantParam>();
+ quantparam->min = nudged_min;
+ quantparam->max = nudged_max;
+ quantparam->scale = scaling_factor;
+ quantparam->zerop = zp;
+ circle_node->quantparam(std::move(quantparam));
+ }
+ // Find min/max per layer-wise
+ else
+ {
+ float min = std::numeric_limits<float>::max();
+ float max = std::numeric_limits<float>::lowest();
+ for (uint32_t i = 0; i < circle_const->size<loco::DataType::FLOAT32>(); i++)
+ {
+ auto data = circle_const->at<loco::DataType::FLOAT32>(i);
+ min = data < min ? data : min;
+ max = data > max ? data : max;
+ }
+ float scaling_factor{0};
+ int64_t zp{0};
+ float nudged_min{0};
+ float nudged_max{0};
+
+ asymmetric_wquant_with_minmax_per_layer(circle_const, min, max, scaling_factor, zp,
+ nudged_min, nudged_max);
+ asymmetric_wdequant_with_minmax_per_layer(circle_const, scaling_factor, nudged_min);
+ auto quantparam = std::make_unique<CircleQuantParam>();
+ quantparam->min.push_back(nudged_min);
+ quantparam->max.push_back(nudged_max);
+ quantparam->scale.push_back(scaling_factor);
+ quantparam->zerop.push_back(zp);
+ circle_node->quantparam(std::move(quantparam));
+ }
+ }
+ }
+ return false;
+ }
+};
+
+} // namespace
+
+bool QuantizeDequantizeWeightsPass::run(loco::Graph *g)
+{
+ LOGGER(l);
+ INFO(l) << "QuantizeDequantizeWeightsPass Start" << std::endl;
+
+ // Quantize weights
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ QuantizeDequantizeWeights qw(_input_dtype, _output_dtype, _granularity);
+ auto circle_node = loco::must_cast<luci::CircleNode *>(node);
+ circle_node->accept(&qw);
+ }
+
+ INFO(l) << "QuantizeDequantizeWeightsPass End" << std::endl;
+ return false; // one time run
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp b/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp
new file mode 100644
index 000000000..0ecab008f
--- /dev/null
+++ b/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp
@@ -0,0 +1,855 @@
+/*
+ * 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 "luci/Pass/QuantizeWithMinMaxPass.h"
+#include "QuantizationUtils.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/CircleNodeVisitor.h>
+#include <luci/Log.h>
+
+#include <oops/UserExn.h>
+
+#include <iostream>
+#include <cmath>
+
+namespace luci
+{
+
+namespace
+{
+
+void overwrite_quantparam(luci::CircleConcatenation *concat, luci::CircleNode *target)
+{
+ auto concat_qparam = concat->quantparam();
+ if (concat_qparam == nullptr)
+ throw std::runtime_error("quantparam of concat is not found during overwrite");
+
+ auto target_qparam = target->quantparam();
+ if (target_qparam == nullptr)
+ {
+ auto quantparam = std::make_unique<CircleQuantParam>();
+ target->quantparam(std::move(quantparam));
+ target_qparam = target->quantparam();
+ }
+ target_qparam->min = concat_qparam->min;
+ target_qparam->max = concat_qparam->max;
+ target_qparam->scale = concat_qparam->scale;
+ target_qparam->zerop = concat_qparam->zerop;
+ target_qparam->quantized_dimension = concat_qparam->quantized_dimension;
+}
+
+void quant_const_values(luci::CircleConst *const_node, float scaling_factor, float zerop,
+ loco::DataType quant_type)
+{
+ uint32_t size = const_node->size<loco::DataType::FLOAT32>();
+
+ const float scaling_factor_inv = 1.0 / scaling_factor;
+ std::vector<int32_t> quantized_values(size);
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ auto data = const_node->at<loco::DataType::FLOAT32>(i);
+ quantized_values[i] = static_cast<int32_t>(std::round(data * scaling_factor_inv) + zerop);
+ }
+
+ switch (quant_type)
+ {
+ case loco::DataType::U8:
+ const_node->dtype(loco::DataType::U8); // change the type of tensor
+ const_node->size<loco::DataType::U8>(size); // resize tensor
+ for (uint32_t i = 0; i < size; ++i)
+ const_node->at<loco::DataType::U8>(i) = std::min(255, std::max(0, quantized_values[i]));
+ break;
+ case loco::DataType::S16:
+ assert(zerop == 0);
+ const_node->dtype(loco::DataType::S16); // change the type of tensor
+ const_node->size<loco::DataType::S16>(size); // resize tensor
+ for (uint32_t i = 0; i < size; ++i)
+ const_node->at<loco::DataType::S16>(i) =
+ std::min(32767, std::max(-32767, quantized_values[i]));
+ break;
+ default:
+ throw std::runtime_error("Unsupported data type");
+ }
+}
+
+void quant_const(CircleConst *node, loco::DataType quant_type)
+{
+ assert(node->dtype() == loco::DataType::FLOAT32);
+
+ float min = std::numeric_limits<float>::max();
+ float max = std::numeric_limits<float>::lowest();
+ for (uint32_t i = 0; i < node->size<loco::DataType::FLOAT32>(); i++)
+ {
+ auto data = node->at<loco::DataType::FLOAT32>(i);
+ min = data < min ? data : min;
+ max = data > max ? data : max;
+ }
+
+ float scaling_factor{0.0};
+ int64_t zp{0};
+ float nudged_min{0.0};
+ float nudged_max{0.0};
+
+ switch (quant_type)
+ {
+ case loco::DataType::U8:
+ asymmetric_wquant_with_minmax_per_layer(node, min, max, scaling_factor, zp, nudged_min,
+ nudged_max);
+ break;
+ case loco::DataType::S16:
+ symmetric_wquant_with_minmax_per_layer(node, min, max, scaling_factor, zp, nudged_min,
+ nudged_max);
+ break;
+ default:
+ throw std::runtime_error("Unsupported data type");
+ }
+
+ auto quantparam = std::make_unique<CircleQuantParam>();
+ quantparam->scale.push_back(scaling_factor);
+ quantparam->zerop.push_back(zp);
+ node->quantparam(std::move(quantparam));
+}
+
+// Check if the node is the bias of Conv2D, DepthwiseConv2D, FullyConnected, or TransposeConv layer
+// If true, return <input, weight> pair of the successor node (used to quantize bias)
+// If flase, return <nullptr, nullptr>
+std::pair<loco::Node *, loco::Node *> get_input_weight_of_bias(CircleNode *node)
+{
+ auto circle_const = dynamic_cast<CircleConst *>(node);
+ if (circle_const == nullptr)
+ return std::make_pair(nullptr, nullptr);
+
+ auto succs = loco::succs(node);
+ if (succs.size() != 1) // assume bias is used by only one node
+ return std::make_pair(nullptr, nullptr);
+
+ for (auto out : succs)
+ {
+ auto conv = dynamic_cast<CircleConv2D *>(out);
+ if (conv != nullptr && conv->bias() == circle_const)
+ {
+ assert(conv->input() != nullptr);
+ assert(conv->filter() != nullptr);
+ return std::make_pair(conv->input(), conv->filter());
+ }
+ auto dw_conv = dynamic_cast<CircleDepthwiseConv2D *>(out);
+ if (dw_conv != nullptr && dw_conv->bias() == circle_const)
+ {
+ assert(dw_conv->input() != nullptr);
+ assert(dw_conv->filter() != nullptr);
+ return std::make_pair(dw_conv->input(), dw_conv->filter());
+ }
+ auto fc = dynamic_cast<CircleFullyConnected *>(out);
+ if (fc != nullptr && fc->bias() == circle_const)
+ {
+ assert(fc->input() != nullptr);
+ assert(fc->weights() != nullptr);
+ return std::make_pair(fc->input(), fc->weights());
+ }
+ auto tconv = dynamic_cast<CircleTransposeConv *>(out);
+ if (tconv != nullptr && tconv->bias() == circle_const)
+ {
+ assert(tconv->outBackprop() != nullptr);
+ assert(tconv->filter() != nullptr);
+ return std::make_pair(tconv->outBackprop(), tconv->filter());
+ }
+ }
+ return std::make_pair(nullptr, nullptr);
+}
+
+void asym_quant_bias_per_layer(CircleConst *node, float input_scale, float weight_scale,
+ float *scaling_factor, int64_t *zp)
+{
+ float scale = input_scale * weight_scale;
+ const float scaling_factor_inv = (scale == 0) ? 0 : 1.0 / scale;
+
+ uint32_t size = node->size<loco::DataType::FLOAT32>();
+ std::vector<int32_t> quantized_values(size);
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ quantized_values[i] =
+ static_cast<int32_t>(std::round(node->at<loco::DataType::FLOAT32>(i) * scaling_factor_inv));
+ }
+
+ node->dtype(loco::DataType::S32); // change the type of tensor
+ node->size<loco::DataType::S32>(size); // resize tensor
+ const int32_t kMinScale = std::numeric_limits<int32_t>::lowest();
+ const int32_t kMaxScale = std::numeric_limits<int32_t>::max();
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ node->at<loco::DataType::S32>(i) =
+ std::min(kMaxScale, std::max(kMinScale, quantized_values[i]));
+ }
+ *scaling_factor = scale;
+ *zp = 0;
+}
+
+void quant_bias_per_channel(CircleConst *node, float input_scale, std::vector<float> &weight_scale,
+ std::vector<float> &scaling_factor, std::vector<int64_t> &zp)
+{
+ float scaling_factor_inv{0};
+
+ uint32_t size = node->size<loco::DataType::FLOAT32>();
+ std::vector<int32_t> quantized_values(size);
+
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ scaling_factor[i] = input_scale * weight_scale[i];
+ scaling_factor_inv = (scaling_factor[i] == 0) ? 0 : 1.0 / scaling_factor[i];
+ quantized_values[i] =
+ static_cast<int32_t>(std::round(node->at<loco::DataType::FLOAT32>(i) * scaling_factor_inv));
+ zp[i] = 0;
+ }
+
+ node->dtype(loco::DataType::S32); // change the type of tensor
+ node->size<loco::DataType::S32>(size); // resize tensor
+ const int32_t kMinScale = std::numeric_limits<int32_t>::lowest();
+ const int32_t kMaxScale = std::numeric_limits<int32_t>::max();
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ node->at<loco::DataType::S32>(i) =
+ std::min(kMaxScale, std::max(kMinScale, quantized_values[i]));
+ }
+}
+
+void int16_quant_bias_per_channel(CircleConst *node, float input_scale,
+ std::vector<float> &weight_scale,
+ std::vector<float> &scaling_factor, std::vector<int64_t> &zp)
+{
+ float scaling_factor_inv{0};
+
+ uint32_t size = node->size<loco::DataType::FLOAT32>();
+ std::vector<int64_t> quantized_values(size);
+
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ scaling_factor[i] = input_scale * weight_scale[i];
+ scaling_factor_inv = (scaling_factor[i] == 0) ? 0 : 1.0 / scaling_factor[i];
+ quantized_values[i] =
+ static_cast<int64_t>(std::round(node->at<loco::DataType::FLOAT32>(i) * scaling_factor_inv));
+ zp[i] = 0;
+ }
+
+ node->dtype(loco::DataType::S64); // change the type of tensor
+ node->size<loco::DataType::S64>(size); // resize tensor
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ node->at<loco::DataType::S64>(i) = quantized_values[i];
+ }
+}
+
+bool has_min_max(const CircleNode *node)
+{
+ return node->quantparam() && !node->quantparam()->min.empty() && !node->quantparam()->max.empty();
+}
+
+void sym_wquant_per_channel(CircleConst *node, std::vector<float> &scaling_factor,
+ int32_t &channel_dim_index)
+{
+ assert(node->dtype() == loco::DataType::FLOAT32);
+
+ const int32_t kMaxScale = std::numeric_limits<int16_t>::max();
+ const int32_t kMinScale = -kMaxScale;
+
+ uint32_t size = node->size<loco::DataType::FLOAT32>();
+ std::vector<int32_t> quantized_values(size);
+
+ 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]++)
+ {
+ int channel_idx = indices[channel_dim_index];
+ const float scaling_factor_inv = 1.0 / scaling_factor[channel_idx];
+ auto data = node->at<loco::DataType::FLOAT32>(cal_offset(dimension, indices));
+ quantized_values[cal_offset(dimension, indices)] =
+ static_cast<int32_t>(std::round(data * scaling_factor_inv));
+ }
+ }
+ }
+ }
+
+ node->dtype(loco::DataType::S16); // change the type of tensor
+ node->size<loco::DataType::S16>(size); // resize tensor
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ node->at<loco::DataType::S16>(i) =
+ std::min(kMaxScale, std::max(kMinScale, quantized_values[i]));
+ }
+}
+
+void asym_wquant_per_channel(CircleConst *node, std::vector<float> &min,
+ std::vector<float> &scaling_factor, int32_t &channel_dim_index)
+{
+ assert(node->dtype() == loco::DataType::FLOAT32);
+
+ const int32_t kMinScale = 0;
+ const int32_t kMaxScale = 255;
+
+ uint32_t size = node->size<loco::DataType::FLOAT32>();
+ std::vector<int32_t> quantized_values(size);
+
+ 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]++)
+ {
+ int channel_idx = indices[channel_dim_index];
+ const float scaling_factor_inv = 1.0 / scaling_factor[channel_idx];
+ auto data = node->at<loco::DataType::FLOAT32>(cal_offset(dimension, indices));
+ quantized_values[cal_offset(dimension, indices)] =
+ static_cast<int32_t>(std::round((data - min[channel_idx]) * scaling_factor_inv));
+ }
+ }
+ }
+ }
+
+ node->dtype(loco::DataType::U8); // change the type of tensor
+ node->size<loco::DataType::U8>(size); // resize tensor
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ node->at<loco::DataType::U8>(i) = std::min(kMaxScale, std::max(kMinScale, quantized_values[i]));
+ }
+}
+
+void asym_wquant_per_layer(CircleConst *node, float min, float scaling_factor)
+{
+ const int32_t kMinScale = 0;
+ const int32_t kMaxScale = 255;
+
+ uint32_t size = node->size<loco::DataType::FLOAT32>();
+
+ const float scaling_factor_inv = 1.0 / scaling_factor;
+ std::vector<int32_t> quantized_values(size);
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ auto data = node->at<loco::DataType::FLOAT32>(i);
+ quantized_values[i] = static_cast<int32_t>(std::round((data - min) * scaling_factor_inv));
+ }
+
+ node->dtype(loco::DataType::U8); // change the type of tensor
+ node->size<loco::DataType::U8>(size); // resize tensor
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ node->at<loco::DataType::U8>(i) = std::min(kMaxScale, std::max(kMinScale, quantized_values[i]));
+ }
+}
+
+/**
+ * @brief QuantizeActivation quantizes tensors for activations
+ * @details Quantize using recorded min/max values
+ */
+struct QuantizeActivation final : public luci::CircleNodeMutableVisitor<bool>
+{
+ QuantizeActivation(loco::DataType input, loco::DataType output)
+ : input_type(input), output_type(output)
+ {
+ }
+
+ loco::DataType input_type;
+ loco::DataType output_type;
+
+ // Quantize input tensors of each node
+ bool visit(luci::CircleNode *node)
+ {
+ LOGGER(l);
+ INFO(l) << "QuantizeActivation 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<luci::CircleNode *>(input_node);
+
+ // Check if this is already quantized
+ if (is_quantized(circle_node))
+ continue;
+
+ // Check if this is bias (bias is quantized later)
+ auto iw = get_input_weight_of_bias(circle_node);
+ if (iw.first != nullptr && iw.second != nullptr)
+ continue;
+
+ // Check if this is activation
+ // We assume min/max are recorded only for activations
+ if (has_min_max(circle_node) && !is_weights(circle_node))
+ {
+ // Quantize using recorded min/max
+ auto quantparam = circle_node->quantparam();
+ assert(quantparam->min.size() == 1); // only support layer-wise quant
+ assert(quantparam->max.size() == 1); // only support layer-wise quant
+ auto min = quantparam->min[0];
+ auto max = quantparam->max[0];
+
+ float scaling_factor{0};
+ int64_t zp{0};
+ float nudged_min{0};
+ float nudged_max{0};
+
+ if (output_type == loco::DataType::U8)
+ {
+ compute_asym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max);
+ circle_node->dtype(loco::DataType::U8);
+ }
+ else
+ {
+ compute_sym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max);
+ circle_node->dtype(loco::DataType::S16);
+ }
+
+ circle_node->quantparam()->min.clear();
+ circle_node->quantparam()->max.clear();
+ circle_node->quantparam()->scale.push_back(scaling_factor);
+ circle_node->quantparam()->zerop.push_back(zp);
+ }
+ }
+ return false;
+ }
+};
+
+struct QuantizeBias final : public luci::CircleNodeMutableVisitor<bool>
+{
+ QuantizeBias(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;
+
+ // Quantize bias node
+ bool visit(luci::CircleNode *node)
+ {
+ // Check if this is already quantized
+ if (is_quantized(node))
+ return false;
+
+ // Check if this is bias
+ auto iw = get_input_weight_of_bias(node);
+ if (iw.first == nullptr || iw.second == nullptr)
+ return false;
+
+ auto input = loco::must_cast<luci::CircleNode *>(iw.first);
+ auto weight = loco::must_cast<luci::CircleNode *>(iw.second);
+
+ if (granularity == QuantizationGranularity::ChannelWise)
+ {
+ assert(input->quantparam()->scale.size() == 1); // input scale's layer-wise
+ auto input_scale = input->quantparam()->scale[0];
+
+ assert(weight->quantparam() != nullptr); // weight scale's channel-wise
+ auto weight_scale = weight->quantparam()->scale;
+
+ auto circle_const = loco::must_cast<luci::CircleConst *>(node);
+
+ uint32_t size = circle_const->size<loco::DataType::FLOAT32>();
+ assert(size == weight_scale.size());
+ std::vector<float> scaling_factor(size);
+ std::vector<int64_t> zp(size);
+
+ if (output_type == loco::DataType::U8)
+ {
+ quant_bias_per_channel(circle_const, input_scale, weight_scale, scaling_factor, zp);
+ }
+ else if (output_type == loco::DataType::S16)
+ {
+ int16_quant_bias_per_channel(circle_const, input_scale, weight_scale, scaling_factor, zp);
+ }
+ else
+ {
+ throw std::runtime_error("Unsupported quantization type.");
+ }
+
+ auto quantparam = std::make_unique<CircleQuantParam>();
+ quantparam->scale = scaling_factor;
+ quantparam->zerop = zp;
+ assert(circle_const->quantparam() == nullptr); // bias should not be quantized before
+ circle_const->quantparam(std::move(quantparam));
+ }
+ else
+ {
+ assert(input->quantparam()->scale.size() == 1); // Only support per-layer quant
+ auto input_scale = input->quantparam()->scale[0];
+
+ assert(weight->quantparam()->scale.size() == 1); // Only support per-layer quant
+ auto weight_scale = weight->quantparam()->scale[0];
+
+ auto circle_const = loco::must_cast<luci::CircleConst *>(node);
+ float scaling_factor{0};
+ int64_t zp{0};
+ asym_quant_bias_per_layer(circle_const, input_scale, weight_scale, &scaling_factor, &zp);
+ auto quantparam = std::make_unique<CircleQuantParam>();
+ quantparam->scale.push_back(scaling_factor);
+ quantparam->zerop.push_back(zp);
+ assert(circle_const->quantparam() == nullptr); // bias should not be quantized before
+ circle_const->quantparam(std::move(quantparam));
+ }
+ return false;
+ }
+};
+
+/**
+ * @brief QuantizeWeights quantizes tensors for weights
+ * @details Find min/max values on the fly and then quantize
+ */
+struct QuantizeWeights final : public luci::CircleNodeMutableVisitor<bool>
+{
+ QuantizeWeights(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;
+
+ // Quantize input tensors of each node
+ bool visit(luci::CircleNode *node)
+ {
+ LOGGER(l);
+ INFO(l) << "QuantizeWeights 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<luci::CircleNode *>(input_node);
+
+ // Check if this is already quantized
+ if (is_quantized(circle_node))
+ continue;
+
+ if (is_weights(circle_node))
+ {
+ auto circle_const = loco::must_cast<luci::CircleConst *>(circle_node);
+
+ // Find min/max per channel-wise
+ if (granularity == QuantizationGranularity::ChannelWise)
+ {
+ auto quantparam = circle_node->quantparam();
+ if (quantparam == nullptr)
+ {
+ assert(false && "quantparam is nullptr");
+ return false;
+ }
+
+ auto min = quantparam->min;
+ auto scaling_factor = quantparam->scale;
+ int32_t channel_dim_index = 0;
+
+ if (output_type == loco::DataType::U8)
+ {
+ asym_wquant_per_channel(circle_const, min, scaling_factor, channel_dim_index);
+ }
+ else
+ {
+ sym_wquant_per_channel(circle_const, scaling_factor, channel_dim_index);
+ }
+ quantparam->min.clear();
+ quantparam->max.clear();
+ quantparam->quantized_dimension = channel_dim_index;
+ }
+ // Find min/max per layer-wise
+ else
+ {
+ // Quantize using recorded quantparam
+ auto quantparam = circle_node->quantparam();
+ assert(quantparam != nullptr);
+ assert(quantparam->min.size() == 1); // only support layer-wise quant
+ assert(quantparam->scale.size() == 1); // only support layer-wise quant
+ auto min = quantparam->min[0];
+ auto scaling_factor = quantparam->scale[0];
+ asym_wquant_per_layer(circle_const, min, scaling_factor);
+ quantparam->min.clear();
+ quantparam->max.clear();
+ }
+ }
+ }
+ return false;
+ }
+};
+
+/**
+ * @brief Quantize const input tensors using min/max of const values
+ */
+void quantize_const_inputs(luci::CircleNode *node, loco::DataType output_type)
+{
+ auto opcode = node->opcode();
+ auto arity = node->arity();
+
+ loco::Node *input_node{nullptr};
+ luci::CircleConst *const_node{nullptr};
+
+ switch (opcode)
+ {
+ case luci::CircleOpcode::CONV_2D:
+ case luci::CircleOpcode::DEPTHWISE_CONV_2D:
+ case luci::CircleOpcode::FULLY_CONNECTED:
+ case luci::CircleOpcode::TRANSPOSE_CONV:
+ // Handled in QuantizeWeights and QuantizeBias
+ break;
+
+ case luci::CircleOpcode::CONCATENATION:
+ // Handled in propagate_concat_quantparam
+ break;
+
+ case luci::CircleOpcode::ARG_MAX:
+ case luci::CircleOpcode::ARG_MIN:
+ case luci::CircleOpcode::MEAN:
+ case luci::CircleOpcode::PAD:
+ case luci::CircleOpcode::REDUCE_ANY:
+ case luci::CircleOpcode::REDUCE_PROD:
+ case luci::CircleOpcode::REDUCE_MAX:
+ case luci::CircleOpcode::REDUCE_MIN:
+ case luci::CircleOpcode::RESHAPE:
+ case luci::CircleOpcode::RESIZE_BILINEAR:
+ case luci::CircleOpcode::RESIZE_NEAREST_NEIGHBOR:
+ case luci::CircleOpcode::REVERSE_SEQUENCE:
+ case luci::CircleOpcode::SUM:
+ case luci::CircleOpcode::TILE:
+ case luci::CircleOpcode::TOPK_V2:
+ case luci::CircleOpcode::TRANSPOSE:
+ // The second input of these Ops should not be quantized
+ // Ex: axis, paddings
+ input_node = node->arg(0);
+ const_node = dynamic_cast<luci::CircleConst *>(input_node);
+ if (const_node != nullptr)
+ quant_const(const_node, output_type);
+ break;
+
+ case luci::CircleOpcode::ADD:
+ case luci::CircleOpcode::ADD_N:
+ case luci::CircleOpcode::DIV:
+ case luci::CircleOpcode::EQUAL:
+ case luci::CircleOpcode::GREATER:
+ case luci::CircleOpcode::GREATER_EQUAL:
+ case luci::CircleOpcode::INSTANCE_NORM:
+ case luci::CircleOpcode::LESS:
+ case luci::CircleOpcode::LESS_EQUAL:
+ case luci::CircleOpcode::MAXIMUM:
+ case luci::CircleOpcode::MINIMUM:
+ case luci::CircleOpcode::MUL:
+ case luci::CircleOpcode::NOT_EQUAL:
+ case luci::CircleOpcode::PRELU:
+ case luci::CircleOpcode::SUB:
+ // Quantize all const inputs using their values
+ for (uint32_t i = 0; i < arity; i++)
+ {
+ input_node = node->arg(i);
+ const_node = dynamic_cast<luci::CircleConst *>(input_node);
+ if (const_node != nullptr)
+ quant_const(const_node, output_type);
+ }
+ break;
+
+ default:
+ for (uint32_t i = 0; i < arity; i++)
+ {
+ input_node = node->arg(i);
+ const_node = dynamic_cast<luci::CircleConst *>(input_node);
+ if (const_node != nullptr)
+ throw std::runtime_error("Unsupported Op for const inputs");
+ }
+ break;
+ }
+}
+
+} // namespace
+
+/** BEFORE
+ *
+ * [CircleNode] [CircleConst]
+ * (U8 qparam1) (FP32)
+ * \ /
+ * \ /
+ * [CircleConcatenation]
+ * (U8 qparam2)
+ *
+ * AFTER
+ * [CircleNode] [CircleConst]
+ * (U8 qparam2) (U8 qparam2)
+ * \ /
+ * \ /
+ * [CircleConcatenation]
+ * (U8 qparam2)
+ */
+void propagate_concat_quantparam(luci::CircleConcatenation *concat, loco::DataType quant_type)
+{
+ assert(concat->quantparam() != nullptr);
+
+ const auto num_inputs = concat->numValues();
+
+ // Quantize const inputs using their values if concat has fused act function
+ if (concat->fusedActivationFunction() != luci::FusedActFunc::NONE)
+ {
+ for (uint32_t i = 0; i < num_inputs; i++)
+ {
+ auto node = concat->arg(i);
+ auto const_node = dynamic_cast<luci::CircleConst *>(node);
+ if (const_node != nullptr)
+ quant_const(const_node, quant_type);
+ }
+ return;
+ }
+
+ for (uint32_t i = 0; i < num_inputs; i++)
+ {
+ auto node = loco::must_cast<luci::CircleNode *>(concat->arg(i));
+
+ // Skip if this input is CONCAT Op
+ if (node->opcode() == luci::CircleOpcode::CONCATENATION)
+ continue;
+
+ // Skip if this input is used by other Ops
+ auto succs = loco::succs(node);
+ if (succs.size() != 1)
+ {
+ if (node->opcode() == luci::CircleOpcode::CIRCLECONST)
+ {
+ luci::CircleConst *const_node = loco::must_cast<luci::CircleConst *>(node);
+ quant_const(const_node, quant_type);
+ }
+ continue;
+ }
+
+ assert(succs.find(concat) != succs.end());
+
+ // Quantize constant values
+ if (node->opcode() == luci::CircleOpcode::CIRCLECONST)
+ {
+ luci::CircleConst *const_node = loco::must_cast<luci::CircleConst *>(node);
+ if (const_node->dtype() != loco::DataType::FLOAT32)
+ throw std::runtime_error("Unsupported data type for constant input of concatenation Op");
+
+ const auto concat_qparam = concat->quantparam();
+ if (concat_qparam == nullptr)
+ throw std::runtime_error("quantparam of concat is not found during propagation");
+
+ assert(concat_qparam->scale.size() == 1);
+ const auto scaling_factor = concat_qparam->scale[0];
+ const auto zerop = concat_qparam->zerop[0];
+
+ quant_const_values(const_node, scaling_factor, zerop, quant_type);
+ }
+ else
+ {
+ // Non-const input must have been quantized
+ assert(node->quantparam() != nullptr);
+ }
+
+ overwrite_quantparam(concat, node);
+ }
+}
+
+bool QuantizeWithMinMaxPass::run(loco::Graph *g)
+{
+ LOGGER(l);
+ INFO(l) << "QuantizeWithMinMaxPass Start" << std::endl;
+
+ // Quantize activation
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ QuantizeActivation qa(_input_dtype, _output_dtype);
+ auto circle_node = loco::must_cast<luci::CircleNode *>(node);
+ circle_node->accept(&qa);
+ }
+
+ // Quantize weights
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ QuantizeWeights qw(_input_dtype, _output_dtype, _granularity);
+ auto circle_node = loco::must_cast<luci::CircleNode *>(node);
+ circle_node->accept(&qw);
+ }
+
+ // Quantize bias
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ QuantizeBias qb(_input_dtype, _output_dtype, _granularity);
+ auto circle_node = loco::must_cast<luci::CircleNode *>(node);
+ circle_node->accept(&qb);
+ }
+
+ // Quantize const inputs other than weights and bias
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ auto circle_node = loco::must_cast<luci::CircleNode *>(node);
+ quantize_const_inputs(circle_node, _output_dtype);
+ }
+
+ // Propagate quantization parameters of concat Op
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ auto concat = dynamic_cast<luci::CircleConcatenation *>(node);
+ if (not concat)
+ continue;
+
+ // Propagate qparam of concat to its inputs if
+ // (1) concat is uint8-quantized
+ // (2) concat has no fused activation function
+ // (3) the input is not concatenation Op
+ // (4) the input is not produced to Ops other than concat
+ propagate_concat_quantparam(concat, _output_dtype);
+ }
+
+ // Update output dtype
+ auto graph_outputs = g->outputs();
+ for (auto node : loco::output_nodes(g))
+ {
+ auto circle_node = loco::must_cast<luci::CircleOutput *>(node);
+ if (static_cast<luci::CircleNode *>(circle_node->from())->dtype() == _output_dtype)
+ {
+ circle_node->dtype(_output_dtype);
+ auto graph_output = graph_outputs->at(circle_node->index());
+ graph_output->dtype(_output_dtype);
+ }
+ }
+
+ INFO(l) << "QuantizeWithMinMaxPass End" << std::endl;
+ return false; // one time run
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/RequantizePass.cpp b/compiler/luci/pass/src/RequantizePass.cpp
new file mode 100644
index 000000000..fe84e3bc3
--- /dev/null
+++ b/compiler/luci/pass/src/RequantizePass.cpp
@@ -0,0 +1,243 @@
+/*
+ * 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 "luci/Pass/RequantizePass.h"
+#include "QuantizationUtils.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/CircleNodeVisitor.h>
+#include <luci/Log.h>
+
+#include <oops/UserExn.h>
+
+#include <iostream>
+#include <cmath>
+
+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<CircleConv2D *>(out);
+ if (conv != nullptr && conv->bias() == node)
+ return true;
+
+ auto dw_conv = dynamic_cast<CircleDepthwiseConv2D *>(out);
+ if (dw_conv != nullptr && dw_conv->bias() == node)
+ return true;
+
+ auto fc = dynamic_cast<CircleFullyConnected *>(out);
+ if (fc != nullptr && fc->bias() == node)
+ return true;
+
+ auto tconv = dynamic_cast<CircleTransposeConv *>(out);
+ if (tconv != nullptr && tconv->bias() == node)
+ return true;
+ }
+ return false;
+}
+
+void requant_nonconst_int8_to_uint8(CircleNode *circle_node)
+{
+ assert(circle_node->dtype() == loco::DataType::S8);
+
+ auto quantparam = circle_node->quantparam();
+ assert(quantparam != nullptr);
+ for (size_t i = 0; i < quantparam->zerop.size(); ++i)
+ {
+ quantparam->zerop[i] += 128;
+ }
+ circle_node->dtype(loco::DataType::U8);
+}
+
+// Requantize CircleConst from symmetric int8 to asymmetric uint8
+// Original values: -127 ~ 127
+// After requantization: 1 ~ 255 (zp <- zp + 128)
+void requant_const_int8_to_uint8(CircleConst *node)
+{
+ assert(node->dtype() == loco::DataType::S8);
+
+ uint32_t size = node->size<loco::DataType::S8>();
+ std::vector<int32_t> requantized_values(size);
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ int32_t data = node->at<loco::DataType::S8>(i);
+ requantized_values[i] = data + 128;
+ }
+
+ node->dtype(loco::DataType::U8); // change the type of tensor
+ node->size<loco::DataType::U8>(size);
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ assert(1 <= requantized_values[i] && requantized_values[i] <= 255);
+ node->at<loco::DataType::U8>(i) = requantized_values[i];
+ }
+
+ auto quantparam = node->quantparam();
+ assert(quantparam != nullptr);
+ for (size_t i = 0; i < quantparam->zerop.size(); ++i)
+ {
+ quantparam->zerop[i] += 128;
+ }
+}
+
+/**
+ * @brief RequantizeNonConst requantizes tensors for activations
+ */
+struct RequantizeNonConst final : public luci::CircleNodeMutableVisitor<bool>
+{
+ 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)
+ {
+ 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<luci::CircleNode *>(input_node);
+
+ // Check if this was quantized (only quantized tensors are requantized)
+ if (circle_node->quantparam() == nullptr)
+ continue;
+
+ // Check if this is already requantized
+ if (circle_node->dtype() == _output_type)
+ continue;
+
+ // Check if this is not const (only non-const is requantized in this function)
+ auto circle_const = dynamic_cast<CircleConst *>(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<bool>
+{
+ RequantizeConst(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)
+ {
+ 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<luci::CircleNode *>(input_node);
+
+ // Check if this was quantized (only quantized tensors are requantized)
+ if (circle_node->quantparam() == nullptr)
+ continue;
+
+ // Check if this is already requantized
+ if (circle_node->dtype() == _output_type)
+ continue;
+
+ // Check if this is const (only const is requantized in this function)
+ auto circle_const = dynamic_cast<CircleConst *>(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;
+ }
+};
+
+} // namespace
+
+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)))
+ {
+ RequantizeNonConst rqnc(_input_dtype, _output_dtype);
+ auto circle_node = loco::must_cast<luci::CircleNode *>(node);
+ circle_node->accept(&rqnc);
+ }
+
+ // Requantize const (including weights, constants)
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ RequantizeConst rqc(_input_dtype, _output_dtype);
+ auto circle_node = loco::must_cast<luci::CircleNode *>(node);
+ circle_node->accept(&rqc);
+ }
+
+ // Update output dtype
+ auto graph_outputs = g->outputs();
+ for (auto node : loco::output_nodes(g))
+ {
+ auto circle_node = loco::must_cast<luci::CircleOutput *>(node);
+ if (static_cast<luci::CircleNode *>(circle_node->from())->dtype() == _output_dtype)
+ {
+ circle_node->dtype(_output_dtype);
+ auto graph_output = graph_outputs->at(circle_node->index());
+ graph_output->dtype(_output_dtype);
+ }
+ }
+
+ INFO(l) << "RequantizePass End" << std::endl;
+ return false; // one time run
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/ResolveCustomOpAddPass.cpp b/compiler/luci/pass/src/ResolveCustomOpAddPass.cpp
new file mode 100644
index 000000000..e52d667d7
--- /dev/null
+++ b/compiler/luci/pass/src/ResolveCustomOpAddPass.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Pass/ResolveCustomOpAddPass.h"
+
+#include "flatbuffers/flexbuffers.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/AttrFusedActFunc.h>
+
+namespace
+{
+
+/// @brief Returns the index of BroadcastTo node among cop's inputs.
+// NOTE This function assumes there is only one BroadcastTo node among its inputs.
+int32_t get_broadcastTo_index_among_inputs_of(luci::CircleCustom *cop)
+{
+ for (uint32_t idx = 0; idx < cop->numInputs(); idx++)
+ {
+ auto input = dynamic_cast<const luci::CircleCustomOut *>(cop->inputs(idx));
+ if (input)
+ {
+ auto broadcastTo = loco::must_cast<luci::CircleCustom *>(input->input());
+ if (broadcastTo->custom_code() == "BroadcastTo")
+ return idx;
+ }
+ }
+
+ return -1;
+}
+
+/** BEFORE
+ * [CircleConst]
+ * |
+ * [CircleNode] [BroadcastTo(CircleCustom)]
+ * \ |
+ * \ [CircleCustomOUt]
+ * \ /
+ * [AddV2(CircleCustom)]
+ * AFTER
+ *
+ * [CircleConst] [CircleNode]
+ * \ /
+ * \ /
+ * [CircleAdd]
+ */
+bool resolve_with_BroadcastTo(luci::CircleCustom *addv2)
+{
+ int32_t broadcastTo_idx = get_broadcastTo_index_among_inputs_of(addv2);
+
+ if (broadcastTo_idx == -1)
+ return false;
+
+ auto input = loco::must_cast<const luci::CircleCustomOut *>(addv2->inputs(broadcastTo_idx));
+ auto broadcastTo = loco::must_cast<luci::CircleCustom *>(input->input());
+
+ auto add = addv2->graph()->nodes()->create<luci::CircleAdd>();
+ add->fusedActivationFunction(luci::FusedActFunc::NONE);
+ add->x(addv2->inputs(1 - broadcastTo_idx));
+ add->y(broadcastTo->inputs(0));
+ auto customOut = loco::succs(addv2);
+ assert(customOut.size() == 1);
+ replace(*customOut.begin()).with(add);
+
+ return true;
+}
+
+bool resolve_custom_op(luci::CircleCustom *addv2)
+{
+ const std::string custom_code = addv2->custom_code();
+ const std::vector<uint8_t> custom_options = addv2->custom_options();
+
+ if (custom_code != "AddV2")
+ return false;
+
+ if (resolve_with_BroadcastTo(addv2))
+ return true;
+
+ auto add = addv2->graph()->nodes()->create<luci::CircleAdd>();
+ add->fusedActivationFunction(luci::FusedActFunc::NONE);
+ add->x(addv2->inputs(0));
+ add->y(addv2->inputs(1));
+ auto customOut = loco::succs(addv2);
+ assert(customOut.size() == 1);
+ replace(*customOut.begin()).with(add);
+
+ return true;
+}
+
+} // namespace
+
+namespace luci
+{
+
+bool ResolveCustomOpAddPass::run(loco::Graph *g)
+{
+ bool changed = false;
+
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ auto cop = dynamic_cast<luci::CircleCustom *>(node);
+ if (not cop)
+ continue;
+
+ changed |= resolve_custom_op(cop);
+ }
+
+ return changed;
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/ResolveCustomOpBatchMatMulPass.cpp b/compiler/luci/pass/src/ResolveCustomOpBatchMatMulPass.cpp
new file mode 100644
index 000000000..145e9cb62
--- /dev/null
+++ b/compiler/luci/pass/src/ResolveCustomOpBatchMatMulPass.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Pass/ResolveCustomOpBatchMatMulPass.h"
+
+#include "flatbuffers/flexbuffers.h"
+
+#include <luci/IR/CircleNodes.h>
+
+namespace
+{
+
+bool resolve_custom_op(luci::CircleCustom *cop)
+{
+ const std::string custom_code = cop->custom_code();
+ const std::vector<uint8_t> custom_options = cop->custom_options();
+
+ if (custom_code == "BatchMatMulV2")
+ {
+ auto batch_matmul = cop->graph()->nodes()->create<luci::CircleBatchMatMul>();
+ // input
+ batch_matmul->x(cop->inputs(0));
+ batch_matmul->y(cop->inputs(1));
+ // TODO find much better way of parsing custom_options
+ // adj
+ auto map = flexbuffers::GetRoot(custom_options).AsMap();
+ batch_matmul->adj_x(map["adj_x"].AsBool());
+ batch_matmul->adj_y(map["adj_y"].AsBool());
+
+ replace(cop).with(batch_matmul);
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+namespace luci
+{
+
+bool ResolveCustomOpBatchMatMulPass::run(loco::Graph *g)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ auto cop = dynamic_cast<luci::CircleCustom *>(node);
+ if (not cop)
+ continue;
+
+ changed |= resolve_custom_op(cop);
+ }
+
+ return changed;
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/ResolveCustomOpMatMulPass.cpp b/compiler/luci/pass/src/ResolveCustomOpMatMulPass.cpp
new file mode 100644
index 000000000..547fd22fc
--- /dev/null
+++ b/compiler/luci/pass/src/ResolveCustomOpMatMulPass.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Pass/ResolveCustomOpMatMulPass.h"
+
+#include "flatbuffers/flexbuffers.h"
+#include <loco/IR/DataTypeTraits.h>
+
+#include <luci/IR/CircleNodes.h>
+
+#include <loco.h>
+#include <oops/InternalExn.h>
+#include <loco/Service/ShapeInference.h>
+#include <loco/Service/TypeInference.h>
+
+namespace
+{
+
+template <typename T>
+luci::CircleConst *create_const_node(loco::Graph *g, const loco::DataType dtype,
+ const std::vector<uint32_t> &shape,
+ const std::vector<T> &values)
+{
+ auto node = g->nodes()->create<luci::CircleConst>();
+ node->dtype(dtype);
+ node->rank(shape.size());
+
+ uint32_t size = 1;
+ for (uint32_t i = 0; i < shape.size(); ++i)
+ {
+ node->dim(i) = shape.at(i);
+ size *= shape.at(i);
+ }
+
+#define INIT_VALUES(DT) \
+ { \
+ node->size<DT>(size); \
+ for (uint32_t i = 0; i < values.size(); ++i) \
+ node->at<DT>(i) = values[i]; \
+ }
+
+ switch (dtype)
+ {
+ case loco::DataType::U8:
+ INIT_VALUES(loco::DataType::U8);
+ break;
+ case loco::DataType::S16:
+ INIT_VALUES(loco::DataType::S16);
+ break;
+ case loco::DataType::S32:
+ INIT_VALUES(loco::DataType::S32);
+ break;
+ case loco::DataType::FLOAT32:
+ INIT_VALUES(loco::DataType::FLOAT32)
+ break;
+ default:
+ INTERNAL_EXN("create_const_node called with unsupported type");
+ break;
+ }
+ return node;
+}
+
+bool resolve_matmul(luci::CircleCustom *cop)
+{
+#define CHECK_OR_FALSE(condition) \
+ if (not(condition)) \
+ return false;
+#define CHECK_OR_THROW(condition, message) \
+ if (not(condition)) \
+ INTERNAL_EXN(message);
+
+ auto graph = cop->graph();
+ const std::vector<uint8_t> custom_options = cop->custom_options();
+ auto map = flexbuffers::GetRoot(custom_options).AsMap();
+ const auto U8 = loco::DataType::U8;
+ const auto S16 = loco::DataType::S16;
+ const auto S32 = loco::DataType::S32;
+ const auto FLOAT32 = loco::DataType::FLOAT32;
+
+ bool transpose_a = map["transpose_a"].AsBool();
+ bool transpose_b = map["transpose_b"].AsBool();
+
+ loco::Node *lhs = cop->inputs(0);
+ loco::Node *rhs = cop->inputs(1);
+
+ // Check that the type of the first input is known
+ CHECK_OR_FALSE(loco::dtype_known(lhs));
+ auto lhs_dtype = loco::dtype_get(cop->inputs(0));
+
+ // If transpose of first input is requested, its shape must be known
+ CHECK_OR_FALSE(!transpose_a || loco::shape_known(lhs));
+ // and its rank should be at least 2
+ CHECK_OR_FALSE(!transpose_a || loco::shape_get(lhs).as<loco::TensorShape>().rank() >= 2);
+ // Check that the shape of the 2nd input is known
+ CHECK_OR_FALSE(loco::shape_known(rhs));
+ // TODO as of 06/23/20 TFLite only supports rank 2 for 2nd input. Fix this once that changes!
+ CHECK_OR_FALSE(loco::shape_get(rhs).as<loco::TensorShape>().rank() == 2);
+ // Check that input data type is supported
+ CHECK_OR_THROW(lhs_dtype == U8 || lhs_dtype == S16 || lhs_dtype == FLOAT32,
+ "Only UInt8, Int16 and Float32 data types are supported by MatMul");
+
+ if (transpose_a)
+ {
+ auto a_shape = loco::shape_get(lhs).as<loco::TensorShape>();
+ // Create a permutation constant node
+ std::vector<uint32_t> perm;
+ for (uint32_t i = 0; i < a_shape.rank(); ++i)
+ perm.push_back(i);
+ std::swap(perm[a_shape.rank() - 1], perm[a_shape.rank() - 2]);
+ auto perm_node = create_const_node(graph, S32, {a_shape.rank()}, perm);
+ // Now make a transpose node
+ auto transpose_node = graph->nodes()->create<luci::CircleTranspose>();
+ transpose_node->a(lhs);
+ transpose_node->perm(perm_node);
+ lhs = transpose_node;
+ }
+
+ // Transpose the second input if needed. TFLite FullyConnected operator
+ // assumes the second input is in column-major order, but the input is
+ // in row-major order, thus we need to convert between them.
+ if (!transpose_b)
+ {
+ const std::vector<uint32_t> perm{1, 0};
+ auto perm_node = create_const_node(graph, S32, {2}, perm);
+ auto transpose_node = graph->nodes()->create<luci::CircleTranspose>();
+ transpose_node->a(rhs);
+ transpose_node->perm(perm_node);
+ rhs = transpose_node;
+ }
+
+ // Make a constant zero-filled bias node
+ auto b_shape = loco::shape_get(cop->inputs(1)).as<loco::TensorShape>();
+ uint32_t bias_size = b_shape.dim(transpose_b ? 1 : 0).value();
+ const std::vector<float> val(bias_size, .0f);
+ auto bias_node = create_const_node(graph, lhs_dtype, {bias_size}, val);
+ auto fc_node = graph->nodes()->create<luci::CircleFullyConnected>();
+ fc_node->input(lhs);
+ fc_node->weights(rhs);
+ fc_node->bias(bias_node);
+ fc_node->fusedActivationFunction(luci::FusedActFunc::NONE);
+
+ replace(cop).with(fc_node);
+ return true;
+}
+
+} // namespace
+
+namespace luci
+{
+
+bool ResolveCustomOpMatMulPass::run(loco::Graph *g)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ auto cop = dynamic_cast<luci::CircleCustom *>(node);
+ if (not cop)
+ continue;
+
+ if (cop->custom_code() != "MatMul")
+ continue;
+
+ if (!resolve_matmul(cop))
+ continue;
+
+ changed = true;
+ }
+
+ return changed;
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/ShapeInferencePass.cpp b/compiler/luci/pass/src/ShapeInferencePass.cpp
new file mode 100644
index 000000000..f681b3d5f
--- /dev/null
+++ b/compiler/luci/pass/src/ShapeInferencePass.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Pass/ShapeInferencePass.h"
+
+#include <luci/IR/CircleDialect.h>
+#include <luci/Service/CircleShapeInferenceRule.h>
+
+#include <loco.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/Service/CanonicalShapeInferenceRule.h>
+#include <loco/Service/ShapeInference.h>
+#include <loco/Service/MultiDialectShapeInferenceRule.h>
+
+namespace luci
+{
+
+bool ShapeInferencePass::run(loco::Graph *g)
+{
+ loco::CanonicalShapeInferenceRule canonical_rule;
+ luci::CircleShapeInferenceRule circle_rule;
+
+ loco::MultiDialectShapeInferenceRule rules;
+
+ rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
+ .bind(luci::CircleDialect::get(), &circle_rule);
+
+ return loco::apply(&rules).to(g);
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/Sparsifier.cpp b/compiler/luci/pass/src/Sparsifier.cpp
new file mode 100644
index 000000000..210c1a34c
--- /dev/null
+++ b/compiler/luci/pass/src/Sparsifier.cpp
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Sparsifier.h"
+
+namespace luci
+{
+
+template <typename T>
+Sparsifier<T>::Sparsifier(const std::vector<int32_t> &shape,
+ const std::vector<int32_t> &traversal_order,
+ const std::vector<DimensionType> &format,
+ const std::vector<int32_t> &block_size,
+ const std::vector<int32_t> &block_map)
+ : _dense_shape(shape), _traversal_order(traversal_order), _block_size(block_size),
+ _block_map(block_map)
+{
+ _dense_size = 1;
+ int32_t block_dim = 0;
+ _blocked_shape.resize(shape.size());
+ _format.resize(shape.size() + block_map.size());
+ for (int32_t i = 0; i < static_cast<int32_t>(shape.size()); i++)
+ {
+ _format.at(i) = format.at(traversal_order.at(i));
+ _dense_size *= shape.at(i);
+ if (block_dim < static_cast<int32_t>(block_map.size()) && block_map[block_dim] == i)
+ {
+ _blocked_shape.at(i) = shape.at(i) / block_size.at(block_dim);
+ block_dim++;
+ }
+ else
+ {
+ _blocked_shape.at(i) = shape.at(i);
+ }
+ }
+
+ // Only dense blocks are supported.
+ for (uint32_t i = 0; i < block_map.size(); i++)
+ {
+ _format[i + shape.size()] = DimensionType::DENSE;
+ }
+}
+
+template <typename T> void Sparsifier<T>::DenseToSparse(const T *src_data)
+{
+ int num_original_dims = _dense_shape.size();
+ int num_block_dims = _block_map.size();
+ int num_expanded_dims = num_original_dims + num_block_dims;
+ std::vector<int> expanded_shape(num_expanded_dims);
+ for (int i = 0; i < num_expanded_dims; i++)
+ {
+ if (i < num_original_dims)
+ {
+ expanded_shape.at(i) = _blocked_shape.at(i);
+ }
+ else
+ {
+ expanded_shape.at(i) = _block_size.at(i - num_original_dims);
+ }
+ }
+
+ std::vector<int> shape_offset(num_original_dims);
+ shape_offset.at(shape_offset.size() - 1) = 1;
+ for (int i = num_original_dims - 1; i > 0; --i)
+ {
+ shape_offset.at(i - 1) = shape_offset.at(i) * _dense_shape.at(i);
+ }
+
+ std::vector<int> expanded_shape_offset(num_expanded_dims);
+ for (int i = 0; i < num_original_dims; ++i)
+ {
+ expanded_shape_offset.at(i) = shape_offset.at(i);
+ }
+ for (int i = 0; i < num_block_dims; ++i)
+ {
+ int mapped_dim = _block_map.at(i);
+ expanded_shape_offset.at(num_original_dims + i) = shape_offset.at(mapped_dim);
+ expanded_shape_offset.at(mapped_dim) *= _block_size.at(i);
+ }
+
+ std::vector<int> dst_ordered_offset(num_expanded_dims);
+ for (int i = 0; i < num_expanded_dims; ++i)
+ {
+ dst_ordered_offset.at(i) = expanded_shape_offset.at(_traversal_order.at(i));
+ }
+
+ std::vector<bool> dst_dim_has_nonzeroes(num_expanded_dims);
+ std::fill(dst_dim_has_nonzeroes.begin(), dst_dim_has_nonzeroes.end(), false);
+ std::vector<int> inner_compressed_dim(num_expanded_dims);
+ int most_recent_compressed_dim = -1;
+ std::vector<int> num_segments_of_next_compressed_dim(num_expanded_dims);
+ int segment_count = 1;
+ for (int i = num_expanded_dims - 1; i >= 0; --i)
+ {
+ inner_compressed_dim.at(i) = most_recent_compressed_dim;
+ if (_format.at(i) == DimensionType::SPARSE_CSR)
+ {
+ most_recent_compressed_dim = i;
+ num_segments_of_next_compressed_dim.at(i) = segment_count;
+ segment_count = 1;
+ }
+ else
+ {
+ num_segments_of_next_compressed_dim.at(i) = -1;
+ segment_count *= expanded_shape.at(_traversal_order.at(i));
+ }
+ }
+
+ _dim_metadata.resize(num_expanded_dims * 2);
+ std::vector<int> dst_sparse_dims;
+ dst_sparse_dims.reserve(num_expanded_dims);
+ for (int i = 0; i < num_expanded_dims; ++i)
+ {
+ _dim_metadata.at(i * 2).clear();
+ _dim_metadata.at(i * 2 + 1).clear();
+ if (_format.at(i) == DimensionType::DENSE)
+ {
+ // If dimension is dense, just store the shape.
+ _dim_metadata.at(i * 2).push_back(expanded_shape.at(_traversal_order.at(i)));
+ }
+ else
+ {
+ _dim_metadata.at(i * 2).push_back(0); // Segment array always begins with 0.
+ dst_sparse_dims.push_back(i); // Add dimension to the sparse list.
+ }
+ }
+
+ // This algorithm assumes that the block size is small enough for all the
+ // elements to fit in cache, so the strided accesses from different traversal
+ // order and the write-first-erase-later strategy shouldn't be too slow
+ int dst_dim_idx = num_expanded_dims;
+ std::vector<int> coordinate(num_expanded_dims, 0);
+ int dense_tensor_idx = 0;
+ while (dst_dim_idx >= 0)
+ {
+ if (dst_dim_idx == num_expanded_dims)
+ {
+ // We have a complete coordinate. Add the element to the value array if it
+ // is not zero, or if the last dimension is dense.
+ if (!IsZero(src_data[dense_tensor_idx]))
+ {
+ _data.push_back(src_data[dense_tensor_idx]);
+ // Mark all sparse dimensions that their current indices have nonzeroes.
+ for (auto dst_dim : dst_sparse_dims)
+ {
+ if (!dst_dim_has_nonzeroes.at(dst_dim))
+ {
+ // Only add the index to the indices array if the current nonzero
+ // is the first nonzero of the block.
+ _dim_metadata.at(2 * dst_dim + 1).push_back(coordinate.at(dst_dim));
+ dst_dim_has_nonzeroes.at(dst_dim) = true;
+ }
+ }
+ }
+ else if (_format.at(num_expanded_dims - 1) == DimensionType::DENSE)
+ {
+ _data.push_back(src_data[dense_tensor_idx]);
+ }
+ --dst_dim_idx;
+ }
+ else
+ {
+ int original_dim_idx = _traversal_order.at(dst_dim_idx);
+ int dim_size = expanded_shape.at(original_dim_idx);
+ if (dst_dim_has_nonzeroes.at(dst_dim_idx))
+ {
+ // If the previous block has nonzeroes, reset the flag to false since
+ // we have just moved to a new block.
+ dst_dim_has_nonzeroes.at(dst_dim_idx) = false;
+ }
+ else if (_format.at(dst_dim_idx) == DimensionType::SPARSE_CSR)
+ {
+ // This block is empty. Delete unnecessary values if compressed.
+ int next_compressed_dim = inner_compressed_dim.at(dst_dim_idx);
+ int erase_offset = _dim_metadata.at(2 * dst_dim_idx + 1).size() *
+ num_segments_of_next_compressed_dim.at(dst_dim_idx);
+ if (next_compressed_dim >= 0)
+ {
+ auto &segments = _dim_metadata.at(2 * inner_compressed_dim.at(dst_dim_idx));
+ segments.erase(segments.begin() + 1 + erase_offset, segments.end());
+ }
+ else
+ {
+ _data.erase(_data.begin() + erase_offset, _data.end());
+ }
+ }
+ if (++coordinate.at(dst_dim_idx) < dim_size)
+ {
+ // The current dst_dim_idx is valid (not out of bound).
+ dense_tensor_idx += dst_ordered_offset.at(dst_dim_idx);
+ ++dst_dim_idx;
+ }
+ else
+ {
+ // dst_dim_idx has reached its dim size. Update segment array and go
+ // back to incrementing the previous dimension (dst_dim_idx - 1).
+ if (_format.at(dst_dim_idx) == DimensionType::SPARSE_CSR)
+ {
+ _dim_metadata.at(2 * dst_dim_idx).push_back(_dim_metadata.at(2 * dst_dim_idx + 1).size());
+ }
+ coordinate.at(dst_dim_idx) = -1;
+ dense_tensor_idx -= dst_ordered_offset.at(dst_dim_idx) * dim_size;
+ --dst_dim_idx;
+ }
+ }
+ }
+}
+
+template <typename T> bool Sparsifier<T>::IsZero(const T val) { return (val == 0); }
+
+template class Sparsifier<int32_t>;
+template class Sparsifier<int8_t>;
+template class Sparsifier<float>;
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/Sparsifier.h b/compiler/luci/pass/src/Sparsifier.h
new file mode 100644
index 000000000..71ea28da9
--- /dev/null
+++ b/compiler/luci/pass/src/Sparsifier.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_SPARSIFIER_H__
+#define __LUCI_SPARSIFIER_H__
+
+#include <vector>
+
+#include <luci/IR/SparsityParam.h>
+
+namespace luci
+{
+
+template <typename T> class Sparsifier
+{
+public:
+ /*
+ * Creates a dense to sparse converter.
+ * @param shape Shape of the dense tensor.
+ * @param traversal_order In what order to traverse all dimensions,
+ * including block dimensions.
+ * @param format Whether each dimension in converted tensor is
+ * dense or sparse (not in the traversal order).
+ * @param block_size Size of each block dimension.
+ * @param block_map Map from block dimension to original tensor
+ * dimension.
+ */
+ Sparsifier(const std::vector<int> &shape, const std::vector<int> &traversal_order,
+ const std::vector<DimensionType> &format, const std::vector<int> &block_size = {},
+ const std::vector<int> &block_map = {});
+
+ std::vector<T> GetData() { return _data; }
+ std::vector<std::vector<int>> GetDimMetadata() { return _dim_metadata; }
+
+ void DenseToSparse(const T *src_data);
+
+private:
+ // Check if val is equal to zero.
+ bool IsZero(const T val);
+
+ // Shape of the conceptual dense tensor.
+ std::vector<int> _dense_shape;
+ // Shape of the dense tensor with inner blocks reduced. For example, a (4, 4)
+ // tensor with (2, 2) block has blocked_shape (2, 2).
+ std::vector<int> _blocked_shape;
+ // Total number of elements in the dense tensor.
+ uint64_t _dense_size;
+ // Has n(original dimension)+k(block_dimension) elements.
+ std::vector<int> _traversal_order;
+ // Format of each dimension in the traversal order.
+ std::vector<DimensionType> _format;
+ // Size of each block dimension, in the same order as block map.
+ std::vector<int> _block_size;
+ // Map from block dimension to the original tensor dimension.
+ std::vector<int> _block_map;
+ // Metadata of each dimension in the traversal order.
+ // Each dimension needs two vectors. For dense dimensions, the first vector
+ // stores the size of that dimension, and the second vector is empty. For
+ // sparse dimensions, the first vector stores the segments and the second one
+ // stores the indices.
+ std::vector<std::vector<int>> _dim_metadata;
+ // Actual buffer holding data after conversion. Could be sparse buffer or
+ // dense buffer.
+ std::vector<T> _data;
+};
+
+extern template class Sparsifier<int32_t>;
+extern template class Sparsifier<int8_t>;
+extern template class Sparsifier<float>;
+
+} // namespace luci
+
+#endif // __LUCI_SPARSIFIER_H__
diff --git a/compiler/luci/pass/src/Sparsifier.test.cpp b/compiler/luci/pass/src/Sparsifier.test.cpp
new file mode 100644
index 000000000..272e0e934
--- /dev/null
+++ b/compiler/luci/pass/src/Sparsifier.test.cpp
@@ -0,0 +1,195 @@
+/*
+ * 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 "Sparsifier.h"
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+TEST(SparsifierTest, NoBlockDenseDense)
+{
+ const std::vector<int32_t> dense_tensor_data = {1, 2, 3, 4, 5, 6};
+ const std::vector<int32_t> dense_shape = {2, 3};
+ const std::vector<int32_t> traversal_order = {0, 1};
+ const std::vector<luci::DimensionType> format = {luci::DimensionType::DENSE,
+ luci::DimensionType::DENSE};
+ luci::Sparsifier<int32_t> sparsifier(dense_shape, traversal_order, format);
+
+ sparsifier.DenseToSparse(dense_tensor_data.data());
+
+ const auto dim_metadata = sparsifier.GetDimMetadata();
+ const std::vector<int32_t> expected_dm0 = {2};
+ const std::vector<int32_t> expected_dm1 = {3};
+
+ EXPECT_EQ(/*dense_shape[0]=*/expected_dm0, dim_metadata[0]);
+ EXPECT_EQ(/*dense_shape[1]=*/expected_dm1, dim_metadata[2]);
+
+ const auto sparsed_data = sparsifier.GetData();
+ const std::vector<int32_t> expected_data = {1, 2, 3, 4, 5, 6};
+ EXPECT_EQ(expected_data, sparsed_data);
+}
+
+TEST(SparsifierTest, NoBlockDenseCSR)
+{
+ const std::vector<int32_t> dense_tensor_data = {0, 1, 2, 3, 0, 5, 0, 0, 0};
+ const std::vector<int32_t> dense_shape = {3, 3};
+ const std::vector<int32_t> traversal_order = {0, 1};
+ const std::vector<luci::DimensionType> format = {luci::DimensionType::DENSE,
+ luci::DimensionType::SPARSE_CSR};
+ luci::Sparsifier<int32_t> sparsifier(dense_shape, traversal_order, format);
+ sparsifier.DenseToSparse(dense_tensor_data.data());
+
+ const auto dim_metadata = sparsifier.GetDimMetadata();
+ const std::vector<int32_t> expected_dm0 = {3};
+ const std::vector<int32_t> expected_dm1 = {};
+ const std::vector<int32_t> expected_dm2 = {0, 2, 4, 4};
+ const std::vector<int32_t> expected_dm3 = {1, 2, 0, 2};
+
+ EXPECT_EQ(expected_dm0, dim_metadata[0]);
+ EXPECT_EQ(expected_dm1, dim_metadata[1]);
+ EXPECT_EQ(expected_dm2, dim_metadata[2]);
+ EXPECT_EQ(expected_dm3, dim_metadata[3]);
+
+ const auto data = sparsifier.GetData();
+ const std::vector<int32_t> expected_data = {1, 2, 3, 5};
+ EXPECT_EQ(expected_data, data);
+}
+
+TEST(SparsifierTest, BlockDenseDense)
+{
+ const std::vector<float> dense_tensor_data = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8,
+ 9.9, 10.0, 11.1, 12.2, 13.3, 14.4, 15.5, 16.6};
+ const std::vector<int32_t> dense_shape = {4, 4};
+ const std::vector<int32_t> traversal_order = {0, 1, 2, 3};
+ const std::vector<luci::DimensionType> format = {luci::DimensionType::DENSE,
+ luci::DimensionType::DENSE};
+ const std::vector<int32_t> block_size = {2, 2};
+ const std::vector<int32_t> block_map = {0, 1};
+ luci::Sparsifier<float> sparsifier(dense_shape, traversal_order, format, block_size, block_map);
+ sparsifier.DenseToSparse(dense_tensor_data.data());
+
+ const auto dim_metadata = sparsifier.GetDimMetadata();
+ const std::vector<int32_t> expected_dm0 = {2};
+ const std::vector<int32_t> expected_dm1 = {};
+ EXPECT_EQ(expected_dm0, dim_metadata[0]);
+ EXPECT_EQ(expected_dm1, dim_metadata[1]);
+ EXPECT_EQ(expected_dm0, dim_metadata[2]);
+ EXPECT_EQ(expected_dm1, dim_metadata[3]);
+ EXPECT_EQ(expected_dm0, dim_metadata[4]);
+ EXPECT_EQ(expected_dm1, dim_metadata[5]);
+ EXPECT_EQ(expected_dm0, dim_metadata[6]);
+ EXPECT_EQ(expected_dm1, dim_metadata[7]);
+
+ const auto data = sparsifier.GetData();
+ const std::vector<float> expected_data = {1.1, 2.2, 5.5, 6.6, 3.3, 4.4, 7.7, 8.8,
+ 9.9, 10.0, 13.3, 14.4, 11.1, 12.2, 15.5, 16.6};
+ EXPECT_EQ(expected_data, data);
+}
+
+TEST(SparsifierTest, BlockDenseSparse)
+{
+ const std::vector<int32_t> dense_tensor_data = {1, 2, 0, 0, 3, 4, 0, 0, 5, 6, 0, 0, 7, 8, 0, 0};
+ const std::vector<int32_t> dense_shape = {4, 4};
+ const std::vector<int32_t> traversal_order = {0, 1, 2, 3};
+ const std::vector<luci::DimensionType> format = {luci::DimensionType::DENSE,
+ luci::DimensionType::SPARSE_CSR};
+ const std::vector<int32_t> block_size = {2, 2};
+ const std::vector<int32_t> block_map = {0, 1};
+ luci::Sparsifier<int32_t> sparsifier(dense_shape, traversal_order, format, block_size, block_map);
+ sparsifier.DenseToSparse(dense_tensor_data.data());
+
+ const auto dim_metadata = sparsifier.GetDimMetadata();
+ const std::vector<int32_t> expected_dm0 = {2};
+ const std::vector<int32_t> expected_dm1 = {};
+ const std::vector<int32_t> expected_dm2 = {0, 1, 2};
+ const std::vector<int32_t> expected_dm3 = {0, 0};
+ EXPECT_EQ(expected_dm0, dim_metadata[0]);
+ EXPECT_EQ(expected_dm1, dim_metadata[1]);
+ EXPECT_EQ(expected_dm2, dim_metadata[2]);
+ EXPECT_EQ(expected_dm3, dim_metadata[3]);
+ EXPECT_EQ(expected_dm0, dim_metadata[4]);
+ EXPECT_EQ(expected_dm1, dim_metadata[5]);
+ EXPECT_EQ(expected_dm0, dim_metadata[6]);
+ EXPECT_EQ(expected_dm1, dim_metadata[7]);
+
+ const auto data = sparsifier.GetData();
+ const std::vector<int32_t> expected_data = {1, 2, 3, 4, 5, 6, 7, 8};
+ EXPECT_EQ(expected_data, data);
+}
+
+TEST(SparsifierTest, BlockDenseSparse_2)
+{
+ const std::vector<int32_t> dense_tensor_data = {0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11,
+ 12, 0, 20, 13, 0, 21, 14, 0, 22, 15, 0, 23};
+ const std::vector<int32_t> dense_shape = {8, 3};
+ const std::vector<int32_t> traversal_order = {0, 1, 2, 3};
+ const std::vector<luci::DimensionType> format = {luci::DimensionType::DENSE,
+ luci::DimensionType::SPARSE_CSR};
+ const std::vector<int32_t> block_size = {4, 1};
+ const std::vector<int32_t> block_map = {0, 1};
+ luci::Sparsifier<int32_t> sparsifier(dense_shape, traversal_order, format, block_size, block_map);
+ sparsifier.DenseToSparse(dense_tensor_data.data());
+
+ const auto dim_metadata = sparsifier.GetDimMetadata();
+ const std::vector<int32_t> expected_dm0 = {2};
+ const std::vector<int32_t> expected_dm1 = {};
+ const std::vector<int32_t> expected_dm2 = {0, 3, 5};
+ const std::vector<int32_t> expected_dm3 = {0, 1, 2, 0, 2};
+ const std::vector<int32_t> expected_dm4 = {4};
+ const std::vector<int32_t> expected_dm6 = {1};
+ EXPECT_EQ(expected_dm0, dim_metadata[0]);
+ EXPECT_EQ(expected_dm1, dim_metadata[1]);
+ EXPECT_EQ(expected_dm2, dim_metadata[2]);
+ EXPECT_EQ(expected_dm3, dim_metadata[3]);
+ EXPECT_EQ(expected_dm4, dim_metadata[4]);
+ EXPECT_EQ(expected_dm1, dim_metadata[5]);
+ EXPECT_EQ(expected_dm6, dim_metadata[6]);
+ EXPECT_EQ(expected_dm1, dim_metadata[7]);
+
+ const auto data = sparsifier.GetData();
+ const std::vector<int32_t> expected_data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 20, 21, 22, 23};
+ EXPECT_EQ(expected_data, data);
+}
+
+TEST(SparsifierTest, WrongTraversalOrderRank_NEG)
+{
+ const std::vector<int32_t> dense_tensor_data = {0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11,
+ 12, 0, 20, 13, 0, 21, 14, 0, 22, 15, 0, 23};
+ const std::vector<int32_t> dense_shape = {8, 3};
+ const std::vector<int32_t> traversal_order = {0, 1};
+ const std::vector<luci::DimensionType> format = {luci::DimensionType::DENSE,
+ luci::DimensionType::SPARSE_CSR};
+ const std::vector<int32_t> block_size = {4, 1};
+ const std::vector<int32_t> block_map = {0, 1};
+ luci::Sparsifier<int32_t> sparsifier(dense_shape, traversal_order, format, block_size, block_map);
+ EXPECT_THROW(sparsifier.DenseToSparse(dense_tensor_data.data()), std::out_of_range);
+}
+
+TEST(SparsifierTest, WrongFormatRank_NEG)
+{
+ const std::vector<int32_t> dense_tensor_data = {0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11,
+ 12, 0, 20, 13, 0, 21, 14, 0, 22, 15, 0, 23};
+ const std::vector<int32_t> dense_shape = {8, 3};
+ const std::vector<int32_t> traversal_order = {0, 1, 2, 3};
+ const std::vector<luci::DimensionType> format = {luci::DimensionType::SPARSE_CSR};
+ const std::vector<int32_t> block_size = {4, 1};
+ const std::vector<int32_t> block_map = {0, 1};
+ EXPECT_THROW(
+ luci::Sparsifier<int32_t>(dense_shape, traversal_order, format, block_size, block_map),
+ std::out_of_range);
+}
diff --git a/compiler/luci/pass/src/SparsifyTensorPass.cpp b/compiler/luci/pass/src/SparsifyTensorPass.cpp
new file mode 100644
index 000000000..2f1a36e77
--- /dev/null
+++ b/compiler/luci/pass/src/SparsifyTensorPass.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Pass/SparsifyTensorPass.h"
+
+#include "Sparsifier.h"
+
+#include <luci/IR/CircleNodes.h>
+
+namespace luci
+{
+
+template <loco::DataType DT> void SparsifyTensorPass::sparsify_tensor(luci::CircleConst *cop)
+{
+ using PRIMITIVE_DTYPE = typename loco::DataTypeImpl<DT>::Type;
+
+ std::vector<int32_t> dense_tensor_shape(cop->rank());
+ for (uint32_t d = 0; d < cop->rank(); d++)
+ {
+ dense_tensor_shape.at(d) = cop->dim(d).value();
+ }
+
+ Sparsifier<PRIMITIVE_DTYPE> sparsifier(dense_tensor_shape, _traversal_order, _format, _block_size,
+ _block_map);
+ // get dense tensor data
+ uint32_t dense_tensor_data_size = cop->size<DT>();
+ std::vector<PRIMITIVE_DTYPE> dense_tensor_data(dense_tensor_data_size);
+ for (uint32_t i = 0; i < dense_tensor_data_size; i++)
+ {
+ dense_tensor_data.at(i) = cop->at<DT>(i);
+ }
+ // sparsify
+ sparsifier.DenseToSparse(dense_tensor_data.data());
+ // get sparse tensor data
+ std::vector<PRIMITIVE_DTYPE> sparse_tensor_data = sparsifier.GetData();
+ uint32_t sparse_tensor_data_size = sparse_tensor_data.size();
+ cop->size<DT>(sparse_tensor_data_size);
+ for (uint32_t i = 0; i < sparse_tensor_data_size; i++)
+ {
+ cop->at<DT>(i) = sparse_tensor_data.at(i);
+ }
+ // make sparsity parameter
+ auto sparsityparam = std::make_unique<SparsityParam>();
+ sparsityparam->traversal_order = _traversal_order;
+ sparsityparam->block_map = _block_map;
+ // get dimension meta data
+ const auto dim_metadata = sparsifier.GetDimMetadata();
+ for (uint32_t idx = 0; idx < _format.size(); idx++)
+ {
+ if (_format.at(idx) == DimensionType::DENSE)
+ {
+ sparsityparam->dim_metadata.emplace_back(DimensionType::DENSE,
+ dim_metadata.at(idx * 2).at(0));
+ }
+ // TODO Set SparseIndexVectorType according to its data range
+ else if (_format.at(idx) == DimensionType::SPARSE_CSR)
+ {
+ sparsityparam->dim_metadata.emplace_back(
+ DimensionType::SPARSE_CSR, /* dense size */ 0,
+ /* array_segments */ SparseIndexVector{SparseIndexVectorType::U16,
+ dim_metadata.at(idx * 2)},
+ /* array_indices */ SparseIndexVector{SparseIndexVectorType::U16,
+ dim_metadata.at(idx * 2 + 1)});
+ }
+ }
+ for (uint32_t i = 0; i < _block_size.size(); i++)
+ {
+ assert(_block_size.at(i) == dim_metadata.at((_format.size() + i) * 2).at(0));
+ sparsityparam->dim_metadata.emplace_back(DimensionType::DENSE, _block_size.at(i));
+ }
+ cop->sparsityparam(std::move(sparsityparam));
+}
+
+bool SparsifyTensorPass::run(loco::Graph *g)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ auto cop = dynamic_cast<luci::CircleConst *>(node);
+ if (not cop)
+ continue;
+
+ if (cop->name() != _tensor_name)
+ continue;
+
+ switch (cop->dtype())
+ {
+ case loco::DataType::S32:
+ sparsify_tensor<loco::DataType::S32>(cop);
+ break;
+ case loco::DataType::S8:
+ sparsify_tensor<loco::DataType::S8>(cop);
+ break;
+ case loco::DataType::FLOAT32:
+ sparsify_tensor<loco::DataType::FLOAT32>(cop);
+ break;
+ default:
+ throw std::runtime_error("SparsifyTensorPass: Unsupported dtype.");
+ }
+ changed = true;
+ }
+
+ return changed;
+}
+
+template void SparsifyTensorPass::sparsify_tensor<loco::DataType::S32>(luci::CircleConst *cop);
+template void SparsifyTensorPass::sparsify_tensor<loco::DataType::S8>(luci::CircleConst *cop);
+template void SparsifyTensorPass::sparsify_tensor<loco::DataType::FLOAT32>(luci::CircleConst *cop);
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/TypeInferencePass.cpp b/compiler/luci/pass/src/TypeInferencePass.cpp
new file mode 100644
index 000000000..2c7b3a897
--- /dev/null
+++ b/compiler/luci/pass/src/TypeInferencePass.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Pass/TypeInferencePass.h"
+
+#include <luci/IR/CircleDialect.h>
+#include <luci/Service/CircleTypeInferenceRule.h>
+
+#include <loco.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/Service/TypeInference.h>
+
+namespace luci
+{
+
+bool TypeInferencePass::run(loco::Graph *g)
+{
+ loco::CanonicalTypeInferenceRule canonical_rule;
+ luci::CircleTypeInferenceRule circle_rule;
+
+ loco::MultiDialectTypeInferenceRule rules;
+
+ rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
+ .bind(luci::CircleDialect::get(), &circle_rule);
+
+ return loco::apply(&rules).to(g);
+}
+
+} // namespace luci
diff --git a/compiler/luci/requires.cmake b/compiler/luci/requires.cmake
new file mode 100644
index 000000000..e52523d45
--- /dev/null
+++ b/compiler/luci/requires.cmake
@@ -0,0 +1,11 @@
+require("foder")
+require("loco")
+require("locop")
+require("logo")
+require("logo-core")
+require("mio-circle")
+require("oops")
+require("hermes")
+require("hermes-std")
+require("tflchef")
+require("tflite2circle")
diff --git a/compiler/luci/service/CMakeLists.txt b/compiler/luci/service/CMakeLists.txt
new file mode 100644
index 000000000..9f50c9c4f
--- /dev/null
+++ b/compiler/luci/service/CMakeLists.txt
@@ -0,0 +1,25 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(luci_service SHARED ${SOURCES})
+target_include_directories(luci_service PRIVATE src)
+target_include_directories(luci_service PUBLIC include)
+target_link_libraries(luci_service PUBLIC luci_lang)
+target_link_libraries(luci_service PUBLIC mio_circle)
+target_link_libraries(luci_service PUBLIC logo_core)
+target_link_libraries(luci_service PRIVATE luci_log)
+target_link_libraries(luci_service PRIVATE nncc_common)
+target_link_libraries(luci_service PRIVATE oops)
+install(TARGETS luci_service DESTINATION lib)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(luci_service_test ${TESTS})
+target_include_directories(luci_service_test PRIVATE src)
+target_link_libraries(luci_service_test luci_service)
+target_link_libraries(luci_service_test oops)
diff --git a/compiler/luci/service/README.md b/compiler/luci/service/README.md
new file mode 100644
index 000000000..ac3583145
--- /dev/null
+++ b/compiler/luci/service/README.md
@@ -0,0 +1,3 @@
+# luci-service
+
+_luci-service_ provides Circle Dialect Services
diff --git a/compiler/luci/service/include/luci/Service/CircleShapeInference.h b/compiler/luci/service/include/luci/Service/CircleShapeInference.h
new file mode 100644
index 000000000..fb934c2cf
--- /dev/null
+++ b/compiler/luci/service/include/luci/Service/CircleShapeInference.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_CIRCLE_SHAPE_INFERENCE_H__
+#define __LUCI_CIRCLE_SHAPE_INFERENCE_H__
+
+#include "ShapeDescription.h"
+
+#include <loco/IR/Nodes.h>
+
+namespace luci
+{
+
+/**
+ * @brief Get the shape of each node as a node annotation
+ *
+ * HOW TO USE
+ *
+ * ShapeInference::get(g->nodes()->at(..));
+ */
+struct ShapeInference
+{
+ static ShapeDescription get(loco::Node *node);
+};
+
+} // namespace luci
+
+#endif // __LUCI_CIRCLE_SHAPE_INFERENCE_H__
diff --git a/compiler/luci/service/include/luci/Service/CircleShapeInferenceRule.h b/compiler/luci/service/include/luci/Service/CircleShapeInferenceRule.h
new file mode 100644
index 000000000..3f63c9633
--- /dev/null
+++ b/compiler/luci/service/include/luci/Service/CircleShapeInferenceRule.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_CIRCLE_SHAPE_INFERENCE_RULE_H__
+#define __LUCI_CIRCLE_SHAPE_INFERENCE_RULE_H__
+
+#include <loco/Service/ShapeInference.h>
+
+namespace luci
+{
+
+struct CircleShapeInferenceRule final : public loco::ShapeInferenceRule
+{
+ bool recognize(const loco::Dialect *) const final;
+ bool infer(const loco::Node *, loco::NodeShape &) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_CIRCLE_SHAPE_INFERENCE_RULE_H__
diff --git a/compiler/luci/service/include/luci/Service/CircleShapeSignatureInferenceRule.h b/compiler/luci/service/include/luci/Service/CircleShapeSignatureInferenceRule.h
new file mode 100644
index 000000000..4d1d83012
--- /dev/null
+++ b/compiler/luci/service/include/luci/Service/CircleShapeSignatureInferenceRule.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_CIRCLE_SHAPE_SIGNATURE_INFERENCE_RULE_H__
+#define __LUCI_CIRCLE_SHAPE_SIGNATURE_INFERENCE_RULE_H__
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/CircleNodeVisitor.h>
+#include <luci/IR/CircleShapeSignature.h>
+
+namespace luci
+{
+
+struct CircleShapeSignatureInferenceRule
+{
+ bool infer(const luci::CircleNode *, ShapeSignature &) const;
+};
+
+class ShapeSignatureInferenceAlgorithm final : public luci::CircleNodeVisitor<ShapeSignature>
+{
+public:
+ // TODO Remove this when visit function is implemented for all the operations.
+ ShapeSignature visit(const luci::CircleNode *node) final { return node->shape_signature(); }
+
+ // ShapeSignature visit(const luci::CircleAbs *node) final;
+ // ShapeSignature visit(const luci::CircleAdd *node) final;
+ // ShapeSignature visit(const luci::CircleAddN *node) final;
+ // ShapeSignature visit(const luci::CircleArgMax *node) final;
+ // ShapeSignature visit(const luci::CircleArgMin *node) final;
+ // ShapeSignature visit(const luci::CircleAveragePool2D *node) final;
+ // ShapeSignature visit(const luci::CircleBatchMatMul *node) final;
+ // ShapeSignature visit(const luci::CircleBatchToSpaceND *node) final;
+ // ShapeSignature visit(const luci::CircleCast *node) final;
+ // ShapeSignature visit(const luci::CircleCeil *node) final;
+ // ShapeSignature visit(const luci::CircleConcatenation *node) final;
+ // ShapeSignature visit(const luci::CircleConst *node) final;
+ // ShapeSignature visit(const luci::CircleConv2D *node) final;
+ // ShapeSignature visit(const luci::CircleCos *node) final;
+ // ShapeSignature visit(const luci::CircleCustom *node) final;
+ // ShapeSignature visit(const luci::CircleDepthToSpace *node) final;
+ // ShapeSignature visit(const luci::CircleDepthwiseConv2D *node) final;
+ // ShapeSignature visit(const luci::CircleDequantize *node) final;
+ // ShapeSignature visit(const luci::CircleDiv *node) final;
+ // ShapeSignature visit(const luci::CircleElu *node) final;
+ // ShapeSignature visit(const luci::CircleEqual *node) final;
+ // ShapeSignature visit(const luci::CircleExp *node) final;
+ // ShapeSignature visit(const luci::CircleExpandDims *node) final;
+ // ShapeSignature visit(const luci::CircleFill *node) final;
+ // ShapeSignature visit(const luci::CircleFloor *node) final;
+ // ShapeSignature visit(const luci::CircleFloorDiv *node) final;
+ // ShapeSignature visit(const luci::CircleFloorMod *node) final;
+ // ShapeSignature visit(const luci::CircleFullyConnected *node) final;
+ // ShapeSignature visit(const luci::CircleGather *node) final;
+ // ShapeSignature visit(const luci::CircleGatherNd *node) final;
+ // ShapeSignature visit(const luci::CircleGreater *node) final;
+ // ShapeSignature visit(const luci::CircleGreaterEqual *node) final;
+ // ShapeSignature visit(const luci::CircleIf *node) final;
+ // ShapeSignature visit(const luci::CircleL2Normalize *node) final;
+ // ShapeSignature visit(const luci::CircleL2Pool2D *node) final;
+ // ShapeSignature visit(const luci::CircleLeakyRelu *node) final;
+ // ShapeSignature visit(const luci::CircleLess *node) final;
+ // ShapeSignature visit(const luci::CircleLessEqual *node) final;
+ // ShapeSignature visit(const luci::CircleLocalResponseNormalization *node) final;
+ // ShapeSignature visit(const luci::CircleLog *node) final;
+ // ShapeSignature visit(const luci::CircleLogicalAnd *node) final;
+ // ShapeSignature visit(const luci::CircleLogicalNot *node) final;
+ // ShapeSignature visit(const luci::CircleLogicalOr *node) final;
+ // ShapeSignature visit(const luci::CircleLogistic *node) final;
+ // ShapeSignature visit(const luci::CircleLogSoftmax *node) final;
+ // ShapeSignature visit(const luci::CircleMatrixDiag *node) final;
+ // ShapeSignature visit(const luci::CircleMatrixSetDiag *node) final;
+ // ShapeSignature visit(const luci::CircleMaximum *node) final;
+ // ShapeSignature visit(const luci::CircleMaxPool2D *node) final;
+ // ShapeSignature visit(const luci::CircleMean *node) final;
+ // ShapeSignature visit(const luci::CircleMinimum *node) final;
+ // ShapeSignature visit(const luci::CircleMirrorPad *node) final;
+ // ShapeSignature visit(const luci::CircleNeg *node) final;
+ // ShapeSignature visit(const luci::CircleNonMaxSuppressionV4 *node) final;
+ // ShapeSignature visit(const luci::CircleNonMaxSuppressionV5 *node) final;
+ // ShapeSignature visit(const luci::CircleNotEqual *node) final;
+ // ShapeSignature visit(const luci::CirclePack *node) final;
+ // ShapeSignature visit(const luci::CirclePad *node) final;
+ // ShapeSignature visit(const luci::CirclePadV2 *node) final;
+ // ShapeSignature visit(const luci::CirclePow *node) final;
+ // ShapeSignature visit(const luci::CirclePRelu *node) final;
+ // ShapeSignature visit(const luci::CircleRange *node) final;
+ // ShapeSignature visit(const luci::CircleRank *node) final;
+ // ShapeSignature visit(const luci::CircleMul *node) final;
+ // ShapeSignature visit(const luci::CircleOneHot *node) final;
+ // ShapeSignature visit(const luci::CircleReduceAny *node) final;
+ // ShapeSignature visit(const luci::CircleReduceMax *node) final;
+ // ShapeSignature visit(const luci::CircleReduceMin *node) final;
+ // ShapeSignature visit(const luci::CircleReduceProd *node) final;
+ // ShapeSignature visit(const luci::CircleRelu *node) final;
+ // ShapeSignature visit(const luci::CircleRelu6 *node) final;
+ // ShapeSignature visit(const luci::CircleReluN1To1 *node) final;
+ // ShapeSignature visit(const luci::CircleReshape *node) final;
+ // ShapeSignature visit(const luci::CircleResizeBilinear *node) final;
+ // ShapeSignature visit(const luci::CircleResizeNearestNeighbor *node) final;
+ // ShapeSignature visit(const luci::CircleReverseSequence *node) final;
+ // ShapeSignature visit(const luci::CircleReverseV2 *node) final;
+ // ShapeSignature visit(const luci::CircleRound *node) final;
+ // ShapeSignature visit(const luci::CircleRsqrt *node) final;
+ // ShapeSignature visit(const luci::CircleScatterNd *node) final;
+ // ShapeSignature visit(const luci::CircleSegmentSum *node) final;
+ // ShapeSignature visit(const luci::CircleSelect *node) final;
+ // ShapeSignature visit(const luci::CircleSelectV2 *node) final;
+ // ShapeSignature visit(const luci::CircleShape *node) final;
+ // ShapeSignature visit(const luci::CircleSin *node) final;
+ // ShapeSignature visit(const luci::CircleSlice *node) final;
+ // ShapeSignature visit(const luci::CircleSoftmax *node) final;
+ // ShapeSignature visit(const luci::CircleSpaceToBatchND *node) final;
+ // ShapeSignature visit(const luci::CircleSpaceToDepth *node) final;
+ // ShapeSignature visit(const luci::CircleSparseToDense *node) final;
+ // ShapeSignature visit(const luci::CircleSplit *node) final;
+ // ShapeSignature visit(const luci::CircleSplitV *node) final;
+ // ShapeSignature visit(const luci::CircleSqrt *node) final;
+ // ShapeSignature visit(const luci::CircleSquare *node) final;
+ // ShapeSignature visit(const luci::CircleSquaredDifference *node) final;
+ // ShapeSignature visit(const luci::CircleSqueeze *node) final;
+ // ShapeSignature visit(const luci::CircleStridedSlice *node) final;
+ // ShapeSignature visit(const luci::CircleSub *node) final;
+ // ShapeSignature visit(const luci::CircleSum *node) final;
+ // ShapeSignature visit(const luci::CircleTanh *node) final;
+ // ShapeSignature visit(const luci::CircleTile *node) final;
+ // ShapeSignature visit(const luci::CircleTopKV2 *node) final;
+ // ShapeSignature visit(const luci::CircleTranspose *node) final;
+ // ShapeSignature visit(const luci::CircleTransposeConv *node) final;
+ // ShapeSignature visit(const luci::CircleUnidirectionalSequenceLSTM *node) final;
+ // ShapeSignature visit(const luci::CircleUnique *node) final;
+ // ShapeSignature visit(const luci::CircleUnpack *node) final;
+ // ShapeSignature visit(const luci::CircleWhere *node) final ;
+ // ShapeSignature visit(const luci::CircleWhile *node) final;
+ // ShapeSignature visit(const luci::CircleZerosLike *node) final;
+
+ // Circle Only
+ // ShapeSignature visit(const luci::CircleBCQFullyConnected *node) final;
+ // ShapeSignature visit(const luci::CircleBCQGather *node) final;
+ // ShapeSignature visit(const luci::CircleInstanceNorm *node) final;
+
+ // Virtual
+ // ShapeSignature visit(const luci::CircleInput *node) final;
+ // ShapeSignature visit(const luci::CircleOutput *node) final;
+ // ShapeSignature visit(const luci::CircleOutputDummy *node) final;
+ // ShapeSignature visit(const luci::CircleOutputExclude *node) final;
+ // ShapeSignature visit(const luci::CircleCustomOut *node) final;
+ // ShapeSignature visit(const luci::CircleIfOut *node) final;
+ // ShapeSignature visit(const luci::CircleNonMaxSuppressionV4Out *node) final;
+ // ShapeSignature visit(const luci::CircleNonMaxSuppressionV5Out *node) final;
+ // ShapeSignature visit(const luci::CircleSplitOut *node) final;
+ // ShapeSignature visit(const luci::CircleSplitVOut *node) final;
+ // ShapeSignature visit(const luci::CircleTopKV2Out *node) final;
+ // ShapeSignature visit(const luci::CircleUniqueOut *node) final;
+ // ShapeSignature visit(const luci::CircleUnpackOut *node) final;
+ // ShapeSignature visit(const luci::CircleWhileOut *node) final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_CIRCLE_SHAPE_SIGNATURE_INFERENCE_RULE_H__
diff --git a/compiler/luci/service/include/luci/Service/CircleTypeInference.h b/compiler/luci/service/include/luci/Service/CircleTypeInference.h
new file mode 100644
index 000000000..ea7a3c5ed
--- /dev/null
+++ b/compiler/luci/service/include/luci/Service/CircleTypeInference.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_CIRCLE_TYPE_INFERENCE_H__
+#define __LUCI_CIRCLE_TYPE_INFERENCE_H__
+
+#include <loco/IR/Nodes.h>
+
+#include <mio/circle/schema_generated.h>
+
+namespace luci
+{
+
+/**
+ * @brief Get the type of each node as NodeAnnotation
+ *
+ * HOW TO USE
+ *
+ * TypeInference::get(g->nodes()->at(0));
+ * TypeInference::get(g->nodes()->at(...));
+ */
+struct TypeInference
+{
+ static circle::TensorType get(loco::Node *node);
+};
+
+} // namespace luci
+
+#endif // __LUCI_CIRCLE_TYPE_INFERENCE_H__
diff --git a/compiler/luci/service/include/luci/Service/CircleTypeInferenceRule.h b/compiler/luci/service/include/luci/Service/CircleTypeInferenceRule.h
new file mode 100644
index 000000000..3b21081ef
--- /dev/null
+++ b/compiler/luci/service/include/luci/Service/CircleTypeInferenceRule.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_CIRCLE_TYPE_INFERENCE_RULE_H__
+#define __LUCI_CIRCLE_TYPE_INFERENCE_RULE_H__
+
+#include <loco/Service/TypeInference.h>
+
+namespace luci
+{
+
+/**
+ * @brief Type Inference Rule for CircleDialect
+ */
+struct CircleTypeInferenceRule final : public loco::TypeInferenceRule
+{
+ bool recognize(const loco::Dialect *) const final;
+ bool infer(const loco::Node *, loco::DataType &) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_CIRCLE_TYPE_INFERENCE_RULE_H__
diff --git a/compiler/luci/service/include/luci/Service/ShapeDescription.h b/compiler/luci/service/include/luci/Service/ShapeDescription.h
new file mode 100644
index 000000000..949cce535
--- /dev/null
+++ b/compiler/luci/service/include/luci/Service/ShapeDescription.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_SHAPE_DESCRIPTION_H__
+#define __LUCI_SHAPE_DESCRIPTION_H__
+
+#include <loco/IR/PermutingCodec.h>
+#include <loco/IR/NodeShape.h>
+
+#include <cstdint>
+#include <vector>
+
+namespace luci
+{
+
+struct ShapeDescription
+{
+ std::vector<int32_t> _dims;
+ bool _rank_known;
+};
+
+// TODO remove these when CircleDialect is fully functioal
+ShapeDescription to_shape_description(const loco::TensorShape &shape);
+ShapeDescription to_shape_description(const loco::FeatureShape &shape);
+ShapeDescription to_shape_description(const loco::FilterShape &shape);
+ShapeDescription to_shape_description(const loco::BiasShape &shape);
+ShapeDescription to_shape_description(const loco::MatrixShape &shape);
+ShapeDescription to_shape_description(const loco::NodeShape &shape);
+
+template <typename Permutation> inline bool isNHWC(Permutation *perm);
+
+template <> inline bool isNHWC(loco::Permutation<loco::Domain::Feature> *perm)
+{
+ return perm->axis(loco::FeatureAxis::Count) == 0 && perm->axis(loco::FeatureAxis::Height) == 1 &&
+ perm->axis(loco::FeatureAxis::Width) == 2 && perm->axis(loco::FeatureAxis::Depth) == 3;
+}
+
+template <> inline bool isNHWC(loco::Permutation<loco::Domain::Filter> *perm)
+{
+ return perm->axis(loco::FilterAxis::Count) == 0 && perm->axis(loco::FilterAxis::Height) == 1 &&
+ perm->axis(loco::FilterAxis::Width) == 2 && perm->axis(loco::FilterAxis::Depth) == 3;
+}
+
+} // namespace luci
+
+#endif // __LUCI_SHAPE_DESCRIPTION_H__
diff --git a/compiler/luci/service/include/luci/Service/Validate.h b/compiler/luci/service/include/luci/Service/Validate.h
new file mode 100644
index 000000000..4b80d1d16
--- /dev/null
+++ b/compiler/luci/service/include/luci/Service/Validate.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LUCI_SERVICE_VALIDATE_H__
+#define __LUCI_SERVICE_VALIDATE_H__
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool validate(loco::Graph *);
+
+} // namespace luci
+
+#endif // __LUCI_SERVICE_VALIDATE_H__
diff --git a/compiler/luci/service/src/Check.h b/compiler/luci/service/src/Check.h
new file mode 100644
index 000000000..e05ec904a
--- /dev/null
+++ b/compiler/luci/service/src/Check.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CHECK_H__
+#define __CHECK_H__
+
+#include <stdexcept>
+#include <cassert>
+#include <iostream>
+
+// TODO Add macro for Release version
+
+#define LUCI_ASSERT(condition, msg) \
+ { \
+ if (!(condition)) \
+ { \
+ std::cerr << "[assert failed] " << (msg) << ". " << std::endl; \
+ assert((condition)); \
+ } \
+ }
+
+#endif // __CHECK_H__
diff --git a/compiler/luci/service/src/CircleShapeInference.cpp b/compiler/luci/service/src/CircleShapeInference.cpp
new file mode 100644
index 000000000..0732849db
--- /dev/null
+++ b/compiler/luci/service/src/CircleShapeInference.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Service/CircleShapeInference.h"
+#include "luci/Service/ShapeDescription.h"
+
+#include <loco.h>
+#include <loco/Service/ShapeInference.h>
+
+#include <cassert>
+
+namespace luci
+{
+
+ShapeDescription ShapeInference::get(loco::Node *node)
+{
+ assert(loco::shape_known(node));
+ return to_shape_description(loco::shape_get(node));
+}
+
+} // namespace luci
diff --git a/compiler/luci/service/src/CircleShapeInferenceRule.cpp b/compiler/luci/service/src/CircleShapeInferenceRule.cpp
new file mode 100644
index 000000000..a55f50b19
--- /dev/null
+++ b/compiler/luci/service/src/CircleShapeInferenceRule.cpp
@@ -0,0 +1,2516 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Service/CircleShapeInferenceRule.h"
+#include "Check.h"
+
+#include "ShapeInfer_StridedSlice.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/CircleDialect.h>
+#include <luci/IR/CircleNodeVisitor.h>
+#include <luci/Log.h>
+
+#include <oops/InternalExn.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <stdexcept>
+
+namespace
+{
+
+std::ostream &operator<<(std::ostream &os, const loco::TensorShape &tensor_shape)
+{
+ os << "[";
+ for (uint32_t r = 0; r < tensor_shape.rank(); ++r)
+ {
+ if (r)
+ os << ",";
+ os << tensor_shape.dim(r).value();
+ }
+ os << "]";
+ return os;
+}
+
+loco::TensorShape own_shape(const luci::CircleNode *node)
+{
+ loco::TensorShape shape;
+ shape.rank(node->rank());
+ for (uint32_t r = 0; r < node->rank(); ++r)
+ shape.dim(r) = loco::Dimension(node->dim(r).value());
+ return shape;
+}
+
+loco::NodeShape use_own(const luci::CircleNode *node)
+{
+ loco::TensorShape shape = own_shape(node);
+ return loco::NodeShape{shape};
+}
+
+/**
+ * @brief Create a higher-rank TensorShape following NumPy broadcasting semantics
+ *
+ * HOW TO USE:
+ *
+ * auto expanded_tensor_shape = expand(tensor_shape).to(N);
+ */
+class TensorShapeExpander
+{
+public:
+ TensorShapeExpander(const loco::TensorShape &shape) : _shape{shape}
+ {
+ // DO NOTHING
+ }
+
+public:
+ loco::TensorShape to(uint32_t output_rank)
+ {
+ auto const &input_shape = _shape;
+ uint32_t const input_rank = input_shape.rank();
+
+ assert(input_rank <= output_rank && "Cannot shrink rank");
+ uint32_t const axis_shift = output_rank - input_rank;
+
+ loco::TensorShape output_shape;
+
+ output_shape.rank(output_rank);
+ for (uint32_t axis = 0; axis < output_rank; ++axis)
+ {
+ output_shape.dim(axis) = (axis < axis_shift) ? 1 : input_shape.dim(axis - axis_shift);
+ }
+
+ return output_shape;
+ }
+
+private:
+ const loco::TensorShape _shape;
+};
+
+/**
+ * @breif Expand shape x and y to same rank by align right and filling with 1
+ */
+void expand_rank(loco::TensorShape &x, loco::TensorShape &y)
+{
+ auto x_rank = x.rank();
+ auto y_rank = y.rank();
+
+ if (x_rank == y_rank)
+ return;
+
+ TensorShapeExpander x_exp(x);
+ TensorShapeExpander y_exp(y);
+
+ auto xy_rank = std::max(x_rank, y_rank);
+
+ x = x_rank > y_rank ? x : x_exp.to(xy_rank);
+ y = y_rank > x_rank ? y : y_exp.to(xy_rank);
+}
+
+/**
+ * @breif Returns shape of expanded dimension of input x and y having same rank
+ */
+loco::TensorShape expand_dimension(const loco::TensorShape &x, const loco::TensorShape &y)
+{
+ assert(x.rank() == y.rank());
+
+ auto rank = x.rank();
+
+ loco::TensorShape output_shape;
+
+ output_shape.rank(rank);
+ for (uint32_t axis = 0; axis < rank; ++axis)
+ {
+ assert(x.dim(axis).known() && y.dim(axis).known());
+
+ auto x_dim = x.dim(axis).value();
+ auto y_dim = y.dim(axis).value();
+
+ // each dimension of x and y should be same or one must be 1 if different
+ if (!((x_dim == y_dim) || (x_dim == 1 || y_dim == 1)))
+ INTERNAL_EXN("Cannot produce expand_dimension of two shapes");
+
+ output_shape.dim(axis) = std::max(x_dim, y_dim);
+ }
+
+ return output_shape;
+}
+
+loco::TensorShape broadcast_shape(const loco::TensorShape &x, const loco::TensorShape &y)
+{
+ auto x_match = x;
+ auto y_match = y;
+
+ expand_rank(x_match, y_match);
+
+ auto output_shape = expand_dimension(x_match, y_match);
+
+ return output_shape;
+}
+
+/**
+ * @brief vector_from_constant will return int64_t vector from CircleConst node
+ */
+template <loco::DataType T> std::vector<int64_t> vector_from_constant(luci::CircleConst *const_node)
+{
+ std::vector<int64_t> result;
+
+ for (uint32_t idx = 0; idx < const_node->size<T>(); ++idx)
+ result.push_back(const_node->at<T>(idx));
+
+ return result;
+}
+
+template <class CIRCLENODE> loco::NodeShape broadcast_xy(const CIRCLENODE *node)
+{
+ auto x_shape = loco::shape_get(node->x()).template as<loco::TensorShape>();
+ auto y_shape = loco::shape_get(node->y()).template as<loco::TensorShape>();
+
+ auto output_shape = broadcast_shape(x_shape, y_shape);
+
+ return loco::NodeShape{output_shape};
+}
+
+template <class CIRCLENODE> loco::NodeShape use_x(const CIRCLENODE *node)
+{
+ auto x_shape = loco::shape_get(node->x()).template as<loco::TensorShape>();
+ return loco::NodeShape{x_shape};
+}
+
+template <class CIRCLENODE> loco::NodeShape use_logits(const CIRCLENODE *node)
+{
+ auto shape = loco::shape_get(node->logits()).template as<loco::TensorShape>();
+ return loco::NodeShape{shape};
+}
+
+template <class CIRCLENODE>
+loco::NodeShape use_paddings(const CIRCLENODE *node, const luci::CircleConst *paddings)
+{
+ const loco::DataType S32 = loco::DataType::S32;
+
+ auto input_shape = loco::shape_get(node->input()).template as<loco::TensorShape>();
+
+ // TODO support other data type
+ LUCI_ASSERT(paddings->dtype() == S32, "Only support int 32 for now");
+ LUCI_ASSERT(paddings->rank() == 2, "paddings should be rank 2")
+
+ int32_t n = paddings->dim(0).value();
+ int32_t v = paddings->dim(1).value();
+
+ LUCI_ASSERT(v == 2, "paddings should be [n, 2]");
+ LUCI_ASSERT(n == int32_t(input_shape.rank()),
+ "paddings [n, 2] should have same value of input rank");
+
+ loco::TensorShape output_shape;
+
+ output_shape.rank(input_shape.rank());
+ for (int32_t ni = 0; ni < n; ++ni)
+ {
+ int32_t idx = ni * 2;
+ int value = input_shape.dim(ni).value();
+ value += paddings->at<S32>(idx + 0); // left
+ value += paddings->at<S32>(idx + 1); // right
+ output_shape.dim(ni) = value;
+ }
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_add_n(const luci::CircleAddN *node)
+{
+ auto shape = loco::shape_get(node->inputs(0)).as<loco::TensorShape>();
+
+ for (uint32_t idx = 1; idx < node->arity(); ++idx)
+ {
+ auto shape_idx = loco::shape_get(node->inputs(idx)).as<loco::TensorShape>();
+ if (!(shape == shape_idx))
+ {
+ INTERNAL_EXN_V("ADD_N shape not same as the first input: ", idx);
+ }
+ }
+ return loco::NodeShape{shape};
+}
+
+loco::NodeShape infer_arg_max(const luci::CircleArgMax *node)
+{
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ auto dimension_shape = loco::shape_get(node->dimension()).as<loco::TensorShape>();
+
+ int64_t select_axis = 0;
+ {
+ LUCI_ASSERT(node->dimension(), "2nd input dimension() should not be nullptr");
+
+ // Only support node's shape() is CircleConst with S32/S64
+ // Support S32 for now.
+ auto const_shape_node = loco::must_cast<luci::CircleConst *>(node->dimension());
+ LUCI_ASSERT(const_shape_node->dtype() == loco::DataType::S32,
+ "Only support int32 CircleConst for CircleArgMax");
+
+ if (const_shape_node->rank() > 1)
+ INTERNAL_EXN_V("Only support rank 0/1 CircleConst",
+ oops::to_uint32(const_shape_node->rank()));
+
+ select_axis = const_shape_node->scalar<loco::DataType::S32>();
+ }
+ assert(select_axis < input_shape.rank());
+ assert(select_axis >= 0); // TODO support minus of this breaks
+
+ // NOTE select_axis is removed
+ loco::TensorShape shape_output;
+ uint32_t rank = input_shape.rank();
+ uint32_t shrink = static_cast<uint32_t>(select_axis);
+ assert(rank > 0);
+ shape_output.rank(rank - 1);
+ for (uint32_t r = 0, d = 0; r < rank; ++r)
+ {
+ if (r == shrink)
+ continue;
+ shape_output.dim(d++) = input_shape.dim(r);
+ }
+ return loco::NodeShape{shape_output};
+}
+
+loco::NodeShape infer_arg_min(const luci::CircleArgMin *node)
+{
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ auto dimension_shape = loco::shape_get(node->dimension()).as<loco::TensorShape>();
+
+ int64_t select_axis = 0;
+ {
+ LUCI_ASSERT(node->dimension(), "2nd input dimension() should not be nullptr");
+
+ // Only support node's shape() is CircleConst with S32/S64
+ // Support S32 for now.
+ auto const_shape_node = loco::must_cast<luci::CircleConst *>(node->dimension());
+ LUCI_ASSERT(const_shape_node->dtype() == loco::DataType::S32,
+ "Only support int32 CircleConst for CircleArgMin");
+
+ if (const_shape_node->rank() > 1)
+ INTERNAL_EXN_V("Only support rank 0/1 CircleConst",
+ oops::to_uint32(const_shape_node->rank()));
+
+ select_axis = const_shape_node->scalar<loco::DataType::S32>();
+ }
+ assert(select_axis < input_shape.rank());
+ assert(select_axis >= 0); // TODO support minus of this breaks
+
+ // NOTE select_axis is removed
+ loco::TensorShape shape_output;
+ uint32_t rank = input_shape.rank();
+ uint32_t shrink = static_cast<uint32_t>(select_axis);
+ assert(rank > 0);
+ shape_output.rank(rank - 1);
+ for (uint32_t r = 0, d = 0; r < rank; ++r)
+ {
+ if (r == shrink)
+ continue;
+ shape_output.dim(d++) = input_shape.dim(r);
+ }
+ return loco::NodeShape{shape_output};
+}
+
+// Call this for CircleAvgPool2D and CircleMaxPool2D only
+template <class Pool2DType> loco::NodeShape infer_pool_2d_shape(const Pool2DType *node)
+{
+ LUCI_ASSERT(loco::shape_known(node->value()), "Shape must be known");
+
+ auto ifm_shape = loco::shape_get(node->value()).template as<loco::TensorShape>();
+ assert(ifm_shape.rank() == 4);
+
+ uint32_t input_height = ifm_shape.dim(1).value();
+ uint32_t input_width = ifm_shape.dim(2).value();
+ uint32_t stride_height = node->stride()->h();
+ uint32_t stride_width = node->stride()->w();
+ uint32_t window_height = node->filter()->h();
+ uint32_t window_width = node->filter()->w();
+ uint32_t dilation_height = 1; // dilation for CircleAvgPool2D and CircleMaxPool2D is 1
+ uint32_t dilation_width = 1;
+ uint32_t effective_window_height = dilation_height * (window_height - 1) + 1;
+ uint32_t effective_window_width = dilation_width * (window_width - 1) + 1;
+
+ uint32_t output_height = 0;
+ uint32_t output_width = 0;
+
+ if (node->padding() == luci::Padding::VALID)
+ {
+ output_height = (input_height + stride_height - effective_window_height) / stride_height;
+ output_width = (input_width + stride_width - effective_window_width) / stride_width;
+ }
+ else if (node->padding() == luci::Padding::SAME)
+ {
+ output_height = (input_height + stride_height - 1) / stride_height;
+ output_width = (input_width + stride_width - 1) / stride_width;
+ }
+ else
+ LUCI_ASSERT(false, "Wrong padding type");
+
+ loco::TensorShape ofm_shape;
+ ofm_shape.rank(4);
+ ofm_shape.dim(0) = ifm_shape.dim(0);
+ ofm_shape.dim(1) = output_height;
+ ofm_shape.dim(2) = output_width;
+ ofm_shape.dim(3) = ifm_shape.dim(3);
+
+ return loco::NodeShape{ofm_shape};
+}
+
+loco::NodeShape infer_batch_to_space_nd(const luci::CircleBatchToSpaceND *node)
+{
+ const loco::DataType S32 = loco::DataType::S32;
+
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ // Support only input rank is 3 and 4
+ assert(input_shape.rank() == 3 || input_shape.rank() == 4);
+
+ // Only support block_shape() with S32 type CircleConst for now
+ auto const_block_shape = loco::must_cast<luci::CircleConst *>(node->block_shape());
+ LUCI_ASSERT(const_block_shape->dtype() == loco::DataType::S32, "Only support int32 block_shape");
+
+ // Only support crops() with S32 type CircleConst for now
+ auto const_crops = loco::must_cast<luci::CircleConst *>(node->crops());
+ LUCI_ASSERT(const_crops->dtype() == loco::DataType::S32, "Only support int32 crops");
+
+ auto const_block_shape_shape = loco::shape_get(const_block_shape).as<loco::TensorShape>();
+ auto const_crops_shape = loco::shape_get(const_crops).as<loco::TensorShape>();
+ assert(const_block_shape_shape.rank() == 1);
+ assert(const_crops_shape.rank() == 2);
+
+ int32_t input_spatial_dim = input_shape.rank() - 2;
+ assert(const_block_shape_shape.dim(0) == input_spatial_dim);
+ assert(const_crops_shape.dim(0) == input_spatial_dim);
+ assert(const_crops_shape.dim(1) == 2);
+
+ loco::TensorShape shape_output;
+
+ shape_output.rank(input_shape.rank());
+
+ int32_t output_batch_size = input_shape.dim(0).value();
+ for (int32_t dim = 0; dim < input_spatial_dim; ++dim)
+ {
+ int dim_size = input_shape.dim(dim + 1).value() * const_block_shape->at<S32>(dim);
+ dim_size -= const_crops->at<S32>(dim * 2);
+ dim_size -= const_crops->at<S32>(dim * 2 + 1);
+ shape_output.dim(dim + 1) = dim_size;
+
+ assert(output_batch_size % const_block_shape->at<S32>(dim) == 0);
+ output_batch_size = output_batch_size / const_block_shape->at<S32>(dim);
+ }
+ shape_output.dim(0) = output_batch_size;
+ shape_output.dim(input_shape.rank() - 1) = input_shape.dim(input_shape.rank() - 1);
+
+ return loco::NodeShape{shape_output};
+}
+
+struct OutputSize
+{
+ uint32_t height = 0;
+ uint32_t width = 0;
+};
+
+template <class Conv2DType> OutputSize infer_conv2d_type(const Conv2DType *node)
+{
+ auto ifm_shape = loco::shape_get(node->input()).template as<loco::TensorShape>();
+ auto ker_shape = loco::shape_get(node->filter()).template as<loco::TensorShape>();
+ assert(ifm_shape.rank() == 4);
+ assert(ker_shape.rank() == 4);
+
+ uint32_t input_height = ifm_shape.dim(1).value();
+ uint32_t input_width = ifm_shape.dim(2).value();
+ uint32_t stride_height = node->stride()->h();
+ uint32_t stride_width = node->stride()->w();
+ uint32_t ker_height = ker_shape.dim(1).value();
+ uint32_t ker_width = ker_shape.dim(2).value();
+ uint32_t dilation_height = node->dilation()->h();
+ uint32_t dilation_width = node->dilation()->w();
+ uint32_t effective_ker_height = dilation_height * (ker_height - 1) + 1;
+ uint32_t effective_ker_width = dilation_width * (ker_width - 1) + 1;
+
+ uint32_t output_height = 0;
+ uint32_t output_width = 0;
+
+ if (node->padding() == luci::Padding::VALID)
+ {
+ output_height = (input_height + stride_height - effective_ker_height) / stride_height;
+ output_width = (input_width + stride_width - effective_ker_width) / stride_width;
+ }
+ else if (node->padding() == luci::Padding::SAME)
+ {
+ output_height = (input_height + stride_height - 1) / stride_height;
+ output_width = (input_width + stride_width - 1) / stride_width;
+ }
+ else
+ LUCI_ASSERT(false, "Wrong padding type");
+
+ OutputSize os{output_height, output_width};
+
+ return os;
+}
+
+// BatchMatMulV2 supports broadcasting in the batch dimensions(BatchMatMul doesn't)
+// TODO Distinguish BatchMatMul and BatchMatMulV2
+loco::NodeShape infer_batchmatmul_shape(const loco::TensorShape &x_shape,
+ const loco::TensorShape &y_shape, bool adj_x, bool adj_y)
+{
+ uint32_t x_rank = x_shape.rank();
+ uint32_t y_rank = y_shape.rank();
+ assert(x_rank >= 2 && y_rank >= 2);
+
+ loco::TensorShape output_shape;
+ output_shape.rank(x_shape.rank());
+ // Braodcast in the batch dimension
+ if (x_rank > 2 || y_rank > 2)
+ {
+ loco::TensorShape dummy_x = x_shape;
+ loco::TensorShape dummy_y = y_shape;
+ expand_rank(dummy_x, dummy_y);
+ if (x_rank < y_rank)
+ expand_rank(output_shape, dummy_y);
+
+ for (uint32_t d = 0; d < output_shape.rank() - 2; d++)
+ {
+ uint32_t max_dim = std::max(dummy_x.dim(d).value(), dummy_y.dim(d).value());
+ if (dummy_x.dim(d) == dummy_y.dim(d) ||
+ dummy_x.dim(d).value() * dummy_y.dim(d).value() == max_dim)
+ output_shape.dim(d).set(max_dim);
+ else
+ INTERNAL_EXN("BatchMatMul has wrong shape");
+ }
+ }
+
+ loco::Dimension x_lhs = adj_x ? x_shape.dim(x_rank - 1) : x_shape.dim(x_rank - 2);
+ loco::Dimension x_rhs = adj_x ? x_shape.dim(x_rank - 2) : x_shape.dim(x_rank - 1);
+ loco::Dimension y_lhs = adj_y ? y_shape.dim(y_rank - 1) : y_shape.dim(y_rank - 2);
+ loco::Dimension y_rhs = adj_y ? y_shape.dim(y_rank - 2) : y_shape.dim(y_rank - 1);
+
+ if (not(x_rhs == y_lhs))
+ INTERNAL_EXN("x_rhs and y_lhs should be same");
+
+ uint32_t out_rank = output_shape.rank();
+ output_shape.dim(out_rank - 2) = x_lhs;
+ output_shape.dim(out_rank - 1) = y_rhs;
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_concatenation(const luci::CircleConcatenation *node)
+{
+ // TODO Support when CircleConcatenation has 0 input
+ assert(node->numValues() > 0);
+
+ auto first_shape = loco::shape_get(node->values(0)).as<loco::TensorShape>();
+ auto axis = node->axis();
+ if (axis < 0)
+ axis += first_shape.rank();
+
+ assert(0 <= axis);
+ assert(first_shape.rank() > static_cast<uint32_t>(axis));
+
+ loco::TensorShape output_shape;
+
+ output_shape.rank(first_shape.rank());
+ for (uint32_t i = 0; i < output_shape.rank(); ++i)
+ output_shape.dim(i) = first_shape.dim(i);
+
+ for (uint32_t i = 1; i < node->numValues(); ++i)
+ {
+ auto input_shape = loco::shape_get(node->values(i)).as<loco::TensorShape>();
+
+ for (uint32_t j = 0; j < output_shape.rank(); ++j)
+ {
+ if (j == static_cast<uint32_t>(axis))
+ output_shape.dim(j) = output_shape.dim(j).value() + input_shape.dim(j).value();
+ else
+ assert(output_shape.dim(j) == input_shape.dim(j));
+ }
+ }
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_conv2d(const luci::CircleConv2D *node)
+{
+ LOGGER(l);
+
+ auto ifm_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); // in NHWC
+ auto ker_shape = loco::shape_get(node->filter()).as<loco::TensorShape>(); // in OHWI
+
+ INFO(l) << "[luci] CircleConv2D ShapeInf ifm(" << ifm_shape.rank() << ") ker(" << ker_shape.rank()
+ << ")" << std::endl;
+
+ assert(ifm_shape.rank() == 4);
+ assert(ker_shape.rank() == 4);
+ assert(ifm_shape.dim(3) == ker_shape.dim(3));
+
+ auto os = infer_conv2d_type(node);
+
+ loco::TensorShape ofm_shape;
+ ofm_shape.rank(4);
+ ofm_shape.dim(0) = ifm_shape.dim(0);
+ ofm_shape.dim(1) = os.height;
+ ofm_shape.dim(2) = os.width;
+ ofm_shape.dim(3) = ker_shape.dim(0);
+
+ return loco::NodeShape{ofm_shape};
+}
+
+loco::NodeShape infer_depth_to_space(const luci::CircleDepthToSpace *node)
+{
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ LUCI_ASSERT(input_shape.rank() == 4, "Only input rank 4 is supported");
+
+ // Only data format NHWC is supported
+ // TODO need to clarify what to do with layout in this operator
+ int32_t height = input_shape.dim(1).value();
+ int32_t width = input_shape.dim(2).value();
+ int32_t depth = input_shape.dim(3).value();
+
+ int block_size = node->block_size();
+
+ if (block_size < 2)
+ INTERNAL_EXN("Block size must be >= 2");
+
+ if (depth % (block_size * block_size))
+ {
+ INTERNAL_EXN("The input tensor's depth must be divisible by block_size^2");
+ }
+
+ loco::TensorShape output_shape;
+ output_shape.rank(4);
+
+ output_shape.dim(0) = input_shape.dim(0).value();
+ output_shape.dim(1) = height * block_size;
+ output_shape.dim(2) = width * block_size;
+ output_shape.dim(3) = depth / (block_size * block_size);
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_depthwise_conv2d(const luci::CircleDepthwiseConv2D *node)
+{
+ auto ifm_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); // in NHWC
+ auto ker_shape = loco::shape_get(node->filter()).as<loco::TensorShape>(); // in 1 H W CM
+
+ assert(ifm_shape.rank() == 4);
+ assert(ker_shape.rank() == 4);
+ assert(ker_shape.dim(0).value() == 1);
+
+ auto os = infer_conv2d_type(node);
+
+ loco::TensorShape ofm_shape;
+ ofm_shape.rank(4);
+ ofm_shape.dim(0) = ifm_shape.dim(0);
+ ofm_shape.dim(1) = os.height;
+ ofm_shape.dim(2) = os.width;
+ ofm_shape.dim(3) = ker_shape.dim(3);
+
+ return loco::NodeShape{ofm_shape};
+}
+
+loco::NodeShape infer_expand_dims(const luci::CircleExpandDims *node)
+{
+ const loco::DataType S32 = loco::DataType::S32;
+ auto x_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ if (x_shape.rank() == 0)
+ {
+ // This maybe for unknown shape. We use shape from the node itself.
+ return use_own(node);
+ }
+ auto const_axis = loco::must_cast<luci::CircleConst *>(node->axis());
+ LUCI_ASSERT(const_axis->dtype() == S32, "Only support int32 CircleConst for axis");
+ if (const_axis->rank() != 0 && const_axis->rank() != 1)
+ {
+ INTERNAL_EXN_V("Non-scalar axis in OP", node->opnum());
+ }
+ int32_t axis = const_axis->at<S32>(0);
+ LUCI_ASSERT((axis <= static_cast<int32_t>(x_shape.rank())) &&
+ (axis >= -1 - static_cast<int32_t>(x_shape.rank())),
+ "Axis has to be between [-(D+1), D], where D is rank of input.");
+ size_t positive_axis = axis < 0 ? x_shape.rank() + axis + 1 : axis;
+ loco::TensorShape output_shape;
+ output_shape.rank(x_shape.rank() + 1);
+ size_t i = 0;
+ for (; i < positive_axis; i++)
+ output_shape.dim(i) = x_shape.dim(i);
+ output_shape.dim(i) = loco::Dimension(1);
+ for (; i < x_shape.rank(); i++)
+ output_shape.dim(i + 1) = x_shape.dim(i);
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_fill(const luci::CircleFill *node)
+{
+ loco::TensorShape shape;
+ {
+ LUCI_ASSERT(node->dims(), "dims input should not be nullptr");
+
+ auto dims_node = dynamic_cast<luci::CircleConst *>(node->dims());
+ if (dims_node != nullptr)
+ {
+ // Only support node with S32
+ LUCI_ASSERT(dims_node->dtype() == loco::DataType::S32, "Only support int32 CircleConst");
+
+ if (dims_node->rank() != 1)
+ INTERNAL_EXN_V("Only support rank 1 CircleConst", oops::to_uint32(dims_node->rank()));
+
+ shape.rank(dims_node->dim(0).value());
+
+ for (uint32_t axis = 0; axis < shape.rank(); ++axis)
+ {
+ shape.dim(axis) = dims_node->at<loco::DataType::S32>(axis);
+ }
+ }
+ else
+ {
+ shape = own_shape(node);
+ }
+ }
+
+ return loco::NodeShape{shape};
+}
+
+loco::NodeShape infer_fully_connected(const luci::CircleFullyConnected *node)
+{
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ auto weights_shape = loco::shape_get(node->weights()).as<loco::TensorShape>();
+
+ // Checking shape capability for fully connected layer
+ // Input: a tensor of at least rank 2 [D1, D2, ... Dn]
+ // Weight: [# of units, K]
+ // Output: [D1 * D2 * ... * Dn / K, # of units]
+ if (input_shape.rank() < 2 || weights_shape.rank() != 2)
+ {
+ // Return node own shape if shape inference is not possible
+ return use_own(node);
+ }
+
+ uint32_t input_size = 1;
+ for (uint32_t i = 0; i < input_shape.rank(); i++)
+ {
+ input_size = input_size * input_shape.dim(i).value();
+ }
+ const uint32_t batch_size = input_size / weights_shape.dim(1).value();
+ loco::TensorShape out_shape;
+ out_shape.rank(2);
+ out_shape.dim(0) = batch_size;
+ out_shape.dim(1) = weights_shape.dim(0);
+
+ return loco::NodeShape{out_shape};
+}
+
+loco::NodeShape infer_gather(const luci::CircleGather *node)
+{
+ loco::TensorShape output_shape;
+
+ const auto input_shape = loco::shape_get(node->params()).as<loco::TensorShape>();
+ const auto positions_shape = loco::shape_get(node->indices()).as<loco::TensorShape>();
+ int32_t axis = node->axis();
+
+ // If CircleGather input has a dynamic shape, it can't inference this shape. So, it returns the
+ // shape that node already has.
+ if (input_shape.rank() == 0 || positions_shape.rank() == 0)
+ return use_own(node);
+
+ if (axis < 0)
+ axis += input_shape.rank();
+
+ output_shape.rank(input_shape.rank() - 1 + positions_shape.rank());
+ int32_t outdim_index = 0;
+ for (int32_t i = 0; i < axis; ++i)
+ output_shape.dim(outdim_index++) = input_shape.dim(i);
+ for (uint32_t i = 0; i < positions_shape.rank(); ++i)
+ output_shape.dim(outdim_index++) = positions_shape.dim(i);
+ for (uint32_t i = axis + 1; i < input_shape.rank(); ++i)
+ output_shape.dim(outdim_index++) = input_shape.dim(i);
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_gather_nd(const luci::CircleGatherNd *node)
+{
+ loco::TensorShape output_shape;
+
+ const auto params_shape = loco::shape_get(node->params()).as<loco::TensorShape>();
+ const auto indices_shape = loco::shape_get(node->indices()).as<loco::TensorShape>();
+
+ const auto params_rank = params_shape.rank();
+ const auto indices_rank = indices_shape.rank();
+
+ // see https://www.tensorflow.org/api_docs/python/tf/gather_nd
+ // output.shape = indices.shape[:-1] + params.shape[indices.shape[-1]:]
+ // batch_dims isn't supported in tflite
+
+ // TODO: replace exceptions with setting shape to unknown?
+
+ if (!indices_shape.dim(indices_rank - 1).known())
+ INTERNAL_EXN("Last indices dimension is unknown");
+
+ auto indices_last_dim = indices_shape.dim(indices_rank - 1).value();
+
+ if (indices_last_dim > params_rank)
+ INTERNAL_EXN("Last indices dimension should be <= params rank");
+
+ const uint32_t output_rank = indices_rank + params_rank - indices_last_dim - 1;
+
+ output_shape.rank(output_rank);
+
+ uint32_t output_index = 0;
+ for (uint32_t i = 0; i < indices_rank - 1; ++i)
+ {
+ auto &dim = indices_shape.dim(i);
+ if (!dim.known())
+ INTERNAL_EXN("Unknown indices dimension is unsupported");
+ output_shape.dim(output_index++).set(dim.value());
+ }
+
+ for (uint32_t i = indices_last_dim; i < params_rank; ++i)
+ {
+ auto &dim = params_shape.dim(i);
+ if (!dim.known())
+ INTERNAL_EXN("Unknown params dimension is unsupported");
+ output_shape.dim(output_index++).set(dim.value());
+ }
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_matrix_diag(const luci::CircleMatrixDiag *node)
+{
+ loco::TensorShape output_shape;
+
+ auto diagonal_shape = loco::shape_get(node->diagonal()).as<loco::TensorShape>();
+ auto rank = diagonal_shape.rank();
+
+ output_shape.rank(rank + 1);
+
+ for (uint32_t i = 0; i < rank; i++)
+ {
+ output_shape.dim(i) = diagonal_shape.dim(i);
+ }
+
+ output_shape.dim(rank) = diagonal_shape.dim(rank - 1);
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_matrix_set_diag(const luci::CircleMatrixSetDiag *node)
+{
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ auto diagonal_shape = loco::shape_get(node->diagonal()).as<loco::TensorShape>();
+
+ auto rank = diagonal_shape.rank();
+
+ LUCI_ASSERT(rank == input_shape.rank() - 1, "diagonal rank = input rank - 1");
+
+ for (uint32_t i = 0; i < rank - 1; i++)
+ {
+ LUCI_ASSERT(diagonal_shape.dim(i) == input_shape.dim(i), "diagonal dims = input dims");
+ }
+
+ auto dim = std::min(input_shape.dim(rank - 1).value(), input_shape.dim(rank).value());
+
+ LUCI_ASSERT(dim == diagonal_shape.dim(rank - 1), "Max diag len error");
+
+ return loco::NodeShape{input_shape};
+}
+
+loco::TensorShape infer_reducer(const loco::Node *input, const loco::Node *indices, bool keep_dims)
+{
+ const loco::DataType S32 = loco::DataType::S32;
+
+ auto input_shape = loco::shape_get(input).as<loco::TensorShape>();
+ auto reduction_indices = loco::must_cast<const luci::CircleConst *>(indices);
+
+ { // Exceptions
+ // TODO support non-const case
+ // TODO support other data type
+ LUCI_ASSERT(reduction_indices->dtype() == S32, "Only support int 32");
+ }
+
+ std::vector<int32_t> reduction_values;
+
+ for (uint32_t i = 0; i < reduction_indices->size<S32>(); ++i)
+ {
+ int32_t axis = reduction_indices->at<S32>(i);
+ if (axis < 0)
+ axis += input_shape.rank();
+ if (not(0 <= axis and axis < static_cast<int32_t>(input_shape.rank())))
+ INTERNAL_EXN_V("Invalid reduction axis for REDUCER", oops::to_uint32(axis));
+ reduction_values.push_back(axis);
+ }
+
+ loco::TensorShape output_shape;
+
+ if (keep_dims)
+ {
+ output_shape.rank(input_shape.rank());
+ for (uint32_t i = 0; i < input_shape.rank(); ++i)
+ output_shape.dim(i) = input_shape.dim(i);
+ for (uint32_t i = 0; i < reduction_values.size(); ++i)
+ output_shape.dim(reduction_values.at(i)) = 1;
+ }
+ else
+ {
+ std::vector<bool> check_reduce(input_shape.rank(), false);
+ for (uint32_t i = 0; i < reduction_values.size(); ++i)
+ check_reduce.at(reduction_values.at(i)) = true;
+
+ uint32_t reduce_cnt = 0;
+ for (uint32_t i = 0; i < check_reduce.size(); ++i)
+ if (check_reduce.at(i))
+ ++reduce_cnt;
+
+ output_shape.rank(input_shape.rank() - reduce_cnt);
+ for (uint32_t i = 0, j = 0; i < check_reduce.size(); ++i)
+ if (check_reduce.at(i) == false)
+ output_shape.dim(j++) = input_shape.dim(i);
+ }
+
+ return output_shape;
+}
+
+loco::NodeShape infer_mirror_pad(const luci::CircleMirrorPad *node)
+{
+ // TODO support non-const case
+ auto paddings = loco::must_cast<luci::CircleConst *>(node->paddings());
+ return use_paddings(node, paddings);
+}
+
+loco::NodeShape infer_one_hot(const luci::CircleOneHot *node)
+{
+ const loco::DataType S32 = loco::DataType::S32;
+ auto indices_shape = loco::shape_get(node->indices()).as<loco::TensorShape>();
+ // Only support OneHot node's depth() is CircleConst with type S32
+ // TODO support depth with other types
+ auto depth = loco::must_cast<luci::CircleConst *>(node->depth());
+ LUCI_ASSERT(depth->dtype() == S32, "Only support int32 CircleConst");
+ if (depth->rank() != 0)
+ INTERNAL_EXN_V("Only support rank 0 CircleOneHot in Depth", oops::to_uint32(depth->rank()));
+ loco::TensorShape output_shape;
+ output_shape.rank(indices_shape.rank() + 1);
+ auto axis = node->axis();
+ if (axis < 0)
+ axis += indices_shape.rank() + 1;
+ LUCI_ASSERT(0 <= axis, "Axis is out of range");
+ LUCI_ASSERT(static_cast<uint32_t>(axis) <= indices_shape.rank(), "Axis is out of range");
+ uint32_t j = 0;
+ for (uint32_t i = 0; i < output_shape.rank(); i++)
+ {
+ if (i == static_cast<uint32_t>(axis))
+ {
+ output_shape.dim(i) = depth->at<S32>(0);
+ }
+ else
+ {
+ output_shape.dim(i) = indices_shape.dim(j++);
+ }
+ }
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_pack(const luci::CirclePack *node)
+{
+ LUCI_ASSERT(node->values_count() > 0, "Only support one or more inputs");
+
+ auto first_shape = loco::shape_get(node->values(0)).as<loco::TensorShape>();
+ // Make sure all inputs have the same shape.
+ for (uint32_t i = 1; i < node->values_count(); ++i)
+ {
+ auto in_shape = loco::shape_get(node->values(i)).as<loco::TensorShape>();
+ LUCI_ASSERT(loco::NodeShape{first_shape} == loco::NodeShape{in_shape},
+ "All inputs must have the same shape");
+ }
+
+ // Checking shape capability for pack layer
+ // Input: tensors [D1, D2, ... Dn]
+ // Axis: K
+ // Output: [D1, D2, ... , D_K-1, n, D_K+1, ... Dn]
+ auto axis = node->axis();
+ if (axis < 0)
+ axis += first_shape.rank() + 1;
+
+ LUCI_ASSERT(0 <= axis, "Axis is out of range");
+ LUCI_ASSERT(static_cast<uint32_t>(axis) <= first_shape.rank(), "Axis is out of range");
+
+ loco::TensorShape output_shape;
+ output_shape.rank(first_shape.rank() + 1);
+
+ uint32_t j = 0;
+ for (uint32_t i = 0; i < output_shape.rank(); ++i)
+ {
+ if (i == static_cast<uint32_t>(axis))
+ {
+ output_shape.dim(i) = node->values_count();
+ }
+ else
+ {
+ output_shape.dim(i) = first_shape.dim(j++);
+ }
+ }
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_pad(const luci::CirclePad *node)
+{
+ // TODO support non-const case
+ auto paddings = loco::must_cast<luci::CircleConst *>(node->paddings());
+ return use_paddings(node, paddings);
+}
+
+loco::NodeShape infer_pad_v2(const luci::CirclePadV2 *node)
+{
+ // TODO support non-const case
+ auto paddings = dynamic_cast<luci::CircleConst *>(node->paddings());
+ if (!paddings)
+ {
+ auto node_shape = own_shape(node);
+ return loco::NodeShape{node_shape};
+ }
+ return use_paddings(node, paddings);
+}
+
+loco::NodeShape infer_p_relu(const luci::CirclePRelu *node)
+{
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ auto alpha_shape = loco::shape_get(node->alpha()).as<loco::TensorShape>();
+
+ auto output_shape = broadcast_shape(input_shape, alpha_shape);
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_range(const luci::CircleRange *node)
+{
+ loco::TensorShape output_shape;
+ output_shape.rank(1);
+
+ auto start_node = dynamic_cast<luci::CircleConst *>(node->start());
+ auto limit_node = dynamic_cast<luci::CircleConst *>(node->limit());
+ auto delta_node = dynamic_cast<luci::CircleConst *>(node->delta());
+
+ if (start_node == nullptr || limit_node == nullptr || delta_node == nullptr)
+ {
+ return use_own(node);
+ }
+
+ double start = 0, limit = 0, delta = 0;
+
+#define GET_RANGE_PARAM(DT) \
+ start = start_node->scalar<DT>(); \
+ limit = limit_node->scalar<DT>(); \
+ delta = delta_node->scalar<DT>();
+
+ switch (start_node->dtype())
+ {
+ case loco::DataType::FLOAT32:
+ GET_RANGE_PARAM(loco::DataType::FLOAT32)
+ break;
+ case loco::DataType::S32:
+ GET_RANGE_PARAM(loco::DataType::S32)
+ break;
+ default:
+ INTERNAL_EXN("Range data type not supported");
+ }
+
+#undef GET_RANGE_PARAM
+
+ if (delta == 0)
+ INTERNAL_EXN("Delta can not be zero");
+
+ output_shape.dim(0) = ceil((limit - start) / delta);
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_reshape(const luci::CircleReshape *node)
+{
+ LOGGER(l);
+
+ const loco::DataType S32 = loco::DataType::S32;
+
+ loco::TensorShape shape_by_input;
+ {
+ LUCI_ASSERT(node->shape(), "2nd input shape() should not be nullptr");
+
+ // Only support node's shape() is CircleConst with S32
+ // TODO support other node with other types
+ auto const_shape_node = dynamic_cast<luci::CircleConst *>(node->shape());
+ if (const_shape_node != nullptr)
+ {
+ LUCI_ASSERT(const_shape_node->dtype() == S32, "Only support int32 CircleConst");
+
+ shape_by_input.rank(const_shape_node->size<S32>());
+
+ for (uint32_t axis = 0; axis < shape_by_input.rank(); ++axis)
+ {
+ shape_by_input.dim(axis) = const_shape_node->at<S32>(axis);
+ }
+ }
+ else
+ {
+ // We use shape from the node itself
+ shape_by_input = own_shape(node);
+ }
+ }
+
+ loco::TensorShape shape_by_attr;
+ {
+ shape_by_attr.rank(node->newShape()->rank());
+
+ for (uint32_t axis = 0; axis < shape_by_attr.rank(); ++axis)
+ {
+ shape_by_attr.dim(axis) = node->newShape()->dim(axis);
+ }
+ }
+
+ if (!(shape_by_input == shape_by_attr))
+ {
+ INFO(l) << "CircleReshape: Two new shape information mismatched : " << std::endl;
+ INFO(l) << " shape_by_input : " << shape_by_input << std::endl;
+ INFO(l) << " shape_by_attr : " << shape_by_attr << std::endl;
+ }
+
+ loco::TensorShape output_shape = shape_by_input;
+
+ // One of the dimensions can have special value -1, meaning its actual value should be inferred.
+ const auto input_shape = loco::shape_get(node->tensor()).as<loco::TensorShape>();
+ const uint32_t input_element_count = loco::element_count(&input_shape);
+ uint32_t output_element_count = 1;
+ uint32_t unknown_dim_index = UINT32_MAX;
+ for (uint32_t dim_index = 0; dim_index < output_shape.rank(); ++dim_index)
+ {
+ const uint32_t dim_value = output_shape.dim(dim_index).value();
+ if (static_cast<int>(dim_value) == -1)
+ {
+ LUCI_ASSERT(unknown_dim_index == UINT32_MAX, "More than one unknown dimension");
+ unknown_dim_index = dim_index;
+ }
+ else
+ {
+ output_element_count *= dim_value;
+ }
+ }
+ if (unknown_dim_index != UINT32_MAX)
+ {
+ output_shape.dim(unknown_dim_index) = input_element_count / output_element_count;
+ }
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_resize_bilinear(const luci::CircleResizeBilinear *node)
+{
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+
+ if (input_shape.rank() != 4)
+ INTERNAL_EXN("Expected ResizeBilinear input to have rank 4");
+
+ auto *const_node = loco::must_cast<luci::CircleConst *>(node->size());
+
+ if (const_node->dtype() != loco::DataType::S32)
+ INTERNAL_EXN("Only S32 datatype is supported for ResizeBilinear size");
+
+ if (const_node->rank() != 1)
+ INTERNAL_EXN("Expected size tensor of rank 1");
+
+ if (const_node->dim(0).value() != 2)
+ INTERNAL_EXN("Expected size tensor with shape [2]");
+
+ loco::TensorShape output_shape;
+ output_shape.rank(4);
+ output_shape.dim(0) = input_shape.dim(0);
+ output_shape.dim(1) = const_node->at<loco::DataType::S32>(0);
+ output_shape.dim(2) = const_node->at<loco::DataType::S32>(1);
+ output_shape.dim(3) = input_shape.dim(3);
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_resize_nearest_neighbor(const luci::CircleResizeNearestNeighbor *node)
+{
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+
+ if (input_shape.rank() != 4)
+ INTERNAL_EXN("Expected ResizeNearesNeighbor input to have rank 4");
+
+ auto *const_node = loco::must_cast<luci::CircleConst *>(node->size());
+
+ if (const_node->dtype() != loco::DataType::S32)
+ INTERNAL_EXN("Only S32 datatype is supported for ResizeNearesNeighbor size");
+
+ if (const_node->rank() != 1)
+ INTERNAL_EXN("Expected size tensor of rank 1");
+
+ if (const_node->dim(0).value() != 2)
+ INTERNAL_EXN("Expected size tensor with shape [2]");
+
+ loco::TensorShape output_shape;
+ output_shape.rank(4);
+ output_shape.dim(0) = input_shape.dim(0);
+ output_shape.dim(1) = const_node->at<loco::DataType::S32>(0);
+ output_shape.dim(2) = const_node->at<loco::DataType::S32>(1);
+ output_shape.dim(3) = input_shape.dim(3);
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_scatter_nd(const luci::CircleScatterNd *node)
+{
+ loco::TensorShape output_shape;
+
+ auto shape_node = loco::must_cast<luci::CircleConst *>(node->shape());
+
+ const loco::DataType S32 = loco::DataType::S32;
+ const loco::DataType S64 = loco::DataType::S64;
+
+ std::vector<int64_t> vect_shape;
+
+ if (shape_node->dtype() == S32)
+ vect_shape = vector_from_constant<S32>(shape_node);
+ else if (shape_node->dtype() == S64)
+ vect_shape = vector_from_constant<S64>(shape_node);
+ else
+ LUCI_ASSERT(false, "Only support int32/int64 for shape()");
+
+ output_shape.rank(vect_shape.size());
+ for (uint32_t i = 0; i < vect_shape.size(); ++i)
+ output_shape.dim(i) = vect_shape[i];
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_segment_sum(const luci::CircleSegmentSum *node)
+{
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ auto segment_shape = loco::shape_get(node->segment_ids()).as<loco::TensorShape>();
+
+ LUCI_ASSERT(segment_shape.rank() == 1, "segment_ids must be 1-D tensor");
+ LUCI_ASSERT(segment_shape.dim(0).value() == input_shape.dim(0).value(),
+ "segment_ids size must be equal to the size of data's first dimension");
+
+ auto ids_shape_value = loco::must_cast<luci::CircleConst *>(node->segment_ids());
+
+ std::vector<int64_t> vect_ids;
+
+ if (ids_shape_value->dtype() == loco::DataType::S32)
+ vect_ids = vector_from_constant<loco::DataType::S32>(ids_shape_value);
+
+ LUCI_ASSERT(std::is_sorted(vect_ids.begin(), vect_ids.end()),
+ "segment_ids values should be sorted")
+
+ loco::TensorShape output_shape;
+
+ output_shape.rank(input_shape.rank());
+
+ for (uint32_t i = 1; i < input_shape.rank(); ++i)
+ output_shape.dim(i) = input_shape.dim(i);
+
+ output_shape.dim(0) = vect_ids.back() + 1;
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_select(const luci::CircleSelect *node)
+{
+ auto t_shape = loco::shape_get(node->t()).as<loco::TensorShape>();
+ assert(t_shape == loco::shape_get(node->e()).as<loco::TensorShape>());
+
+ // condition shape validation
+ auto c_shape = loco::shape_get(node->condition()).as<loco::TensorShape>();
+ if (c_shape.rank() != t_shape.rank())
+ {
+ if (c_shape.rank() != 0 && c_shape.rank() != 1)
+ INTERNAL_EXN_V("CircleSelect condition rank is not 0 nor 1: ", c_shape.rank());
+
+ if (c_shape.rank() == 1)
+ {
+ if (c_shape.dim(0).value() != t_shape.dim(0).value())
+ INTERNAL_EXN("CircleSelect condition dim(0) should match with t.dim(0)");
+ }
+ }
+
+ return loco::NodeShape{t_shape};
+}
+
+loco::NodeShape infer_select_v2(const luci::CircleSelectV2 *node)
+{
+ auto c_shape = loco::shape_get(node->condition()).as<loco::TensorShape>();
+ auto t_shape = loco::shape_get(node->t()).as<loco::TensorShape>();
+ auto e_shape = loco::shape_get(node->e()).as<loco::TensorShape>();
+
+ // validate ability to broadcast shapes to each other
+ auto b_shape = broadcast_shape(broadcast_shape(c_shape, t_shape), e_shape);
+ return loco::NodeShape{b_shape};
+}
+
+loco::NodeShape infer_shape(const luci::CircleShape *node)
+{
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+
+ loco::TensorShape output_shape;
+
+ output_shape.rank(1);
+ output_shape.dim(0) = input_shape.rank();
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_slice(const luci::CircleSlice *node)
+{
+ const loco::DataType S32 = loco::DataType::S32;
+ const loco::DataType S64 = loco::DataType::S64;
+
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+
+ auto const_begin = loco::must_cast<luci::CircleConst *>(node->begin());
+ auto const_size = loco::must_cast<luci::CircleConst *>(node->size());
+
+ loco::TensorShape output_shape;
+ std::vector<int64_t> vect_begin; // to hold both S32/S64, we use int64_t
+ std::vector<int64_t> vect_size;
+
+ if (const_begin->dtype() == S32)
+ vect_begin = vector_from_constant<S32>(const_begin);
+ else if (const_begin->dtype() == S64)
+ vect_begin = vector_from_constant<S64>(const_begin);
+ else
+ LUCI_ASSERT(false, "Only support int32/int64 for begin()");
+
+ if (const_size->dtype() == S32)
+ vect_size = vector_from_constant<S32>(const_size);
+ else if (const_size->dtype() == S64)
+ vect_size = vector_from_constant<S64>(const_size);
+ else
+ LUCI_ASSERT(false, "Only support int32/int64 for size()");
+
+ assert(input_shape.rank() == vect_begin.size());
+ assert(input_shape.rank() == vect_size.size());
+
+ output_shape.rank(vect_begin.size());
+ for (uint32_t idx = 0; idx < vect_begin.size(); ++idx)
+ {
+ auto size = vect_size.at(idx);
+ if (size == -1)
+ {
+ size = input_shape.dim(idx).value() - vect_begin.at(idx);
+ }
+ output_shape.dim(idx) = size;
+ }
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_space_to_batch_nd(const luci::CircleSpaceToBatchND *node)
+{
+ const loco::DataType S32 = loco::DataType::S32;
+
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ // Support only input rank is 3 and 4
+ assert(input_shape.rank() == 3 || input_shape.rank() == 4);
+
+ // Only support block_shape() with S32 type CircleConst for now
+ auto const_block_shape = loco::must_cast<luci::CircleConst *>(node->block_shape());
+ LUCI_ASSERT(const_block_shape->dtype() == S32, "Only support int32 block_shape");
+
+ // Only support paddings() with S32 type CircleConst for now
+ auto const_paddings = loco::must_cast<luci::CircleConst *>(node->paddings());
+ LUCI_ASSERT(const_paddings->dtype() == S32, "Only support int32 paddings");
+
+ auto const_block_shape_shape = loco::shape_get(const_block_shape).as<loco::TensorShape>();
+ auto const_paddings_shape = loco::shape_get(const_paddings).as<loco::TensorShape>();
+ assert(const_block_shape_shape.rank() == 1);
+ assert(const_paddings_shape.rank() == 2);
+
+ int32_t input_spatial_dim = input_shape.rank() - 2;
+ assert(const_block_shape_shape.dim(0) == input_spatial_dim);
+ assert(const_paddings_shape.dim(0) == input_spatial_dim);
+ assert(const_paddings_shape.dim(1) == 2);
+
+ // Check all values of block_shape >= 1
+ uint32_t ele_count = const_block_shape->size<S32>();
+ for (uint32_t e = 0; e < ele_count; ++e)
+ {
+ auto val = const_block_shape->at<S32>(e);
+ if (val < 1)
+ {
+ INTERNAL_EXN_V("All values of block_shape >= 1: ", e);
+ }
+ }
+
+ loco::TensorShape shape_output;
+
+ shape_output.rank(input_shape.rank());
+
+ int32_t output_batch_size = input_shape.dim(0).value();
+ for (int32_t dim = 0; dim < input_spatial_dim; ++dim)
+ {
+ int dim_size = input_shape.dim(dim + 1).value();
+ dim_size += const_paddings->at<S32>(dim * 2);
+ dim_size += const_paddings->at<S32>(dim * 2 + 1);
+ shape_output.dim(dim + 1) = dim_size / const_block_shape->at<S32>(dim);
+
+ assert(dim_size % const_block_shape->at<S32>(dim) == 0);
+ output_batch_size = output_batch_size * const_block_shape->at<S32>(dim);
+ }
+ shape_output.dim(0) = output_batch_size;
+ shape_output.dim(input_shape.rank() - 1) = input_shape.dim(input_shape.rank() - 1);
+
+ return loco::NodeShape{shape_output};
+}
+
+loco::NodeShape infer_space_to_depth(const luci::CircleSpaceToDepth *node)
+{
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ LUCI_ASSERT(input_shape.rank() == 4, "Only input rank 4 is supported");
+
+ // Only data format NHWC is supported
+ int32_t height = input_shape.dim(1).value();
+ int32_t width = input_shape.dim(2).value();
+ int32_t depth = input_shape.dim(3).value();
+
+ int block_size = node->block_size();
+
+ if (block_size < 2)
+ INTERNAL_EXN("Block size must be >= 2");
+
+ if ((height % block_size) || (width % block_size))
+ {
+ INTERNAL_EXN("The input tensor's height and width must be divisible by block_size");
+ }
+
+ loco::TensorShape output_shape;
+ output_shape.rank(4);
+
+ output_shape.dim(0) = input_shape.dim(0).value();
+ output_shape.dim(1) = height / block_size;
+ output_shape.dim(2) = width / block_size;
+ output_shape.dim(3) = block_size * block_size * depth;
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_sparse_to_dense(const luci::CircleSparseToDense *node)
+{
+ loco::TensorShape shape;
+ {
+ LUCI_ASSERT(node->output_shape(), "dims input should not be nullptr");
+
+ auto output_shape_node = dynamic_cast<luci::CircleConst *>(node->output_shape());
+ if (output_shape_node != nullptr)
+ {
+ // Only support node with S32
+ LUCI_ASSERT(output_shape_node->dtype() == loco::DataType::S32,
+ "Only support int32 CircleConst");
+
+ if (output_shape_node->rank() != 1)
+ INTERNAL_EXN_V("Only support rank 1 CircleConst",
+ oops::to_uint32(output_shape_node->rank()));
+
+ shape.rank(output_shape_node->size<loco::DataType::S32>());
+
+ for (uint32_t axis = 0; axis < shape.rank(); ++axis)
+ {
+ shape.dim(axis) = output_shape_node->at<loco::DataType::S32>(axis);
+ }
+ }
+ else
+ {
+ shape = own_shape(node);
+ }
+ }
+
+ return loco::NodeShape{shape};
+}
+
+loco::NodeShape infer_strided_slice(const luci::CircleStridedSlice *node)
+{
+ auto begin_node = dynamic_cast<luci::CircleConst *>(node->begin());
+ auto end_node = dynamic_cast<luci::CircleConst *>(node->end());
+ auto strides_node = dynamic_cast<luci::CircleConst *>(node->strides());
+
+ if (begin_node == nullptr || end_node == nullptr || strides_node == nullptr)
+ {
+ return use_own(node);
+ }
+
+ loco::TensorShape shape = infer_output_shape(node);
+ return loco::NodeShape{shape};
+}
+
+loco::NodeShape infer_squeeze(const luci::CircleSqueeze *node)
+{
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+
+ // TODO input shape may be unknown before runtime
+ std::vector<bool> do_squeeze(input_shape.rank(), false);
+ uint32_t num_squeezed = 0;
+
+ if (!node->squeeze_dims().empty())
+ {
+ // SqueezeDims not empty, squeeze only dims specified
+ for (int32_t raw_dim : node->squeeze_dims())
+ {
+ int32_t dim = raw_dim < 0 ? raw_dim + input_shape.rank() : raw_dim;
+
+ if (dim < 0 || static_cast<uint32_t>(dim) >= input_shape.rank() ||
+ input_shape.dim(dim).value() != 1)
+ {
+ INTERNAL_EXN("invalid dimention specified to Squeeze");
+ }
+
+ if (!do_squeeze[dim])
+ ++num_squeezed;
+ do_squeeze[dim] = true;
+ }
+ }
+ else
+ {
+ // SqueezeDims empty, squeeze any dims with size == 1
+ for (uint32_t dim = 0; dim < input_shape.rank(); ++dim)
+ {
+ if (input_shape.dim(dim) == 1)
+ {
+ do_squeeze[dim] = true;
+ ++num_squeezed;
+ }
+ }
+ }
+
+ loco::TensorShape output_shape;
+ output_shape.rank(input_shape.rank() - num_squeezed);
+
+ for (uint32_t in_dim = 0, out_dim = 0; in_dim < input_shape.rank(); ++in_dim)
+ {
+ if (!do_squeeze[in_dim])
+ {
+ output_shape.dim(out_dim++) = input_shape.dim(in_dim);
+ }
+ }
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_tile(const luci::CircleTile *node)
+{
+ const loco::DataType S32 = loco::DataType::S32;
+
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ auto multiples = loco::must_cast<luci::CircleConst *>(node->multiples());
+
+ // TODO support non-const case
+ // TODO support S64 type
+ LUCI_ASSERT(multiples->dtype() == S32, "Only support int32 multiples");
+ LUCI_ASSERT(multiples->rank() == 1, "multiples should be rank 1")
+
+ uint32_t n = multiples->dim(0).value();
+
+ LUCI_ASSERT(n == input_shape.rank(), "length of multiples should be the same with input rank");
+
+ loco::TensorShape output_shape;
+
+ output_shape.rank(input_shape.rank());
+ for (uint32_t ni = 0; ni < n; ++ni)
+ {
+ int32_t multiple = multiples->at<S32>(ni);
+ output_shape.dim(ni) = input_shape.dim(ni).value() * static_cast<uint32_t>(multiple);
+ }
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_transpose(const luci::CircleTranspose *node)
+{
+ auto input_shape = loco::shape_get(node->a()).as<loco::TensorShape>();
+
+ auto perm_node = loco::must_cast<luci::CircleConst *>(node->perm());
+
+ loco::TensorShape output_shape;
+ output_shape.rank(input_shape.rank());
+
+ assert(perm_node->dtype() == loco::DataType::S32);
+ assert(input_shape.rank() == perm_node->template size<loco::DataType::S32>());
+
+ for (uint32_t out_axis = 0; out_axis < output_shape.rank(); out_axis++)
+ {
+ auto in_axis = perm_node->template at<loco::DataType::S32>(out_axis);
+ output_shape.dim(out_axis) = input_shape.dim(in_axis);
+ }
+
+ return output_shape;
+}
+
+loco::NodeShape infer_transpose_conv(const luci::CircleTransposeConv *node)
+{
+ // TransposeConv's output shape is written in its 'inputSizes' argument
+ auto input_sizes_const = loco::must_cast<luci::CircleConst *>(node->inputSizes());
+ // TODO support non-const type
+ LUCI_ASSERT(input_sizes_const->dtype() == loco::DataType::S32, "Only support S32 dtype")
+ LUCI_ASSERT(input_sizes_const->rank() == 1 && input_sizes_const->dim(0).value() == 4,
+ "Only support rank 1 with 4 entries")
+
+ loco::TensorShape shape;
+
+ shape.rank(4);
+ for (uint32_t axis = 0; axis < 4; ++axis)
+ shape.dim(axis) = input_sizes_const->at<loco::DataType::S32>(axis);
+
+ return loco::NodeShape{shape};
+}
+
+loco::NodeShape infer_unpack(const luci::CircleUnpack *node)
+{
+ // CircleUnpack provides list(array) of Tensors which has one less dimension of the input
+ // We'll set shape of CircleUnpack to shape of actual outputs
+ // TODO fix this if any problem rises
+ auto value_shape = loco::shape_get(node->value()).as<loco::TensorShape>();
+
+ auto axis = node->axis();
+ auto num = node->num();
+ auto rank = static_cast<int32_t>(value_shape.rank());
+
+ if (rank == 0)
+ {
+ // Unknown shape
+ return use_own(node);
+ }
+
+ LUCI_ASSERT(-rank <= axis && axis < rank, "Axis is out of range");
+
+ if (axis < 0)
+ axis += rank;
+
+ LUCI_ASSERT(num == static_cast<int32_t>(value_shape.dim(axis).value()),
+ "num, axis maybe incorrect");
+
+ loco::TensorShape output_shape;
+ output_shape.rank(rank - 1);
+
+ for (int32_t i = 0, o = 0; i < rank; ++i)
+ {
+ if (i != axis)
+ output_shape.dim(o++) = value_shape.dim(i);
+ }
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_unidirectionalsequencelstm(const luci::CircleUnidirectionalSequenceLSTM *node)
+{
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ auto recurrent_to_output_weights =
+ loco::shape_get(node->recurrent_to_output_weights()).as<loco::TensorShape>();
+ auto rank = input_shape.rank();
+ loco::TensorShape output_shape;
+ output_shape.rank(rank);
+ for (uint32_t i = 0; i < rank - 1; i++)
+ {
+ output_shape.dim(i) = input_shape.dim(i);
+ }
+ output_shape.dim(rank - 1) = recurrent_to_output_weights.dim(1);
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_unique(const luci::CircleUnique *node)
+{
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+
+ assert(input_shape.rank() == 1);
+
+ loco::TensorShape shape_output;
+ shape_output = own_shape(node);
+
+ return loco::NodeShape{shape_output};
+}
+
+// Circle Only
+loco::NodeShape infer_bcq_fully_connected(const luci::CircleBCQFullyConnected *node)
+{
+ loco::TensorShape out_shape;
+
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ auto weights_clusters = loco::must_cast<luci::CircleConst *>(node->weights_clusters());
+
+ LUCI_ASSERT(input_shape.rank() == 2, "Input rank of BCQFullyConnected should be 2");
+
+ int32_t qbits_sum = 0;
+ for (uint32_t i = 0; i < weights_clusters->dim(0).value(); ++i)
+ {
+ qbits_sum += weights_clusters->at<loco::DataType::S32>(i * 2 + 1);
+ }
+
+ out_shape.rank(2);
+ out_shape.dim(0) = qbits_sum;
+ out_shape.dim(1) = input_shape.dim(1);
+
+ return loco::NodeShape{out_shape};
+}
+
+loco::NodeShape infer_bcq_gather(const luci::CircleBCQGather *node)
+{
+ loco::TensorShape input_shape;
+ loco::TensorShape output_shape;
+
+ const auto input_binary_shape = loco::shape_get(node->input_binary()).as<loco::TensorShape>();
+ const auto indices_shape = loco::shape_get(node->indices()).as<loco::TensorShape>();
+ auto axis = node->axis();
+
+ auto input_clusters = loco::must_cast<luci::CircleConst *>(node->input_clusters());
+ auto qbits_sum = 0;
+ for (uint32_t i = 0; i < input_clusters->dim(0).value(); ++i)
+ {
+ qbits_sum += input_clusters->at<loco::DataType::S32>(i * 2 + 1);
+ }
+
+ input_shape.rank(2);
+ input_shape.dim(0) = qbits_sum;
+ input_shape.dim(1) = input_binary_shape.dim(1).value() * 32;
+
+ output_shape.rank(input_shape.rank() - 1 + indices_shape.rank());
+ int32_t outdim_index = 0;
+ for (int32_t i = 0; i < axis; ++i)
+ output_shape.dim(outdim_index++) = input_shape.dim(i);
+ for (uint32_t i = 0; i < indices_shape.rank(); ++i)
+ output_shape.dim(outdim_index++) = indices_shape.dim(i);
+ for (uint32_t i = axis + 1; i < input_shape.rank(); ++i)
+ output_shape.dim(outdim_index++) = input_shape.dim(i);
+
+ return loco::NodeShape{output_shape};
+}
+
+// Virtual
+loco::NodeShape infer_input(const luci::CircleInput *node)
+{
+ loco::TensorShape shape;
+
+ shape.rank(node->rank());
+ for (uint32_t axis = 0; axis < node->rank(); axis++)
+ shape.dim(axis) = node->dim(axis);
+
+ return loco::NodeShape{shape};
+}
+
+loco::NodeShape infer_output(const luci::CircleOutput *node)
+{
+ auto graph_outputs = node->graph()->outputs();
+ auto graph_output = graph_outputs->at(node->index());
+ auto output_shape = graph_output->shape();
+
+ return loco::NodeShape{*output_shape};
+}
+
+loco::NodeShape infer_if_out(const luci::CircleIfOut *node)
+{
+ /**
+ * @note IF operator type and shape are that of the "then" and "else"
+ * Graph Outputs.
+ */
+ auto circle_if = dynamic_cast<const luci::CircleIf *>(node->input());
+ if (circle_if == nullptr)
+ {
+ INTERNAL_EXN("CircleIf IR is not configured correctly");
+ }
+
+ auto index = node->index();
+ auto then_graph = circle_if->then_graph();
+ auto else_graph = circle_if->else_graph();
+ assert(then_graph != nullptr);
+ assert(else_graph != nullptr);
+
+ // shape and type are assumed to be same
+ // these are checked at post_import_graph() in Import
+ auto then_outputs = loco::output_nodes(then_graph);
+ auto else_outputs = loco::output_nodes(else_graph);
+ assert(then_outputs.size() == else_outputs.size());
+ assert(index < static_cast<int32_t>(then_outputs.size()));
+
+ auto then_out = loco::must_cast<luci::CircleOutput *>(then_outputs.at(index));
+ auto else_out = loco::must_cast<luci::CircleOutput *>(else_outputs.at(index));
+
+ auto then_graph_outputs = then_graph->outputs(); // loco::GraphOutput items
+ auto else_graph_outputs = else_graph->outputs();
+ assert(then_graph_outputs->size() == else_graph_outputs->size());
+
+ auto then_graph_output = then_graph_outputs->at(then_out->index());
+ auto else_graph_output = else_graph_outputs->at(else_out->index());
+ (void)else_graph_output; // make compiler happy for unused variable warnings
+ assert(*then_graph_output->shape() == *else_graph_output->shape());
+
+ return loco::NodeShape{*then_graph_output->shape()};
+}
+
+loco::NodeShape infer_non_max_suppression_v4_out(const luci::CircleNonMaxSuppressionV4Out *node)
+{
+ const loco::DataType S32 = loco::DataType::S32;
+
+ auto nmsv4 = dynamic_cast<const luci::CircleNonMaxSuppressionV4 *>(node->input());
+ if (nmsv4 == nullptr)
+ INTERNAL_EXN("CircleNonMaxSuppressionV4 IR is not configured correctly");
+
+ auto index = node->index();
+ if (index == 1)
+ return loco::TensorShape({0});
+
+ assert(index == 0);
+
+ auto unknown = loco::TensorShape{loco::Dimension()};
+ auto max_output_size = dynamic_cast<const luci::CircleConst *>(nmsv4->max_output_size());
+ if (max_output_size == nullptr)
+ return unknown; // we need CircleConst for max output size
+
+ LUCI_ASSERT(max_output_size->dtype() == S32, "Only support int32 for max_output_size");
+
+ if (max_output_size->size<S32>() < 1)
+ return unknown;
+
+ auto max_output_size_value = uint32_t(max_output_size->at<S32>(0));
+ return loco::TensorShape{max_output_size_value};
+}
+
+loco::NodeShape infer_non_max_suppression_v5_out(const luci::CircleNonMaxSuppressionV5Out *node)
+{
+ const loco::DataType S32 = loco::DataType::S32;
+
+ auto nmsv5 = dynamic_cast<const luci::CircleNonMaxSuppressionV5 *>(node->input());
+ if (nmsv5 == nullptr)
+ INTERNAL_EXN("CircleNonMaxSuppressionV5 IR is not configured correctly");
+
+ auto index = node->index();
+ if (index == 2)
+ return loco::TensorShape({0});
+
+ assert(index == 0 || index == 1);
+
+ auto unknown = loco::TensorShape{loco::Dimension()};
+ auto max_output_size = dynamic_cast<const luci::CircleConst *>(nmsv5->max_output_size());
+ if (max_output_size == nullptr)
+ return unknown; // we need CircleConst for max output size
+
+ LUCI_ASSERT(max_output_size->dtype() == S32, "Only support int32 for max_output_size");
+
+ if (max_output_size->size<S32>() < 1)
+ return unknown;
+
+ auto max_output_size_value = uint32_t(max_output_size->at<S32>(0));
+ return loco::TensorShape{max_output_size_value};
+}
+
+loco::NodeShape infer_split_out(const luci::CircleSplitOut *node)
+{
+ const loco::DataType S32 = loco::DataType::S32;
+
+ auto split = dynamic_cast<const luci::CircleSplit *>(node->input());
+ if (split == nullptr)
+ INTERNAL_EXN("CircleSplit IR is not configured correctly");
+
+ loco::NodeShape unknown;
+
+ auto split_shape = loco::shape_get(split).as<loco::TensorShape>();
+
+ auto split_dim = dynamic_cast<const luci::CircleConst *>(split->split_dim());
+ if (split_dim == nullptr)
+ return unknown; // we need CircleConst for split_dim
+ LUCI_ASSERT(split_dim->dtype() == S32, "Only support int32 for split_dim");
+
+ assert(split_dim->size<S32>() == 1);
+ auto split_dim_axis = split_dim->at<S32>(0);
+ if (split_dim_axis < 0)
+ split_dim_axis += split_shape.rank();
+
+ auto split_dim_value = split_shape.dim(split_dim_axis).value();
+ assert(split_dim_value % split->num_split() == 0);
+ const int split_depth = split_dim_value / split->num_split();
+
+ loco::TensorShape output_shape = split_shape;
+
+ // All shapes are equally same
+ output_shape.dim(split_dim_axis) = loco::Dimension(split_depth);
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_split_v_out(const luci::CircleSplitVOut *node)
+{
+ const loco::DataType S32 = loco::DataType::S32;
+
+ auto split = dynamic_cast<const luci::CircleSplitV *>(node->input());
+ if (split == nullptr)
+ INTERNAL_EXN("CircleSplit IR is not configured correctly");
+
+ loco::NodeShape unknown;
+
+ auto split_shape = loco::shape_get(split).as<loco::TensorShape>();
+
+ auto size_splits = dynamic_cast<const luci::CircleConst *>(split->size_splits());
+ if (size_splits == nullptr)
+ return unknown; // we need CircleConst for size_splits
+ LUCI_ASSERT(size_splits->dtype() == S32, "Only support int32 for size_splits");
+
+ auto split_dim = dynamic_cast<const luci::CircleConst *>(split->split_dim());
+ if (split_dim == nullptr)
+ return unknown; // we need CircleConst for split_dim
+ LUCI_ASSERT(split_dim->dtype() == S32, "Only support int32 for split_dim");
+
+ // fetch axis
+ assert(split_dim->size<S32>() == 1);
+ auto split_dim_axis = split_dim->at<S32>(0);
+ if (split_dim_axis < 0)
+ split_dim_axis += split_shape.rank();
+
+ // interpret size_splits values
+ int32_t size_splits_count = static_cast<int32_t>(size_splits->size<S32>());
+ assert(size_splits_count == split->num_split());
+
+ int64_t minus_one_count = 0, size_splits_sum = 0;
+ for (int32_t idx = 0; idx < size_splits_count; ++idx)
+ {
+ auto size = size_splits->at<S32>(idx);
+ assert(size >= -1);
+ if (size == -1)
+ ++minus_one_count;
+ else
+ size_splits_sum += size;
+ }
+ if (minus_one_count > 1)
+ INTERNAL_EXN("CircleSplitV size_splits has more than two -1 values");
+
+ // calcuate this SplitVOut shape
+ auto input_size = split_shape.dim(split_dim_axis).value();
+ assert(size_splits_sum <= input_size);
+
+ auto index_this = node->index();
+ assert(0 <= index_this && index_this < split->num_split());
+ auto split_depth = size_splits->at<S32>(index_this);
+ if (split_depth == -1)
+ split_depth = input_size - size_splits_sum;
+
+ loco::TensorShape output_shape = split_shape;
+
+ output_shape.dim(split_dim_axis) = loco::Dimension(split_depth);
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_top_k_v2_out(const luci::CircleTopKV2Out *node)
+{
+ const loco::DataType S32 = loco::DataType::S32;
+
+ auto topkv2 = dynamic_cast<const luci::CircleTopKV2 *>(node->input());
+ if (topkv2 == nullptr)
+ INTERNAL_EXN("CircleSplit IR is not configured correctly");
+
+ // shape of topkv2 is same as topkv2->input()
+ auto input_shape = loco::shape_get(topkv2).as<loco::TensorShape>();
+
+ auto node_k = loco::must_cast<const luci::CircleConst *>(topkv2->k());
+ LUCI_ASSERT(node_k->dtype() == S32, "Only support Int32");
+ assert(node_k->size<S32>() == 1);
+
+ loco::TensorShape output_shape;
+
+ output_shape.rank(input_shape.rank());
+ for (uint32_t idx = 0; idx < input_shape.rank() - 1; ++idx)
+ {
+ output_shape.dim(idx) = input_shape.dim(idx);
+ }
+ output_shape.dim(input_shape.rank() - 1) = node_k->at<S32>(0);
+
+ return loco::NodeShape{output_shape};
+}
+
+loco::NodeShape infer_unique_out(const luci::CircleUniqueOut *node)
+{
+ if (node->index() == 0)
+ {
+ auto unique_shape = own_shape(node);
+ return loco::NodeShape{unique_shape};
+ }
+ assert(node->index() == 1);
+ auto unique = loco::must_cast<luci::CircleUnique *>(node->input());
+ auto unique_shape = loco::shape_get(unique->input()).as<loco::TensorShape>();
+
+ assert(unique_shape.rank() == 1);
+
+ loco::TensorShape shape_output;
+ shape_output.rank(1);
+ shape_output.dim(0) = unique_shape.dim(0);
+ return loco::NodeShape{shape_output};
+}
+
+loco::NodeShape infer_unpack_out(const luci::CircleUnpackOut *node)
+{
+ auto unpack = dynamic_cast<const luci::CircleUnpack *>(node->input());
+ if (unpack == nullptr)
+ {
+ INTERNAL_EXN("CircleUnpack IR is not configured correctly");
+ }
+
+ auto unpack_shape = loco::shape_get(unpack).as<loco::TensorShape>();
+
+ return loco::NodeShape{unpack_shape};
+}
+
+loco::NodeShape infer_while_out(const luci::CircleWhileOut *node)
+{
+ /**
+ * @note WHILE operator's shape is the same with the "cond"
+ * Graph input.
+ */
+ auto circle_while = dynamic_cast<const luci::CircleWhile *>(node->input());
+ if (circle_while == nullptr)
+ {
+ INTERNAL_EXN("CircleWhile IR is not configured correctly");
+ }
+
+ auto index = node->index();
+ auto cond_graph = circle_while->cond_graph();
+ assert(cond_graph != nullptr);
+
+ // Assumption: the index of CircleWhileOut matches with the index of input nodes returned by
+ // loco::input_nodes
+ auto cond_inputs = loco::input_nodes(cond_graph);
+ auto cond_in = loco::must_cast<luci::CircleInput *>(cond_inputs.at(index));
+
+ auto cond_graph_inputs = cond_graph->inputs();
+ auto cond_graph_input = cond_graph_inputs->at(cond_in->index());
+
+ auto cond_graph_input_shape = *cond_graph_input->shape();
+ auto this_shape = own_shape(node);
+
+ if (!(this_shape == cond_graph_input_shape))
+ {
+ LOGGER(l);
+ WARN(l) << "Warning: CircleWhileOut '" << node->name() << "' shape mispatch " << this_shape
+ << " vs " << cond_graph_input_shape;
+ }
+
+ return loco::NodeShape{this_shape};
+}
+
+/**
+ * @brief Class to infer the shape of CircleNode
+ *
+ * @note All CircleNode's inputs and outputs are always loco::Domain::Tensor
+ */
+class ShapeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::NodeShape>
+{
+public:
+ loco::NodeShape visit(const luci::CircleAbs *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleAdd *node) final { return broadcast_xy(node); }
+
+ loco::NodeShape visit(const luci::CircleAddN *node) final { return infer_add_n(node); }
+
+ loco::NodeShape visit(const luci::CircleArgMax *node) final { return infer_arg_max(node); }
+
+ loco::NodeShape visit(const luci::CircleArgMin *node) final { return infer_arg_min(node); }
+
+ loco::NodeShape visit(const luci::CircleAveragePool2D *node) final
+ {
+ return infer_pool_2d_shape(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleBatchMatMul *node) final
+ {
+ auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+ auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>();
+
+ return infer_batchmatmul_shape(x_shape, y_shape, node->adj_x(), node->adj_y());
+ }
+
+ loco::NodeShape visit(const luci::CircleBatchToSpaceND *node) final
+ {
+ return infer_batch_to_space_nd(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleCast *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleCeil *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleConcatenation *node) final
+ {
+ return infer_concatenation(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleConst *node) final { return use_own(node); }
+
+ loco::NodeShape visit(const luci::CircleConv2D *node) final { return infer_conv2d(node); }
+
+ loco::NodeShape visit(const luci::CircleCos *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleCustom *node) final { return use_own(node); }
+
+ loco::NodeShape visit(const luci::CircleDepthToSpace *node) final
+ {
+ return infer_depth_to_space(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleDepthwiseConv2D *node) final
+ {
+ return infer_depthwise_conv2d(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleDequantize *node) final
+ {
+ const auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleDiv *node) final { return broadcast_xy(node); }
+
+ loco::NodeShape visit(const luci::CircleElu *node) final
+ {
+ auto input_shape = loco::shape_get(node->features()).as<loco::TensorShape>();
+
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleEqual *node) final { return broadcast_xy(node); }
+
+ loco::NodeShape visit(const luci::CircleExp *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleExpandDims *node) final
+ {
+ return infer_expand_dims(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleFill *node) final { return infer_fill(node); }
+
+ loco::NodeShape visit(const luci::CircleFloor *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleFloorDiv *node) final { return broadcast_xy(node); }
+
+ loco::NodeShape visit(const luci::CircleFloorMod *node) final { return broadcast_xy(node); }
+
+ loco::NodeShape visit(const luci::CircleFullyConnected *node) final
+ {
+ return infer_fully_connected(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleGather *node) final { return infer_gather(node); }
+
+ loco::NodeShape visit(const luci::CircleGatherNd *node) final { return infer_gather_nd(node); }
+
+ 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::CircleIf *node) final
+ {
+ // Shape of CircleIf is not used. Just use input 0
+ assert(node->input_count() > 0);
+ const auto input_shape = loco::shape_get(node->input(0)).as<loco::TensorShape>();
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleL2Normalize *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleL2Pool2D *node) final
+ {
+ return infer_pool_2d_shape(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleLeakyRelu *node) final
+ {
+ const auto input_shape = loco::shape_get(node->features()).as<loco::TensorShape>();
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleLess *node) final { return broadcast_xy(node); }
+
+ loco::NodeShape visit(const luci::CircleLessEqual *node) final { return broadcast_xy(node); }
+
+ loco::NodeShape visit(const luci::CircleLocalResponseNormalization *node) final
+ {
+ const auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleLog *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleLogicalAnd *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleLogicalNot *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleLogicalOr *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleLogistic *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleLogSoftmax *node) final { return use_logits(node); }
+
+ loco::NodeShape visit(const luci::CircleMatrixDiag *node) final
+ {
+ return infer_matrix_diag(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleMatrixSetDiag *node) final
+ {
+ return infer_matrix_set_diag(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleMaximum *node) final { return broadcast_xy(node); }
+
+ loco::NodeShape visit(const luci::CircleMaxPool2D *node) final
+ {
+ return infer_pool_2d_shape(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleMean *node) final
+ {
+ auto output_shape = infer_reducer(node->input(), node->reduction_indices(), node->keep_dims());
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleMinimum *node) final { return broadcast_xy(node); }
+
+ loco::NodeShape visit(const luci::CircleMirrorPad *node) final { return infer_mirror_pad(node); }
+
+ loco::NodeShape visit(const luci::CircleMul *node) final { return broadcast_xy(node); }
+
+ loco::NodeShape visit(const luci::CircleNeg *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleNonMaxSuppressionV4 *node) final
+ {
+ const auto boxes_shape = loco::shape_get(node->boxes()).as<loco::TensorShape>();
+ return loco::NodeShape{boxes_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleNonMaxSuppressionV5 *node) final
+ {
+ const auto boxes_shape = loco::shape_get(node->boxes()).as<loco::TensorShape>();
+ return loco::NodeShape{boxes_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleNotEqual *node) final { return broadcast_xy(node); }
+
+ loco::NodeShape visit(const luci::CircleOneHot *node) final { return infer_one_hot(node); }
+
+ loco::NodeShape visit(const luci::CirclePack *node) final { return infer_pack(node); }
+
+ loco::NodeShape visit(const luci::CirclePad *node) final { return infer_pad(node); }
+
+ loco::NodeShape visit(const luci::CirclePadV2 *node) final { return infer_pad_v2(node); }
+
+ loco::NodeShape visit(const luci::CirclePow *node) final { return broadcast_xy(node); }
+
+ loco::NodeShape visit(const luci::CirclePRelu *node) final { return infer_p_relu(node); }
+
+ loco::NodeShape visit(const luci::CircleRange *node) final { return infer_range(node); }
+
+ loco::NodeShape visit(const luci::CircleRank *) final
+ {
+ loco::TensorShape shape_output;
+ shape_output.rank(0);
+
+ return loco::NodeShape{shape_output};
+ }
+
+ loco::NodeShape visit(const luci::CircleReduceAny *node) final
+ {
+ auto output_shape = infer_reducer(node->input(), node->reduction_indices(), node->keep_dims());
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleReduceMax *node) final
+ {
+ auto output_shape = infer_reducer(node->input(), node->reduction_indices(), node->keep_dims());
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleReduceMin *node) final
+ {
+ auto output_shape = infer_reducer(node->input(), node->reduction_indices(), node->keep_dims());
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleReduceProd *node) final
+ {
+ auto output_shape = infer_reducer(node->input(), node->reduction_indices(), node->keep_dims());
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleRelu *node) final
+ {
+ auto input_shape = loco::shape_get(node->features()).as<loco::TensorShape>();
+
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleRelu6 *node) final
+ {
+ auto input_shape = loco::shape_get(node->features()).as<loco::TensorShape>();
+
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleReluN1To1 *node) final
+ {
+ auto input_shape = loco::shape_get(node->features()).as<loco::TensorShape>();
+
+ return loco::NodeShape{input_shape};
+ }
+
+ /**
+ * @note CircleReshape has new shape info in two places: 2nd input and attribute.
+ * This shape inference uses shape from input 'shape' node when it's constant.
+ * If not, shape will be from node itself. shape from attribute is not used.
+ *
+ * TODO Change this policy when not appropriate
+ */
+ loco::NodeShape visit(const luci::CircleReshape *node) final { return infer_reshape(node); }
+
+ loco::NodeShape visit(const luci::CircleResizeBilinear *node) final
+ {
+ return infer_resize_bilinear(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleResizeNearestNeighbor *node) final
+ {
+ return infer_resize_nearest_neighbor(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleReverseSequence *node) final
+ {
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleRound *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleReverseV2 *node) final
+ {
+ auto input_shape = loco::shape_get(node->tensor()).as<loco::TensorShape>();
+
+ LUCI_ASSERT(loco::shape_get(node->axis()).as<loco::TensorShape>().rank() == 1,
+ "Tensor must be 1-D");
+
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleRsqrt *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleScatterNd *node) final { return infer_scatter_nd(node); }
+
+ loco::NodeShape visit(const luci::CircleSegmentSum *node) final
+ {
+ return infer_segment_sum(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleSelect *node) final { return infer_select(node); }
+
+ loco::NodeShape visit(const luci::CircleSelectV2 *node) final { return infer_select_v2(node); }
+
+ loco::NodeShape visit(const luci::CircleShape *node) final { return infer_shape(node); }
+
+ loco::NodeShape visit(const luci::CircleSin *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleSlice *node) final { return infer_slice(node); }
+
+ loco::NodeShape visit(const luci::CircleSoftmax *node) final { return use_logits(node); }
+
+ loco::NodeShape visit(const luci::CircleSpaceToBatchND *node) final
+ {
+ return infer_space_to_batch_nd(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleSpaceToDepth *node) final
+ {
+ return infer_space_to_depth(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleSparseToDense *node) final
+ {
+ return infer_sparse_to_dense(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleSplit *node) final
+ {
+ // We'll set Split output as same as input so that SplitOut can handle it's own shape
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleSplitV *node) final
+ {
+ // We'll set SplitV output as same as input so that SplitOut can handle it's own shape
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleSqrt *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleSquare *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleSquaredDifference *node) final
+ {
+ return broadcast_xy(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleStridedSlice *node) final
+ {
+ return infer_strided_slice(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleSqueeze *node) final { return infer_squeeze(node); }
+
+ loco::NodeShape visit(const luci::CircleSub *node) final { return broadcast_xy(node); }
+
+ loco::NodeShape visit(const luci::CircleSum *node) final
+ {
+ auto output_shape = infer_reducer(node->input(), node->reduction_indices(), node->keep_dims());
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleTanh *node) final { return use_x(node); }
+
+ loco::NodeShape visit(const luci::CircleTile *node) final { return infer_tile(node); }
+
+ loco::NodeShape visit(const luci::CircleTopKV2 *node) final
+ {
+ // set shape of this node as same as input
+ const auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleTranspose *node) final { return infer_transpose(node); }
+
+ loco::NodeShape visit(const luci::CircleTransposeConv *node) final
+ {
+ return infer_transpose_conv(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleUnpack *node) final { return infer_unpack(node); }
+
+ loco::NodeShape visit(const luci::CircleUnidirectionalSequenceLSTM *node) final
+ {
+ return infer_unidirectionalsequencelstm(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleUnique *node) final { return infer_unique(node); }
+
+ loco::NodeShape visit(const luci::CircleWhere *node) final { return use_own(node); }
+
+ loco::NodeShape visit(const luci::CircleWhile *node) final
+ {
+ // Shape of CircleWhile is not used. Just use input 0
+ assert(node->arity() > 0);
+ const auto input_shape = loco::shape_get(node->input(0)).as<loco::TensorShape>();
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleZerosLike *node) final
+ {
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+
+ return loco::NodeShape{input_shape};
+ }
+
+ // Circle Only
+ loco::NodeShape visit(const luci::CircleBCQFullyConnected *node) final
+ {
+ return infer_bcq_fully_connected(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleBCQGather *node) final { return infer_bcq_gather(node); }
+
+ loco::NodeShape visit(const luci::CircleInstanceNorm *node) final
+ {
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+
+ return loco::NodeShape{input_shape};
+ }
+
+ // Virtual
+ loco::NodeShape visit(const luci::CircleInput *node) final { return infer_input(node); }
+
+ loco::NodeShape visit(const luci::CircleOutput *node) final { return infer_output(node); }
+
+ loco::NodeShape visit(const luci::CircleOutputDummy *node) final { return use_own(node); }
+
+ loco::NodeShape visit(const luci::CircleOutputExclude *node) final { return use_own(node); }
+
+ loco::NodeShape visit(const luci::CircleCustomOut *node) final { return use_own(node); }
+
+ loco::NodeShape visit(const luci::CircleIfOut *node) final { return infer_if_out(node); }
+
+ loco::NodeShape visit(const luci::CircleNonMaxSuppressionV4Out *node) final
+ {
+ return infer_non_max_suppression_v4_out(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleNonMaxSuppressionV5Out *node) final
+ {
+ return infer_non_max_suppression_v5_out(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleSplitOut *node) final { return infer_split_out(node); }
+
+ loco::NodeShape visit(const luci::CircleSplitVOut *node) final { return infer_split_v_out(node); }
+
+ loco::NodeShape visit(const luci::CircleTopKV2Out *node) final
+ {
+ return infer_top_k_v2_out(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleUniqueOut *node) final { return infer_unique_out(node); }
+
+ loco::NodeShape visit(const luci::CircleUnpackOut *node) final { return infer_unpack_out(node); }
+
+ loco::NodeShape visit(const luci::CircleWhileOut *node) final { return infer_while_out(node); }
+};
+
+} // namespace
+
+namespace luci
+{
+
+bool CircleShapeInferenceRule::recognize(const loco::Dialect *d) const
+{
+ return CircleDialect::get() == d;
+}
+
+bool CircleShapeInferenceRule::infer(const loco::Node *node, loco::NodeShape &shape) const
+{
+ LOGGER(l);
+
+ assert(node->dialect() == CircleDialect::get());
+
+ ShapeInferenceAlgorithm alg;
+ auto circle_node = loco::must_cast<const CircleNode *>(node);
+
+ bool is_shape_undefined = (circle_node->shape_status() == ShapeStatus::UNDEFINED);
+ bool is_shape_none = (circle_node->shape_status() == ShapeStatus::NOSHAPE);
+ bool is_scalar = (circle_node->rank() == 0);
+
+ if (is_shape_undefined)
+ shape = circle_node->accept(&alg);
+ else
+ {
+ if (is_shape_none || is_scalar)
+ shape = own_shape(circle_node);
+ else
+ shape = circle_node->accept(&alg);
+ }
+
+ VERBOSE(l, 1) << "[luci] shape: " << circle_node->name();
+ VERBOSE(l, 1) << " own_shape: " << own_shape(circle_node)
+ << " -> infer: " << shape.as<loco::TensorShape>();
+
+ return true;
+}
+
+} // namespace luci
diff --git a/compiler/luci/service/src/CircleShapeInferenceRule.test.cpp b/compiler/luci/service/src/CircleShapeInferenceRule.test.cpp
new file mode 100644
index 000000000..ac27db3bd
--- /dev/null
+++ b/compiler/luci/service/src/CircleShapeInferenceRule.test.cpp
@@ -0,0 +1,626 @@
+/*
+ * 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 "TestGraph.h"
+#include "luci/Service/CircleShapeInferenceRule.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/CircleDialect.h>
+
+#include <loco.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/Service/ShapeInference.h>
+#include <loco/Service/CanonicalShapeInferenceRule.h>
+#include <loco/Service/MultiDialectShapeInferenceRule.h>
+
+#include <oops/InternalExn.h>
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+namespace
+{
+
+bool shape_pass(loco::Graph *g)
+{
+ loco::CanonicalShapeInferenceRule canonical_rule;
+ luci::CircleShapeInferenceRule circle_rule;
+ loco::MultiDialectShapeInferenceRule rules;
+
+ rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
+ .bind(luci::CircleDialect::get(), &circle_rule);
+
+ return loco::apply(&rules).to(g);
+}
+
+} // namespace
+
+TEST(CircleShapeInferenceRuleTest, minimal_with_CircleRelu)
+{
+ // Create a simple network
+ luci::test::TestGraph graph;
+ auto relu_node = graph.append<luci::CircleRelu>(graph.input_node);
+ graph.complete(relu_node);
+
+ // set shape
+ {
+ graph.input_node->rank(2);
+ graph.input_node->dim(0) = 3;
+ graph.input_node->dim(1) = 4;
+
+ graph.output_node->rank(2);
+ graph.output_node->dim(0) = 3;
+ graph.output_node->dim(1) = 4;
+
+ luci::test::graph_input_shape(graph.input_node);
+ luci::test::graph_output_shape(graph.output_node);
+ }
+
+ // pre-check
+ ASSERT_FALSE(loco::shape_known(relu_node));
+
+ // shape inference
+ while (shape_pass(graph.graph()) == true)
+ ;
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(relu_node));
+ ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(relu_node).domain());
+
+ auto shape = loco::shape_get(relu_node).as<loco::TensorShape>();
+ ASSERT_EQ(2, shape.rank());
+ ASSERT_EQ(3, shape.dim(0));
+ ASSERT_EQ(4, shape.dim(1));
+ }
+}
+
+// based on the case shown in
+// https://www.corvil.com/kb/what-is-the-difference-between-same-and-valid-padding-in-tf-nn-max-pool-of-tensorflow
+TEST(CircleShapeInferenceRuleTest, avgpool2d_valid)
+{
+ luci::test::TestGraph graph;
+ auto avg_node = graph.append<luci::CircleAveragePool2D>(graph.input_node);
+ graph.complete();
+
+ auto input_node = graph.input_node;
+ {
+ input_node->shape({1, 4, 3, 1});
+ luci::test::graph_input_shape(input_node);
+ }
+ auto output_node = graph.output_node;
+ {
+ output_node->shape({1, 2, 1, 1});
+ luci::test::graph_output_shape(output_node);
+ }
+ // setting CircleAveragePool2D
+ {
+ avg_node->filter()->h(2);
+ avg_node->filter()->w(2);
+ avg_node->stride()->h(2);
+ avg_node->stride()->w(2);
+ avg_node->fusedActivationFunction(luci::FusedActFunc::NONE);
+ avg_node->padding(luci::Padding::VALID);
+ }
+ ASSERT_FALSE(loco::shape_known(avg_node));
+
+ // shape inference
+ while (shape_pass(graph.graph()) == true)
+ ;
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(avg_node));
+ ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(avg_node).domain());
+
+ auto shape = loco::shape_get(avg_node).as<loco::TensorShape>();
+ ASSERT_EQ(4, shape.rank());
+ ASSERT_EQ(1, shape.dim(0).value());
+ ASSERT_EQ(2, shape.dim(1).value());
+ ASSERT_EQ(1, shape.dim(2).value());
+ ASSERT_EQ(1, shape.dim(3).value());
+ }
+}
+
+TEST(CircleShapeInferenceRuleTest, avgpool2d_same)
+{
+ luci::test::TestGraph graph;
+ auto avg_node = graph.append<luci::CircleAveragePool2D>(graph.input_node);
+ graph.complete();
+
+ auto input_node = graph.input_node;
+ {
+ input_node->shape({1, 4, 3, 1});
+ luci::test::graph_input_shape(input_node);
+ }
+ auto output_node = graph.output_node;
+ {
+ output_node->shape({1, 2, 2, 1});
+ luci::test::graph_output_shape(output_node);
+ }
+
+ // setting CircleAveragePool2D
+ {
+ avg_node->filter()->h(2);
+ avg_node->filter()->w(2);
+ avg_node->stride()->h(2);
+ avg_node->stride()->w(2);
+ avg_node->fusedActivationFunction(luci::FusedActFunc::NONE);
+ avg_node->padding(luci::Padding::SAME);
+ }
+
+ ASSERT_FALSE(loco::shape_known(avg_node));
+
+ // shape inference
+ while (shape_pass(graph.graph()) == true)
+ ;
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(avg_node));
+ ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(avg_node).domain());
+
+ auto shape = loco::shape_get(avg_node).as<loco::TensorShape>();
+ ASSERT_EQ(4, shape.rank());
+ ASSERT_EQ(1, shape.dim(0).value());
+ ASSERT_EQ(2, shape.dim(1).value());
+ ASSERT_EQ(2, shape.dim(2).value());
+ ASSERT_EQ(1, shape.dim(3).value());
+ }
+}
+
+/**
+ * @note Function to test: Shape inference of two different input shapes
+ *
+ * Rank expansion to higher input side
+ * x(2,1,5) + y(3,5) --> x(2,1,5) + y(1,3,5)
+ * Do output shape inference like numpy
+ * x(2,1,5) + y(1,3,5) --> output(2,3,5)
+ * For each axis, dim value should be same OR one of them should be 1
+ */
+TEST(CircleShapeInferenceRuleTest, TFAdd_shapeinf_different)
+{
+ auto g = loco::make_graph();
+
+ auto x_node = g->nodes()->create<luci::CircleInput>();
+ {
+ x_node->rank(3);
+ x_node->dim(0) = 2;
+ x_node->dim(1) = 1;
+ x_node->dim(2) = 5;
+ }
+ auto y_node = g->nodes()->create<luci::CircleInput>();
+ {
+ y_node->rank(2);
+ y_node->dim(0) = 3;
+ y_node->dim(1) = 5;
+ }
+ auto add_node = g->nodes()->create<luci::CircleAdd>();
+ {
+ add_node->x(x_node);
+ add_node->y(y_node);
+ }
+ auto output_node = g->nodes()->create<luci::CircleOutput>();
+ {
+ output_node->from(add_node);
+ }
+
+ auto x_input = g->inputs()->create();
+ {
+ x_input->name("x");
+ luci::link(x_input, x_node);
+ }
+ auto y_input = g->inputs()->create();
+ {
+ y_input->name("y");
+ luci::link(y_input, y_node);
+ }
+ auto output = g->outputs()->create();
+ {
+ output->name("output");
+ luci::link(output, output_node);
+ }
+
+ luci::test::graph_input_shape(x_node);
+ luci::test::graph_input_shape(y_node);
+ luci::test::graph_output_shape(output_node);
+
+ // pre-check
+ ASSERT_FALSE(loco::shape_known(add_node));
+
+ // shape inference
+ while (shape_pass(g.get()) == true)
+ ;
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(add_node));
+ ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(add_node).domain());
+
+ auto shape = loco::shape_get(add_node).as<loco::TensorShape>();
+ ASSERT_EQ(3, shape.rank());
+ ASSERT_EQ(2, shape.dim(0));
+ ASSERT_EQ(3, shape.dim(1));
+ ASSERT_EQ(5, shape.dim(2));
+ }
+}
+
+TEST(CircleShapeInferenceRuleTest, CircleTranspose_simple)
+{
+ luci::test::ExampleGraph<luci::test::ExampleGraphType::CircleTranspose> g;
+
+ g.input_node->rank(3);
+ g.input_node->dim(0) = 3;
+ g.input_node->dim(1) = 8;
+ g.input_node->dim(2) = 1;
+
+ g.const_perm->dtype(loco::DataType::S32);
+ g.const_perm->rank(1);
+ g.const_perm->dim(0) = 3;
+ g.const_perm->size<loco::DataType::S32>(3);
+ g.const_perm->at<loco::DataType::S32>(0) = 1;
+ g.const_perm->at<loco::DataType::S32>(1) = 2;
+ g.const_perm->at<loco::DataType::S32>(2) = 0;
+
+ luci::test::graph_input_shape(g.input_node);
+ luci::test::graph_output_shape(g.output_node);
+
+ // pre-check
+ ASSERT_FALSE(loco::shape_known(g.transpose_node));
+
+ // shape inference
+ while (shape_pass(g.graph()) == true)
+ ;
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(g.transpose_node));
+
+ auto shape = loco::shape_get(g.transpose_node).as<loco::TensorShape>();
+ ASSERT_EQ(3, shape.rank());
+ ASSERT_EQ(8, shape.dim(0));
+ ASSERT_EQ(1, shape.dim(1));
+ ASSERT_EQ(3, shape.dim(2));
+ }
+}
+
+TEST(CircleShapeInferenceRuleTest, CircleSqueeze)
+{
+ luci::test::TestGraph graph;
+ auto squeeze_node = graph.append<luci::CircleSqueeze>(graph.input_node);
+ graph.complete();
+
+ auto input_node = graph.input_node;
+ {
+ input_node->shape({1, 4, 3, 1});
+ }
+ auto output_node = graph.output_node;
+ {
+ output_node->shape({4, 3, 1});
+ }
+
+ luci::test::graph_input_shape(input_node);
+ luci::test::graph_output_shape(output_node);
+
+ squeeze_node->squeeze_dims({0});
+
+ // pre-check
+ ASSERT_FALSE(loco::shape_known(squeeze_node));
+
+ // shape inference
+ while (shape_pass(graph.graph()) == true)
+ ;
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(squeeze_node));
+
+ auto shape = loco::shape_get(squeeze_node).as<loco::TensorShape>();
+ ASSERT_EQ(3, shape.rank());
+ ASSERT_EQ(4, shape.dim(0));
+ ASSERT_EQ(3, shape.dim(1));
+ ASSERT_EQ(1, shape.dim(2));
+ }
+}
+
+TEST(CircleShapeInferenceRuleTest, CircleExpandDims)
+{
+ luci::test::TestGraph graph;
+ auto axis = graph.append<luci::CircleConst>();
+ axis->dtype(loco::DataType::S32);
+ axis->rank(0);
+ axis->size<loco::DataType::S32>(1);
+ axis->at<loco::DataType::S32>(0) = 1;
+
+ auto expand_dims = graph.append<luci::CircleExpandDims>(graph.input_node, axis);
+ graph.complete();
+
+ auto input_node = graph.input_node;
+ {
+ input_node->shape({4, 3});
+ }
+
+ auto output_node = graph.output_node;
+ {
+ output_node->from(expand_dims);
+ }
+
+ luci::test::graph_input_shape(input_node);
+ luci::test::graph_output_shape(output_node);
+
+ // shape inference
+ while (shape_pass(graph.graph()))
+ ;
+
+ // validation
+ {
+ ASSERT_TRUE(loco::shape_known(expand_dims));
+
+ auto shape = loco::shape_get(expand_dims).as<loco::TensorShape>();
+
+ ASSERT_EQ(3, shape.rank());
+ ASSERT_EQ(4, shape.dim(0));
+ ASSERT_EQ(1, shape.dim(1));
+ ASSERT_EQ(3, shape.dim(2));
+ }
+}
+
+TEST(CircleShapeInferenceRuleTest, CircleSqueezeAll)
+{
+ luci::test::TestGraph graph;
+ auto squeeze_node = graph.append<luci::CircleSqueeze>(graph.input_node);
+ graph.complete();
+
+ auto input_node = graph.input_node;
+ {
+ input_node->shape({1, 4, 3, 1});
+ }
+ auto output_node = graph.output_node;
+ {
+ input_node->shape({4, 3});
+ }
+
+ luci::test::graph_input_shape(input_node);
+ luci::test::graph_output_shape(output_node);
+
+ squeeze_node->squeeze_dims({});
+
+ // pre-check
+ ASSERT_FALSE(loco::shape_known(squeeze_node));
+
+ // shape inference
+ while (shape_pass(graph.graph()) == true)
+ ;
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(squeeze_node));
+
+ auto shape = loco::shape_get(squeeze_node).as<loco::TensorShape>();
+ ASSERT_EQ(2, shape.rank());
+ ASSERT_EQ(4, shape.dim(0));
+ ASSERT_EQ(3, shape.dim(1));
+ }
+}
+
+TEST(CircleShapeInferenceRuleTest, CircleGatherNd_simple)
+{
+ luci::test::TestGraph graph;
+ auto indices_const = graph.append<luci::CircleConst>();
+ auto gather_nd_node = graph.append<luci::CircleGatherNd>(graph.input_node, indices_const);
+ graph.complete();
+
+ {
+ auto input_node = graph.input_node;
+ input_node->shape({1, 4, 4, 3});
+ luci::test::graph_input_shape(input_node);
+ }
+ {
+ auto output_node = graph.output_node;
+ output_node->shape({1, 2, 2, 3});
+ luci::test::graph_output_shape(output_node);
+ }
+
+ {
+ indices_const->shape({1, 2, 3});
+ }
+
+ // pre-check
+ ASSERT_FALSE(loco::shape_known(gather_nd_node));
+
+ // shape inference
+ while (shape_pass(graph.graph()) == true)
+ ;
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(gather_nd_node));
+
+ auto shape = loco::shape_get(gather_nd_node).as<loco::TensorShape>();
+ ASSERT_EQ(3, shape.rank());
+ ASSERT_EQ(1, shape.dim(0));
+ ASSERT_EQ(2, shape.dim(1));
+ ASSERT_EQ(3, shape.dim(2));
+ }
+}
+
+TEST(CircleShapeInferenceRuleTest, CircleGatherNd_slices)
+{
+ luci::test::TestGraph graph;
+ auto indices_const = graph.append<luci::CircleConst>();
+ auto gather_nd_node = graph.append<luci::CircleGatherNd>(graph.input_node, indices_const);
+ graph.complete();
+
+ {
+ auto input_node = graph.input_node;
+ input_node->shape({1, 4, 4, 3});
+ luci::test::graph_input_shape(input_node);
+ }
+ {
+ auto output_node = graph.output_node;
+ output_node->shape({1, 2, 4, 4, 3});
+ luci::test::graph_output_shape(output_node);
+ }
+
+ {
+ indices_const->shape({1, 2, 1});
+ }
+
+ // pre-check
+ ASSERT_FALSE(loco::shape_known(gather_nd_node));
+
+ // shape inference
+ while (shape_pass(graph.graph()) == true)
+ ;
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(gather_nd_node));
+
+ auto shape = loco::shape_get(gather_nd_node).as<loco::TensorShape>();
+ ASSERT_EQ(5, shape.rank());
+ ASSERT_EQ(1, shape.dim(0));
+ ASSERT_EQ(2, shape.dim(1));
+ ASSERT_EQ(4, shape.dim(2));
+ ASSERT_EQ(4, shape.dim(3));
+ ASSERT_EQ(3, shape.dim(4));
+ }
+}
+
+TEST(CircleShapeInferenceRuleTest, CircleGatherNd_NEG)
+{
+ luci::test::TestGraph graph;
+ auto indices_const = graph.append<luci::CircleConst>();
+ auto gather_nd_node = graph.append<luci::CircleGatherNd>(graph.input_node, indices_const);
+ graph.complete();
+
+ {
+ auto input_node = graph.input_node;
+ input_node->shape({1, 4, 4, 3});
+ luci::test::graph_input_shape(input_node);
+ }
+ {
+ // Does not matter, because test should fail anyway
+ auto output_node = graph.output_node;
+ output_node->shape({0, 0, 0});
+ luci::test::graph_output_shape(output_node);
+ }
+
+ {
+ indices_const->shape({1, 2, 5});
+ }
+
+ // pre-check
+ ASSERT_FALSE(loco::shape_known(gather_nd_node));
+
+ // had to pack into lambda to check throw
+ auto lambda = [&]() {
+ // shape inference
+ while (shape_pass(graph.graph()) == true)
+ ;
+ };
+
+ ASSERT_THROW(lambda(), oops::InternalExn);
+}
+
+TEST(CircleShapeInferenceRuleTest, CircleResizeNearestNeighbor)
+{
+ luci::test::TestGraph graph;
+ auto size_const = graph.append<luci::CircleConst>();
+ size_const->dtype(loco::DataType::S32);
+ size_const->rank(1);
+ size_const->dim(0) = 2;
+ size_const->size<loco::DataType::S32>(2);
+ size_const->at<loco::DataType::S32>(0) = 16;
+ size_const->at<loco::DataType::S32>(1) = 16;
+ auto resize_node = graph.append<luci::CircleResizeNearestNeighbor>(graph.input_node, size_const);
+ graph.complete();
+
+ {
+ auto input_node = graph.input_node;
+ input_node->shape({1, 4, 4, 3});
+ luci::test::graph_input_shape(input_node);
+ }
+ {
+ auto output_node = graph.output_node;
+ output_node->from(resize_node);
+ luci::test::graph_output_shape(output_node);
+ }
+
+ // pre-check
+ ASSERT_FALSE(loco::shape_known(resize_node));
+
+ // shape inference
+ while (shape_pass(graph.graph()) == true)
+ ;
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(resize_node));
+
+ auto shape = loco::shape_get(resize_node).as<loco::TensorShape>();
+ ASSERT_EQ(4, shape.rank());
+ ASSERT_EQ(1, shape.dim(0));
+ ASSERT_EQ(16, shape.dim(1));
+ ASSERT_EQ(16, shape.dim(2));
+ ASSERT_EQ(3, shape.dim(3));
+ }
+}
+
+TEST(CircleShapeInferenceRuleTest, CircleResizeBilinear)
+{
+ luci::test::TestGraph graph;
+ auto size_const = graph.append<luci::CircleConst>();
+ size_const->dtype(loco::DataType::S32);
+ size_const->rank(1);
+ size_const->dim(0) = 2;
+ size_const->size<loco::DataType::S32>(2);
+ size_const->at<loco::DataType::S32>(0) = 16;
+ size_const->at<loco::DataType::S32>(1) = 16;
+ auto resize_node = graph.append<luci::CircleResizeBilinear>(graph.input_node, size_const);
+ graph.complete();
+
+ {
+ auto input_node = graph.input_node;
+ input_node->shape({1, 4, 4, 3});
+ luci::test::graph_input_shape(input_node);
+ }
+ {
+ auto output_node = graph.output_node;
+ output_node->from(resize_node);
+ luci::test::graph_output_shape(output_node);
+ }
+
+ // pre-check
+ ASSERT_FALSE(loco::shape_known(resize_node));
+
+ // shape inference
+ while (shape_pass(graph.graph()) == true)
+ ;
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(resize_node));
+
+ auto shape = loco::shape_get(resize_node).as<loco::TensorShape>();
+ ASSERT_EQ(4, shape.rank());
+ ASSERT_EQ(1, shape.dim(0));
+ ASSERT_EQ(16, shape.dim(1));
+ ASSERT_EQ(16, shape.dim(2));
+ ASSERT_EQ(3, shape.dim(3));
+ }
+}
diff --git a/compiler/luci/service/src/CircleShapeSignatureInferenceRule.cpp b/compiler/luci/service/src/CircleShapeSignatureInferenceRule.cpp
new file mode 100644
index 000000000..dc7df3e39
--- /dev/null
+++ b/compiler/luci/service/src/CircleShapeSignatureInferenceRule.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Service/CircleShapeSignatureInferenceRule.h"
+
+#include <luci/Log.h>
+
+namespace
+{
+
+std::ostream &operator<<(std::ostream &os, const luci::ShapeSignature &shape_signature)
+{
+ os << "[";
+ for (uint32_t r = 0; r < shape_signature.rank(); ++r)
+ {
+ if (r)
+ os << ",";
+ os << shape_signature.dim(r);
+ }
+ os << "]";
+ return os;
+}
+
+} // namespace
+
+namespace luci
+{
+
+bool CircleShapeSignatureInferenceRule::infer(const luci::CircleNode *circle_node,
+ ShapeSignature &shape_signature) const
+{
+ LOGGER(l);
+
+ // There is nothing to check before ShapeSignatureInference.
+
+ ShapeSignatureInferenceAlgorithm alg;
+
+ shape_signature = circle_node->accept(&alg);
+
+ VERBOSE(l, 1) << "[luci] Shape Signature( " << circle_node->name() << " )";
+ VERBOSE(l, 1) << " before: " << circle_node->shape_signature();
+ VERBOSE(l, 1) << " after: " << shape_signature;
+
+ return true;
+}
+
+} // namespace luci
diff --git a/compiler/luci/service/src/CircleTypeInference.cpp b/compiler/luci/service/src/CircleTypeInference.cpp
new file mode 100644
index 000000000..aa8524a55
--- /dev/null
+++ b/compiler/luci/service/src/CircleTypeInference.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Service/CircleTypeInference.h"
+
+#include <loco.h>
+#include <loco/Service/TypeInference.h>
+
+#include <mio/circle/schema_generated.h>
+#include <oops/InternalExn.h>
+
+#include <type_traits>
+
+namespace
+{
+
+circle::TensorType translateLocoTypeToCircle(loco::DataType dtype)
+{
+ switch (dtype)
+ {
+ case loco::DataType::U8:
+ return circle::TensorType_UINT8;
+ // case loco::DataType::U16: unsupported
+ // case loco::DataType::U32: unsupported
+ // case loco::DataType::U64: unsupported
+ case loco::DataType::S8:
+ return circle::TensorType_INT8;
+ case loco::DataType::S16:
+ return circle::TensorType_INT16;
+ case loco::DataType::S32:
+ return circle::TensorType_INT32;
+ case loco::DataType::S64:
+ return circle::TensorType_INT64;
+ case loco::DataType::FLOAT16:
+ return circle::TensorType_FLOAT16;
+ case loco::DataType::FLOAT32:
+ return circle::TensorType_FLOAT32;
+ // case loco::DataType::FLOAT64: unsupported
+ case loco::DataType::BOOL:
+ return circle::TensorType_BOOL;
+ default:
+ break;
+ }
+
+ INTERNAL_EXN_V("Invalid loco dtype", oops::to_uint32(dtype));
+}
+
+} // namespace
+
+namespace luci
+{
+
+circle::TensorType TypeInference::get(loco::Node *node)
+{
+ assert(loco::dtype_known(node));
+ return translateLocoTypeToCircle(loco::dtype_get(node));
+}
+
+} // namespace luci
diff --git a/compiler/luci/service/src/CircleTypeInferenceRule.cpp b/compiler/luci/service/src/CircleTypeInferenceRule.cpp
new file mode 100644
index 000000000..f738ab5a8
--- /dev/null
+++ b/compiler/luci/service/src/CircleTypeInferenceRule.cpp
@@ -0,0 +1,707 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Service/CircleTypeInferenceRule.h"
+
+#include <luci/IR/CircleDialect.h>
+#include <luci/IR/CircleNodeVisitor.h>
+#include <luci/IR/CircleNodes.h>
+
+#include <cassert>
+
+namespace
+{
+
+struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataType>
+{
+ // TODO Given a tensor x of complex numbers, Abs operation returns a tensor of type float32 or
+ // float64.
+ loco::DataType visit(const luci::CircleAbs *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleAdd *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleAddN *node) final
+ {
+ auto dtype = loco::dtype_get(node->inputs(0));
+
+ for (uint32_t idx = 1; idx < node->arity(); ++idx)
+ {
+ auto dtype_idx = loco::dtype_get(node->inputs(idx));
+ if (dtype != dtype_idx)
+ {
+ INTERNAL_EXN_V("ADD_N dtype not same as the first input: ", idx);
+ }
+ }
+
+ return loco::dtype_get(node->inputs(0));
+ }
+
+ loco::DataType visit(const luci::CircleArgMax *node) final { return node->output_type(); }
+
+ loco::DataType visit(const luci::CircleArgMin *node) final { return node->output_type(); }
+
+ loco::DataType visit(const luci::CircleAveragePool2D *node) final
+ {
+ return loco::dtype_get(node->value());
+ }
+
+ loco::DataType visit(const luci::CircleBatchMatMul *node) final
+ {
+ return loco::dtype_get(node->x());
+ }
+
+ loco::DataType visit(const luci::CircleBatchToSpaceND *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleCast *node) final { return node->dtype(); }
+
+ loco::DataType visit(const luci::CircleCeil *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleConcatenation *node) final
+ {
+ // TODO Support when CircleConcatenation has 0 input
+ assert(node->numValues() > 0);
+
+ for (uint32_t i = 1; i < node->numValues(); ++i)
+ assert(loco::dtype_get(node->values(i - 1)) == loco::dtype_get(node->values(i)));
+
+ return loco::dtype_get(node->values(0));
+ }
+
+ loco::DataType visit(const luci::CircleConst *node) final { return node->dtype(); }
+
+ loco::DataType visit(const luci::CircleConv2D *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleCos *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleCustom *node) final
+ {
+ if (node->custom_code() == "BatchMatMulV2")
+ {
+ return loco::dtype_get(node->inputs(0));
+ }
+ return node->dtype();
+ }
+
+ loco::DataType visit(const luci::CircleDepthToSpace *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleDepthwiseConv2D *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleDequantize *) final { return loco::DataType::FLOAT32; }
+
+ loco::DataType visit(const luci::CircleDiv *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleElu *node) final
+ {
+ return loco::dtype_get(node->features());
+ }
+
+ loco::DataType visit(const luci::CircleEqual *) final { return loco::DataType::BOOL; }
+
+ loco::DataType visit(const luci::CircleExp *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleExpandDims *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleFill *node) final
+ {
+ return loco::dtype_get(node->value());
+ }
+
+ loco::DataType visit(const luci::CircleFloor *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleFloorDiv *node) final
+ {
+ return loco::dtype_get(node->x());
+ }
+
+ loco::DataType visit(const luci::CircleFloorMod *node) final
+ {
+ return loco::dtype_get(node->x());
+ }
+
+ loco::DataType visit(const luci::CircleFullyConnected *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleGather *node) final
+ {
+ return loco::dtype_get(node->params());
+ }
+
+ loco::DataType visit(const luci::CircleGatherNd *node) final
+ {
+ return loco::dtype_get(node->params());
+ }
+
+ 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::CircleIf *node) final
+ {
+ // Type of If is not used. Just use input 0
+ assert(node->input_count() > 0);
+ return loco::dtype_get(node->input(0));
+ }
+
+ loco::DataType visit(const luci::CircleL2Normalize *node) final
+ {
+ return loco::dtype_get(node->x());
+ }
+
+ loco::DataType visit(const luci::CircleL2Pool2D *node) final
+ {
+ return loco::dtype_get(node->value());
+ }
+
+ loco::DataType visit(const luci::CircleLeakyRelu *node) final
+ {
+ return loco::dtype_get(node->features());
+ }
+
+ loco::DataType visit(const luci::CircleLess *) final { return loco::DataType::BOOL; }
+
+ loco::DataType visit(const luci::CircleLessEqual *) final { return loco::DataType::BOOL; }
+
+ loco::DataType visit(const luci::CircleLocalResponseNormalization *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleLog *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleLogicalAnd *node) final
+ {
+ return loco::dtype_get(node->x());
+ }
+
+ loco::DataType visit(const luci::CircleLogicalNot *node) final
+ {
+ return loco::dtype_get(node->x());
+ }
+
+ loco::DataType visit(const luci::CircleLogicalOr *node) final
+ {
+ return loco::dtype_get(node->x());
+ }
+
+ loco::DataType visit(const luci::CircleLogistic *node) final
+ {
+ return loco::dtype_get(node->x());
+ }
+
+ loco::DataType visit(const luci::CircleLogSoftmax *node) final
+ {
+ return loco::dtype_get(node->logits());
+ }
+
+ loco::DataType visit(const luci::CircleMatrixDiag *node) final
+ {
+ return loco::dtype_get(node->diagonal());
+ }
+
+ loco::DataType visit(const luci::CircleMatrixSetDiag *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleMaximum *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleMaxPool2D *node) final
+ {
+ return loco::dtype_get(node->value());
+ }
+
+ loco::DataType visit(const luci::CircleMean *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleMinimum *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleMirrorPad *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleNeg *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleNonMaxSuppressionV4 *node) final
+ {
+ return loco::dtype_get(node->boxes());
+ }
+
+ loco::DataType visit(const luci::CircleNonMaxSuppressionV5 *node) final
+ {
+ return loco::dtype_get(node->boxes());
+ }
+
+ loco::DataType visit(const luci::CircleNotEqual *) final { return loco::DataType::BOOL; }
+
+ loco::DataType visit(const luci::CirclePack *node) final
+ {
+ // Only support CirclePack with one or more inputs
+ assert(node->values_count() > 0);
+
+ auto first_value_type = loco::dtype_get(node->values(0));
+ for (uint32_t i = 1; i < node->values_count(); ++i)
+ assert(first_value_type == loco::dtype_get(node->values(i)));
+
+ return first_value_type;
+ }
+
+ loco::DataType visit(const luci::CirclePad *node) final { return loco::dtype_get(node->input()); }
+
+ loco::DataType visit(const luci::CirclePadV2 *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CirclePow *node) final
+ {
+ // TODO make sure types cannot differ
+ auto x_type = loco::dtype_get(node->x());
+ auto y_type = loco::dtype_get(node->y());
+
+ if (x_type != y_type)
+ INTERNAL_EXN("Different datatype for x and y are not supported");
+
+ return x_type;
+ }
+
+ loco::DataType visit(const luci::CirclePRelu *node) final
+ {
+ auto input_type = loco::dtype_get(node->input());
+ auto alpha_type = loco::dtype_get(node->alpha());
+
+ if (input_type != alpha_type)
+ INTERNAL_EXN("Different datatype for input and alpha are not supported");
+
+ return input_type;
+ }
+
+ loco::DataType visit(const luci::CircleRange *node) final
+ {
+ return loco::dtype_get(node->start());
+ }
+
+ loco::DataType visit(const luci::CircleRank *) final { return loco::DataType::S32; }
+
+ loco::DataType visit(const luci::CircleMul *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleOneHot *node) final
+ {
+ return loco::dtype_get(node->on_value());
+ }
+
+ loco::DataType visit(const luci::CircleReduceAny *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleReduceMax *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleReduceMin *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleReduceProd *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleRelu *node) final
+ {
+ return loco::dtype_get(node->features());
+ }
+
+ loco::DataType visit(const luci::CircleRelu6 *node) final
+ {
+ return loco::dtype_get(node->features());
+ }
+
+ loco::DataType visit(const luci::CircleReluN1To1 *node) final
+ {
+ return loco::dtype_get(node->features());
+ }
+
+ loco::DataType visit(const luci::CircleReshape *node) final
+ {
+ return loco::dtype_get(node->tensor());
+ }
+
+ loco::DataType visit(const luci::CircleResizeBilinear *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleResizeNearestNeighbor *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleReverseSequence *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleReverseV2 *node) final
+ {
+ return loco::dtype_get(node->tensor());
+ }
+
+ loco::DataType visit(const luci::CircleRound *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleRsqrt *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleScatterNd *node) final
+ {
+ return loco::dtype_get(node->updates());
+ }
+
+ loco::DataType visit(const luci::CircleSegmentSum *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleSelect *node) final
+ {
+ assert(loco::dtype_get(node->t()) == loco::dtype_get(node->e()));
+ return loco::dtype_get(node->t());
+ }
+
+ loco::DataType visit(const luci::CircleSelectV2 *node) final
+ {
+ assert(loco::dtype_get(node->t()) == loco::dtype_get(node->e()));
+ return loco::dtype_get(node->t());
+ }
+
+ loco::DataType visit(const luci::CircleShape *node) final { return node->out_type(); }
+
+ loco::DataType visit(const luci::CircleSin *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleSlice *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleSoftmax *node) final
+ {
+ return loco::dtype_get(node->logits());
+ }
+
+ loco::DataType visit(const luci::CircleSpaceToBatchND *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleSpaceToDepth *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleSparseToDense *node) final
+ {
+ return loco::dtype_get(node->values());
+ }
+
+ loco::DataType visit(const luci::CircleSplit *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleSplitV *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleSqrt *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleSquare *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleSquaredDifference *node) final
+ {
+ return loco::dtype_get(node->x());
+ }
+
+ loco::DataType visit(const luci::CircleSqueeze *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleStridedSlice *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleSub *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleSum *node) final { return loco::dtype_get(node->input()); }
+
+ loco::DataType visit(const luci::CircleTanh *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleTile *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleTopKV2 *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleTranspose *node) final
+ {
+ return loco::dtype_get(node->a());
+ }
+
+ loco::DataType visit(const luci::CircleTransposeConv *node) final
+ {
+ return loco::dtype_get(node->outBackprop());
+ }
+
+ loco::DataType visit(const luci::CircleUnidirectionalSequenceLSTM *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleUnique *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleUnpack *node) final
+ {
+ return loco::dtype_get(node->value());
+ }
+
+ loco::DataType visit(const luci::CircleWhere *) final { return loco::DataType::S64; }
+
+ loco::DataType visit(const luci::CircleWhile *node) final
+ {
+ // Type of While is not used. Just use input 0
+ assert(node->input_count() > 0);
+ return loco::dtype_get(node->input(0));
+ }
+
+ loco::DataType visit(const luci::CircleZerosLike *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ // Circle Only
+ loco::DataType visit(const luci::CircleBCQFullyConnected *) final
+ {
+ return loco::DataType::FLOAT32;
+ }
+
+ loco::DataType visit(const luci::CircleBCQGather *) final { return loco::DataType::FLOAT32; }
+
+ loco::DataType visit(const luci::CircleInstanceNorm *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ // Virtual
+ loco::DataType visit(const luci::CircleInput *node) final { return node->dtype(); }
+
+ loco::DataType visit(const luci::CircleOutput *node) final
+ {
+ auto graph_outputs = node->graph()->outputs();
+ auto graph_output = graph_outputs->at(node->index());
+ auto output_dtype = graph_output->dtype();
+
+ if (dynamic_cast<luci::CircleOutputDummy *>(node->from()) == nullptr &&
+ dynamic_cast<luci::CircleOutputExclude *>(node->from()) == nullptr)
+ {
+ // We don't care for the type if from() is CircleOutputDummy or CircleOutputExclude
+ // from() type should match that of CircleOutput
+ assert(output_dtype == loco::dtype_get(node->from()));
+ }
+ return output_dtype;
+ }
+
+ loco::DataType visit(const luci::CircleOutputDummy *node) final { return node->dtype(); }
+
+ loco::DataType visit(const luci::CircleOutputExclude *node) final { return node->dtype(); }
+
+ loco::DataType visit(const luci::CircleCustomOut *node) final { return node->dtype(); }
+
+ loco::DataType visit(const luci::CircleIfOut *node) final
+ {
+ /**
+ * @note IF operator type and shape are that of the "then" and "else"
+ * Graph Outputs.
+ */
+ auto circle_if = dynamic_cast<const luci::CircleIf *>(node->input());
+ if (circle_if == nullptr)
+ {
+ INTERNAL_EXN("CircleIf IR is not configured correctly");
+ }
+
+ auto index = node->index();
+ auto then_graph = circle_if->then_graph();
+ auto else_graph = circle_if->else_graph();
+ assert(then_graph != nullptr);
+ assert(else_graph != nullptr);
+
+ // shape and type are assumed to be same
+ // these are checked at post_import_graph() in Import
+ auto then_outputs = loco::output_nodes(then_graph);
+ auto else_outputs = loco::output_nodes(else_graph);
+ assert(then_outputs.size() == else_outputs.size());
+ assert(index < static_cast<int32_t>(then_outputs.size()));
+
+ auto then_out = loco::must_cast<luci::CircleOutput *>(then_outputs.at(index));
+ auto else_out = loco::must_cast<luci::CircleOutput *>(else_outputs.at(index));
+
+ auto then_graph_outputs = then_graph->outputs(); // loco::GraphOutput items
+ auto else_graph_outputs = else_graph->outputs();
+ assert(then_graph_outputs->size() == else_graph_outputs->size());
+
+ auto then_graph_output = then_graph_outputs->at(then_out->index());
+ auto else_graph_output = else_graph_outputs->at(else_out->index());
+ (void)else_graph_output; // make compiler happy for unused variable warnings
+ assert(then_graph_output->dtype() == else_graph_output->dtype());
+
+ return then_graph_output->dtype();
+ }
+
+ loco::DataType visit(const luci::CircleNonMaxSuppressionV4Out *node) final
+ {
+ (void)node;
+ assert(node->index() == 0 || node->index() == 1);
+ return loco::DataType::S32;
+ }
+
+ loco::DataType visit(const luci::CircleNonMaxSuppressionV5Out *node) final
+ {
+ (void)node;
+ if (node->index() == 0 || node->index() == 2)
+ {
+ return loco::DataType::S32;
+ }
+ assert(node->index() == 1);
+ return loco::DataType::FLOAT32;
+ }
+
+ loco::DataType visit(const luci::CircleSplitOut *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleSplitVOut *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleTopKV2Out *node) final
+ {
+ // First output is same as input
+ if (node->index() == 0)
+ return loco::dtype_get(node->input());
+ // Second outout is always S32
+ assert(node->index() == 1);
+ return loco::DataType::S32;
+ }
+
+ loco::DataType visit(const luci::CircleUniqueOut *node) final
+ {
+ if (node->index() == 0)
+ {
+ return loco::dtype_get(node->input());
+ }
+ assert(node->index() == 1);
+ auto unique = loco::must_cast<luci::CircleUnique *>(node->input());
+ return unique->idx_out_type();
+ }
+
+ loco::DataType visit(const luci::CircleUnpackOut *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleWhileOut *node) final
+ {
+ /**
+ * @note WHILE operator's type is the same with the "cond"
+ * Graph Input.
+ */
+ auto circle_while = dynamic_cast<const luci::CircleWhile *>(node->input());
+ if (circle_while == nullptr)
+ {
+ INTERNAL_EXN("CircleWhile IR is not configured correctly");
+ }
+
+ auto index = node->index();
+ auto cond_graph = circle_while->cond_graph();
+ assert(cond_graph != nullptr);
+
+ // Assumption: the index of CircleWhileOut matches with the index of input nodes returned by
+ // loco::input_nodes
+ auto cond_inputs = loco::input_nodes(cond_graph);
+ auto cond_in = loco::must_cast<luci::CircleInput *>(cond_inputs.at(index));
+
+ auto cond_graph_inputs = cond_graph->inputs();
+ auto cond_graph_input = cond_graph_inputs->at(cond_in->index());
+
+ return cond_graph_input->dtype();
+ }
+};
+
+} // namespace
+
+namespace luci
+{
+
+bool CircleTypeInferenceRule::recognize(const loco::Dialect *d) const
+{
+ return CircleDialect::get() == d;
+}
+
+bool CircleTypeInferenceRule::infer(const loco::Node *node, loco::DataType &dtype) const
+{
+ assert(node->dialect() == CircleDialect::get());
+
+ TypeInferenceAlgorithm alg;
+
+ auto circle_node = loco::must_cast<const CircleNode *>(node);
+ dtype = circle_node->accept(&alg);
+ assert(dtype != loco::DataType::Unknown);
+
+ return true;
+}
+
+} // namespace luci
diff --git a/compiler/luci/service/src/CircleTypeInferenceRule.test.cpp b/compiler/luci/service/src/CircleTypeInferenceRule.test.cpp
new file mode 100644
index 000000000..711a489af
--- /dev/null
+++ b/compiler/luci/service/src/CircleTypeInferenceRule.test.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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 "TestGraph.h"
+#include <luci/Service/CircleTypeInferenceRule.h>
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/CircleDialect.h>
+
+#include <loco.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/Service/TypeInference.h>
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+TEST(CircleTypeInferenceRuleTest, minimal_with_CircleRelu)
+{
+ // Create a simple network
+ luci::test::TestGraph graph;
+ auto relu_node = graph.append<luci::CircleRelu>(graph.input_node);
+ graph.complete(relu_node);
+
+ // set dtype for nodes; like setting them in import
+ graph.input_node->dtype(loco::DataType::S32);
+ relu_node->dtype(loco::DataType::S32);
+ graph.output_node->dtype(loco::DataType::S32);
+
+ luci::test::graph_input_dtype(graph.input_node);
+ luci::test::graph_output_dtype(graph.output_node);
+
+ // pre-check
+ ASSERT_FALSE(loco::dtype_known(relu_node));
+
+ // type inference
+ luci::CircleTypeInferenceRule circle_rule;
+ loco::CanonicalTypeInferenceRule canon_rule;
+ loco::MultiDialectTypeInferenceRule rules;
+
+ rules.bind(loco::CanonicalDialect::get(), &canon_rule);
+ rules.bind(luci::CircleDialect::get(), &circle_rule);
+
+ loco::apply(&rules).to(graph.g.get());
+
+ // Verify
+ ASSERT_TRUE(loco::dtype_known(relu_node));
+ auto type = loco::dtype_get(relu_node);
+ ASSERT_EQ(loco::DataType::S32, type);
+}
diff --git a/compiler/luci/service/src/ShapeDescription.cpp b/compiler/luci/service/src/ShapeDescription.cpp
new file mode 100644
index 000000000..cbc302f70
--- /dev/null
+++ b/compiler/luci/service/src/ShapeDescription.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Service/ShapeDescription.h"
+
+#include <oops/InternalExn.h>
+
+#include <cassert>
+
+namespace luci
+{
+
+ShapeDescription to_shape_description(const loco::TensorShape &shape)
+{
+ ShapeDescription res;
+
+ res._rank_known = true;
+
+ res._dims.resize(shape.rank());
+ for (uint32_t axis = 0; axis < shape.rank(); ++axis)
+ {
+ // All the dimensions SHOULD be known
+ assert(shape.dim(axis).known());
+ res._dims.at(axis) = shape.dim(axis).value();
+ }
+
+ return res;
+}
+
+ShapeDescription to_shape_description(const loco::FeatureShape &shape)
+{
+ ShapeDescription res;
+
+ res._rank_known = true;
+
+ // T/F Lite encodes a feature map as a NHWC tensor
+ res._dims.resize(4);
+ res._dims.at(0) = shape.count().value();
+ res._dims.at(1) = shape.height().value();
+ res._dims.at(2) = shape.width().value();
+ res._dims.at(3) = shape.depth().value();
+
+ return res;
+}
+
+ShapeDescription to_shape_description(const loco::FilterShape &shape)
+{
+ ShapeDescription res;
+
+ res._rank_known = true;
+
+ // T/F Lite encodes a convolution filter as a NHWC tensor
+ res._dims.resize(4);
+ res._dims.at(0) = shape.count().value();
+ res._dims.at(1) = shape.height().value();
+ res._dims.at(2) = shape.width().value();
+ res._dims.at(3) = shape.depth().value();
+
+ return res;
+}
+
+ShapeDescription to_shape_description(const loco::DepthwiseFilterShape &shape)
+{
+ ShapeDescription res;
+
+ res._rank_known = true;
+
+ // T/F Lite encodes a depthwise convolution filter as a [1, H, W, C*M] tensor
+ res._dims.resize(4);
+ res._dims.at(0) = 1;
+ res._dims.at(1) = shape.height().value();
+ res._dims.at(2) = shape.width().value();
+ res._dims.at(3) = shape.depth().value() * shape.multiplier().value();
+
+ return res;
+}
+
+ShapeDescription to_shape_description(const loco::BiasShape &shape)
+{
+ ShapeDescription res;
+
+ res._rank_known = true;
+
+ res._dims.resize(1);
+ res._dims.at(0) = shape.length().value();
+
+ return res;
+}
+
+ShapeDescription to_shape_description(const loco::MatrixShape &shape)
+{
+ ShapeDescription res;
+
+ res._rank_known = true;
+
+ res._dims.resize(2);
+ res._dims.at(0) = shape.height().value();
+ res._dims.at(1) = shape.width().value();
+
+ return res;
+}
+
+ShapeDescription to_shape_description(const loco::NodeShape &shape)
+{
+ switch (shape.domain())
+ {
+ case loco::Domain::Tensor:
+ return to_shape_description(shape.as<loco::TensorShape>());
+ case loco::Domain::Feature:
+ return to_shape_description(shape.as<loco::FeatureShape>());
+ case loco::Domain::Filter:
+ return to_shape_description(shape.as<loco::FilterShape>());
+ case loco::Domain::DepthwiseFilter:
+ return to_shape_description(shape.as<loco::DepthwiseFilterShape>());
+ case loco::Domain::Bias:
+ return to_shape_description(shape.as<loco::BiasShape>());
+ case loco::Domain::Matrix:
+ return to_shape_description(shape.as<loco::MatrixShape>());
+ default:
+ break;
+ }
+
+ INTERNAL_EXN_V("Unsupported loco domain", oops::to_uint32(shape.domain()));
+}
+
+} // namespace luci
diff --git a/compiler/luci/service/src/ShapeInfer_StridedSlice.cpp b/compiler/luci/service/src/ShapeInfer_StridedSlice.cpp
new file mode 100644
index 000000000..341201148
--- /dev/null
+++ b/compiler/luci/service/src/ShapeInfer_StridedSlice.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ShapeInfer_StridedSlice.h"
+#include "Check.h"
+
+#include <luci/IR/CircleNode.h>
+#include <loco/IR/DataType.h>
+#include <loco/IR/NodeShape.h>
+#include <oops/InternalExn.h>
+#include <loco/Service/ShapeInference.h>
+
+#include <cmath>
+#include <cstdint>
+#include <limits>
+
+namespace
+{
+
+// This Op only supports 1-4D cases and since we use the reference 4D
+// implementation, the 1-3D tensors are mapped to 4D.
+const int kMaxDim = 4;
+
+const loco::DataType S32 = loco::DataType::S32;
+
+using int8 = int8_t;
+using int16 = int16_t;
+
+struct StridedSliceParams
+{
+ int8 start_indices_count;
+ int16 start_indices[kMaxDim];
+ int8 stop_indices_count;
+ int16 stop_indices[kMaxDim];
+ int8 strides_count;
+ int16 strides[kMaxDim];
+
+ int16 begin_mask;
+ int16 ellipsis_mask;
+ int16 end_mask;
+ int16 new_axis_mask;
+ int16 shrink_axis_mask;
+};
+
+// Use until std::clamp() is available from C++17.
+inline int Clamp(const int32_t v, const int32_t lo, const int32_t hi)
+{
+ LUCI_ASSERT(!(hi < lo), "Clamp hi < lo");
+ if (hi < v)
+ return hi;
+ if (v < lo)
+ return lo;
+ return v;
+}
+
+// Return the index for the first element along that axis. This index will be a
+// positive integer between [0, axis_size - 1] that can be used to index
+// directly into the data.
+inline int StartForAxis(const StridedSliceParams &params, const loco::TensorShape &input_shape,
+ uint32_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<int>(input_shape.dim(axis).value());
+ if (axis_size == 0)
+ {
+ return 0;
+ }
+ // Begin with the specified index.
+ int32_t 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<int32_t>::lowest();
+ }
+ else
+ {
+ // Backward iteration - use the last element.
+ start = std::numeric_limits<int32_t>::max();
+ }
+ }
+
+ // Handle negative indices
+ if (start < 0)
+ {
+ start += axis_size;
+ }
+
+ // Clamping
+ start = Clamp(start, 0, axis_size - 1);
+
+ return start;
+}
+
+// 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 &params, const loco::TensorShape &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 = static_cast<int32_t>(input_shape.dim(axis).value());
+ if (axis_size == 0)
+ {
+ return 0;
+ }
+
+ // Begin with the specified index
+ const bool shrink_axis = shrink_axis_mask & (1 << axis);
+ int32_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
+ // start_for_axis + 1 to generate a length 1 slice, since start_for_axis has
+ // already been adjusted for negative indices.
+ if (shrink_axis)
+ {
+ stop = 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<int32_t>::max();
+ }
+ else
+ {
+ // Backward iteration - use the first element.
+ stop = std::numeric_limits<int32_t>::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;
+}
+
+StridedSliceParams BuildStridedSliceParams(const luci::CircleStridedSlice *node)
+{
+ StridedSliceParams op_params;
+
+ if (kMaxDim < node->rank())
+ {
+ INTERNAL_EXN_V("Cannot support StridedSlice rank > ", kMaxDim);
+ }
+
+ auto begin_node = loco::must_cast<luci::CircleConst *>(node->begin());
+ auto end_node = loco::must_cast<luci::CircleConst *>(node->end());
+ auto strides_node = loco::must_cast<luci::CircleConst *>(node->strides());
+
+ uint32_t dims_count = begin_node->size<S32>();
+
+ op_params.start_indices_count = dims_count;
+ op_params.stop_indices_count = dims_count;
+ op_params.strides_count = dims_count;
+
+ for (uint32_t i = 0; i < dims_count; ++i)
+ {
+ op_params.start_indices[i] = begin_node->at<S32>(i);
+ op_params.stop_indices[i] = end_node->at<S32>(i);
+ op_params.strides[i] = strides_node->at<S32>(i);
+ }
+
+ op_params.begin_mask = node->begin_mask();
+ op_params.ellipsis_mask = 0;
+ op_params.end_mask = node->end_mask();
+ op_params.new_axis_mask = 0;
+ op_params.shrink_axis_mask = node->shrink_axis_mask();
+
+ return op_params;
+}
+
+} // namespace
+
+namespace luci
+{
+
+loco::TensorShape infer_output_shape(const CircleStridedSlice *node)
+{
+ loco::TensorShape output_shape;
+
+ auto input_node = loco::must_cast<luci::CircleNode *>(node->input());
+
+ auto begin_node = dynamic_cast<luci::CircleConst *>(node->begin());
+ auto end_node = dynamic_cast<luci::CircleConst *>(node->end());
+ auto strides_node = dynamic_cast<luci::CircleConst *>(node->strides());
+ if (begin_node == nullptr || end_node == nullptr || strides_node == nullptr)
+ {
+ INTERNAL_EXN("StridedSlice begin/end/strides nodes are not Constant");
+ }
+
+ LUCI_ASSERT(begin_node->dtype() == S32, "Only support S32 for begin_node");
+ LUCI_ASSERT(end_node->dtype() == S32, "Only support S32 for end_node");
+ LUCI_ASSERT(strides_node->dtype() == S32, "Only support S32 for strides_node");
+
+ assert(node->ellipsis_mask() == 0);
+ assert(node->new_axis_mask() == 0);
+
+ auto op_params = BuildStridedSliceParams(node);
+ loco::TensorShape input_shape = loco::shape_get(input_node).as<loco::TensorShape>();
+
+ uint32_t num_input_axes = input_shape.rank();
+ assert(begin_node->size<S32>() <= num_input_axes);
+ assert(end_node->size<S32>() <= num_input_axes);
+ assert(strides_node->size<S32>() <= num_input_axes);
+ for (uint32_t i = 0; i < strides_node->size<S32>(); i++)
+ {
+ LUCI_ASSERT(strides_node->at<S32>(i) != 0, "Stride value has to be non-zero");
+ }
+
+ uint32_t shape_size = 0;
+ std::array<int32_t, 16> output_shape_data;
+
+ for (uint32_t idx = 0; idx < num_input_axes; ++idx)
+ {
+ int32_t begin = StartForAxis(op_params, input_shape, idx);
+ int32_t end = StopForAxis(op_params, input_shape, idx, begin);
+ if (end < 0)
+ end = input_shape.dim(idx).value() + end + 1;
+
+ // This is valid for both positive and negative strides
+ int32_t stride = strides_node->at<S32>(idx);
+ int32_t dim_shape = std::ceil(static_cast<float>(end - begin) / stride);
+ assert(dim_shape > 0);
+
+ // When shrinking an axis, the end position does not matter (and can be
+ // incorrect when negative indexing is used, see Issue #19260). Always use
+ // begin + 1 to generate a length 1 slice, since begin has
+ // already been adjusted for negative indices by StartForAxis.
+ const bool shrink_axis = node->shrink_axis_mask() & (1 << idx);
+ if (shrink_axis)
+ {
+ assert(dim_shape == 1);
+ }
+ else
+ {
+ output_shape_data[shape_size++] = dim_shape;
+ }
+ }
+
+ output_shape.rank(shape_size);
+ for (uint32_t idx = 0; idx < shape_size; ++idx)
+ {
+ output_shape.dim(idx) = output_shape_data[idx];
+ }
+
+ return output_shape;
+}
+
+} // namespace luci
diff --git a/compiler/luci/service/src/ShapeInfer_StridedSlice.h b/compiler/luci/service/src/ShapeInfer_StridedSlice.h
new file mode 100644
index 000000000..fa800b720
--- /dev/null
+++ b/compiler/luci/service/src/ShapeInfer_StridedSlice.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SHAPE_INFER_STRIDED_SLICE_H__
+#define __SHAPE_INFER_STRIDED_SLICE_H__
+
+#include <luci/IR/CircleNodes.h>
+
+#include <loco/IR/NodeShape.h>
+
+namespace luci
+{
+
+loco::TensorShape infer_output_shape(const CircleStridedSlice *node);
+
+} // namespace luci
+
+#endif // __SHAPE_INFER_STRIDED_SLICE_H__
diff --git a/compiler/luci/service/src/TestGraph.h b/compiler/luci/service/src/TestGraph.h
new file mode 100644
index 000000000..2865b0f44
--- /dev/null
+++ b/compiler/luci/service/src/TestGraph.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TEST_GRAPH_H__
+#define __TEST_GRAPH_H__
+
+#include <luci/IR/CircleNodes.h>
+
+#include <loco.h>
+
+#include <cassert>
+#include <memory>
+
+// TODO Change all Canonical nodes to Circle nodes
+
+namespace luci
+{
+namespace test
+{
+
+class TestGraph
+{
+public:
+ std::unique_ptr<loco::Graph> g;
+ luci::CircleInput *input_node = nullptr;
+ luci::CircleOutput *output_node = nullptr;
+
+ TestGraph() // creates Pull and Push
+ {
+ g = loco::make_graph();
+
+ input_node = g->nodes()->create<luci::CircleInput>();
+
+ output_node = g->nodes()->create<luci::CircleOutput>();
+
+ auto input = g->inputs()->create();
+ {
+ input->name("input");
+ luci::link(input, input_node);
+ }
+ auto output = g->outputs()->create();
+ {
+ output->name("output");
+ luci::link(output, output_node);
+ }
+
+ _next_input = input_node;
+ }
+
+ loco::Graph *graph() { return g.get(); }
+
+ /// @brief Creates node with NO arg and appends it to graph
+ template <class T> T *append()
+ {
+ auto node = g->nodes()->create<T>();
+ _next_input = node;
+
+ return node;
+ }
+
+ /// @brief Creates op T (arity=1) with arg1 as an input and appends it to graph
+ template <class T> T *append(luci::CircleNode *arg1)
+ {
+ auto node = g->nodes()->create<T>();
+ setInput(node, arg1);
+ _next_input = node;
+
+ return node;
+ }
+
+ /// @brief Creates op T (arity=2) with arg1, arg2 as inputs and appends it to graph
+ template <class T> T *append(luci::CircleNode *arg1, luci::CircleNode *arg2)
+ {
+ auto node = g->nodes()->create<T>();
+ setInput(node, arg1, arg2);
+ _next_input = node;
+
+ return node;
+ }
+
+ /// @brief Creates op T (arity=3) with arg1, arg2, arg3 as inputs and appends it to graph
+ template <class T>
+ T *append(luci::CircleNode *arg1, luci::CircleNode *arg2, luci::CircleNode *arg3)
+ {
+ auto node = g->nodes()->create<T>();
+ setInput(node, arg1, arg2, arg3);
+ _next_input = node;
+
+ return node;
+ }
+
+ // output will get the last appended node
+ void complete() { output_node->from(_next_input); }
+
+ void complete(luci::CircleNode *last_node) { output_node->from(last_node); }
+
+private:
+ // arity 1
+ void setInput(luci::CircleNode *, luci::CircleNode *) { assert(false && "NYI"); };
+
+ void setInput(luci::CircleAveragePool2D *node, luci::CircleNode *input) { node->value(input); };
+ void setInput(luci::CircleRelu *node, luci::CircleNode *input) { node->features(input); };
+ void setInput(luci::CircleSqueeze *node, luci::CircleNode *input) { node->input(input); };
+
+ void setInput(luci::CircleGatherNd *node, luci::CircleNode *params, luci::CircleNode *indices)
+ {
+ node->params(params);
+ node->indices(indices);
+ };
+
+ // arity 2
+ void setInput(luci::CircleNode *, luci::CircleNode *, luci::CircleNode *)
+ {
+ assert(false && "NYI");
+ };
+
+ void setInput(luci::CircleExpandDims *node, luci::CircleNode *arg1, luci::CircleNode *arg2)
+ {
+ node->input(arg1);
+ node->axis(arg2);
+ };
+
+ void setInput(luci::CircleTranspose *node, luci::CircleNode *arg1, luci::CircleNode *arg2)
+ {
+ node->a(arg1);
+ node->perm(arg2);
+ };
+
+ void setInput(luci::CircleResizeBilinear *node, luci::CircleNode *input, luci::CircleNode *size)
+ {
+ node->input(input);
+ node->size(size);
+ };
+
+ void setInput(luci::CircleResizeNearestNeighbor *node, luci::CircleNode *input,
+ luci::CircleNode *size)
+ {
+ node->input(input);
+ node->size(size);
+ };
+
+ // arity 3
+ void setInput(luci::CircleNode *, luci::CircleNode *, luci::CircleNode *, luci::CircleNode *)
+ {
+ assert(false && "NYI");
+ };
+
+private:
+ loco::Node *_next_input;
+};
+
+enum class ExampleGraphType
+{
+ CircleTranspose,
+};
+
+template <ExampleGraphType T> class ExampleGraph;
+
+/**
+ * @brief Class to create the following:
+ *
+ * CircleInput -- CircleTranspose -- CircleOutput
+ */
+template <> class ExampleGraph<ExampleGraphType::CircleTranspose> : public TestGraph
+{
+public:
+ luci::CircleConst *const_perm = nullptr;
+ luci::CircleTranspose *transpose_node = nullptr;
+
+public:
+ ExampleGraph()
+ {
+ const_perm = append<luci::CircleConst>();
+ transpose_node = append<luci::CircleTranspose>(input_node, const_perm);
+ complete(transpose_node);
+ }
+};
+
+} // namespace test
+} // namespace luci
+
+namespace luci
+{
+namespace test
+{
+
+/// @brief This will set GraphInput shape from CircleInput shape
+void graph_input_shape(luci::CircleInput *input);
+
+/// @brief This will set GraphOutput shape from CircleOutput shape
+void graph_output_shape(luci::CircleOutput *output);
+
+/// @brief This will set GraphInput dtype from CircleInput dtype
+void graph_input_dtype(luci::CircleInput *input);
+
+/// @brief This will set GraphOutput dtype from CircleOutput dtype
+void graph_output_dtype(luci::CircleOutput *output);
+
+} // namespace test
+} // namespace luci
+
+#endif // __TEST_GRAPH_H__
diff --git a/compiler/luci/service/src/TestGraph.test.cpp b/compiler/luci/service/src/TestGraph.test.cpp
new file mode 100644
index 000000000..9ede70370
--- /dev/null
+++ b/compiler/luci/service/src/TestGraph.test.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestGraph.h"
+
+namespace luci
+{
+namespace test
+{
+
+void graph_input_shape(luci::CircleInput *input)
+{
+ auto index = input->index();
+ auto inputs = input->graph()->inputs();
+
+ for (uint32_t idx = 0; idx < inputs->size(); ++idx)
+ {
+ auto gi = inputs->at(idx);
+ if (gi->index() == index)
+ {
+ auto input_shape = std::make_unique<loco::TensorShape>();
+
+ input_shape->rank(input->rank());
+ for (uint32_t r = 0; r < input->rank(); ++r)
+ input_shape->dim(r) = loco::Dimension(input->dim(r));
+
+ gi->shape(std::move(input_shape));
+ break;
+ }
+ }
+}
+
+void graph_output_shape(luci::CircleOutput *output)
+{
+ auto index = output->index();
+ auto outputs = output->graph()->outputs();
+
+ for (uint32_t idx = 0; idx < outputs->size(); ++idx)
+ {
+ auto go = outputs->at(idx);
+ if (go->index() == index)
+ {
+ auto output_shape = std::make_unique<loco::TensorShape>();
+
+ output_shape->rank(output->rank());
+ for (uint32_t r = 0; r < output->rank(); ++r)
+ output_shape->dim(r) = loco::Dimension(output->dim(r));
+
+ go->shape(std::move(output_shape));
+ break;
+ }
+ }
+}
+
+void graph_input_dtype(luci::CircleInput *input)
+{
+ auto index = input->index();
+ auto inputs = input->graph()->inputs();
+
+ for (uint32_t idx = 0; idx < inputs->size(); ++idx)
+ {
+ auto gi = inputs->at(idx);
+ if (gi->index() == index)
+ {
+ gi->dtype(input->dtype());
+ break;
+ }
+ }
+}
+
+void graph_output_dtype(luci::CircleOutput *output)
+{
+ auto index = output->index();
+ auto outputs = output->graph()->outputs();
+
+ for (uint32_t idx = 0; idx < outputs->size(); ++idx)
+ {
+ auto go = outputs->at(idx);
+ if (go->index() == index)
+ {
+ go->dtype(output->dtype());
+ break;
+ }
+ }
+}
+
+} // namespace test
+} // namespace luci
diff --git a/compiler/luci/service/src/Validate.cpp b/compiler/luci/service/src/Validate.cpp
new file mode 100644
index 000000000..d224fd172
--- /dev/null
+++ b/compiler/luci/service/src/Validate.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Service/Validate.h"
+
+#include <luci/IR/Nodes/CircleOutput.h>
+#include <luci/Log.h>
+
+#include <loco/IR/NodeShape.h>
+#include <loco/Service/ShapeInference.h>
+#include <loco/Service/TypeInference.h>
+
+#include <cassert>
+#include <vector>
+
+namespace
+{
+
+std::ostream &operator<<(std::ostream &os, const loco::TensorShape &tensor_shape)
+{
+ os << "[";
+ for (uint32_t r = 0; r < tensor_shape.rank(); ++r)
+ {
+ if (r)
+ os << ",";
+ os << tensor_shape.dim(r).value();
+ }
+ os << "]";
+ return os;
+}
+
+/**
+ * @brief returns a node that is CircleOutput with index is out_index in nodes
+ */
+luci::CircleOutput *find_node(std::vector<loco::Node *> nodes, loco::GraphOutputIndex out_index)
+{
+ for (auto node : nodes)
+ {
+ auto circle_output = dynamic_cast<luci::CircleOutput *>(node);
+ if (circle_output != nullptr)
+ {
+ if (circle_output->indexed() && circle_output->index() == out_index)
+ return circle_output;
+ }
+ }
+ return nullptr;
+}
+
+bool validate_shape_dtype(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<luci::CircleNode *>(circle_output->from());
+
+ // Shape and dtype validation for CiecleOutputExclude is not needed
+ if (dynamic_cast<luci::CircleOutputExclude *>(circle_node))
+ continue;
+
+ assert(loco::shape_known(circle_node));
+
+ // check if output node shape is same as graph output shape
+ auto co_tensor_shape = loco::shape_get(circle_node).as<loco::TensorShape>();
+ auto go_tensor_shape = graph_out->shape();
+ assert(go_tensor_shape);
+ if (!(co_tensor_shape == *go_tensor_shape))
+ {
+ INFO(l) << "[luci] Shape for output #" << out_index << " not same " << std::endl;
+ INFO(l) << "[luci] " << circle_node->name() << " " << co_tensor_shape << " vs "
+ << *go_tensor_shape << std::endl;
+ return false;
+ }
+
+ // check if data type match
+ assert(loco::dtype_known(circle_node));
+ if (graph_out->dtype() != loco::dtype_get(circle_node))
+ {
+ INFO(l) << "[luci] Type for output #" << out_index << " not same " << std::endl;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace luci
+{
+
+bool validate(loco::Graph *g)
+{
+ if (!loco::valid(g))
+ return false;
+
+ if (!validate_shape_dtype(g))
+ return false;
+
+ // TODO add more validation
+
+ return true;
+}
+
+} // namespace luci
diff --git a/compiler/luci/tester/CMakeLists.txt b/compiler/luci/tester/CMakeLists.txt
new file mode 100644
index 000000000..3ac06ef3a
--- /dev/null
+++ b/compiler/luci/tester/CMakeLists.txt
@@ -0,0 +1,30 @@
+nnas_include(TargetRequire)
+
+unset(REQUIRED_TARGETS)
+list(APPEND REQUIRED_TARGETS safemain)
+TargetRequire_Return(${REQUIRED_TARGETS})
+
+set(SRCS_READ_TESTER
+ src/ReadTester.cpp
+ )
+
+add_executable(luci_readtester "${SRCS_READ_TESTER}")
+target_link_libraries(luci_readtester PRIVATE luci_import)
+target_link_libraries(luci_readtester PRIVATE luci_service)
+target_link_libraries(luci_readtester PRIVATE luci_pass)
+target_link_libraries(luci_readtester PRIVATE foder)
+target_link_libraries(luci_readtester PRIVATE oops)
+target_link_libraries(luci_readtester PRIVATE safemain)
+
+set(SRCS_WRITE_TESTER
+ src/WriteTester.cpp
+ )
+
+add_executable(luci_writetester "${SRCS_WRITE_TESTER}")
+target_link_libraries(luci_writetester PRIVATE luci_import)
+target_link_libraries(luci_writetester PRIVATE luci_service)
+target_link_libraries(luci_writetester PRIVATE luci_pass)
+target_link_libraries(luci_writetester PRIVATE luci_export)
+target_link_libraries(luci_writetester PRIVATE foder)
+target_link_libraries(luci_writetester PRIVATE oops)
+target_link_libraries(luci_writetester PRIVATE safemain)
diff --git a/compiler/luci/tester/src/ReadTester.cpp b/compiler/luci/tester/src/ReadTester.cpp
new file mode 100644
index 000000000..a1aead1bd
--- /dev/null
+++ b/compiler/luci/tester/src/ReadTester.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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 <foder/FileLoader.h>
+
+#include <luci/Importer.h>
+#include <luci/Service/Validate.h>
+#include <luci/Pass/ShapeInferencePass.h>
+#include <luci/Pass/TypeInferencePass.h>
+
+#include <iostream>
+#include <map>
+#include <string>
+
+namespace
+{
+
+void show_help_message(const char *progname, std::ostream &os)
+{
+ os << "USAGE: " << progname << " circlefile" << std::endl << std::endl;
+}
+
+void show_error_message(const char *progname, std::ostream &os, const std::string &msg)
+{
+ os << "ERROR: " << msg << std::endl;
+ os << std::endl;
+
+ show_help_message(progname, os);
+}
+
+} // namespace
+
+/*
+ * @brief ReadTest main
+ *
+ * Give one Circle file as an argument
+ *
+ * This will use luci_import to read the file and get loco graph
+ * In luci_import, LUCI_LOG environment will be checked and will
+ * dump graph to console if set.
+ * i.e. "LUCI_LOG=1 luci_readtester mymodel.circle"
+ */
+int entry(int argc, char **argv)
+{
+ if (argc != 2)
+ {
+ show_error_message(argv[0], std::cerr, "Circle file is not specified");
+ return 255;
+ }
+
+ std::string input_path = argv[1];
+
+ std::cout << "[INFO] Circle is '" << input_path << "'" << std::endl;
+
+ // Load model from the file
+ foder::FileLoader file_loader{input_path};
+ std::vector<char> model_data = file_loader.load();
+ const circle::Model *circle_model = circle::GetModel(model_data.data());
+ if (circle_model == nullptr)
+ {
+ std::cerr << "ERROR: Failed to load circle '" << input_path << "'" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ luci::Importer importer;
+ auto module = importer.importModule(circle_model);
+ assert(module->size() > 0);
+
+ for (size_t g = 0; g < module->size(); ++g)
+ {
+ auto graph = module->graph(g);
+ if (graph == nullptr)
+ return 255;
+
+ {
+ luci::ShapeInferencePass pass;
+ while (pass.run(graph) == true)
+ ;
+ }
+ {
+ luci::TypeInferencePass pass;
+ while (pass.run(graph) == true)
+ ;
+ }
+
+ if (!luci::validate(graph))
+ return 255;
+ }
+ return 0;
+}
diff --git a/compiler/luci/tester/src/WriteTester.cpp b/compiler/luci/tester/src/WriteTester.cpp
new file mode 100644
index 000000000..aa7085c77
--- /dev/null
+++ b/compiler/luci/tester/src/WriteTester.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <foder/FileLoader.h>
+
+#include <luci/Importer.h>
+#include <luci/Pass/ShapeInferencePass.h>
+#include <luci/Pass/TypeInferencePass.h>
+#include <luci/Service/Validate.h>
+#include <luci/CircleExporter.h>
+#include <oops/InternalExn.h>
+
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <string>
+
+namespace
+{
+
+void show_help_message(const char *progname, std::ostream &os)
+{
+ os << "USAGE: " << progname << " circlefile_in circlefile_out" << std::endl << std::endl;
+}
+
+void show_error_message(const char *progname, std::ostream &os, const std::string &msg)
+{
+ os << "ERROR: " << msg << std::endl;
+ os << std::endl;
+
+ show_help_message(progname, os);
+}
+
+struct CircleExpContract : public luci::CircleExporter::Contract
+{
+public:
+ CircleExpContract(loco::Graph *graph, const std::string &filename)
+ : _graph(graph), _filepath(filename)
+ {
+ // NOTHING TO DO
+ }
+ CircleExpContract(luci::Module *module, const std::string &filename)
+ : _module(module), _filepath(filename)
+ {
+ // NOTHING TO DO
+ }
+ virtual ~CircleExpContract() = default;
+
+public:
+ loco::Graph *graph(void) const final { return _graph; }
+
+ luci::Module *module(void) const final { return _module; }
+
+public:
+ bool store(const char *ptr, const size_t size) const final;
+
+private:
+ loco::Graph *_graph{nullptr};
+ luci::Module *_module{nullptr};
+ const std::string _filepath;
+};
+
+bool CircleExpContract::store(const char *ptr, const size_t size) const
+{
+ if (!ptr)
+ INTERNAL_EXN("Graph was not serialized by FlatBuffer for some reason");
+
+ std::ofstream fs(_filepath.c_str(), std::ofstream::binary);
+ fs.write(ptr, size);
+
+ return fs.good();
+}
+
+} // namespace
+
+/*
+ * @brief WriteTester main
+ *
+ * Give two Circle file as an argument
+ *
+ * This will use luci_import to read the first file and get loco graph
+ * With the graph, this will use luci_export to write to the second file
+ * Like ReadTester, LUCI_LOG=1 environment variable is available to dump the graph
+ */
+int entry(int argc, char **argv)
+{
+ if (argc != 3)
+ {
+ show_error_message(argv[0], std::cerr, "In/Out Circle file path is not specified");
+ return 255;
+ }
+
+ std::string input_path = argv[1];
+ std::string output_path = argv[2];
+
+ std::cout << "[INFO] Circle from '" << input_path << "' to '" << output_path << "'" << std::endl;
+
+ // Load model from the file
+ foder::FileLoader file_loader{input_path};
+ std::vector<char> model_data = file_loader.load();
+ const circle::Model *circle_model = circle::GetModel(model_data.data());
+ if (circle_model == nullptr)
+ {
+ std::cerr << "ERROR: Failed to load circle '" << input_path << "'" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Import from input Circle file
+ luci::Importer importer;
+ auto module = importer.importModule(circle_model);
+ assert(module->size() > 0);
+
+ for (size_t g = 0; g < module->size(); ++g)
+ {
+ auto graph = module->graph(g);
+ if (graph == nullptr)
+ return 255;
+
+ {
+ luci::ShapeInferencePass pass;
+ while (pass.run(graph) == true)
+ ;
+ }
+ {
+ luci::TypeInferencePass pass;
+ while (pass.run(graph) == true)
+ ;
+ }
+
+ if (!luci::validate(graph))
+ return 255;
+ }
+
+ // Export to output Circle file
+ luci::CircleExporter exporter;
+
+ CircleExpContract contract(module.get(), output_path);
+
+ return exporter.invoke(&contract) ? 0 : 255;
+}
diff --git a/compiler/mocotest-tf/.gitignore b/compiler/luci/tests/.gitignore
index 8dbfa9012..8dbfa9012 100644
--- a/compiler/mocotest-tf/.gitignore
+++ b/compiler/luci/tests/.gitignore
diff --git a/compiler/luci/tests/CMakeLists.txt b/compiler/luci/tests/CMakeLists.txt
new file mode 100644
index 000000000..c03835823
--- /dev/null
+++ b/compiler/luci/tests/CMakeLists.txt
@@ -0,0 +1,126 @@
+# TODO use local test.recipe files for small networks
+file(GLOB RECIPES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*/test.recipe")
+
+foreach(RECIPE IN ITEMS ${RECIPES})
+ get_filename_component(RECIPE_PREFIX ${RECIPE} DIRECTORY)
+
+ set(RECIPE_SOURCE_FILE "${RECIPE_PREFIX}.recipe")
+ set(RECIPE_OUTPUT_FILE "${RECIPE_PREFIX}.tflite")
+ set(CIRCLE_OUTPUT_FILE "${RECIPE_PREFIX}.circle")
+
+ # Copy .recipe
+ add_custom_command(OUTPUT "${RECIPE_SOURCE_FILE}"
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
+ "${CMAKE_CURRENT_SOURCE_DIR}/${RECIPE}" "${RECIPE_SOURCE_FILE}"
+ DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${RECIPE}"
+ COMMENT "Generating ${RECIPE_SOURCE_FILE}")
+
+ # Generate .tflite
+ add_custom_command(OUTPUT "${RECIPE_OUTPUT_FILE}"
+ COMMAND tflchef-file "${RECIPE_SOURCE_FILE}" "${RECIPE_OUTPUT_FILE}"
+ DEPENDS tflchef-file "${RECIPE_SOURCE_FILE}"
+ COMMENT "Generating ${RECIPE_OUTPUT_FILE}")
+
+ # Generate .circle
+ add_custom_command(OUTPUT "${CIRCLE_OUTPUT_FILE}"
+ COMMAND tflite2circle "${RECIPE_OUTPUT_FILE}" "${CIRCLE_OUTPUT_FILE}"
+ DEPENDS tflite2circle "${RECIPE_OUTPUT_FILE}"
+ COMMENT "Generating ${CIRCLE_OUTPUT_FILE}")
+
+ list(APPEND TESTFILES "${CIRCLE_OUTPUT_FILE}")
+endforeach(RECIPE)
+
+# Generate from res/TensorFlowLiteRecipes
+nncc_find_resource(TensorFlowLiteRecipes)
+set(TENSORFLOWLITERECIPES_DIR "${TensorFlowLiteRecipes_DIR}")
+
+file(GLOB RECIPES RELATIVE ${TENSORFLOWLITERECIPES_DIR} "${TENSORFLOWLITERECIPES_DIR}/*/test.recipe")
+
+foreach(RECIPE IN ITEMS ${RECIPES})
+ get_filename_component(RECIPE_PREFIX ${RECIPE} DIRECTORY)
+
+ set(RECIPE_SOURCE_FILE "${RECIPE_PREFIX}.recipe")
+ set(RECIPE_OUTPUT_FILE "${RECIPE_PREFIX}.tflite")
+ set(CIRCLE_OUTPUT_FILE "${RECIPE_PREFIX}.circle")
+
+ # Copy .recipe
+ add_custom_command(OUTPUT "${RECIPE_SOURCE_FILE}"
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
+ "${TENSORFLOWLITERECIPES_DIR}/${RECIPE}" "${RECIPE_SOURCE_FILE}"
+ DEPENDS "${TENSORFLOWLITERECIPES_DIR}/${RECIPE}"
+ COMMENT "Generating ${RECIPE_SOURCE_FILE}")
+
+ # Generate .tflite
+ add_custom_command(OUTPUT "${RECIPE_OUTPUT_FILE}"
+ COMMAND tflchef-file "${RECIPE_SOURCE_FILE}" "${RECIPE_OUTPUT_FILE}"
+ DEPENDS tflchef-file "${RECIPE_SOURCE_FILE}"
+ COMMENT "Generating ${RECIPE_OUTPUT_FILE}")
+
+ # Generate .circle
+ add_custom_command(OUTPUT "${CIRCLE_OUTPUT_FILE}"
+ COMMAND tflite2circle "${RECIPE_OUTPUT_FILE}" "${CIRCLE_OUTPUT_FILE}"
+ DEPENDS tflite2circle "${RECIPE_OUTPUT_FILE}"
+ COMMENT "Generating ${CIRCLE_OUTPUT_FILE}")
+
+ list(APPEND TESTFILES "${CIRCLE_OUTPUT_FILE}")
+endforeach(RECIPE)
+
+# Generate from res/CircleRecipes
+# NOTE duplicate names should not exist or test may be incorrect
+nncc_find_resource(CircleRecipes)
+set(CIRCLERECIPES_DIR "${CircleRecipes_DIR}")
+
+file(GLOB RECIPES2 RELATIVE ${CIRCLERECIPES_DIR} "${CIRCLERECIPES_DIR}/*/test.recipe")
+
+foreach(RECIPE IN ITEMS ${RECIPES2})
+ get_filename_component(RECIPE_PREFIX ${RECIPE} DIRECTORY)
+
+ set(RECIPE_SOURCE_FILE "${RECIPE_PREFIX}.recipe")
+ set(CIRCLE_OUTPUT_FILE "${RECIPE_PREFIX}.circle")
+
+ # Copy .recipe
+ add_custom_command(OUTPUT "${RECIPE_SOURCE_FILE}"
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
+ "${CIRCLERECIPES_DIR}/${RECIPE}" "${RECIPE_SOURCE_FILE}"
+ DEPENDS "${CIRCLERECIPES_DIR}/${RECIPE}"
+ COMMENT "Generating ${RECIPE_SOURCE_FILE}")
+
+ # Generate .circle
+ add_custom_command(OUTPUT "${CIRCLE_OUTPUT_FILE}"
+ COMMAND circlechef-file "${RECIPE_SOURCE_FILE}" "${CIRCLE_OUTPUT_FILE}"
+ DEPENDS circlechef-file "${RECIPE_SOURCE_FILE}"
+ COMMENT "Generating ${CIRCLE_OUTPUT_FILE}")
+
+ list(APPEND TESTFILES "${CIRCLE_OUTPUT_FILE}")
+endforeach(RECIPE)
+
+# Add a dummy target to create a target-level dependency.
+# TODO Find a way to create dependency between CTest tests (added below) and generated testfiles.
+add_custom_target(luci_testfiles ALL DEPENDS ${TESTFILES})
+
+macro(addread NAME)
+ list(APPEND DAILY_READ_TESTS ${NAME})
+endmacro(addread)
+
+macro(addwrite NAME)
+ list(APPEND DAILY_WRITE_TESTS ${NAME})
+endmacro(addwrite)
+
+# Read "test.lst"
+include("test.lst")
+# Read "test.local.lst" if exists
+include("test.local.lst" OPTIONAL)
+
+add_test(NAME luci_unit_readtest
+ COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/readverify.sh"
+ "${CMAKE_CURRENT_BINARY_DIR}"
+ "$<TARGET_FILE:luci_readtester>"
+ ${DAILY_READ_TESTS}
+)
+
+add_test(NAME luci_unit_writetest
+ COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/writeverify.sh"
+ "${CMAKE_CURRENT_BINARY_DIR}"
+ "$<TARGET_FILE:luci_writetester>"
+ ${DAILY_WRITE_TESTS}
+)
diff --git a/compiler/luci/tests/readverify.sh b/compiler/luci/tests/readverify.sh
new file mode 100755
index 000000000..6d6753d39
--- /dev/null
+++ b/compiler/luci/tests/readverify.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+
+# This script verifies the basic behavior of luci frontend
+#
+# HOW TO USE
+#
+# ./readverify.sh <path/to/luci_readtester> <TEST 1> <TEST 2> ...
+VERIFY_SOURCE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+# set LOG enable to execute/test luci/logex codes
+export LUCI_LOG=100
+
+WORKDIR="$1"; shift
+VERIFY_BINARY_PATH="$1"; shift
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+for TESTCASE in "$@"; do
+ TESTED+=("${TESTCASE}")
+
+ TESTCASE_FILE="${WORKDIR}/${TESTCASE}"
+
+ PASSED_TAG="${TESTCASE_FILE}.passed"
+ rm -f "${PASSED_TAG}"
+
+ cat > "${TESTCASE_FILE}.log" <(
+ exec 2>&1
+ set -ex
+
+ "${VERIFY_BINARY_PATH}" "${TESTCASE_FILE}.circle"
+
+ if [[ $? -eq 0 ]]; then
+ touch "${PASSED_TAG}"
+ fi
+ )
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ PASSED+=("${TESTCASE}")
+ else
+ FAILED+=("${TESTCASE}")
+ fi
+done
+
+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/luci/tests/test.lst b/compiler/luci/tests/test.lst
new file mode 100644
index 000000000..897d41983
--- /dev/null
+++ b/compiler/luci/tests/test.lst
@@ -0,0 +1,432 @@
+addread(Abs_000)
+addread(Add_000)
+addread(Add_001)
+addread(Add_U8_000)
+addread(AddN_000)
+addread(ArgMax_000)
+addread(ArgMax_001)
+addread(ArgMax_002)
+addread(ArgMax_003)
+addread(ArgMax_U8_000)
+addread(ArgMax_U8_001)
+addread(ArgMax_U8_002)
+addread(ArgMax_U8_003)
+addread(ArgMin_000)
+addread(ArgMin_001)
+addread(ArgMin_002)
+addread(ArgMin_003)
+addread(ArgMin_U8_000)
+addread(ArgMin_U8_001)
+addread(ArgMin_U8_002)
+addread(ArgMin_U8_003)
+addread(AveragePool2D_000)
+addread(AveragePool2D_U8_000)
+addread(BatchMatMul_000)
+addread(BatchMatMulV2_000)
+addread(BatchMatMulV2_001)
+addread(BatchToSpaceND_000)
+addread(Cast_000)
+addread(Cast_001)
+addread(Ceil_000)
+addread(Concatenation_000)
+addread(Concatenation_U8_000)
+addread(Conv2D_000)
+addread(Conv2D_001)
+addread(Conv2D_002)
+addread(Conv2D_003)
+addread(Conv2D_U8_000)
+addread(Conv2D_U8_001)
+addread(Cos_000)
+addread(DepthToSpace_000)
+addread(DepthwiseConv2D_000)
+addread(DepthwiseConv2D_U8_000)
+addread(DepthwiseConv2D_U8_001)
+addread(DepthwiseConv2D_001)
+addread(Dequantize_000)
+addread(Div_000)
+addread(ELU_000)
+addread(Equal_000)
+addread(Exp_000)
+addread(ExpandDims_000)
+addread(ExpandDims_001)
+addread(ExpandDims_002)
+addread(ExpandDims_003)
+addread(Fill_000)
+addread(Fill_001)
+addread(Floor_000)
+addread(FloorDiv_000)
+addread(FloorDiv_001)
+addread(FloorMod_000)
+addread(FloorMod_001)
+addread(FullyConnected_000)
+addread(FullyConnected_001)
+addread(FullyConnected_002)
+addread(FullyConnected_U8_000)
+addread(Gather_000)
+addread(GatherNd_000)
+addread(Greater_000)
+addread(GreaterEqual_000)
+addread(If_000)
+addread(If_001)
+addread(L2Normalize_000)
+addread(L2Normalize_U8_000)
+addread(L2Pool2D_000)
+addread(L2Pool2D_U8_000)
+addread(LeakyRelu_000)
+addread(Less_000)
+addread(LessEqual_000)
+addread(LocalResponseNormalization_000)
+addread(Log_000)
+addread(LogicalAnd_000)
+addread(LogicalNot_000)
+addread(LogicalOr_000)
+addread(Logistic_000)
+addread(Logistic_U8_000)
+addread(LogSoftmax_000)
+addread(MatMul_000)
+addread(MatrixDiag_000)
+addread(MatrixSetDiag_000)
+addread(Maximum_000)
+addread(MaxPool2D_000)
+addread(MaxPool2D_U8_000)
+addread(Mean_000)
+addread(Mean_001)
+addread(Mean_U8_000)
+addread(Minimum_000)
+addread(MirrorPad_000)
+addread(Mul_000)
+addread(Mul_U8_000)
+addread(Neg_000)
+addread(NonMaxSuppressionV4_000)
+addread(NonMaxSuppressionV4_001)
+addread(NonMaxSuppressionV5_000)
+addread(NonMaxSuppressionV5_001)
+addread(NotEqual_000)
+addread(OneHot_000)
+addread(OneHot_001)
+addread(OneHot_002)
+addread(OneHot_003)
+addread(Pack_000)
+addread(Pack_U8_000)
+addread(Pad_000)
+addread(Pad_U8_000)
+addread(PadV2_000)
+addread(Pow_000)
+addread(PRelu_000)
+addread(Range_000)
+addread(Rank_000)
+addread(ReduceAny_000)
+addread(ReduceAny_001)
+addread(ReduceAny_002)
+addread(ReduceAny_003)
+addread(ReduceMax_000)
+addread(ReduceMin_000)
+addread(ReduceProd_000)
+addread(ReduceProd_001)
+addread(ReduceProd_002)
+addread(ReduceProd_003)
+addread(ReLU_000)
+addread(ReLU6_000)
+addread(ReLUN1To1_000)
+addread(Reshape_000)
+addread(Reshape_001)
+addread(Reshape_002)
+addread(Reshape_003)
+addread(Reshape_U8_000)
+addread(ResizeBilinear_000)
+addread(ResizeBilinear_U8_000)
+addread(ResizeNearestNeighbor_000)
+addread(ReverseSequence_000)
+addread(ReverseV2_000)
+addread(Round_000)
+addread(Rsqrt_000)
+addread(ScatterNd_000)
+addread(SegmentSum_000)
+addread(Select_000)
+addread(Select_001)
+addread(Select_002)
+addread(SelectV2_000)
+addread(SelectV2_001)
+addread(SelectV2_002)
+addread(Shape_000)
+addread(Sin_000)
+addread(Slice_000)
+addread(Softmax_000)
+addread(Softmax_U8_000)
+addread(SpaceToBatchND_000)
+addread(SpaceToBatchND_001)
+addread(SpaceToBatchND_002)
+addread(SpaceToBatchND_003)
+addread(SpaceToDepth_000)
+addread(SpaceToDepth_U8_000)
+addread(SparseToDense_000)
+addread(Split_000)
+addread(SplitV_000)
+addread(Sqrt_000)
+addread(Square_000)
+addread(SquaredDifference_000)
+addread(Squeeze_000)
+addread(StridedSlice_000)
+addread(StridedSlice_001)
+addread(StridedSlice_002)
+addread(Sub_000)
+addread(Sub_U8_000)
+addread(Sum_000)
+addread(Sum_001)
+addread(Tanh_000)
+addread(Tanh_U8_000)
+addread(Tile_000)
+addread(Tile_U8_000)
+addread(TopKV2_000)
+addread(TopKV2_001)
+addread(Transpose_000)
+addread(TransposeConv_000)
+addread(UnidirectionalSequenceLSTM_000)
+addread(UnidirectionalSequenceLSTM_001)
+addread(Unique_000)
+addread(Unique_001)
+addread(Unique_002)
+addread(Unique_003)
+addread(Unique_U8_000)
+addread(Unique_U8_001)
+addread(Unpack_000)
+addread(Unpack_001)
+addread(Unpack_002)
+addread(Unpack_003)
+addread(Where_000)
+addread(Where_001)
+addread(While_000)
+addread(While_001)
+addread(While_002)
+addread(While_003)
+addread(YUV_TO_RGB_U8_000)
+addread(ZerosLike_000)
+
+addread(Net_Dangle_001)
+addread(Net_InstanceNorm_001)
+addread(Net_InstanceNorm_002)
+addread(Net_UnpackAdd_001)
+addread(Net_ZeroDim_001)
+
+# from res/CircleRecipes
+addread(BCQFullyConnected_000)
+addread(BCQFullyConnected_001)
+addread(BCQGather_000)
+addread(CircleBatchMatMul_000)
+addread(InstanceNorm_000)
+
+addwrite(Abs_000)
+addwrite(Add_000)
+addwrite(Add_001)
+addwrite(Add_U8_000)
+addwrite(AddN_000)
+addwrite(ArgMax_000)
+addwrite(ArgMax_001)
+addwrite(ArgMax_002)
+addwrite(ArgMax_003)
+addwrite(ArgMax_U8_000)
+addwrite(ArgMax_U8_001)
+addwrite(ArgMax_U8_002)
+addwrite(ArgMax_U8_003)
+addwrite(ArgMin_000)
+addwrite(ArgMin_001)
+addwrite(ArgMin_002)
+addwrite(ArgMin_003)
+addwrite(ArgMin_U8_000)
+addwrite(ArgMin_U8_001)
+addwrite(ArgMin_U8_002)
+addwrite(ArgMin_U8_003)
+addwrite(AveragePool2D_000)
+addwrite(AveragePool2D_U8_000)
+addwrite(BatchMatMul_000)
+addwrite(BatchMatMulV2_000)
+addwrite(BatchMatMulV2_001)
+addwrite(BatchToSpaceND_000)
+addwrite(Cast_000)
+addwrite(Cast_001)
+addwrite(Ceil_000)
+addwrite(Concatenation_000)
+addwrite(Concatenation_U8_000)
+addwrite(Conv2D_000)
+addwrite(Conv2D_001)
+addwrite(Conv2D_002)
+addwrite(Conv2D_003)
+addwrite(Conv2D_U8_000)
+addwrite(Conv2D_U8_001)
+addwrite(Cos_000)
+addwrite(DepthToSpace_000)
+addwrite(DepthwiseConv2D_000)
+addwrite(DepthwiseConv2D_U8_000)
+addwrite(DepthwiseConv2D_U8_001)
+addwrite(DepthwiseConv2D_001)
+addwrite(Dequantize_000)
+addwrite(Div_000)
+addwrite(ELU_000)
+addwrite(Equal_000)
+addwrite(Exp_000)
+addwrite(ExpandDims_000)
+addwrite(ExpandDims_001)
+addwrite(ExpandDims_002)
+addwrite(ExpandDims_003)
+addwrite(Fill_000)
+addwrite(Fill_001)
+addwrite(Floor_000)
+addwrite(FloorDiv_000)
+addwrite(FloorDiv_001)
+addwrite(FloorMod_000)
+addwrite(FloorMod_001)
+addwrite(FullyConnected_000)
+addwrite(FullyConnected_001)
+addwrite(FullyConnected_002)
+addwrite(FullyConnected_U8_000)
+addwrite(Gather_000)
+addwrite(GatherNd_000)
+addwrite(Greater_000)
+addwrite(GreaterEqual_000)
+addwrite(If_000)
+addwrite(If_001)
+addwrite(L2Normalize_000)
+addwrite(L2Normalize_U8_000)
+addwrite(L2Pool2D_000)
+addwrite(L2Pool2D_U8_000)
+addwrite(LeakyRelu_000)
+addwrite(Less_000)
+addwrite(LessEqual_000)
+addwrite(LocalResponseNormalization_000)
+addwrite(Log_000)
+addwrite(LogicalAnd_000)
+addwrite(LogicalNot_000)
+addwrite(LogicalOr_000)
+addwrite(Logistic_000)
+addwrite(Logistic_U8_000)
+addwrite(LogSoftmax_000)
+addwrite(MatMul_000)
+addwrite(MatrixDiag_000)
+addwrite(MatrixSetDiag_000)
+addwrite(Maximum_000)
+addwrite(MaxPool2D_000)
+addwrite(MaxPool2D_U8_000)
+addwrite(Mean_000)
+addwrite(Mean_001)
+addwrite(Mean_U8_000)
+addwrite(Minimum_000)
+addwrite(MirrorPad_000)
+addwrite(Mul_000)
+addwrite(Mul_U8_000)
+addwrite(Neg_000)
+addwrite(NonMaxSuppressionV4_000)
+addwrite(NonMaxSuppressionV4_001)
+addwrite(NonMaxSuppressionV5_000)
+addwrite(NonMaxSuppressionV5_001)
+addwrite(NotEqual_000)
+addwrite(OneHot_000)
+addwrite(OneHot_001)
+addwrite(OneHot_002)
+addwrite(OneHot_003)
+addwrite(Pack_000)
+addwrite(Pack_U8_000)
+addwrite(Pad_000)
+addwrite(PadV2_000)
+addwrite(Pow_000)
+addwrite(PRelu_000)
+addwrite(Range_000)
+addwrite(Rank_000)
+addwrite(ReduceAny_000)
+addwrite(ReduceAny_001)
+addwrite(ReduceAny_002)
+addwrite(ReduceAny_003)
+addwrite(ReduceMax_000)
+addwrite(ReduceMin_000)
+addwrite(ReduceProd_000)
+addwrite(ReduceProd_001)
+addwrite(ReduceProd_002)
+addwrite(ReduceProd_003)
+addwrite(ReLU_000)
+addwrite(ReLU6_000)
+addwrite(ReLUN1To1_000)
+addwrite(Reshape_000)
+addwrite(Reshape_001)
+addwrite(Reshape_002)
+addwrite(Reshape_003)
+addwrite(Reshape_U8_000)
+addwrite(ResizeBilinear_000)
+addwrite(ResizeBilinear_U8_000)
+addwrite(ResizeNearestNeighbor_000)
+addwrite(ReverseSequence_000)
+addwrite(ReverseV2_000)
+addwrite(Round_000)
+addwrite(Rsqrt_000)
+addwrite(ScatterNd_000)
+addwrite(SegmentSum_000)
+addwrite(Select_000)
+addwrite(Select_001)
+addwrite(Select_002)
+addwrite(SelectV2_000)
+addwrite(SelectV2_001)
+addwrite(SelectV2_002)
+addwrite(Shape_000)
+addwrite(Sin_000)
+addwrite(Slice_000)
+addwrite(Softmax_000)
+addwrite(Softmax_U8_000)
+addwrite(SpaceToBatchND_000)
+addwrite(SpaceToBatchND_001)
+addwrite(SpaceToBatchND_002)
+addwrite(SpaceToBatchND_003)
+addwrite(SpaceToDepth_000)
+addwrite(SpaceToDepth_U8_000)
+addwrite(SparseToDense_000)
+addwrite(Split_000)
+addwrite(SplitV_000)
+addwrite(Sqrt_000)
+addwrite(Square_000)
+addwrite(SquaredDifference_000)
+addwrite(Squeeze_000)
+addwrite(StridedSlice_000)
+addwrite(StridedSlice_001)
+addwrite(StridedSlice_002)
+addwrite(Sub_000)
+addwrite(Sub_U8_000)
+addwrite(Sum_000)
+addwrite(Sum_001)
+addwrite(Tanh_000)
+addwrite(Tanh_U8_000)
+addwrite(Tile_000)
+addwrite(Tile_U8_000)
+addwrite(TopKV2_000)
+addwrite(TopKV2_001)
+addwrite(Transpose_000)
+addwrite(TransposeConv_000)
+addwrite(UnidirectionalSequenceLSTM_000)
+addwrite(UnidirectionalSequenceLSTM_001)
+addwrite(Unique_000)
+addwrite(Unique_001)
+addwrite(Unique_002)
+addwrite(Unique_003)
+addwrite(Unique_U8_000)
+addwrite(Unique_U8_001)
+addwrite(Unpack_000)
+addwrite(Unpack_001)
+addwrite(Unpack_002)
+addwrite(Unpack_003)
+addwrite(Where_000)
+addwrite(Where_001)
+addwrite(While_000)
+addwrite(While_001)
+addwrite(While_002)
+addwrite(While_003)
+addwrite(YUV_TO_RGB_U8_000)
+addwrite(ZerosLike_000)
+
+addwrite(Net_Dangle_001)
+addwrite(Net_InstanceNorm_001)
+addwrite(Net_InstanceNorm_002)
+addwrite(Net_UnpackAdd_001)
+addwrite(Net_ZeroDim_001)
+
+# from res/CircleRecipes
+addwrite(BCQFullyConnected_000)
+addwrite(BCQFullyConnected_001)
+addwrite(BCQGather_000)
+addwrite(CircleBatchMatMul_000)
+addwrite(InstanceNorm_000)
diff --git a/compiler/luci/tests/writeverify.sh b/compiler/luci/tests/writeverify.sh
new file mode 100755
index 000000000..6980bac44
--- /dev/null
+++ b/compiler/luci/tests/writeverify.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+# This script verifies the basic behavior of luci frontend
+#
+# HOW TO USE
+#
+# ./writeverify.sh <path/to/luci_writetester> <TEST 1> <TEST 2> ...
+VERIFY_SOURCE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+WORKDIR="$1"; shift
+VERIFY_BINARY_PATH="$1"; shift
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+for TESTCASE in "$@"; do
+ TESTED+=("${TESTCASE}")
+
+ TESTCASE_FILE="${WORKDIR}/${TESTCASE}"
+
+ PASSED_TAG="${TESTCASE_FILE}_w.passed"
+ rm -f "${PASSED_TAG}"
+
+ cat > "${TESTCASE_FILE}_w.log" <(
+ exec 2>&1
+ set -ex
+
+ "${VERIFY_BINARY_PATH}" "${TESTCASE_FILE}.circle" "${TESTCASE_FILE}_w.circle"
+
+ if [[ $? -eq 0 ]]; then
+ touch "${PASSED_TAG}"
+ fi
+ )
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ PASSED+=("${TESTCASE}")
+ else
+ FAILED+=("${TESTCASE}")
+ fi
+done
+
+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/mio-circle/CMakeLists.txt b/compiler/mio-circle/CMakeLists.txt
new file mode 100644
index 000000000..9c1126d6f
--- /dev/null
+++ b/compiler/mio-circle/CMakeLists.txt
@@ -0,0 +1,28 @@
+nnas_find_package(FlatBuffers QUIET)
+
+if(NOT FlatBuffers_FOUND)
+ return()
+endif(NOT FlatBuffers_FOUND)
+
+message(STATUS "Build mio-circle: TRUE")
+
+# TODO Find a better way
+set(SCHEMA_FILE "${NNAS_PROJECT_SOURCE_DIR}/nnpackage/schema/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_circle
+ 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-circle" library
+add_executable(mio_circle_example example.cpp)
+target_link_libraries(mio_circle_example mio_circle)
diff --git a/compiler/mio-circle/README.md b/compiler/mio-circle/README.md
new file mode 100644
index 000000000..e90ec513f
--- /dev/null
+++ b/compiler/mio-circle/README.md
@@ -0,0 +1,3 @@
+# mio-circle
+
+Let's make it easy to read and write Circle models.
diff --git a/compiler/mio-circle/example.cpp b/compiler/mio-circle/example.cpp
new file mode 100644
index 000000000..6418e0411
--- /dev/null
+++ b/compiler/mio-circle/example.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// This example shows how to include and use "mio-circle"
+//
+#include <mio/circle/schema_generated.h>
+
+#include <fstream>
+#include <iostream>
+#include <vector>
+
+int main(int argc, char **argv)
+{
+ std::ifstream ifs(argv[1], std::ios_base::binary);
+ std::vector<char> buf(std::istreambuf_iterator<char>{ifs}, std::istreambuf_iterator<char>{});
+
+ flatbuffers::Verifier verifier{reinterpret_cast<uint8_t *>(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-tf/CMakeLists.txt b/compiler/mio-tf/CMakeLists.txt
new file mode 100644
index 000000000..133d4684a
--- /dev/null
+++ b/compiler/mio-tf/CMakeLists.txt
@@ -0,0 +1,48 @@
+nnas_find_package(Protobuf QUIET)
+# TensorFlowSource package is used to use ~.proto files
+nnas_find_package(TensorFlowSource EXACT 2.3 QUIET)
+
+if(NOT Protobuf_FOUND)
+ return()
+endif(NOT Protobuf_FOUND)
+
+if(NOT TensorFlowSource_FOUND)
+ return()
+endif(NOT TensorFlowSource_FOUND)
+
+message(STATUS "Build mio-tf: TRUE")
+
+# Minimal Protocol Buffer specification for GraphDef file (.pb) encoding/decoding
+unset(PROTO_FILES)
+list(APPEND PROTO_FILES tensorflow/core/framework/versions.proto)
+list(APPEND PROTO_FILES tensorflow/core/framework/resource_handle.proto)
+list(APPEND PROTO_FILES tensorflow/core/framework/types.proto)
+list(APPEND PROTO_FILES tensorflow/core/framework/tensor.proto)
+list(APPEND PROTO_FILES tensorflow/core/framework/tensor_shape.proto)
+list(APPEND PROTO_FILES tensorflow/core/framework/attr_value.proto)
+list(APPEND PROTO_FILES tensorflow/core/framework/op_def.proto)
+list(APPEND PROTO_FILES tensorflow/core/framework/node_def.proto)
+list(APPEND PROTO_FILES tensorflow/core/framework/function.proto)
+list(APPEND PROTO_FILES tensorflow/core/framework/graph.proto)
+
+Protobuf_Generate(GRAPHDEF_PROTO
+ "${CMAKE_CURRENT_BINARY_DIR}/generated"
+ "${TensorFlowSource_DIR}"
+ ${PROTO_FILES})
+
+add_library(mio_tf STATIC ${GRAPHDEF_PROTO_SOURCES})
+set_target_properties(mio_tf PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(mio_tf PUBLIC ${GRAPHDEF_PROTO_INCLUDE_DIRS})
+target_link_libraries(mio_tf PUBLIC ${GRAPHDEF_PROTO_LIBRARIES})
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+
+GTest_AddTest(mio_tf_test ${TESTS})
+target_include_directories(mio_tf_test PRIVATE src)
+target_link_libraries(mio_tf_test mio_tf)
diff --git a/compiler/mio-tf/README.md b/compiler/mio-tf/README.md
new file mode 100644
index 000000000..18f475a85
--- /dev/null
+++ b/compiler/mio-tf/README.md
@@ -0,0 +1,3 @@
+# mio-tf
+
+_mio-tf_ provides a library to access TensorFlow model files
diff --git a/compiler/mio-tf/src/mio_tf.test.cpp b/compiler/mio-tf/src/mio_tf.test.cpp
new file mode 100644
index 000000000..013dc2d54
--- /dev/null
+++ b/compiler/mio-tf/src/mio_tf.test.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <tensorflow/core/framework/graph.pb.h>
+
+#include <gtest/gtest.h>
+
+TEST(MIO_TF_Test, instance)
+{
+ tensorflow::GraphDef gd;
+ tensorflow::NodeDef nd;
+
+ SUCCEED();
+}
diff --git a/compiler/mio-tflite/CMakeLists.txt b/compiler/mio-tflite/CMakeLists.txt
new file mode 100644
index 000000000..9ef2859b9
--- /dev/null
+++ b/compiler/mio-tflite/CMakeLists.txt
@@ -0,0 +1,38 @@
+nnas_find_package(FlatBuffers QUIET)
+
+if(NOT FlatBuffers_FOUND)
+ message(STATUS "Build mio-tflite: FAILED (missing Flatbuffers)")
+ return()
+endif(NOT FlatBuffers_FOUND)
+
+nnas_find_package(TensorFlowSource EXACT 2.3.0 QUIET)
+
+if(NOT TensorFlowSource_FOUND)
+ return()
+endif(NOT TensorFlowSource_FOUND)
+
+message(STATUS "Build mio-tflite: TRUE")
+
+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_tflite
+ 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_tflite_example example.cpp)
+target_link_libraries(mio_tflite_example mio_tflite)
+
+# Temporay tflite validation tool to replace nnkit-tflite
+# TODO provide full tflite validation with runtime/interpreter
+add_executable(mio_tflite_validate example.cpp)
+target_link_libraries(mio_tflite_validate mio_tflite)
diff --git a/compiler/mio-tflite/README.md b/compiler/mio-tflite/README.md
new file mode 100644
index 000000000..187b1a5c6
--- /dev/null
+++ b/compiler/mio-tflite/README.md
@@ -0,0 +1,3 @@
+# mio-tflite
+
+_mio-tflite_ provides a library to access TensorFlow lite model files
diff --git a/compiler/mio-tflite/example.cpp b/compiler/mio-tflite/example.cpp
new file mode 100644
index 000000000..54d15103c
--- /dev/null
+++ b/compiler/mio-tflite/example.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// This example shows how to include and use "mio-tflite"
+//
+#include <mio/tflite/schema_generated.h>
+
+#include <fstream>
+#include <iostream>
+#include <vector>
+
+int main(int argc, char **argv)
+{
+ std::ifstream ifs(argv[1], std::ios_base::binary);
+ std::vector<char> buf(std::istreambuf_iterator<char>{ifs}, std::istreambuf_iterator<char>{});
+
+ flatbuffers::Verifier verifier{reinterpret_cast<uint8_t *>(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/mir-caffe-importer/CMakeLists.txt b/compiler/mir-caffe-importer/CMakeLists.txt
deleted file mode 100644
index 8bf829e64..000000000
--- a/compiler/mir-caffe-importer/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-nncc_find_package(CaffeProto QUIET)
-
-if (NOT CaffeProto_FOUND)
- return()
-endif ()
-
-set(MIR_CAFFE_IMPORTER_SOURCES
- caffe_importer.cpp
- caffe_importer.h
- caffe_op_creator.cpp
- caffe_op_creator.h
- caffe_op_types.h)
-
-add_library(mir_caffe_importer STATIC ${MIR_CAFFE_IMPORTER_SOURCES})
-set_target_properties(mir_caffe_importer PROPERTIES POSITION_INDEPENDENT_CODE ON)
-target_include_directories(mir_caffe_importer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
-target_link_libraries(mir_caffe_importer PUBLIC mir caffeproto PRIVATE stdex)
diff --git a/compiler/mir-caffe-importer/caffe_importer.cpp b/compiler/mir-caffe-importer/caffe_importer.cpp
deleted file mode 100644
index 346b79e02..000000000
--- a/compiler/mir-caffe-importer/caffe_importer.cpp
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "caffe_importer.h"
-#include "caffe_op_creator.h"
-#include "caffe_op_types.h"
-
-#include "mir/ops/OutputOp.h"
-
-#include <google/protobuf/io/zero_copy_stream_impl.h>
-#include <google/protobuf/io/coded_stream.h>
-
-#include <fcntl.h>
-
-#include <cassert>
-#include <cerrno>
-#include <cstring>
-#include <stdex/Memory.h>
-#include <stdexcept>
-#include <utility>
-#include <vector>
-
-namespace mir_caffe
-{
-
-using namespace ::caffe;
-
-CaffeImporter::CaffeImporter(std::string filename) : _modelFilename(std::move(filename))
-{
- _graph = stdex::make_unique<mir::Graph>();
- _opCreator = stdex::make_unique<CaffeOpCreator>(_graph.get());
-}
-
-CaffeImporter::~CaffeImporter() = default;
-
-static void loadModelFile(const std::string &filename, caffe::NetParameter *net)
-{
- GOOGLE_PROTOBUF_VERIFY_VERSION;
-
- int file_handle = open(filename.c_str(), O_RDONLY);
-
- if (file_handle == -1)
- throw std::runtime_error("Couldn't open file \"" + filename + "\": " + std::strerror(errno) +
- ".");
-
- google::protobuf::io::FileInputStream file_stream(file_handle);
- file_stream.SetCloseOnDelete(true);
-
- google::protobuf::io::CodedInputStream coded_stream(&file_stream);
- coded_stream.SetTotalBytesLimit(INT_MAX, INT_MAX);
-
- if (!net->ParseFromCodedStream(&coded_stream))
- throw std::runtime_error("Couldn't parse file \"" + filename + "\".");
-
- // If the file has not been consumed entirely, assume that the file is in the wrong format.
- if (!coded_stream.ConsumedEntireMessage())
- throw std::runtime_error("File \"" + filename + "\" has not been consumed entirely.");
-}
-
-void CaffeImporter::import()
-{
- _net = stdex::make_unique<NetParameter>();
- loadModelFile(_modelFilename, _net.get());
-
- collectUnsupportedLayers();
-}
-
-std::unique_ptr<mir::Graph> CaffeImporter::createIR()
-{
- for (int i = 0; i < _net->layer_size(); ++i)
- createMIRNodesFromLayer(_net->layer(i));
-
- setGraphOutputs();
-
- return std::move(_graph);
-}
-
-std::unique_ptr<mir::Graph> CaffeImporter::importModel()
-{
- import();
- return createIR();
-}
-
-void CaffeImporter::collectUnsupportedLayers()
-{
- processDeprecatedInput();
-
- for (int i = 0; i < _net->layer_size(); ++i)
- collectUnsupportedOp(_net->layer(i));
-
- if (!_problemsOpSet.empty())
- {
- std::string msg("NNC can't load model. Detected problems:");
- for (const auto &problemStr : _problemsOpSet)
- msg.append("\n * " + problemStr);
- throw std::runtime_error(msg);
- }
-}
-
-void CaffeImporter::createMIRNodesFromLayer(const LayerParameter &layer)
-{
- std::vector<mir::Operation::Output *> inputs = getMIRInputsForLayer(layer);
- std::vector<mir::Operation::Output *> outputs;
-
- switch (_operatorTypes.at(layer.type()))
- {
- case CaffeOpType::input:
- outputs = _opCreator->convertInput(layer);
- break;
- case CaffeOpType::convolution:
- outputs = _opCreator->convertConvolution(layer, inputs);
- break;
- case CaffeOpType::innerProduct:
- outputs = _opCreator->convertInnerProduct(layer, inputs);
- break;
- case CaffeOpType::pooling:
- outputs = _opCreator->convertPooling(layer, inputs);
- break;
- case CaffeOpType::concat:
- outputs = _opCreator->convertConcat(layer, inputs);
- break;
- case CaffeOpType::reshape:
- outputs = _opCreator->convertReshape(layer, inputs);
- break;
- case CaffeOpType::ReLU:
- outputs = _opCreator->convertReLU(layer, inputs);
- break;
- case CaffeOpType::softmax:
- outputs = _opCreator->convertSoftmax(layer, inputs);
- break;
- case CaffeOpType::scale:
- outputs = _opCreator->convertScale(layer, inputs);
- break;
- case CaffeOpType::batchNorm:
- outputs = _opCreator->convertBatchNorm(layer, inputs);
- break;
- case CaffeOpType::dropout:
- outputs = _opCreator->convertDropout(layer, inputs);
- break;
- case CaffeOpType::tanh:
- outputs = _opCreator->convertTanH(layer, inputs);
- break;
- case CaffeOpType::ELU:
- outputs = _opCreator->convertELU(layer, inputs);
- break;
- case CaffeOpType::eltwise:
- outputs = _opCreator->convertEltwise(layer, inputs);
- break;
- case CaffeOpType::embed:
- outputs = _opCreator->convertEmbed(layer, inputs);
- break;
- case CaffeOpType::deconvolution:
- outputs = _opCreator->convertDeconvolution(layer, inputs);
- break;
- case CaffeOpType::split:
- outputs = _opCreator->convertSplit(layer, inputs);
- break;
- case CaffeOpType::sigmoid:
- outputs = _opCreator->convertSigmoid(layer, inputs);
- break;
- case CaffeOpType::LSTM:
- outputs = _opCreator->convertLSTM(layer, inputs);
- break;
- default:
- assert(false && "All unsupported types should have been found before this pass.");
- }
-
- assert(static_cast<int>(outputs.size()) == layer.top_size() && "Number of outputs differs.");
- for (int i = 0; i < layer.top_size(); ++i)
- setOutputForBlob(layer.top(i), outputs[i]);
-}
-
-void CaffeImporter::collectUnsupportedOp(const LayerParameter &lp)
-{
-
- auto it = _operatorTypes.find(lp.type());
- if (it == _operatorTypes.end())
- {
- _problemsOpSet.insert(lp.type() + ": unknown layer");
- return;
- }
-
- CaffeOpType op_type = it->second;
-
- switch (op_type)
- {
- case CaffeOpType::concat:
- case CaffeOpType::input:
- case CaffeOpType::softmax:
- case CaffeOpType::scale:
- case CaffeOpType::dropout:
- case CaffeOpType::split:
- case CaffeOpType::eltwise:
- case CaffeOpType::ELU:
- case CaffeOpType::ReLU:
- case CaffeOpType::embed:
- case CaffeOpType::sigmoid:
- case CaffeOpType::tanh:
- case CaffeOpType::innerProduct:
- // No checks
- break;
- case CaffeOpType::deconvolution:
- case CaffeOpType::convolution:
- _opCreator->checkConvolution(lp.convolution_param(), _problemsOpSet);
- break;
- case CaffeOpType::pooling:
- _opCreator->checkPooling(lp.pooling_param(), _problemsOpSet);
- break;
- case CaffeOpType::reshape:
- _opCreator->checkReshape(lp.reshape_param(), _problemsOpSet);
- break;
- case CaffeOpType::batchNorm:
- _opCreator->checkBatchNorm(lp, _problemsOpSet);
- break;
- case CaffeOpType::LSTM:
- _opCreator->checkLSTM(lp, _problemsOpSet);
- break;
- default:
- _problemsOpSet.insert(lp.type() + ": unsupported layer");
- break;
- }
-}
-
-void CaffeImporter::processDeprecatedInput()
-{
- if (_net->input_dim_size() != 0 || _net->input_shape_size() != 0)
- throw std::runtime_error("Deprecated Caffe input types are not supported");
-}
-
-std::vector<mir::Operation::Output *>
-CaffeImporter::getMIRInputsForLayer(const LayerParameter &layer)
-{
- std::vector<mir::Operation::Output *> inputs;
-
- for (const auto &input_name : layer.bottom())
- inputs.push_back(getOutputForBlob(input_name));
-
- return inputs;
-}
-
-mir::Operation::Output *CaffeImporter::getOutputForBlob(const std::string &blob_name) const
-{
- return _blobNameToOpOutput.at(blob_name);
-}
-
-void CaffeImporter::setOutputForBlob(const std::string &blob_name, mir::Operation::Output *output)
-{
- const auto it = _blobNameToOpOutput.find(blob_name);
- if (it != _blobNameToOpOutput.cend())
- {
- // caffe input blob name could be same as output blob name, and next line will overwrite
- // '_blobNameToOpOutput' element, but in all networks that I saw it was not a problem
- it->second->setName("");
- }
-
- // Do not overwrite the name in case of fall-through layers (ex. Dropout, Split).
- // TODO Find a way to handle it properly.
- if (output->getName().empty())
- output->setName(blob_name);
-
- _blobNameToOpOutput[blob_name] = output;
-}
-
-void CaffeImporter::setGraphOutputs()
-{
- // TODO For now, we assume that:
- // - there is exactly one output;
- // - the output is from the last layer.
- const auto &last_layer = *_net->layer().rbegin();
- auto output = getOutputForBlob(last_layer.top(0));
- _graph->create<mir::ops::OutputOp>(output);
-}
-
-const std::map<std::string, CaffeOpType> CaffeImporter::_operatorTypes = {
- {"AbsVal", CaffeOpType::absVal},
- {"Accuracy", CaffeOpType::accuracy},
- {"ArgMax", CaffeOpType::argMax},
- {"BatchNorm", CaffeOpType::batchNorm},
- {"BatchReindex", CaffeOpType::batchReindex},
- {"Bias", CaffeOpType::bias},
- {"BNLL", CaffeOpType::BNLL},
- {"Clip", CaffeOpType::clip},
- {"Concat", CaffeOpType::concat},
- {"ContrastiveLoss", CaffeOpType::contrastiveLoss},
- {"Convolution", CaffeOpType::convolution},
- {"Crop", CaffeOpType::crop},
- {"Data", CaffeOpType::data},
- {"Deconvolution", CaffeOpType::deconvolution},
- {"Dropout", CaffeOpType::dropout},
- {"DummyData", CaffeOpType::dummyData},
- {"Eltwise", CaffeOpType::eltwise},
- {"ELU", CaffeOpType::ELU},
- {"Embed", CaffeOpType::embed},
- {"EuclidianLoss", CaffeOpType::euclidianLoss},
- {"Exp", CaffeOpType::exp},
- {"Filter", CaffeOpType::filter},
- {"Flatten", CaffeOpType::flatten},
- {"HDF5Data", CaffeOpType::HDF5Data},
- {"HDF5Output", CaffeOpType::HDF5Output},
- {"HingeLoss", CaffeOpType::hingeLoss},
- {"Im2Col", CaffeOpType::im2Col},
- {"ImageData", CaffeOpType::imageData},
- {"InfogainLoss", CaffeOpType::infogainLoss},
- {"InnerProduct", CaffeOpType::innerProduct},
- {"Input", CaffeOpType::input},
- {"Log", CaffeOpType::log},
- {"LRN", CaffeOpType::LRN},
- {"LSTM", CaffeOpType::LSTM},
- {"MemoryData", CaffeOpType::memoryData},
- {"MultinomialLogisticLoss", CaffeOpType::multinomialLogisticLoss},
- {"MVN", CaffeOpType::MVN},
- {"Parameter", CaffeOpType::parameter},
- {"Pooling", CaffeOpType::pooling},
- {"Power", CaffeOpType::power},
- {"PReLU", CaffeOpType::PReLU},
- {"Python", CaffeOpType::python},
- {"Recurrent", CaffeOpType::recurrent},
- {"Reduction", CaffeOpType::reduction},
- {"ReLU", CaffeOpType::ReLU},
- {"Reshape", CaffeOpType::reshape},
- {"RNN", CaffeOpType::RNN},
- {"Scale", CaffeOpType::scale},
- {"SigmoidCrossEntropyLoss", CaffeOpType::sigmoidCrossEntropyLoss},
- {"Sigmoid", CaffeOpType::sigmoid},
- {"Silence", CaffeOpType::silence},
- {"Softmax", CaffeOpType::softmax},
- {"SoftmaxWithLoss", CaffeOpType::softmaxWithLoss},
- {"SPP", CaffeOpType::SPP},
- {"Split", CaffeOpType::split},
- {"Slice", CaffeOpType::slice},
- {"TanH", CaffeOpType::tanh},
- {"Threshold", CaffeOpType::threshold},
- {"Tile", CaffeOpType::tile},
- {"WindowData", CaffeOpType::windowData}};
-
-} // namespace mir_caffe
diff --git a/compiler/mir-caffe-importer/caffe_importer.h b/compiler/mir-caffe-importer/caffe_importer.h
deleted file mode 100644
index 4fb609f6a..000000000
--- a/compiler/mir-caffe-importer/caffe_importer.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_CAFFE_IMPORTER_H
-#define MIR_CAFFE_IMPORTER_H
-
-#include <set>
-#include <string>
-#include <memory>
-
-#include "caffe/proto/caffe.pb.h"
-#include "caffe_op_creator.h"
-#include "caffe_op_types.h"
-
-namespace mir_caffe
-{
-
-class CaffeImporter
-{
-public:
- explicit CaffeImporter(std::string filename);
-
- void import();
- std::unique_ptr<mir::Graph> createIR();
-
- /// @brief Load the model and convert it into a MIR Graph.
- std::unique_ptr<mir::Graph> importModel();
-
- ~CaffeImporter();
-
-private:
- std::string _modelFilename;
- std::unique_ptr<::caffe::NetParameter> _net;
- std::unique_ptr<mir::Graph> _graph;
- std::unique_ptr<CaffeOpCreator> _opCreator;
-
- // Maps Caffe blob names to corresponding MIR operation outputs.
- std::map<std::string, mir::Operation::Output *> _blobNameToOpOutput;
-
- static const std::map<std::string, CaffeOpType> _operatorTypes;
- // set of strings describing incorrect parts of network and parts of network unsupported by NNC
- std::set<std::string> _problemsOpSet;
-
- /**
- * @brief Mark output MIR nodes
- */
- void setGraphOutputs();
-
- /**
- * @brief Pass through caffe graph and collect unsupported by NNC layers
- * @throw PassException with message, containing detected problems
- */
- void collectUnsupportedLayers();
-
- /**
- * @brief Create MIR node from single caffe layer
- */
- void createMIRNodesFromLayer(const ::caffe::LayerParameter &layer);
-
- mir::Operation::Output *getOutputForBlob(const std::string &blob_name) const;
- void setOutputForBlob(const std::string &blob_name, mir::Operation::Output *output);
-
- /**
- * @brief Collect unsupported parts of caffe layer
- */
- void collectUnsupportedOp(const ::caffe::LayerParameter &lp);
-
- /**
- * @brief Returns MIR operation outputs corresponding to the inputs of the given layer.
- */
- std::vector<mir::Operation::Output *> getMIRInputsForLayer(const ::caffe::LayerParameter &layer);
-
- void processDeprecatedInput();
-};
-
-} // namespace mir_caffe
-
-#endif // MIR_CAFFE_IMPORTER_H
diff --git a/compiler/mir-caffe-importer/caffe_op_creator.cpp b/compiler/mir-caffe-importer/caffe_op_creator.cpp
deleted file mode 100644
index 09d48d251..000000000
--- a/compiler/mir-caffe-importer/caffe_op_creator.cpp
+++ /dev/null
@@ -1,910 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "caffe_op_creator.h"
-
-#include "mir/ops/AddOp.h"
-#include "mir/ops/AvgPool2DOp.h"
-#include "mir/ops/CappedReluOp.h"
-#include "mir/ops/ConcatOp.h"
-#include "mir/ops/ConstantOp.h"
-#include "mir/ops/Conv2DOp.h"
-#include "mir/ops/Deconv2DOp.h"
-#include "mir/ops/DepthwiseConv2DOp.h"
-#include "mir/ops/EluOp.h"
-#include "mir/ops/FullyConnectedOp.h"
-#include "mir/ops/GatherOp.h"
-#include "mir/ops/LeakyReluOp.h"
-#include "mir/ops/MaxOp.h"
-#include "mir/ops/MaxPool2DOp.h"
-#include "mir/ops/MulOp.h"
-#include "mir/ops/ReluOp.h"
-#include "mir/ops/ReshapeOp.h"
-#include "mir/ops/SigmoidOp.h"
-#include "mir/ops/SliceOp.h"
-#include "mir/ops/SoftmaxOp.h"
-#include "mir/ops/TanhOp.h"
-#include "mir/ops/TransposeOp.h"
-#include "mir/Index.h"
-#include "mir/ShapeRange.h"
-#include "mir/Tensor.h"
-#include "mir/TensorUtil.h"
-
-#include <cmath>
-#include <iostream>
-#include <set>
-
-namespace mir_caffe
-{
-
-static TensorVariant fixGroupedKernel(int groups, const TensorVariant &folded_kernel)
-{
- const int kernel_in_chan_num = 2;
- const int kernel_out_chan_num = 3;
-
- const mir::Shape &kernel_shape = folded_kernel.getShape();
- auto kernel_in_channels = kernel_shape.dim(kernel_in_chan_num);
- auto kernel_out_channels = kernel_shape.dim(kernel_out_chan_num);
- auto in_channels = kernel_in_channels * groups;
-
- // Original kernel has shape [H, W, inputChannels/groups, outputChannels]
- // here creates unfolded kernel with shape [H, W, inputChannels, outputChannels]
- mir::Shape unfold_kernel_shape(kernel_shape);
- unfold_kernel_shape.dim(kernel_in_chan_num) = in_channels;
- size_t data_size = folded_kernel.getElementSize();
- TensorVariant unfold_kernel(folded_kernel.getDataType(), unfold_kernel_shape);
-
- int in_group_size = kernel_in_channels;
- int out_group_size = kernel_out_channels / groups;
- assert(kernel_out_channels % groups == 0);
-
- // Iterate over "unfolded" kernel Shape and insert appropriate values into result kernel
- for (const mir::Index &idx : mir::ShapeRange(unfold_kernel_shape))
- {
- auto in_group_no = idx.at(kernel_in_chan_num) / in_group_size;
- auto out_group_no = idx.at(kernel_out_chan_num) / out_group_size;
- // check that input channel group fits output channel group
- if (in_group_no == out_group_no)
- {
- // compute index in original kernel that corresponds output index
- mir::Index folded_idx(idx);
- folded_idx.at(kernel_in_chan_num) %= in_group_size;
-
- std::copy(folded_kernel.at(folded_idx), folded_kernel.at(folded_idx) + data_size,
- unfold_kernel.at(idx));
- }
- else
- {
- // fill element of output kernel with zero element
- assert(folded_kernel.getDataType() == mir::DataType::FLOAT32 &&
- "unsupported data type, add appropriate zero element creation");
- auto elem = reinterpret_cast<float *>(unfold_kernel.at(idx));
- *elem = 0.0f;
- }
- }
- return unfold_kernel;
-}
-
-using namespace ::caffe;
-
-static mir::Shape convertBlobShape(const BlobShape &shape)
-{
- mir::Shape mir_shape(shape.dim_size());
-
- for (int i = 0; i < shape.dim_size(); ++i)
- {
- mir_shape.dim(i) = shape.dim(i);
- }
-
- return mir_shape;
-}
-
-using namespace mir;
-
-/// @brief Split arg into @p num_parts equal parts along @p axis axis.
-std::vector<mir::Operation::Output *> CaffeOpCreator::createSplit(mir::Operation::Output *arg,
- int32_t num_parts, int32_t axis)
-{
- const auto &arg_shape = arg->getShape();
-
- assert(axis >= 0 && axis < arg_shape.rank());
- int32_t part_size = arg_shape.dim(axis) / num_parts;
- assert(part_size * num_parts == arg_shape.dim(axis));
-
- Shape starts(arg_shape.rank());
- Shape sizes(arg_shape);
- sizes.dim(axis) = part_size;
-
- std::vector<mir::Operation::Output *> outputs(num_parts);
- for (int32_t i = 0; i < num_parts; ++i)
- {
- outputs[i] = createOp<ops::SliceOp>(arg, starts, sizes)->getOutput(0);
- starts.dim(axis) += part_size;
- }
-
- return outputs;
-}
-
-/// @brief Helper function for creating FullyConnected operation with non-square input.
-mir::Operation::Output *CaffeOpCreator::createFullyConnected(mir::Operation::Output *input,
- mir::Operation::Output *weights,
- int32_t axis)
-{
- const auto &input_shape = input->getShape();
- const auto &weights_shape = weights->getShape();
-
- assert(axis >= 0 && axis < input_shape.rank());
- assert(weights_shape.rank() == 2);
-
- // Result shape is: input.shape[0:axis] + weights.shape[1].
- Shape result_shape = input_shape;
- result_shape.resize(axis + 1);
- result_shape.dim(axis) = weights_shape.dim(1);
-
- // Flatten input to 2-D shape.
- int32_t outer_size = 1;
- for (int32_t i = 0; i < axis; ++i)
- outer_size *= input_shape.dim(i);
- int32_t inner_size = 1;
- for (int32_t i = axis; i < input_shape.rank(); ++i)
- inner_size *= input_shape.dim(i);
-
- auto flatten = createOp<ops::ReshapeOp>(input, Shape{outer_size, inner_size})->getOutput(0);
- auto fc = createOp<ops::FullyConnectedOp>(flatten, weights)->getOutput(0);
- return createOp<ops::ReshapeOp>(fc, result_shape)->getOutput(0);
-}
-
-TensorVariant CaffeOpCreator::convertBlob(const BlobProto &blob)
-{
- const void *src_data;
-
- mir::DataType dtype;
- if (blob.data_size() != 0)
- {
- assert(blob.double_data_size() == 0);
- dtype = mir::DataType::FLOAT32;
- src_data = blob.data().data();
- }
- else if (blob.double_data_size() != 0)
- {
- dtype = mir::DataType::FLOAT64;
- src_data = blob.double_data().data();
- }
- else
- {
- throw std::runtime_error("No data in Caffe BlobProto, investigate");
- }
-
- const mir::Shape shape = convertBlobShape(blob.shape());
- return TensorVariant(dtype, shape, src_data);
-}
-
-std::vector<mir::Operation::Output *> CaffeOpCreator::convertInput(const LayerParameter &layer)
-{
- const auto &params = layer.input_param();
- const auto num_inputs = layer.top_size();
- const auto num_shapes = params.shape_size();
- std::vector<mir::Operation::Output *> outputs;
-
- assert((num_shapes == 1 || num_shapes == num_inputs) && "Unsupported number of shapes.");
-
- for (int i = 0; i < num_inputs; ++i)
- {
- const auto &blob_shape = params.shape(num_shapes == 1 ? 0 : i);
- const mir::Shape shape = convertBlobShape(blob_shape);
- auto input = createOp<ops::InputOp>(shape)->getOutput(0);
- outputs.push_back(input);
- }
-
- return outputs;
-}
-
-static void convertConvolutionParam(const ConvolutionParameter &conv_param,
- std::vector<std::int32_t> &strides,
- std::vector<std::int32_t> &padding_before,
- std::vector<std::int32_t> &padding_after)
-{
- std::int32_t stride_h, stride_w;
- if (conv_param.has_stride_h() || conv_param.has_stride_w())
- {
- // If stride_h or stride_w are set, they take precedence.
- stride_h = conv_param.stride_h();
- stride_w = conv_param.stride_w();
- }
- else if (conv_param.stride_size() == 0)
- {
- // If no strides specified, they defaults to 1.
- stride_h = stride_w = 1;
- }
- else if (conv_param.stride_size() == 1)
- {
- // If only one stride specified, all strides take the same value.
- stride_h = stride_w = conv_param.stride(0);
- }
- else
- {
- // Otherwise, there must be a stride for each dimension.
- assert(conv_param.stride_size() == 2);
- stride_h = conv_param.stride(0);
- stride_w = conv_param.stride(1);
- }
- strides = {stride_h, stride_w};
-
- std::int32_t pad_h, pad_w;
- if (conv_param.has_pad_h() || conv_param.has_pad_w())
- {
- // If pad_h or pad_w are set, they take precedence.
- pad_h = conv_param.pad_h();
- pad_w = conv_param.pad_w();
- }
- else if (conv_param.pad_size() == 0)
- {
- // If no pads specified, they defaults to 0.
- pad_h = pad_w = 0;
- }
- else if (conv_param.pad_size() == 1)
- {
- // If only one pad specified, all pads take the same value.
- pad_h = pad_w = conv_param.pad(0);
- }
- else
- {
- // Otherwise, there must be a pad for each dimension.
- assert(conv_param.pad_size() == 2);
- pad_h = conv_param.pad(0);
- pad_w = conv_param.pad(1);
- }
- padding_after = padding_before = {pad_h, pad_w};
-}
-
-void CaffeOpCreator::checkConvolution(const ConvolutionParameter &opts,
- std::set<std::string> &problems_ops_set)
-{
- assert(opts.stride_size() <= 2);
-
- if (opts.axis() != 1)
- problems_ops_set.insert("Conv2D: Unsupported axis");
-
- if (opts.pad_size() != 0 && (opts.has_pad_h() || opts.has_pad_w()))
- problems_ops_set.insert("Conv2D: Conflicting padding properties");
-
- if (opts.pad_size() > 2)
- problems_ops_set.insert("Conv2D: Unsupported number of pads");
-}
-
-std::vector<mir::Operation::Output *>
-CaffeOpCreator::convertConvolution(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- const auto &params = layer.convolution_param();
- std::vector<std::int32_t> strides;
- std::vector<std::int32_t> padding_before;
- std::vector<std::int32_t> padding_after;
-
- convertConvolutionParam(params, strides, padding_before, padding_after);
-
- assert(layer.blobs(0).shape().dim_size() == 4);
- auto kernel_weights = convertBlob(layer.blobs(0));
- kernel_weights = transposeTensor<2, 3, 1, 0>(kernel_weights);
-
- Operation::Output *result;
- auto in_group_size = kernel_weights.getShape().dim(2);
- auto out_channels = kernel_weights.getShape().dim(3);
- int32_t num_groups = params.group();
- bool is_depthwise = (num_groups != 1) && (in_group_size == 1) && (out_channels == num_groups);
- if (is_depthwise)
- {
- // This is depthwise convolution
- // TODO handle properly kernel with layer multiplier
- auto transposed_tensor = transposeTensor<0, 1, 3, 2>(kernel_weights);
- auto kernel = createOp<ops::ConstantOp>(transposed_tensor)->getOutput(0);
- result = createOp<ops::DepthwiseConv2DOp>(inputs[0], kernel, strides, padding_before,
- padding_after, DataFormat::NCHW)
- ->getOutput(0);
- }
- else
- {
- if (num_groups != 1)
- {
- // first we need to convert kernel of grouped convolution to appropriate ordinary kernel
- kernel_weights = fixGroupedKernel(params.group(), kernel_weights);
- }
- kernel_weights = transposeTensor<3, 0, 1, 2>(kernel_weights);
- auto kernel = createOp<ops::ConstantOp>(kernel_weights)->getOutput(0);
- result = createOp<ops::Conv2DOp>(inputs[0], kernel, strides, padding_before, padding_after,
- DataFormat::NCHW)
- ->getOutput(0);
- }
-
- // Add the bias, if any.
- if (params.bias_term())
- {
- auto bias = createOp<ops::ConstantOp>(convertBlob(layer.blobs(1)))->getOutput(0);
- bias = createOp<ops::ReshapeOp>(bias, Shape{1, bias->getShape().dim(0), 1, 1})->getOutput(0);
- result = createOp<ops::AddOp>(result, bias)->getOutput(0);
- }
-
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-CaffeOpCreator::convertDeconvolution(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto &opts = layer.convolution_param();
- std::vector<std::int32_t> strides;
- std::vector<std::int32_t> padding_before;
- std::vector<std::int32_t> padding_after;
-
- convertConvolutionParam(opts, strides, padding_before, padding_after);
-
- auto kernel_weights = convertBlob(layer.blobs(0));
- kernel_weights = transposeTensor<2, 3, 1, 0>(kernel_weights);
-
- if (opts.group() != 1)
- {
- // first we need to convert kernel of grouped convolution to appropriate ordinary kernel
- kernel_weights = fixGroupedKernel(opts.group(), kernel_weights);
- }
- auto kernel = createOp<ops::ConstantOp>(kernel_weights)->getOutput(0);
- auto result = createOp<ops::DeConv2DOp>(inputs[0], kernel, strides, padding_before, padding_after,
- DataFormat::NCHW)
- ->getOutput(0);
-
- // bias_term is optional (so might not be present) and defaults to true
- if (opts.bias_term())
- {
- auto bias = createOp<ops::ConstantOp>(convertBlob(layer.blobs(1)))->getOutput(0);
- bias = createOp<ops::ReshapeOp>(bias, Shape{1, bias->getShape().dim(0), 1, 1})->getOutput(0);
- result = createOp<ops::AddOp>(result, bias)->getOutput(0);
- }
-
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-CaffeOpCreator::convertInnerProduct(const LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- const auto &params = layer.inner_product_param();
- auto weights_tensor = convertBlob(layer.blobs(0));
-
- if (!params.transpose())
- weights_tensor = transposeTensor<1, 0>(weights_tensor);
-
- auto weights = createOp<ops::ConstantOp>(weights_tensor)->getOutput(0);
- auto result = createFullyConnected(inputs[0], weights, params.axis());
-
- // Add the bias, if any.
- if (params.bias_term())
- {
- auto bias = createOp<ops::ConstantOp>(convertBlob(layer.blobs(1)))->getOutput(0);
- result = createOp<ops::AddOp>(result, bias)->getOutput(0);
- }
-
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-CaffeOpCreator::convertConcat(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- const auto &params = layer.concat_param();
- auto concat = createOp<ops::ConcatOp>(inputs, params.axis());
- return {concat->getOutput(0)};
-}
-
-static void
-convertPoolingParam(const caffe::PoolingParameter &params, const mir::Shape &input_shape,
- std::vector<std::int32_t> &window_size, std::vector<std::int32_t> &strides,
- std::vector<int32_t> &padding_before, std::vector<int32_t> &padding_after)
-{
- std::int32_t kernel_h, kernel_w;
- assert(!params.global_pooling());
- if (params.has_kernel_size())
- {
- kernel_h = kernel_w = params.kernel_size();
- }
- else
- {
- kernel_h = params.kernel_h();
- kernel_w = params.kernel_w();
- }
- window_size = {kernel_h, kernel_w};
-
- std::int32_t stride_h, stride_w;
- if (params.has_stride_h() || params.has_stride_w())
- {
- stride_h = params.stride_h();
- stride_w = params.stride_w();
- }
- else
- {
- stride_h = stride_w = params.stride();
- }
- strides = {stride_h, stride_w};
-
- std::int32_t pad_h, pad_w;
- if (params.has_pad_h() || params.has_pad_w())
- {
- pad_h = params.pad_h();
- pad_w = params.pad_w();
- }
- else
- {
- pad_h = pad_w = params.pad();
- }
-
- padding_before = padding_after = {pad_h, pad_w};
-
- // Caffe uses different formula for computing output shape than MIR. Adjust padding so that
- // the output shape stays the same.
- constexpr int num_spatial_dims = 2;
- for (int i = 0; i < num_spatial_dims; ++i)
- {
- // Assuming NCHW format.
- const std::int32_t padded_input = input_shape.dim(2 + i) + padding_before[i] + padding_after[i];
- if ((padded_input - window_size[i]) % strides[i] != 0)
- ++padding_after[i];
- }
-}
-
-void CaffeOpCreator::checkPooling(const PoolingParameter &params,
- std::set<std::string> &problems_ops_set)
-{
- if (params.has_global_pooling() && params.global_pooling())
- problems_ops_set.insert("Pooling: pooling layer global_pooling param is not supported yet");
-
- if (params.pool() != PoolingParameter::AVE && params.pool() != PoolingParameter::MAX)
- problems_ops_set.insert("Pooling: unsupported pooling type");
-
- if (params.has_pad() && (params.has_pad_h() || params.has_pad_w()))
- problems_ops_set.insert("Pooling: conflicting padding properties in pooling");
-}
-
-std::vector<mir::Operation::Output *>
-CaffeOpCreator::convertPooling(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- const auto &params = layer.pooling_param();
-
- assert(inputs.size() == 1);
- auto input = inputs[0];
-
- std::vector<std::int32_t> window_size;
- std::vector<std::int32_t> strides;
- std::vector<std::int32_t> padding_before, padding_after;
- convertPoolingParam(params, input->getShape(), window_size, strides, padding_before,
- padding_after);
-
- mir::Operation::Output *result;
-
- switch (params.pool())
- {
- case PoolingParameter::AVE:
- result = createOp<ops::AvgPool2DOp>(input, window_size, strides, padding_before,
- padding_after, true, mir::DataFormat::NCHW)
- ->getOutput(0);
- break;
- case PoolingParameter::MAX:
- result = createOp<ops::MaxPool2DOp>(input, window_size, strides, padding_before,
- padding_after, mir::DataFormat::NCHW)
- ->getOutput(0);
- break;
- default:
- assert(false);
- }
-
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-CaffeOpCreator::convertSoftmax(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- const auto &params = layer.softmax_param();
-
- // CPP and ACL backends are able to perform Softmax only along the last axis.
- // FIXME Do it in backends.
- if (inputs[0]->getShape().rank() == 4)
- {
- // For now, we only account for the most common case.
- if (params.axis() != 1)
- throw std::runtime_error("Softmax: unsupported axis");
- int32_t axis = 3;
- auto input = createOp<ops::TransposeOp>(inputs[0], std::vector<std::size_t>{0, 2, 3, 1});
- auto softmax = createOp<ops::SoftmaxOp>(input->getOutput(0), axis);
- auto result =
- createOp<ops::TransposeOp>(softmax->getOutput(0), std::vector<std::size_t>{0, 3, 1, 2});
- return {result->getOutput(0)};
- }
-
- auto softmax = createOp<ops::SoftmaxOp>(inputs[0], params.axis());
- return {softmax->getOutput(0)};
-}
-
-void CaffeOpCreator::checkReshape(const ReshapeParameter &opts,
- std::set<std::string> &problems_ops_set)
-{
- if (opts.has_axis() || opts.has_num_axes())
- problems_ops_set.insert("Reshape layer axis and num_axes params are not supported yet");
-
- if (!opts.has_shape())
- problems_ops_set.insert("Reshape layer doesn't have shape parameter");
-
- const mir::Shape newShape = convertBlobShape(opts.shape());
-
- for (int32_t i = 0; i < newShape.rank(); ++i)
- if (newShape.dim(i) == 0)
- problems_ops_set.insert("Reshape layer zero shape values are not supported yet");
-}
-
-/**
- * @brief Converts Caffe Reshape layer to Model IR Reshape operation.
- * @todo Support "axis" and "num_axes" parameters as needed.
- * @todo Decide how to react to the absence of "shape" parameter.
- * @todo Support zero values in "shape" parameter.
- */
-std::vector<mir::Operation::Output *>
-CaffeOpCreator::convertReshape(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto &opts = layer.reshape_param();
- const mir::Shape new_shape = convertBlobShape(opts.shape());
- auto reshape = createOp<ops::ReshapeOp>(inputs[0], new_shape);
- return {reshape->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-CaffeOpCreator::convertReLU(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- mir::Operation *relu;
- if (layer.relu_param().has_negative_slope())
- {
- float alpha = layer.relu_param().negative_slope();
- relu = createOp<ops::LeakyReluOp>(inputs[0], alpha);
- }
- else
- {
- relu = createOp<ops::ReluOp>(inputs[0]);
- }
-
- return {relu->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-CaffeOpCreator::convertScale(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- const auto &params = layer.scale_param();
- auto scale = createOp<ops::ConstantOp>(convertBlob(layer.blobs(0)))->getOutput(0);
- scale = createOp<ops::ReshapeOp>(scale, Shape{1, scale->getShape().dim(0), 1, 1})->getOutput(0);
- auto result = createOp<ops::MulOp>(inputs[0], scale)->getOutput(0);
-
- // Add the bias, if any.
- if (params.bias_term())
- {
- auto bias = createOp<ops::ConstantOp>(convertBlob(layer.blobs(1)))->getOutput(0);
- bias = createOp<ops::ReshapeOp>(bias, Shape{1, bias->getShape().dim(0), 1, 1})->getOutput(0);
- result = createOp<ops::AddOp>(result, bias)->getOutput(0);
- }
-
- return {result};
-}
-
-void CaffeOpCreator::checkBatchNorm(const caffe::LayerParameter &layer,
- std::set<std::string> &problems_ops_set)
-{
- const auto &scale_shape = layer.blobs(2).shape();
-
- // Check that last blob(with scaleFactor) containing only one number
- if (scale_shape.dim_size() != 1 || scale_shape.dim(0) != 1)
- problems_ops_set.insert("Unexpected shape of scale parameter in batch norm");
-}
-
-std::vector<mir::Operation::Output *>
-CaffeOpCreator::convertBatchNorm(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- const auto &opts = layer.batch_norm_param();
- auto input = inputs[0];
- auto mean_tensor = convertBlob(layer.blobs(0));
- auto var_tensor = convertBlob(layer.blobs(1));
- auto scale_tensor = convertBlob(layer.blobs(2));
- const float eps = opts.eps();
-
- float scale_factor = *reinterpret_cast<float *>(scale_tensor.at(mir::Index{0}));
-
- // See https://github.com/BVLC/caffe/blob/master/src/caffe/layers/batch_norm_layer.cpp#L100
- // Y = (X - mean / scale_factor) / sqrt(var / scale_factor + epsilon) =
- // = (X + C1) * C2
- if (scale_factor != 0.0f)
- scale_factor = 1.0f / scale_factor;
-
- // C1 = -mean / scale_factor
- Tensor<float> mean_accessor(mean_tensor);
- for (const auto &idx : ShapeRange(mean_accessor.getShape()))
- mean_accessor.at(idx) *= -scale_factor;
- auto c1 = createOp<ops::ConstantOp>(mean_tensor)->getOutput(0);
-
- // C2 = 1 / sqrt(var / scale_factor + epsilon)
- Tensor<float> var_accessor(var_tensor);
- for (const auto &idx : ShapeRange(var_accessor.getShape()))
- var_accessor.at(idx) = 1.0f / std::sqrt(var_accessor.at(idx) * scale_factor + eps);
- auto c2 = createOp<ops::ConstantOp>(var_tensor)->getOutput(0);
-
- c1 = createOp<ops::ReshapeOp>(c1, Shape{1, c1->getShape().dim(0), 1, 1})->getOutput(0);
- c2 = createOp<ops::ReshapeOp>(c2, Shape{1, c2->getShape().dim(0), 1, 1})->getOutput(0);
-
- // Y = (X + C1) * C2
- auto result = createOp<ops::AddOp>(input, c1)->getOutput(0);
- result = createOp<ops::MulOp>(result, c2)->getOutput(0);
-
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-CaffeOpCreator::convertDropout(const caffe::LayerParameter &,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- // This is a no-op in inference mode.
- return {inputs[0]};
-}
-
-std::vector<mir::Operation::Output *>
-CaffeOpCreator::convertELU(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto &opts = layer.elu_param();
- auto elu = createOp<ops::EluOp>(inputs[0], opts.alpha());
- return {elu->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-CaffeOpCreator::convertEmbed(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- const auto &params = layer.embed_param();
- auto data = createOp<ops::ConstantOp>(convertBlob(layer.blobs(0)));
- auto result = createOp<ops::GatherOp>(data->getOutput(0), inputs[0], 0)->getOutput(0);
-
- // Add the bias, if any.
- if (params.bias_term())
- {
- auto bias = createOp<ops::ConstantOp>(convertBlob(layer.blobs(1)))->getOutput(0);
- result = createOp<ops::AddOp>(result, bias)->getOutput(0);
- }
-
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-CaffeOpCreator::convertSigmoid(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto result = createOp<ops::SigmoidOp>(inputs[0]);
- return {result->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-CaffeOpCreator::convertTanH(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto tanh = createOp<ops::TanhOp>(inputs[0]);
- return {tanh->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-CaffeOpCreator::convertEltwise(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto &params = layer.eltwise_param();
-
- mir::Operation::Output *result;
- switch (params.operation())
- {
- case EltwiseParameter_EltwiseOp_PROD:
- {
- result = createOp<ops::MulOp>(inputs[0], inputs[1])->getOutput(0);
- for (int i = 2; i < layer.bottom_size(); ++i)
- {
- result = createOp<ops::MulOp>(result, inputs[i])->getOutput(0);
- }
- break;
- }
- case EltwiseParameter_EltwiseOp_SUM:
- {
- std::vector<mir::Operation::Output *> scaled_inputs = inputs;
- if (params.coeff_size() > 0)
- {
- assert(params.coeff_size() == layer.bottom_size());
- for (int i = 0; i < layer.bottom_size(); i++)
- {
- if (params.coeff(i) != 1.0f)
- {
- const float coeff_val = params.coeff(i);
- TensorVariant coeff_tensor(mir::DataType::FLOAT32, Shape{}, &coeff_val);
- auto coeff_const = createOp<ops::ConstantOp>(coeff_tensor)->getOutput(0);
- scaled_inputs[i] = createOp<ops::MulOp>(coeff_const, inputs[i])->getOutput(0);
- }
- }
- }
- result = createOp<ops::AddOp>(scaled_inputs[0], scaled_inputs[1])->getOutput(0);
- for (int i = 2; i < layer.bottom_size(); ++i)
- {
- result = createOp<ops::AddOp>(result, scaled_inputs[i])->getOutput(0);
- }
- break;
- }
- case EltwiseParameter_EltwiseOp_MAX:
- {
- result = createOp<ops::MaxOp>(inputs[0], inputs[1])->getOutput(0);
- for (int i = 2; i < layer.bottom_size(); ++i)
- {
- result = createOp<ops::MaxOp>(result, inputs[i])->getOutput(0);
- }
- break;
- }
- default:
- throw std::runtime_error("Unknown element-wise operation.");
- }
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-CaffeOpCreator::convertSplit(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- std::vector<mir::Operation::Output *> outputs(layer.top_size(), inputs.at(0));
- return outputs;
-}
-
-void CaffeOpCreator::checkLSTM(const caffe::LayerParameter &layer,
- std::set<std::string> &problems_ops_set)
-{
- const auto &params = layer.recurrent_param();
- if (params.expose_hidden())
- problems_ops_set.insert("LSTM: parameter 'expose_hidden' has unsupported value: " +
- std::to_string(params.expose_hidden()));
-}
-
-static TensorVariant createZeroedTensor(const mir::Shape &shape)
-{
- // TODO For now it is hardcoded float32.
- auto elem_type = mir::DataType::FLOAT32;
- std::vector<float> zeros(static_cast<std::size_t>(shape.numElements()), 0.0f);
- return TensorVariant(elem_type, shape, zeros.data());
-}
-
-/* See the following links for details on implementation:
- * https://github.com/BVLC/caffe/blob/master/src/caffe/layers/recurrent_layer.cpp
- * https://github.com/BVLC/caffe/blob/master/src/caffe/layers/lstm_layer.cpp
- * https://github.com/BVLC/caffe/blob/master/src/caffe/layers/lstm_unit_layer.cpp
- *
- * Inputs:
- * x -- The time-varying input. Shape: [T, N, d0, d1, ..., dn].
- * cont -- The sequence continuation indicators. Shape: [T, N].
- * x_static -- The static (non-time-varying) input. Shape: [N, ...].
- * This parameter is optional and not currently supported.
- *
- * Additional inputs when parameter "expose_hidden" is true (not currently supported):
- * h_0 -- The initial value of the hidden state. Shape: [1, N, D].
- * c_0 -- The initial value of the cell state. Shape: [1, N, D].
- *
- * Learned parameters:
- * xw -- x weights for input, output, forget and cell gates concatenated.
- * Shape: [4 * D, d0 * d1 * ... * dn].
- * xb -- x biases for input, output, forget and cell gates concatenated. Shape: [4 * D].
- * hw -- h weights for input, output, forget and cell gates concatenated. Shape: [4 * D, D].
- *
- * Outputs:
- * h -- The time-varying output. Shape: [T, N, D].
- *
- * Additional outputs when parameter "expose_hidden" is true (not currently supported):
- * h_T -- The value of the hidden state at the last timestep. Shape: [1, N, D].
- * c_T -- The value of the cell state at the last timestep. Shape: [1, N, D].
- *
- * Here:
- * T - the number of timesteps,
- * N - the number of independent streams.
- * D - the number of hidden parameters.
- *
- * Formulas:
- * c_cont = c[t-1] * cont[t]
- * h_cont = h[t-1] * cont[t]
- * i[t] = Sigmoid(x[t] . xw_i + xb_i + h_cont . hw_i)
- * f[t] = Sigmoid(x[t] . xw_f + xb_f + h_cont . hw_f)
- * o[t] = Sigmoid(x[t] . xw_o + xb_o + h_cont . hw_o)
- * g[t] = Tanh(x[t] . xw_g + xb_g + h_cont . hw_g)
- * c[t] = c_cont * f[t] + i[t] * g[t]
- * h[t] = o[t] * Tanh(c[t])
- *
- * Here:
- * t -- the timestep (ranges from 1 to T),
- * * -- the inner product,
- * . -- the Hadamard product (elementwise product).
- *
- * In this implementation the inner products for all gates are performed as single inner product for
- * efficiency.
- */
-std::vector<mir::Operation::Output *>
-CaffeOpCreator::convertLSTM(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- const auto &params = layer.recurrent_param();
-
- // Inputs to the layer.
- auto x = inputs[0];
- auto cont = inputs[1];
- assert(inputs.size() == 2);
-
- const auto &x_shape = x->getShape();
- const int32_t seq_length = x_shape.dim(0);
- const int32_t batch_size = x_shape.dim(1);
- const int32_t hidden_size = params.num_output();
-
- // Learned parameters of the layer. Tensors are transposed to match the ModelIR.
- auto xw_tensor = transposeTensor<1, 0>(convertBlob(layer.blobs(0)));
- auto xb_tensor = convertBlob(layer.blobs(1));
- auto hw_tensor = transposeTensor<1, 0>(convertBlob(layer.blobs(2)));
- auto xw = createOp<ops::ConstantOp>(xw_tensor)->getOutput(0);
- auto xb = createOp<ops::ConstantOp>(xb_tensor)->getOutput(0);
- auto hw = createOp<ops::ConstantOp>(hw_tensor)->getOutput(0);
-
- // Add a dummy dimension so that element-wise operations perform properly.
- cont = createOp<ops::ReshapeOp>(cont, Shape{seq_length, batch_size, 1})->getOutput(0);
-
- // Initialize cell and hidden states with zeros.
- auto zero_tensor = createZeroedTensor(Shape{1, batch_size, hidden_size});
- auto c_t = createOp<ops::ConstantOp>(zero_tensor)->getOutput(0);
- auto h_t = createOp<ops::ConstantOp>(zero_tensor)->getOutput(0);
-
- auto x_xw = createFullyConnected(x, xw, 2);
- auto x_xw_b = createOp<ops::AddOp>(x_xw, xb)->getOutput(0);
-
- // Split input and continuation tensors into seq_length slices.
- std::vector<mir::Operation::Output *> x_xw_b_slices = createSplit(x_xw_b, seq_length, 0);
- std::vector<mir::Operation::Output *> cont_slices = createSplit(cont, seq_length, 0);
- std::vector<mir::Operation::Output *> h_slices(seq_length);
-
- for (int32_t t = 0; t < seq_length; t++)
- {
- auto c_cont_t = createOp<ops::MulOp>(c_t, cont_slices[t])->getOutput(0);
- auto h_cont_t = createOp<ops::MulOp>(h_t, cont_slices[t])->getOutput(0);
-
- auto x_xw_b_t = x_xw_b_slices[t];
- auto h_hw_t = createFullyConnected(h_cont_t, hw, 2);
- auto activation_inputs_concat = createOp<ops::AddOp>(x_xw_b_t, h_hw_t)->getOutput(0);
- auto activation_inputs = createSplit(activation_inputs_concat, 4, 2);
-
- auto i_t = createOp<ops::SigmoidOp>(activation_inputs[0])->getOutput(0);
- auto f_t = createOp<ops::SigmoidOp>(activation_inputs[1])->getOutput(0);
- auto o_t = createOp<ops::SigmoidOp>(activation_inputs[2])->getOutput(0);
- auto g_t = createOp<ops::TanhOp>(activation_inputs[3])->getOutput(0);
-
- c_t = createOp<ops::AddOp>(createOp<ops::MulOp>(c_cont_t, f_t)->getOutput(0),
- createOp<ops::MulOp>(i_t, g_t)->getOutput(0))
- ->getOutput(0);
- h_t = createOp<ops::MulOp>(createOp<ops::TanhOp>(c_t)->getOutput(0), o_t)->getOutput(0);
-
- h_slices[t] = h_t;
- }
-
- return {createOp<ops::ConcatOp>(h_slices, 0)->getOutput(0)};
-}
-
-} // namespace mir_caffe
diff --git a/compiler/mir-caffe-importer/caffe_op_creator.h b/compiler/mir-caffe-importer/caffe_op_creator.h
deleted file mode 100644
index efd5dc053..000000000
--- a/compiler/mir-caffe-importer/caffe_op_creator.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_CAFFE_OP_CREATOR_H
-#define MIR_CAFFE_OP_CREATOR_H
-
-#include <set>
-#include <map>
-#include <vector>
-#include <memory>
-
-#include "mir/Graph.h"
-#include "mir/TensorVariant.h"
-#include "mir/Shape.h"
-
-#include "caffe/proto/caffe.pb.h"
-
-namespace mir_caffe
-{
-
-using mir::TensorVariant;
-
-class CaffeOpCreator
-{
-public:
- explicit CaffeOpCreator(mir::Graph *g) : _graph(g){};
-
- std::vector<mir::Operation::Output *> convertInput(const caffe::LayerParameter &layer);
-
- std::vector<mir::Operation::Output *>
- convertConvolution(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertInnerProduct(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertConcat(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertPooling(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertSoftmax(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertReshape(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertReLU(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertScale(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertBatchNorm(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertDropout(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertDeconvolution(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertELU(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertEmbed(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertSigmoid(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertTanH(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertEltwise(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertSplit(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertLSTM(const caffe::LayerParameter &layer,
- const std::vector<mir::Operation::Output *> &inputs);
-
- void checkConvolution(const caffe::ConvolutionParameter &opts,
- std::set<std::string> &problems_ops_set);
-
- void checkPooling(const caffe::PoolingParameter &params, std::set<std::string> &problems_ops_set);
-
- void checkReshape(const caffe::ReshapeParameter &opts, std::set<std::string> &problems_ops_set);
-
- void checkBatchNorm(const caffe::LayerParameter &layer, std::set<std::string> &problems_ops_set);
-
- void checkLSTM(const caffe::LayerParameter &layer, std::set<std::string> &problems_ops_set);
-
-private:
- mir::Graph *_graph = nullptr;
-
- std::vector<mir::Operation::Output *> createSplit(mir::Operation::Output *arg, int32_t num_parts,
- int32_t axis);
-
- mir::Operation::Output *createFullyConnected(mir::Operation::Output *input,
- mir::Operation::Output *weights, int32_t axis);
-
- TensorVariant convertBlob(const caffe::BlobProto &blob);
-
- template <typename OpType, typename... Types> mir::Operation *createOp(Types &&... args);
-};
-
-template <typename OpType, typename... Types>
-mir::Operation *CaffeOpCreator::createOp(Types &&... args)
-{
- return _graph->create<OpType>(std::forward<Types>(args)...);
-}
-
-} // namespace mir_caffe
-
-#endif // MIR_CAFFE_OP_CREATOR_H
diff --git a/compiler/mir-caffe2-importer/CMakeLists.txt b/compiler/mir-caffe2-importer/CMakeLists.txt
deleted file mode 100644
index 20dd4a735..000000000
--- a/compiler/mir-caffe2-importer/CMakeLists.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-nncc_find_package(PytorchSource QUIET)
-nncc_find_package(Protobuf QUIET)
-
-if (NOT PytorchSource_FOUND OR NOT Protobuf_FOUND)
- return()
-endif()
-
-Protobuf_Generate(CAFFE2_PROTO "${CMAKE_CURRENT_BINARY_DIR}/generated/caffe2"
- "${PytorchSource_DIR}" "caffe2/proto/caffe2.proto")
-
-add_library(caffe2proto STATIC ${CAFFE2_PROTO_SOURCES})
-set_target_properties(caffe2proto PROPERTIES POSITION_INDEPENDENT_CODE ON)
-target_include_directories(caffe2proto PUBLIC ${CAFFE2_PROTO_INCLUDE_DIRS})
-target_link_libraries(caffe2proto PUBLIC libprotobuf)
-
-
-set(MIR_CAFFE2_IMPORTER_SOURCES
- caffe2_importer.cpp
- caffe2_importer.h
- caffe2_op_creator.cpp
- caffe2_op_creator.h
- caffe2_op_types.h
- caffe2_proto_helper.cpp
- caffe2_proto_helper.h)
-
-add_library(mir_caffe2_importer STATIC ${MIR_CAFFE2_IMPORTER_SOURCES})
-set_target_properties(mir_caffe2_importer PROPERTIES POSITION_INDEPENDENT_CODE ON)
-target_include_directories(mir_caffe2_importer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
-target_link_libraries(mir_caffe2_importer PUBLIC mir caffe2proto PRIVATE stdex)
diff --git a/compiler/mir-caffe2-importer/caffe2_importer.cpp b/compiler/mir-caffe2-importer/caffe2_importer.cpp
deleted file mode 100644
index b8b6f41b1..000000000
--- a/compiler/mir-caffe2-importer/caffe2_importer.cpp
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "caffe2_importer.h"
-#include "caffe2_op_types.h"
-#include "caffe2_op_creator.h"
-#include "caffe2_proto_helper.h"
-
-#include "mir/ops/InputOp.h"
-#include "mir/ops/OutputOp.h"
-
-#include <google/protobuf/io/zero_copy_stream_impl.h>
-#include <google/protobuf/io/coded_stream.h>
-
-#include <fcntl.h>
-
-#include <cassert>
-#include <cerrno>
-#include <cstring>
-#include <stdex/Memory.h>
-#include <stdexcept>
-#include <utility>
-#include <vector>
-
-namespace mir_caffe2
-{
-
-using namespace ::caffe2;
-using mir::Shape;
-
-Caffe2Importer::Caffe2Importer(std::string predict_net, std::string init_net,
- const std::vector<std::vector<int>> &input_shapes)
- : _predictNet(std::move(predict_net)), _initNet(std::move(init_net))
-{
- for (auto &shape : input_shapes)
- _inputShapes.emplace_back(shape);
-
- _graph = stdex::make_unique<mir::Graph>();
- _opCreator = stdex::make_unique<Caffe2OpCreator>(_graph.get());
-}
-
-Caffe2Importer::~Caffe2Importer() = default;
-
-static void loadModelFile(const std::string &filename, caffe2::NetDef *net)
-{
- GOOGLE_PROTOBUF_VERIFY_VERSION;
-
- int file_handle = open(filename.c_str(), O_RDONLY);
-
- if (file_handle == -1)
- throw std::runtime_error("Couldn't open file \"" + filename + "\": " + std::strerror(errno) +
- ".");
-
- google::protobuf::io::FileInputStream file_stream(file_handle);
- file_stream.SetCloseOnDelete(true);
-
- google::protobuf::io::CodedInputStream coded_stream(&file_stream);
- coded_stream.SetTotalBytesLimit(INT_MAX, INT_MAX);
-
- if (!net->ParseFromCodedStream(&coded_stream))
- throw std::runtime_error("Couldn't parse file \"" + filename + "\".");
-
- // If the file has not been consumed entirely, assume that the file is in the wrong format.
- if (!coded_stream.ConsumedEntireMessage())
- throw std::runtime_error("File \"" + filename + "\" has not been consumed entirely.");
-}
-
-void Caffe2Importer::import()
-{
- _predict_net = stdex::make_unique<NetDef>();
- loadModelFile(_predictNet, _predict_net.get());
-
- _init_net = stdex::make_unique<NetDef>();
- loadModelFile(_initNet, _init_net.get());
-
- collectUnsupportedOps();
-}
-
-std::unique_ptr<mir::Graph> Caffe2Importer::createIR()
-{
- // Load initializers.
- for (const auto &op : _init_net->op())
- createMIRNodesFromOp(op);
-
- // Create inputs. This has to be done after processing initializers, because they may contain
- // fake inputs.
- // TODO Caffe2 does not provide a way to detect model inputs and outputs. For now assume that:
- // - there is exactly one input;
- // - the input is for the first layer.
- const auto &input_name = _predict_net->op(0).input(0);
- auto input = _graph->create<mir::ops::InputOp>(_inputShapes[0])->getOutput(0);
- setOutputForTensor(input_name, input);
-
- for (const auto &op : _predict_net->op())
- createMIRNodesFromOp(op);
-
- setGraphOutputs();
-
- return std::move(_graph);
-}
-
-std::unique_ptr<mir::Graph> Caffe2Importer::importModel()
-{
- import();
- return createIR();
-}
-
-void Caffe2Importer::collectUnsupportedOps()
-{
- std::set<std::string> unsupportedOps;
- for (const auto &op : _predict_net->op())
- {
- if (_operatorTypes.find(op.type()) == _operatorTypes.end())
- unsupportedOps.insert(op.type());
- }
-
- if (!unsupportedOps.empty())
- {
- std::string exceptionMsg("Can't load model, unsupported operators:");
- for (const auto &op : unsupportedOps)
- exceptionMsg.append("\n * " + op);
- throw std::runtime_error(exceptionMsg);
- }
-}
-
-void Caffe2Importer::createMIRNodesFromOp(const OperatorDef &op)
-{
- std::vector<mir::Operation::Output *> outputs;
-
- auto inputs = getInputMIROps(op);
-
- SupportedCaffe2OpType opType = _operatorTypes.at(op.type());
- switch (opType)
- {
- case SupportedCaffe2OpType::constantFill:
- case SupportedCaffe2OpType::givenTensorFill:
- case SupportedCaffe2OpType::givenTensorInt64Fill:
- outputs = _opCreator->convertConstant(inputs, op);
- break;
- case SupportedCaffe2OpType::add:
- outputs = _opCreator->convertAdd(inputs, op);
- break;
- case SupportedCaffe2OpType::averagePool:
- outputs = _opCreator->convertAveragePool(inputs, op);
- break;
- case SupportedCaffe2OpType::conv:
- outputs = _opCreator->convertConv(inputs, op);
- break;
- case SupportedCaffe2OpType::concat:
- outputs = _opCreator->convertConcat(inputs, op);
- break;
- case SupportedCaffe2OpType::dropout:
- outputs = _opCreator->convertDropout(inputs, op);
- break;
- case SupportedCaffe2OpType::FC:
- outputs = _opCreator->convertFC(inputs, op);
- break;
- case SupportedCaffe2OpType::maxPool:
- outputs = _opCreator->convertMaxPool(inputs, op);
- break;
- case SupportedCaffe2OpType::mul:
- outputs = _opCreator->convertMul(inputs, op);
- break;
- case SupportedCaffe2OpType::relu:
- outputs = _opCreator->convertRelu(inputs);
- break;
- case SupportedCaffe2OpType::resizeNearest:
- outputs = _opCreator->convertResizeNearest(inputs, op);
- break;
- case SupportedCaffe2OpType::sigmoid:
- outputs = _opCreator->convertSigmoid(inputs);
- break;
- case SupportedCaffe2OpType::softmax:
- outputs = _opCreator->convertSoftmax(inputs, op);
- break;
- case SupportedCaffe2OpType::spatialBN:
- outputs = _opCreator->convertSpatialBN(inputs, op);
- break;
- case SupportedCaffe2OpType::sum:
- outputs = _opCreator->convertSum(inputs);
- break;
- case SupportedCaffe2OpType::clip:
- outputs = _opCreator->convertClip(inputs, op);
- break;
- case SupportedCaffe2OpType::reshape:
- outputs = _opCreator->convertReshape(inputs, op);
- break;
- default:
- assert(false && "All unsupported types should have been found before this pass.");
- }
-
- for (size_t i = 0; i < outputs.size(); ++i)
- {
- setOutputForTensor(op.output(i), outputs[i]);
- }
-}
-
-std::vector<mir::Operation::Output *> Caffe2Importer::getInputMIROps(const OperatorDef &op)
-{
- std::vector<mir::Operation::Output *> inputs;
-
- for (const auto &input_name : op.input())
- {
- inputs.push_back(getOutputForTensor(input_name));
- }
-
- return inputs;
-}
-
-void Caffe2Importer::setOutputForTensor(const std::string &tensor_name, Operation::Output *output)
-{
- auto it = _blobNameToOutput.find(tensor_name);
- if (it != _blobNameToOutput.cend())
- {
- // caffe2 input blob name could be same as output blob name, and next line will overwrite
- // '_blobNameToOpOutput' element, but in all networks that I saw it was not a problem
- it->second->setName("");
- }
- output->setName(tensor_name);
- _blobNameToOutput[tensor_name] = output;
-}
-
-mir::Operation::Output *Caffe2Importer::getOutputForTensor(const std::string &name) const
-{
- return _blobNameToOutput.at(name);
-}
-
-void Caffe2Importer::setGraphOutputs()
-{
- // Create outputs.
- // TODO Caffe2 does not provide a way to detect model inputs and outputs. For now assume that:
- // - there is exactly one output;
- // - the output is from the last layer.
- const auto &output_name = _predict_net->op().rbegin()->output(0);
- auto output = getOutputForTensor(output_name);
- _graph->create<mir::ops::OutputOp>(output);
-}
-
-const std::map<std::string, SupportedCaffe2OpType> Caffe2Importer::_operatorTypes = {
- {"Add", SupportedCaffe2OpType::add},
- {"AveragePool", SupportedCaffe2OpType::averagePool},
- {"Conv", SupportedCaffe2OpType::conv},
- {"Concat", SupportedCaffe2OpType::concat},
- {"ConstantFill", SupportedCaffe2OpType::constantFill},
- {"Dropout", SupportedCaffe2OpType::dropout},
- {"FC", SupportedCaffe2OpType::FC},
- {"GivenTensorFill", SupportedCaffe2OpType::givenTensorFill},
- {"MaxPool", SupportedCaffe2OpType::maxPool},
- {"Mul", SupportedCaffe2OpType::mul},
- {"Relu", SupportedCaffe2OpType::relu},
- {"ResizeNearest", SupportedCaffe2OpType::resizeNearest},
- {"Sigmoid", SupportedCaffe2OpType::sigmoid},
- {"Softmax", SupportedCaffe2OpType::softmax},
- {"SpatialBN", SupportedCaffe2OpType::spatialBN},
- {"Sum", SupportedCaffe2OpType::sum},
- {"Clip", SupportedCaffe2OpType::clip},
- {"Reshape", SupportedCaffe2OpType::reshape},
- {"GivenTensorInt64Fill", SupportedCaffe2OpType::givenTensorInt64Fill},
-};
-
-} // namespace mir_caffe2
diff --git a/compiler/mir-caffe2-importer/caffe2_importer.h b/compiler/mir-caffe2-importer/caffe2_importer.h
deleted file mode 100644
index 9af8387c8..000000000
--- a/compiler/mir-caffe2-importer/caffe2_importer.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_CAFFE2_IMPORTER_H
-#define MIR_CAFFE2_IMPORTER_H
-
-#include <set>
-#include <string>
-#include <memory>
-
-#include "caffe2/proto/caffe2.pb.h"
-#include "caffe2_op_creator.h"
-#include "caffe2_op_types.h"
-
-namespace mir_caffe2
-{
-
-class Caffe2Importer
-{
-public:
- explicit Caffe2Importer(std::string predict_net, std::string init_net,
- const std::vector<std::vector<int>> &input_shapes);
-
- /// @brief Load the model and convert it into a MIR Graph.
- std::unique_ptr<mir::Graph> importModel();
-
- ~Caffe2Importer();
-
-private:
- std::string _predictNet;
- std::string _initNet;
- std::unique_ptr<mir::Graph> _graph;
- std::unique_ptr<caffe2::NetDef> _predict_net;
- std::unique_ptr<caffe2::NetDef> _init_net;
- std::unique_ptr<Caffe2OpCreator> _opCreator;
- std::vector<mir::Shape> _inputShapes;
-
- static const std::map<std::string, SupportedCaffe2OpType> _operatorTypes;
-
- // Maps Caffe2 operator input names to corresponding MIR operation outputs.
- std::unordered_map<std::string, mir::Operation::Output *> _blobNameToOutput;
-
- void import();
- std::unique_ptr<mir::Graph> createIR();
-
- /**
- * @brief Pass through caffe2 graph and collect ops unsupported by NNC
- * @throw PassException with message, containing detected problems
- */
- void collectUnsupportedOps();
-
- /**
- * @brief Creating MIR node from single caffe2 operator
- */
- void createMIRNodesFromOp(const ::caffe2::OperatorDef &op);
-
- /**
- * @brief Returns MIR operation outputs corresponding to the inputs of the given operator.
- */
- std::vector<mir::Operation::Output *> getInputMIROps(const ::caffe2::OperatorDef &op);
-
- void setOutputForTensor(const std::string &tensor_name, Operation::Output *output);
- mir::Operation::Output *getOutputForTensor(const std::string &name) const;
-
- /**
- * @brief Mark output MIR nodes
- */
- void setGraphOutputs();
-};
-
-} // namespace mir_caffe2
-
-#endif // MIR_CAFFE2_IMPORTER_H
diff --git a/compiler/mir-caffe2-importer/caffe2_op_creator.cpp b/compiler/mir-caffe2-importer/caffe2_op_creator.cpp
deleted file mode 100644
index c26e2bd0a..000000000
--- a/compiler/mir-caffe2-importer/caffe2_op_creator.cpp
+++ /dev/null
@@ -1,633 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "caffe2_op_creator.h"
-#include "caffe2_proto_helper.h"
-
-#include "mir/ops/AddOp.h"
-#include "mir/ops/AvgPool2DOp.h"
-#include "mir/ops/CappedReluOp.h"
-#include "mir/ops/ConcatOp.h"
-#include "mir/ops/ConstantOp.h"
-#include "mir/ops/Conv2DOp.h"
-#include "mir/ops/DepthwiseConv2DOp.h"
-#include "mir/ops/FullyConnectedOp.h"
-#include "mir/ops/MaxPool2DOp.h"
-#include "mir/ops/MulOp.h"
-#include "mir/ops/ReluOp.h"
-#include "mir/ops/ReshapeOp.h"
-#include "mir/ops/ResizeOp.h"
-#include "mir/ops/SigmoidOp.h"
-#include "mir/ops/SoftmaxOp.h"
-#include "mir/ops/TransposeOp.h"
-
-#include "mir/Index.h"
-#include "mir/Shape.h"
-#include "mir/ShapeRange.h"
-#include "mir/Tensor.h"
-#include "mir/TensorUtil.h"
-
-#include <cmath>
-#include <vector>
-
-namespace mir_caffe2
-{
-
-static mir::TensorVariant fixGroupedKernel(int groups, const mir::TensorVariant &folded_kernel)
-{
- const int kernel_in_chan_num = 2;
- const int kernel_out_chan_num = 3;
-
- const mir::Shape &kernel_shape = folded_kernel.getShape();
- auto kernel_in_channels = kernel_shape.dim(kernel_in_chan_num);
- auto kernel_out_channels = kernel_shape.dim(kernel_out_chan_num);
- auto in_channels = kernel_in_channels * groups;
-
- // Original kernel has shape [H, W, inputChannels/groups, outputChannels]
- // here creates unfolded kernel with shape [H, W, inputChannels, outputChannels]
- mir::Shape unfold_kernel_shape(kernel_shape);
- unfold_kernel_shape.dim(kernel_in_chan_num) = in_channels;
- size_t data_size = folded_kernel.getElementSize();
- mir::TensorVariant unfold_kernel(folded_kernel.getDataType(), unfold_kernel_shape);
-
- int in_group_size = kernel_in_channels;
- int out_group_size = kernel_out_channels / groups;
- assert(kernel_out_channels % groups == 0);
-
- // Iterate over "unfolded" kernel Shape and insert appropriate values into result kernel
- for (const mir::Index &idx : mir::ShapeRange(unfold_kernel_shape))
- {
- auto in_group_no = idx.at(kernel_in_chan_num) / in_group_size;
- auto out_group_no = idx.at(kernel_out_chan_num) / out_group_size;
- // check that input channel group fits output channel group
- if (in_group_no == out_group_no)
- {
- // compute index in original kernel that corresponds output index
- mir::Index folded_idx(idx);
- folded_idx.at(kernel_in_chan_num) %= in_group_size;
-
- std::copy(folded_kernel.at(folded_idx), folded_kernel.at(folded_idx) + data_size,
- unfold_kernel.at(idx));
- }
- else
- {
- // fill element of output kernel with zero element
- assert(folded_kernel.getDataType() == mir::DataType::FLOAT32 &&
- "unsupported data type, add appropriate zero element creation");
- auto elem = reinterpret_cast<float *>(unfold_kernel.at(idx));
- *elem = 0.0f;
- }
- }
- return unfold_kernel;
-}
-
-using namespace ::caffe2;
-using namespace mir;
-using mir::transposeTensor;
-
-//
-// Helper functions
-//
-
-static std::pair<std::vector<int32_t>, std::vector<int32_t>>
-getPadding(const ::caffe2::OperatorDef &op)
-{
-
- if (hasArgument(op.arg(), "pads"))
- {
- // pads order: t l b r
- auto pads_arg = findArgumentByName(op.arg(), "pads");
-
- std::vector<int32_t> paddings;
- for (const auto &pad : pads_arg.ints())
- paddings.push_back(static_cast<int32_t>(pad));
-
- assert(paddings.size() == 4);
-
- int32_t pad_t = paddings[0];
- int32_t pad_l = paddings[1];
- int32_t pad_b = paddings[2];
- int32_t pad_r = paddings[3];
-
- std::vector<int32_t> padding_before{pad_t, pad_l};
- std::vector<int32_t> padding_after{pad_b, pad_r};
- return {padding_before, padding_after};
- }
-
- bool has_custom_pad = hasArgument(op.arg(), "pad_l") || hasArgument(op.arg(), "pad_r") ||
- hasArgument(op.arg(), "pad_t") || hasArgument(op.arg(), "pad_b");
-
- if (has_custom_pad)
- {
- int32_t pad_l = getSingleArgument(op, "pad_l", 0);
- int32_t pad_t = getSingleArgument(op, "pad_t", 0);
- int32_t pad_r = getSingleArgument(op, "pad_r", 0);
- int32_t pad_b = getSingleArgument(op, "pad_b", 0);
-
- std::vector<int32_t> padding_before{pad_t, pad_l};
- std::vector<int32_t> padding_after{pad_b, pad_r};
- return {padding_before, padding_after};
- }
-
- int32_t pad = getSingleArgument(op, "pad", 0);
- return {{pad, pad}, {pad, pad}};
-}
-
-static std::vector<std::int32_t> getStrides(const ::caffe2::OperatorDef &op)
-{
- std::vector<std::int32_t> strides;
-
- if (hasArgument(op.arg(), "stride"))
- {
- std::int32_t stride = getSingleArgument(op, "stride", 1);
- strides = {stride, stride};
- }
-
- if (hasArgument(op.arg(), "strides"))
- {
- // strides order: h w
- auto strides_arg = findArgumentByName(op.arg(), "strides");
- for (const auto &s : strides_arg.ints())
- strides.push_back(s);
- }
-
- assert(!strides.empty() && "Strides not found");
-
- return strides;
-}
-
-static std::vector<std::int32_t> getWindowSize(const ::caffe2::OperatorDef &op,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- int is_global_pooling = getSingleArgument(op, "global_pooling", 0);
- bool has_custom_kernel_size =
- hasArgument(op.arg(), "kernel_h") || hasArgument(op.arg(), "kernel_w");
- bool has_custom_kernels_size = hasArgument(op.arg(), "kernels");
-
- int kernel_h(0), kernel_w(0);
- if (is_global_pooling)
- {
- const auto &input_shape = inputs[0]->getShape();
- assert(input_shape.rank() == 4 && "getWindowSize() inputs must be of rank 4");
- kernel_h = input_shape.dim(2);
- kernel_w = input_shape.dim(3);
- }
- else
- {
- if (has_custom_kernel_size)
- {
- kernel_h = getSingleArgument(op, "kernel_h", 0);
- kernel_w = getSingleArgument(op, "kernel_w", 0);
- }
- else
- {
- if (has_custom_kernels_size)
- {
- // kernels order: h w
- std::vector<int32_t> kernels;
- auto kernels_arg = findArgumentByName(op.arg(), "kernels");
- for (const auto &ker : kernels_arg.ints())
- kernels.push_back(static_cast<int32_t>(ker));
- assert(kernels.size() == 2);
- kernel_h = kernels[0];
- kernel_w = kernels[1];
- }
- else
- {
- kernel_h = kernel_w = getSingleArgument(op, "kernel", 0);
- }
- }
- }
- return {kernel_h, kernel_w};
-}
-
-//
-// Check functions
-//
-
-static void checkLayout(const OperatorDef &op)
-{
- if (getSingleArgument(op, "order", "NCHW") != "NCHW")
- throw std::runtime_error(op.type() + ": only 'NCHW' axis order is supported");
-}
-
-static void checkConvLikeOp(const ::caffe2::OperatorDef &op)
-{
- checkLayout(op);
-
- // Padding
- bool has_custom_pad = hasArgument(op.arg(), "pad_l") || hasArgument(op.arg(), "pad_r") ||
- hasArgument(op.arg(), "pad_t") || hasArgument(op.arg(), "pad_b");
-
- if (has_custom_pad && hasArgument(op.arg(), "pad"))
- throw std::runtime_error("Custom pad can't be combined with overall pad");
-
- if (has_custom_pad &&
- !(hasArgument(op.arg(), "pad_l") && hasArgument(op.arg(), "pad_r") &&
- hasArgument(op.arg(), "pad_t") && hasArgument(op.arg(), "pad_b")))
- throw std::runtime_error("If one custom pad specified - all custom pads must be specified");
-
- // Kernel size
- bool has_custom_kernel_size =
- hasArgument(op.arg(), "kernel_h") || hasArgument(op.arg(), "kernel_w");
-
- if (has_custom_kernel_size && hasArgument(op.arg(), "kernel"))
- throw std::runtime_error("Custom kernel size can't be combined with overall kernel size");
-
- if (has_custom_kernel_size &&
- !(hasArgument(op.arg(), "kernel_h") && hasArgument(op.arg(), "kernel_w")))
- throw std::runtime_error(
- "If one custom kernel size specified - all custom kernel sizes must be specified");
-}
-
-static mir::TensorVariant createTensor(const OperatorDef &op)
-{
- assert(hasArgument(op.arg(), "shape") && hasArgument(op.arg(), "values"));
-
- const auto &shape = findArgumentByName(op.arg(), "shape");
- const auto &values = findArgumentByName(op.arg(), "values");
-
- mir::DataType element_type;
- const void *src_data;
- // if values on floats
- if (!values.floats().empty())
- {
- element_type = mir::DataType::FLOAT32;
- src_data = values.floats().data();
- }
- else
- {
- assert(!values.ints().empty());
- if (op.type() == "GivenTensorInt64Fill")
- {
- element_type = mir::DataType::INT64;
- }
- else
- {
- element_type = mir::DataType::INT32;
- }
- src_data = values.ints().data();
- }
-
- mir::Shape tensor_shape(shape.ints_size());
-
- for (int i = 0; i < shape.ints_size(); ++i)
- {
- tensor_shape.dim(i) = shape.ints(i);
- }
-
- return mir::TensorVariant(element_type, tensor_shape, src_data);
-}
-
-//
-// Convert functions
-//
-
-std::vector<mir::Operation::Output *>
-Caffe2OpCreator::convertConstant(const std::vector<mir::Operation::Output *> &inputs,
- const ::caffe2::OperatorDef &op)
-{
- // Constant may not contain any data if it is a fake input.
- if (!hasArgument(op.arg(), "values"))
- return {};
-
- return {createOp<ops::ConstantOp>(createTensor(op))->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-Caffe2OpCreator::convertAdd(const std::vector<mir::Operation::Output *> &inputs,
- const ::caffe2::OperatorDef &op)
-{
- assert(inputs.size() == 2);
- auto lhs = inputs[0];
- auto rhs = inputs[1];
-
- if (getSingleArgument(op, "broadcast", 0) != 0)
- {
- // FIXME This only works when 'axis' == 1 and the second input is 1-D.
- rhs = createOp<ops::ReshapeOp>(rhs, Shape{1, rhs->getShape().dim(0), 1, 1})->getOutput(0);
- auto result = createOp<ops::AddOp>(lhs, rhs)->getOutput(0);
- return {result};
- }
-
- auto result = createOp<ops::AddOp>(lhs, rhs)->getOutput(0);
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-Caffe2OpCreator::convertAveragePool(const std::vector<mir::Operation::Output *> &inputs,
- const OperatorDef &op)
-{
- checkConvLikeOp(op);
-
- assert(inputs.size() == 1);
- auto input = inputs[0];
-
- const auto window_size = getWindowSize(op, inputs);
- const auto strides = getStrides(op);
- std::vector<std::int32_t> pad_before, pad_after;
- std::tie(pad_before, pad_after) = getPadding(op);
-
- auto result = createOp<ops::AvgPool2DOp>(input, window_size, strides, pad_before, pad_after,
- false, DataFormat::NCHW)
- ->getOutput(0);
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-Caffe2OpCreator::convertConv(const std::vector<mir::Operation::Output *> &inputs,
- const ::caffe2::OperatorDef &op)
-{
- // dilation order: h w (not used)
- std::vector<std::int32_t> strides = getStrides(op);
-
- std::vector<int32_t> pad_before, pad_after;
- std::tie(pad_before, pad_after) = getPadding(op);
-
- auto kernel_op = dynamic_cast<mir::ops::ConstantOp *>(inputs[1]->getNode());
- if (kernel_op == nullptr)
- throw std::runtime_error("Conv: non-constant kernels is not supported yet.");
-
- // [M, C, kH, kW] -> [kH, kW, C, M]
- auto kernel_tensor = transposeTensor<2, 3, 1, 0>(kernel_op->getValue());
- auto in_group_size = kernel_tensor.getShape().dim(2);
- auto out_channels = kernel_tensor.getShape().dim(3);
- int num_groups = getSingleArgument(op, "group", 1);
- bool is_depthwise = (num_groups != 1) && (in_group_size == 1) && (out_channels == num_groups);
-
- mir::Operation::Output *result;
- if (is_depthwise)
- {
- // TODO handle properly kernel with layer multiplier
- auto transposed_tensor = mir::transposeTensor<0, 1, 3, 2>(kernel_tensor);
- auto kernel = createOp<ops::ConstantOp>(transposed_tensor)->getOutput(0);
- result = createOp<ops::DepthwiseConv2DOp>(inputs[0], kernel, strides, pad_before, pad_after,
- DataFormat::NCHW)
- ->getOutput(0);
- }
- else
- {
- // first we need to convert kernel of grouped convolution to appropriate ordinary kernel
- if (num_groups != 1)
- kernel_tensor = fixGroupedKernel(num_groups, kernel_tensor);
- kernel_tensor = transposeTensor<3, 0, 1, 2>(kernel_tensor);
- auto kernel = createOp<ops::ConstantOp>(kernel_tensor)->getOutput(0);
- result =
- createOp<ops::Conv2DOp>(inputs[0], kernel, strides, pad_before, pad_after, DataFormat::NCHW)
- ->getOutput(0);
- }
-
- if (op.input_size() > 2)
- {
- auto bias = inputs[2];
- bias = createOp<ops::ReshapeOp>(bias, Shape{1, bias->getShape().dim(0), 1, 1})->getOutput(0);
- result = createOp<ops::AddOp>(result, bias)->getOutput(0);
- }
-
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-Caffe2OpCreator::convertConcat(const std::vector<mir::Operation::Output *> &inputs,
- const ::caffe2::OperatorDef &op)
-{
- checkLayout(op);
-
- // `1` corresponds to the default (channels) axis.
- int axis = getSingleArgument(op, "axis", 1);
- auto result = createOp<ops::ConcatOp>(inputs, axis);
- return {result->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-Caffe2OpCreator::convertDropout(const std::vector<mir::Operation::Output *> &inputs,
- const ::caffe2::OperatorDef &)
-{
- // This is a no-op in inference mode.
- return {inputs[0]};
-}
-
-std::vector<mir::Operation::Output *>
-Caffe2OpCreator::convertFC(const std::vector<mir::Operation::Output *> &inputs,
- const ::caffe2::OperatorDef &op)
-{
- for (auto &s : {"axis", "axis_w", "float16_compute"})
- if (hasArgument(op.arg(), s))
- throw std::runtime_error(std::string("FC: only default '") + s + "' value is supported");
-
- auto weights_op = dynamic_cast<mir::ops::ConstantOp *>(inputs[1]->getNode());
- if (weights_op == nullptr)
- throw std::runtime_error("FC: non-constant weights is not supported yet.");
-
- auto weights_tensor = transposeTensor<1, 0>(weights_op->getValue());
-
- const auto &input_shape = inputs[0]->getShape();
- // Transform input into 2-D tensor by flattening axes
- Shape shape{input_shape.dim(0), input_shape.numElements() / input_shape.dim(0)};
-
- auto reshape = createOp<ops::ReshapeOp>(inputs[0], shape)->getOutput(0);
- auto weights = createOp<ops::ConstantOp>(weights_tensor)->getOutput(0);
- auto result = createOp<ops::FullyConnectedOp>(reshape, weights)->getOutput(0);
- result = createOp<ops::AddOp>(result, inputs[2])->getOutput(0);
-
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-Caffe2OpCreator::convertMaxPool(const std::vector<mir::Operation::Output *> &inputs,
- const OperatorDef &op)
-{
- checkConvLikeOp(op);
-
- assert(inputs.size() == 1);
- auto input = inputs[0];
-
- const auto window_size = getWindowSize(op, inputs);
- const auto strides = getStrides(op);
- std::vector<std::int32_t> pad_before, pad_after;
- std::tie(pad_before, pad_after) = getPadding(op);
-
- auto result = createOp<ops::MaxPool2DOp>(input, window_size, strides, pad_before, pad_after,
- DataFormat::NCHW)
- ->getOutput(0);
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-Caffe2OpCreator::convertMul(const std::vector<mir::Operation::Output *> &inputs,
- const ::caffe2::OperatorDef &op)
-{
- assert(inputs.size() == 2);
- auto lhs = inputs[0];
- auto rhs = inputs[1];
-
- if (getSingleArgument(op, "broadcast", 0) != 0)
- {
- // FIXME This only works when `axis` == 1 and the second input is 1-D.
- rhs = createOp<ops::ReshapeOp>(rhs, Shape{1, rhs->getShape().dim(0), 1, 1})->getOutput(0);
- auto result = createOp<ops::MulOp>(lhs, rhs)->getOutput(0);
- return {result};
- }
-
- auto result = createOp<ops::MulOp>(lhs, rhs)->getOutput(0);
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-Caffe2OpCreator::convertRelu(const std::vector<mir::Operation::Output *> &inputs)
-{
- auto relu = createOp<ops::ReluOp>(inputs[0]);
- return {relu->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-Caffe2OpCreator::convertResizeNearest(const std::vector<mir::Operation::Output *> &inputs,
- const ::caffe2::OperatorDef &op)
-{
- std::vector<float> scales(4);
- assert(inputs[0]->getShape().rank() == 4 && "only 4d tensors is supported");
- // Assuming NCHW format.
- scales[0] = 1.0f;
- scales[1] = 1.0f;
- scales[2] = getSingleArgument(op, "height_scale", 1.0f);
- scales[3] = getSingleArgument(op, "width_scale", 1.0f);
- auto result =
- createOp<ops::ResizeOp>(inputs[0], ops::ResizeOp::ResizeMethod::nearestNeighbor, scales)
- ->getOutput(0);
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-Caffe2OpCreator::convertSigmoid(const std::vector<mir::Operation::Output *> &inputs)
-{
- auto result = createOp<ops::SigmoidOp>(inputs[0]);
- return {result->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-Caffe2OpCreator::convertSoftmax(const std::vector<mir::Operation::Output *> &inputs,
- const ::caffe2::OperatorDef &op)
-{
- int axis = getSingleArgument(op, "axis", 1);
- auto softmax = createOp<ops::SoftmaxOp>(inputs[0], axis);
- return {softmax->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-Caffe2OpCreator::convertSpatialBN(const std::vector<mir::Operation::Output *> &inputs,
- const ::caffe2::OperatorDef &op)
-{
- checkLayout(op);
-
- // Sanity checks
- if (op.input_size() != 5)
- throw std::runtime_error(
- "SpatialBN must have exactly 5 inputs ('sums' and 'sumsq' are not supported yet)");
- if (getSingleArgument(op, "is_test", 1) != 1)
- throw std::runtime_error("SpatialBN: only test mode supported");
-
- // overall_res = (X - mean) / sqrt(var + epsilon) * scale + bias
-
- auto scale_op = dynamic_cast<mir::ops::ConstantOp *>(inputs[1]->getNode());
- auto bias_op = dynamic_cast<mir::ops::ConstantOp *>(inputs[2]->getNode());
- auto mean_op = dynamic_cast<mir::ops::ConstantOp *>(inputs[3]->getNode());
- auto var_op = dynamic_cast<mir::ops::ConstantOp *>(inputs[4]->getNode());
- if (scale_op == nullptr || bias_op == nullptr || mean_op == nullptr || var_op == nullptr)
- throw std::runtime_error(
- "SpatialBN: non-constant 'scale', 'bias', 'mean' and 'var' inputs are not supported yet.");
-
- const auto &scale_tensor = scale_op->getValue();
- const auto &bias_tensor = bias_op->getValue();
- const auto &mean_tensor = mean_op->getValue();
- const auto &var_tensor = var_op->getValue();
- float eps = getSingleArgument(op, "epsilon", 1e-5f);
-
- // res1 = X - mean
- Tensor<float> bias_data(mean_tensor);
- for (auto &idx : ShapeRange(bias_data.getShape()))
- bias_data.at(idx) *= -1;
-
- auto mean = createOp<ops::ConstantOp>(mean_tensor)->getOutput(0);
- mean = createOp<ops::ReshapeOp>(mean, Shape{1, mean->getShape().dim(0), 1, 1})->getOutput(0);
- auto result = createOp<ops::AddOp>(inputs[0], mean)->getOutput(0);
-
- // res2 = res1 * scale / (var + epsilon)
- Tensor<float> multiplier(scale_tensor);
- for (auto &idx : ShapeRange(scale_tensor.getShape()))
- multiplier.at(idx) /= std::sqrt(*reinterpret_cast<float *>(var_tensor.at(idx)) + eps);
- auto scale = createOp<ops::ConstantOp>(scale_tensor)->getOutput(0);
- scale = createOp<ops::ReshapeOp>(scale, Shape{1, scale->getShape().dim(0), 1, 1})->getOutput(0);
- result = createOp<ops::MulOp>(result, scale)->getOutput(0);
-
- // overall_res = res2 + bias
- auto bias = createOp<ops::ConstantOp>(bias_tensor)->getOutput(0);
- bias = createOp<ops::ReshapeOp>(bias, Shape{1, bias->getShape().dim(0), 1, 1})->getOutput(0);
- result = createOp<ops::AddOp>(result, bias)->getOutput(0);
-
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-Caffe2OpCreator::convertSum(const std::vector<mir::Operation::Output *> &inputs)
-{
- auto result = createOp<ops::AddOp>(inputs[0], inputs[1])->getOutput(0);
- for (int i = 2; i < static_cast<int>(inputs.size()); ++i)
- {
- result = createOp<ops::AddOp>(result, inputs[i])->getOutput(0);
- }
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-Caffe2OpCreator::convertClip(const std::vector<mir::Operation::Output *> &inputs,
- const ::caffe2::OperatorDef &op)
-{
-
- float max = getSingleArgument(op, "max", float(0));
- float min = getSingleArgument(op, "min", float(0));
-
- assert(max > 0.0 && min == 0.0 && "Support only if clip is CappedRelu");
- auto cap_relu = createOp<ops::CappedReluOp>(inputs[0], max);
-
- return {cap_relu->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-Caffe2OpCreator::convertReshape(const std::vector<mir::Operation::Output *> &inputs,
- const ::caffe2::OperatorDef &op)
-{
- auto shape_op = dynamic_cast<mir::ops::ConstantOp *>(inputs[1]->getNode());
- if (shape_op == nullptr)
- throw std::runtime_error("Reshape: non-constant shape is not supported yet.");
-
- const auto &shape_tensor = shape_op->getValue();
-
- Tensor<int64_t> out_shape_tensor(shape_tensor);
-
- ShapeRange range(out_shape_tensor.getShape());
- std::vector<int32_t> shape_vec;
- for (const auto &index : range)
- {
- shape_vec.push_back(static_cast<int32_t>(out_shape_tensor.at(index)));
- }
- Shape out_shape(shape_vec);
-
- auto reshape = createOp<ops::ReshapeOp>(inputs[0], out_shape);
-
- return {reshape->getOutput(0)};
-}
-
-} // namespace mir_caffe2
diff --git a/compiler/mir-caffe2-importer/caffe2_proto_helper.cpp b/compiler/mir-caffe2-importer/caffe2_proto_helper.cpp
deleted file mode 100644
index a7cde64cf..000000000
--- a/compiler/mir-caffe2-importer/caffe2_proto_helper.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "caffe2_proto_helper.h"
-
-namespace mir_caffe2
-{
-
-const ::caffe2::Argument &findArgumentByName(RepArgument args, const std::string &name)
-{
- for (auto &arg : args)
- if (arg.name() == name)
- return arg;
- throw std::runtime_error("Can't find argument with name: " + name);
-}
-
-const bool hasArgument(RepArgument args, const std::string &name)
-{
- for (auto &arg : args)
- if (arg.name() == name)
- return true;
- return false;
-}
-
-int getSingleArgument(const ::caffe2::OperatorDef &op, const std::string &argument_name,
- const int default_value)
-{
- if (hasArgument(op.arg(), argument_name))
- return static_cast<int>(findArgumentByName(op.arg(), argument_name).i());
- return default_value;
-}
-
-float getSingleArgument(const ::caffe2::OperatorDef &op, const std::string &argument_name,
- const float default_value)
-{
- if (hasArgument(op.arg(), argument_name))
- return findArgumentByName(op.arg(), argument_name).f();
- return default_value;
-}
-
-std::string getSingleArgument(const ::caffe2::OperatorDef &op, const std::string &argument_name,
- const std::string &default_value)
-{
- if (hasArgument(op.arg(), argument_name))
- return findArgumentByName(op.arg(), argument_name).s();
- return default_value;
-}
-
-} // namespace mir_caffe2
diff --git a/compiler/mir-caffe2-importer/caffe2_proto_helper.h b/compiler/mir-caffe2-importer/caffe2_proto_helper.h
deleted file mode 100644
index 4c47edec8..000000000
--- a/compiler/mir-caffe2-importer/caffe2_proto_helper.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_CAFFE2_PROTO_HELPER_H
-#define MIR_CAFFE2_PROTO_HELPER_H
-
-#include "caffe2/proto/caffe2.pb.h"
-
-namespace mir_caffe2
-{
-
-using RepArgument = const ::google::protobuf::RepeatedPtrField<::caffe2::Argument> &;
-
-const ::caffe2::Argument &findArgumentByName(RepArgument args, const std::string &name);
-
-const bool hasArgument(RepArgument args, const std::string &name);
-
-int getSingleArgument(const ::caffe2::OperatorDef &op, const std::string &argument_name,
- int default_value);
-float getSingleArgument(const ::caffe2::OperatorDef &op, const std::string &argument_name,
- float default_value);
-std::string getSingleArgument(const ::caffe2::OperatorDef &op, const std::string &argument_name,
- const std::string &default_value);
-
-} // namespace mir_caffe2
-
-#endif // MIR_CAFFE2_PROTO_HELPER_H
diff --git a/compiler/mir-caffe2-importer/requires.cmake b/compiler/mir-caffe2-importer/requires.cmake
deleted file mode 100644
index 1059c50d3..000000000
--- a/compiler/mir-caffe2-importer/requires.cmake
+++ /dev/null
@@ -1 +0,0 @@
-require("mir")
diff --git a/compiler/mir-interpreter/CMakeLists.txt b/compiler/mir-interpreter/CMakeLists.txt
new file mode 100644
index 000000000..814612ae9
--- /dev/null
+++ b/compiler/mir-interpreter/CMakeLists.txt
@@ -0,0 +1,4 @@
+file(GLOB_RECURSE interp_src ./*.cpp ./*.h)
+add_library(mir_interpreter SHARED ${interp_src})
+target_link_libraries(mir_interpreter PUBLIC mir)
+target_include_directories(mir_interpreter PUBLIC include)
diff --git a/compiler/mir-interpreter/README.md b/compiler/mir-interpreter/README.md
new file mode 100644
index 000000000..4ed0c7350
--- /dev/null
+++ b/compiler/mir-interpreter/README.md
@@ -0,0 +1 @@
+# mir-interpreter
diff --git a/compiler/mir-interpreter/include/MirInterpreter.h b/compiler/mir-interpreter/include/MirInterpreter.h
new file mode 100644
index 000000000..c3d971716
--- /dev/null
+++ b/compiler/mir-interpreter/include/MirInterpreter.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MIR_INTERPRETER_
+#define _MIR_INTERPRETER_
+
+#include "mir/Visitor.h"
+#include "mir/Operation.h"
+#include "mir/TensorVariant.h"
+#include <unordered_map>
+#include <vector>
+
+namespace mir_interpreter
+{
+
+class MIRInterpreter : public mir::Visitor
+{
+public:
+ explicit MIRInterpreter() = default;
+
+ ~MIRInterpreter() override = default;
+
+ /// @brief Set tensor to the interpreter environment.
+ void setTensor(const mir::Operation::Output *output, mir::TensorVariant tensor);
+
+ /// @brief Get tensor from the interpreter environment.
+ const mir::TensorVariant &getTensor(const mir::Operation::Output *) const;
+
+ void visit(mir::ops::AddOp &op) override;
+ void visit(mir::ops::AbsOp &op) override;
+ void visit(mir::ops::AvgPool2DOp &op) override;
+ void visit(mir::ops::CappedReluOp &op) override;
+ void visit(mir::ops::ConcatOp &op) override;
+ void visit(mir::ops::ConstantOp &op) override;
+ void visit(mir::ops::Conv2DOp &op) override;
+ void visit(mir::ops::DeConv2DOp &op) override;
+ void visit(mir::ops::DepthwiseConv2DOp &op) override;
+ void visit(mir::ops::DequantizeOp &op) override;
+ void visit(mir::ops::DivOp &op) override;
+ void visit(mir::ops::EluOp &op) override;
+ void visit(mir::ops::EqualOp &op) override;
+ void visit(mir::ops::FullyConnectedOp &op) override;
+ void visit(mir::ops::GatherOp &op) override;
+ void visit(mir::ops::GreaterOp &op) override;
+ void visit(mir::ops::HardSwishOp &op) override;
+ void visit(mir::ops::InputOp &op) override;
+ void visit(mir::ops::LeakyReluOp &op) override;
+ void visit(mir::ops::LessOp &op) override;
+ void visit(mir::ops::MaxOp &op) override;
+ void visit(mir::ops::MaxPool2DOp &op) override;
+ void visit(mir::ops::MulOp &op) override;
+ void visit(mir::ops::OutputOp &op) override;
+ void visit(mir::ops::PadOp &op) override;
+ void visit(mir::ops::QuantizeOp &op) override;
+ void visit(mir::ops::ReduceMeanOp &op) override;
+ void visit(mir::ops::ReluOp &op) override;
+ void visit(mir::ops::ReshapeOp &op) override;
+ void visit(mir::ops::ResizeOp &op) override;
+ void visit(mir::ops::SigmoidOp &op) override;
+ void visit(mir::ops::SliceOp &op) override;
+ void visit(mir::ops::SoftmaxOp &op) override;
+ void visit(mir::ops::SqrtOp &op) override;
+ void visit(mir::ops::SqueezeOp &op) override;
+ void visit(mir::ops::SubOp &op) override;
+ void visit(mir::ops::TanhOp &op) override;
+ void visit(mir::ops::TransposeOp &op) override;
+ void visit(mir::ops::BroadcastOp &op) override;
+
+protected:
+ void visit_fallback(mir::Operation &op) override;
+
+private:
+ mir::TensorVariant &allocateTensor(const mir::Operation::Output *output);
+
+ /// @brief Gets the computed inputs for the operation.
+ std::vector<std::reference_wrapper<const mir::TensorVariant>>
+ getInputTensors(const mir::Operation &op);
+
+ std::vector<std::reference_wrapper<mir::TensorVariant>>
+ allocateOutputTensors(const mir::Operation &op);
+
+ /// @brief Mapping of operation outputs to corresponding tensors.
+ std::unordered_map<const mir::Operation::Output *, mir::TensorVariant> _tensors;
+};
+
+} // namespace mir_interpreter
+
+#endif // _MIR_INTERPRETER_
diff --git a/compiler/mir-caffe-importer/requires.cmake b/compiler/mir-interpreter/requires.cmake
index 1059c50d3..1059c50d3 100644
--- a/compiler/mir-caffe-importer/requires.cmake
+++ b/compiler/mir-interpreter/requires.cmake
diff --git a/compiler/mir-interpreter/src/MirInterpreter.cpp b/compiler/mir-interpreter/src/MirInterpreter.cpp
new file mode 100644
index 000000000..245f7ddab
--- /dev/null
+++ b/compiler/mir-interpreter/src/MirInterpreter.cpp
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MirInterpreter.h"
+
+#include "ops/Add.h"
+#include "ops/Abs.h"
+#include "ops/AvgPool2D.h"
+#include "ops/CappedReLU.h"
+#include "ops/Concat.h"
+#include "ops/Conv2D.h"
+#include "ops/DeConv2D.h"
+#include "ops/DepthwiseConv2D.h"
+#include "ops/Div.h"
+#include "ops/ELU.h"
+#include "ops/Equal.h"
+#include "ops/Fill.h"
+#include "ops/FullyConnected.h"
+#include "ops/Gather.h"
+#include "ops/Greater.h"
+#include "ops/HardSwish.h"
+#include "ops/LeakyReLU.h"
+#include "ops/Less.h"
+#include "ops/Max.h"
+#include "ops/MaxPool2D.h"
+#include "ops/Mul.h"
+#include "ops/Pad.h"
+#include "ops/Quantization.h"
+#include "ops/ReduceMean.h"
+#include "ops/ReLU.h"
+#include "ops/Reshape.h"
+#include "ops/Sigmoid.h"
+#include "ops/Slice.h"
+#include "ops/Softmax.h"
+#include "ops/Sqrt.h"
+#include "ops/Sub.h"
+#include "ops/Tanh.h"
+#include "ops/Transpose.h"
+
+#include "ops/Common.h"
+
+#include "mir/OpDefs.h"
+
+#include <cassert>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+void MIRInterpreter::setTensor(const Operation::Output *output, TensorVariant tensor)
+{
+ const auto result = _tensors.emplace(output, std::move(tensor));
+ if (!result.second)
+ {
+ const std::string &name = output->getName();
+ throw std::runtime_error("Attempt to overwrite data for tensor \"" + name + "\".");
+ }
+}
+
+TensorVariant &MIRInterpreter::allocateTensor(const Operation::Output *output)
+{
+ const auto result = _tensors.emplace(output, output->getType());
+ if (!result.second)
+ {
+ const std::string &name = output->getName();
+ throw std::runtime_error("Attempt to overwrite data for tensor \"" + name + "\".");
+ }
+ return result.first->second;
+}
+
+const TensorVariant &MIRInterpreter::getTensor(const Operation::Output *output) const
+{
+ const auto it = _tensors.find(output);
+ if (it == _tensors.end())
+ {
+ const std::string &name = output->getName();
+ throw std::runtime_error("Can't find data for tensor \"" + name + "\".");
+ }
+ return it->second;
+}
+
+std::vector<std::reference_wrapper<const TensorVariant>>
+MIRInterpreter::getInputTensors(const Operation &op)
+{
+ std::vector<std::reference_wrapper<const TensorVariant>> tensors;
+ for (const Operation::Output *input : op.getInputs())
+ {
+ tensors.emplace_back(getTensor(input));
+ }
+ return tensors;
+}
+
+std::vector<std::reference_wrapper<TensorVariant>>
+MIRInterpreter::allocateOutputTensors(const Operation &op)
+{
+ std::vector<std::reference_wrapper<TensorVariant>> tensors;
+ for (const Operation::Output &output : op.getOutputs())
+ {
+ tensors.emplace_back(allocateTensor(&output));
+ }
+ return tensors;
+}
+
+void MIRInterpreter::visit(ops::InputOp &op)
+{
+ assert(_tensors.find(op.getOutput(0)) != _tensors.end());
+}
+
+void MIRInterpreter::visit(ops::AvgPool2DOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ AvgPool2D(op, inputs[0], outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::ConstantOp &op) { setTensor(op.getOutput(0), op.getValue()); }
+
+void MIRInterpreter::visit(ops::ConcatOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Concat(inputs, op.getAxis(), outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::Conv2DOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ const mir::TensorVariant *bias = nullptr;
+ if (inputs.size() > 2)
+ {
+ bias = &(inputs[2].get());
+ }
+ Conv2D(inputs[0], inputs[1], op.getAttributes(), outputs[0], bias);
+}
+
+void MIRInterpreter::visit(ops::MaxPool2DOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ MaxPool2D(inputs[0], op, outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::ReshapeOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Reshape(inputs[0], outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::ReluOp &op)
+{
+ auto args = getInputTensors(op);
+ auto results = allocateOutputTensors(op);
+ ReLU(args[0], results[0]);
+}
+
+void MIRInterpreter::visit(ops::SigmoidOp &op)
+{
+ auto args = getInputTensors(op);
+ auto results = allocateOutputTensors(op);
+ Sigmoid(args[0], results[0]);
+}
+
+void MIRInterpreter::visit(ops::SoftmaxOp &op)
+{
+ auto inputs = getInputTensors(op);
+ assert(inputs.size() == 1);
+ auto outputs = allocateOutputTensors(op);
+ Softmax(inputs[0], op.getAxis(), outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::FullyConnectedOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ const mir::TensorVariant *bias = nullptr;
+ if (inputs.size() > 2)
+ {
+ bias = &(inputs[3].get());
+ }
+ FullyConnected(inputs[0], inputs[1], op, outputs[0], bias);
+}
+
+void MIRInterpreter::visit(ops::CappedReluOp &op)
+{
+ auto args = getInputTensors(op);
+ auto results = allocateOutputTensors(op);
+ CappedReLU(args[0], op.getCap(), results[0]);
+}
+
+void MIRInterpreter::visit(ops::DepthwiseConv2DOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ const mir::TensorVariant *bias = nullptr;
+ if (inputs.size() > 2)
+ {
+ bias = &inputs[3].get();
+ }
+ DepthwiseConv2D(op, inputs[0], inputs[1], outputs[0], bias);
+}
+
+void MIRInterpreter::visit(ops::SliceOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto input = inputs[0];
+ auto outputs = allocateOutputTensors(op);
+ Slice(input, op.getStarts(), outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::TanhOp &op)
+{
+ auto args = getInputTensors(op);
+ auto results = allocateOutputTensors(op);
+ Tanh(args[0], results[0]);
+}
+
+void MIRInterpreter::visit(ops::DeConv2DOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ DeConv2D(inputs[0], inputs[1], op.getAttributes(), outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::EluOp &op)
+{
+ auto args = getInputTensors(op);
+ auto results = allocateOutputTensors(op);
+ ELU(args[0], op.getAlpha(), results[0]);
+}
+
+void MIRInterpreter::visit(ops::SqueezeOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ // Squeeze is just a special case of reshape.
+ Reshape(inputs[0], outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::PadOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Pad(inputs[0], op, outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::SqrtOp &op)
+{
+ auto args = getInputTensors(op);
+ auto results = allocateOutputTensors(op);
+ Sqrt(args[0], results[0]);
+}
+
+void MIRInterpreter::visit(ops::ResizeOp &op)
+{
+ // TODO support types other than float32
+ auto inputs = getInputTensors(op);
+ assert(inputs[0].get().getElementType() == mir::DataType::FLOAT32);
+ auto outputs = allocateOutputTensors(op);
+
+ Tensor<float> input(inputs[0]);
+ assert(op.getMode() == ops::ResizeOp::ResizeMethod::nearestNeighbor);
+
+ auto scales = op.getScales();
+ Fill(outputs[0], [&scales, &input](const Index &id) {
+ Index in_idx;
+ in_idx.resize(4);
+ for (int i = 0; i < input.getShape().rank(); i++)
+ {
+ in_idx.at(i) = static_cast<int>(floorf(id.at(i) / scales[i]));
+ }
+ return input.at(in_idx);
+ });
+}
+
+void MIRInterpreter::visit(ops::ReduceMeanOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ ReduceMean(inputs[0], op, outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::TransposeOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Transpose(inputs[0], op, outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::GatherOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Gather(inputs[0], inputs[1], op, outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::LeakyReluOp &op)
+{
+ auto args = getInputTensors(op);
+ auto results = allocateOutputTensors(op);
+ LeakyReLU(args[0], op.getAlpha(), results[0]);
+}
+
+void MIRInterpreter::visit(ops::OutputOp &op)
+{
+ assert(_tensors.find(op.getInput(0)) != _tensors.end());
+}
+
+void MIRInterpreter::visit(ops::AddOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Add(inputs[0], inputs[1], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::DivOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Div(inputs[0], inputs[1], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::MaxOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Max(inputs[0], inputs[1], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::MulOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Mul(inputs[0], inputs[1], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::SubOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Sub(inputs[0], inputs[1], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::DequantizeOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Dequantize(inputs[0], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::QuantizeOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Quantize(inputs[0], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::HardSwishOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ HardSwish(inputs[0], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::GreaterOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Greater(inputs[0], inputs[1], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::LessOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Less(inputs[0], inputs[1], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::EqualOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Equal(inputs[0], inputs[1], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::AbsOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Abs(inputs[0], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::BroadcastOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ outputs[0].get() = TensorVariant{inputs[0], op.getOutputShape(0)};
+}
+
+void MIRInterpreter::visit_fallback(mir::Operation &) { throw std::runtime_error("NYI operation"); }
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Abs.cpp b/compiler/mir-interpreter/src/ops/Abs.cpp
new file mode 100644
index 000000000..547009ffd
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Abs.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Abs.h"
+#include "Common.h"
+
+#include <mir/ShapeRange.h>
+#include <mir/Tensor.h>
+
+#include <cmath>
+
+namespace mir_interpreter
+{
+
+template <typename T> struct AbsImpl
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+ {
+ mir::Tensor<T> arg_accessor(arg);
+ mir::Tensor<T> res_accessor(result);
+
+ for (const auto &index : mir::ShapeRange(result.getShape()))
+ {
+ res_accessor.at(index) = std::abs(arg_accessor.at(index));
+ }
+ }
+};
+
+template <> struct AbsImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+ {
+ throw std::runtime_error{"NYI"};
+ }
+};
+
+void Abs(const mir::TensorVariant &arg, mir::TensorVariant &result)
+{
+ dispatch<AbsImpl>(arg.getElementType(), arg, result);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Abs.h b/compiler/mir-interpreter/src/ops/Abs.h
new file mode 100644
index 000000000..1ba59e647
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Abs.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_ABS_
+#define _NNC_CORE_BACKEND_INTERPRETER_ABS_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void Abs(const mir::TensorVariant &arg, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_ABS_
diff --git a/compiler/mir-interpreter/src/ops/Add.cpp b/compiler/mir-interpreter/src/ops/Add.cpp
new file mode 100644
index 000000000..631b854b7
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Add.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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 "Add.h"
+#include "Common.h"
+
+#include "QuantizationHelpers.h"
+#include "mir/Tensor.h"
+#include "mir/ShapeRange.h"
+
+#include <cmath>
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct AddImpl
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res);
+};
+
+template <typename T>
+void AddImpl<T>::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ TensorVariant broadcasted_lhs(lhs, res.getShape());
+ TensorVariant broadcasted_rhs(rhs, res.getShape());
+ Tensor<T> lhs_accessor(broadcasted_lhs);
+ Tensor<T> rhs_accessor(broadcasted_rhs);
+ Tensor<T> res_accessor(res);
+
+ for (const auto &index : ShapeRange(res.getShape()))
+ {
+ res_accessor.at(index) = lhs_accessor.at(index) + rhs_accessor.at(index);
+ }
+}
+
+template <> struct AddImpl<uint8_t>
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res);
+};
+
+void AddImpl<uint8_t>::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ const auto &lhs_type = lhs.getType();
+ const auto &rhs_type = rhs.getType();
+ const auto &res_type = res.getType();
+
+ assert(lhs_type.isQuantized());
+ assert(rhs_type.isQuantized());
+ assert(res_type.isQuantized());
+
+ int32_t lhs_offset = -lhs_type.getQuantization().getZeroPoint();
+ int32_t rhs_offset = -rhs_type.getQuantization().getZeroPoint();
+ int32_t output_offset = res_type.getQuantization().getZeroPoint();
+
+ double lhs_scale = lhs_type.getQuantization().getScale();
+ double rhs_scale = rhs_type.getQuantization().getScale();
+ double output_scale = res_type.getQuantization().getScale();
+
+ int left_shift = 20;
+ const double twice_max_input_scale = 2 * std::max(lhs_scale, rhs_scale);
+ const double real_lhs_multiplier = lhs_scale / twice_max_input_scale;
+ const double real_rhs_multiplier = rhs_scale / twice_max_input_scale;
+ const double real_output_multiplier = twice_max_input_scale / ((1 << left_shift) * output_scale);
+
+ int32_t lhs_multiplier = 0;
+ int32_t rhs_multiplier = 0;
+ int32_t output_multiplier = 0;
+ int lhs_shift = 0;
+ int rhs_shift = 0;
+ int output_shift = 0;
+
+ QuantizeMultiplierSmallerThanOneExp(real_lhs_multiplier, &lhs_multiplier, &lhs_shift);
+ QuantizeMultiplierSmallerThanOneExp(real_rhs_multiplier, &rhs_multiplier, &rhs_shift);
+ QuantizeMultiplierSmallerThanOneExp(real_output_multiplier, &output_multiplier, &output_shift);
+
+ TensorVariant broadcasted_lhs(lhs, res_type.getShape());
+ TensorVariant broadcasted_rhs(rhs, res_type.getShape());
+
+ Tensor<uint8_t> lhs_accessor(broadcasted_lhs);
+ Tensor<uint8_t> rhs_accessor(broadcasted_rhs);
+ Tensor<uint8_t> res_accessor(res);
+
+ int32_t output_min = std::numeric_limits<uint8_t>::min();
+ int32_t output_max = std::numeric_limits<uint8_t>::max();
+
+ for (const auto &index : ShapeRange(res_type.getShape()))
+ {
+ const int32_t lhs_val = lhs_accessor.at(index) + lhs_offset;
+ const int32_t rhs_val = rhs_accessor.at(index) + rhs_offset;
+ const int32_t shifted_lhs_val = lhs_val * (1 << left_shift);
+ const int32_t shifted_rhs_val = rhs_val * (1 << left_shift);
+ const int32_t scaled_lhs_val =
+ MultiplyByQuantizedMultiplierSmallerThanOneExp(shifted_lhs_val, lhs_multiplier, lhs_shift);
+ const int32_t scaled_rhs_val =
+ MultiplyByQuantizedMultiplierSmallerThanOneExp(shifted_rhs_val, rhs_multiplier, rhs_shift);
+ const int32_t raw_sum = scaled_lhs_val + scaled_rhs_val;
+ const int32_t raw_output =
+ MultiplyByQuantizedMultiplierSmallerThanOneExp(raw_sum, output_multiplier, output_shift) +
+ output_offset;
+ const int32_t clamped_output = std::min(output_max, std::max(output_min, raw_output));
+ res_accessor.at(index) = static_cast<uint8_t>(clamped_output);
+ }
+}
+
+void Add(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ if (lhs.getElementType() != rhs.getElementType())
+ {
+ throw std::runtime_error{"Add with different input types is unsupported"};
+ }
+ dispatch<AddImpl>(res.getElementType(), lhs, rhs, res);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Add.h b/compiler/mir-interpreter/src/ops/Add.h
new file mode 100644
index 000000000..48508226f
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Add.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_ADD_
+#define _NNC_CORE_BACKEND_INTERPRETER_ADD_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Add(const mir::TensorVariant &lhs, const mir::TensorVariant &rhs, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_ADD_
diff --git a/compiler/mir-interpreter/src/ops/AvgPool2D.cpp b/compiler/mir-interpreter/src/ops/AvgPool2D.cpp
new file mode 100644
index 000000000..3f1d65100
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/AvgPool2D.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2019 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 "AvgPool2D.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> class AvgPool2DImpl
+{
+public:
+ static void run(const mir::ops::AvgPool2DOp &op, const mir::TensorVariant &input_var,
+ mir::TensorVariant &output);
+};
+
+template <typename T>
+void AvgPool2DImpl<T>::run(const ops::AvgPool2DOp &op, const TensorVariant &input_var,
+ TensorVariant &output)
+{
+ const auto &input_shape = op.getInputShape(0);
+ const auto &output_shape = op.getOutputShape(0);
+ const auto &window_size = op.getWindowSize();
+ const auto &strides = op.getStrides();
+ const auto &padding_before = op.getPaddingBefore();
+ const auto &padding_after = op.getPaddingAfter();
+ (void)padding_after;
+
+ constexpr int num_spatial_dims = 2;
+ assert(input_var.getShape().rank() == 4);
+ assert(window_size.size() == num_spatial_dims);
+ assert(strides.size() == num_spatial_dims);
+ assert(padding_before.size() == num_spatial_dims);
+ assert(padding_after.size() == num_spatial_dims);
+
+ Tensor<T> res_accessor(output);
+ Tensor<T> input(input_var);
+
+ ShapeRange in_range(input_shape);
+ Index in_index(input_shape.rank());
+
+ for (const auto &out_index : ShapeRange(output_shape))
+ {
+ T result = 0;
+ size_t num_elements = 0;
+
+ // Assuming NHWC format.
+ in_index.at(0) = out_index.at(0);
+ in_index.at(3) = out_index.at(3);
+
+ for (const auto &window_index : ShapeRange(Shape(window_size)))
+ {
+ // Assuming NHWC format.
+ for (int i = 0; i < num_spatial_dims; ++i)
+ in_index.at(1 + i) =
+ out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i];
+
+ if (in_range.contains(in_index))
+ {
+ num_elements++;
+ result += input.at(in_index);
+ }
+ else if (op.getIncludePad())
+ {
+ num_elements++;
+ }
+ }
+
+ result /= num_elements;
+ res_accessor.at(out_index) = result;
+ }
+}
+
+template <> struct AvgPool2DImpl<uint8_t>
+{
+ static void run(const mir::ops::AvgPool2DOp &op, const mir::TensorVariant &input,
+ mir::TensorVariant &output);
+};
+
+void AvgPool2DImpl<uint8_t>::run(const ops::AvgPool2DOp &op, const TensorVariant &input,
+ TensorVariant &output)
+{
+ const auto &input_type = input.getType();
+ const auto &output_type = op.getOutput(0)->getType();
+ (void)input_type;
+
+ assert(input_type.isQuantized());
+ assert(output_type.isQuantized());
+ assert(input_type.getElementType() == DataType::UINT8);
+
+ const auto &input_shape = op.getInputShape(0);
+ const auto &output_shape = op.getOutputShape(0);
+ const auto &window_size = op.getWindowSize();
+ const auto &strides = op.getStrides();
+ const auto &padding_before = op.getPaddingBefore();
+ const auto &padding_after = op.getPaddingAfter();
+ (void)padding_after;
+
+ constexpr int num_spatial_dims = 2;
+ assert(input.getShape().rank() == 4);
+ assert(window_size.size() == num_spatial_dims);
+ assert(strides.size() == num_spatial_dims);
+ assert(padding_before.size() == num_spatial_dims);
+ assert(padding_after.size() == num_spatial_dims);
+
+ Tensor<uint8_t> input_accessor(input);
+ Tensor<uint8_t> res_accessor(output);
+
+ ShapeRange in_range(input_shape);
+ Index in_index(input_shape.rank());
+
+ int32_t output_min = std::numeric_limits<uint8_t>::min();
+ int32_t output_max = std::numeric_limits<uint8_t>::max();
+
+ for (const auto &out_index : ShapeRange(output_shape))
+ {
+ int32_t result = 0;
+ size_t num_elements = 0;
+
+ // Assuming NHWC format.
+ in_index.at(0) = out_index.at(0);
+ in_index.at(3) = out_index.at(3);
+
+ for (const auto &window_index : ShapeRange(Shape(window_size)))
+ {
+ // Assuming NHWC format.
+ for (int i = 0; i < num_spatial_dims; ++i)
+ in_index.at(1 + i) =
+ out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i];
+
+ if (in_range.contains(in_index))
+ {
+ num_elements++;
+ result += input_accessor.at(in_index);
+ }
+ else if (op.getIncludePad())
+ {
+ num_elements++;
+ }
+ }
+ result = (result + num_elements / 2) / num_elements;
+ result = std::max(result, output_min);
+ result = std::min(result, output_max);
+ res_accessor.at(out_index) = static_cast<uint8_t>(result);
+ }
+}
+
+void AvgPool2D(const mir::ops::AvgPool2DOp &op, const mir::TensorVariant &input,
+ mir::TensorVariant &output)
+{
+ dispatch<AvgPool2DImpl>(output.getElementType(), op, input, output);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/AvgPool2D.h b/compiler/mir-interpreter/src/ops/AvgPool2D.h
new file mode 100644
index 000000000..b30574cee
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/AvgPool2D.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_AVG_POOL_2D_
+#define _NNC_CORE_BACKEND_INTERPRETER_AVG_POOL_2D_
+
+#include "mir/ops/AvgPool2DOp.h"
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void AvgPool2D(const mir::ops::AvgPool2DOp &op, const mir::TensorVariant &input,
+ mir::TensorVariant &output);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_AVG_POOL_2D_
diff --git a/compiler/mir-interpreter/src/ops/CappedReLU.cpp b/compiler/mir-interpreter/src/ops/CappedReLU.cpp
new file mode 100644
index 000000000..1ac95ac16
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/CappedReLU.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CappedReLU.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+#include "Common.h"
+
+#include <algorithm>
+#include <cstdint>
+
+namespace mir_interpreter
+{
+
+template <typename T> struct CappedReLUImpl
+{
+ static void run(const mir::TensorVariant &arg, float cap, mir::TensorVariant &result);
+};
+
+template <typename T>
+void CappedReLUImpl<T>::run(const mir::TensorVariant &arg, float cap, mir::TensorVariant &result)
+{
+ mir::Tensor<T> arg_accessor(arg);
+ mir::Tensor<T> res_accessor(result);
+
+ for (const auto &index : mir::ShapeRange(result.getShape()))
+ {
+ res_accessor.at(index) = std::min(std::max(arg_accessor.at(index), T(0)), static_cast<T>(cap));
+ }
+}
+
+static float dequantize(uint8_t x, const mir::AffineQuantization &q)
+{
+ return (static_cast<int>(x) - q.getZeroPoint()) * q.getScale();
+}
+
+static uint8_t quantize(float x, const mir::AffineQuantization &q)
+{
+ return (static_cast<float>(x) / q.getScale() + q.getZeroPoint());
+}
+
+template <> struct CappedReLUImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &arg, float cap, mir::TensorVariant &result)
+ {
+ mir::Tensor<uint8_t> arg_accessor(arg);
+ mir::Tensor<uint8_t> res_accessor(result);
+
+ auto quant_info = arg.getType().getQuantization();
+ assert(!quant_info.empty());
+
+ for (const auto &index : mir::ShapeRange(result.getShape()))
+ {
+ auto value = dequantize(arg_accessor.at(index), quant_info);
+ auto out_value =
+ quantize(std::min(std::max(value, 0.0f), cap), result.getType().getQuantization());
+ res_accessor.at(index) = out_value;
+ }
+ }
+};
+
+void CappedReLU(const mir::TensorVariant &arg, float cap, mir::TensorVariant &result)
+{
+ dispatch<CappedReLUImpl>(arg.getElementType(), arg, cap, result);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/CappedReLU.h b/compiler/mir-interpreter/src/ops/CappedReLU.h
new file mode 100644
index 000000000..ffb756d2a
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/CappedReLU.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_CAPPEDRELU_
+#define _NNC_CORE_BACKEND_INTERPRETER_CAPPEDRELU_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void CappedReLU(const mir::TensorVariant &arg, float cap, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_CAPPEDRELU_
diff --git a/compiler/mir-interpreter/src/ops/Common.cpp b/compiler/mir-interpreter/src/ops/Common.cpp
new file mode 100644
index 000000000..dae207f2e
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Common.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cassert>
+
+#include "Common.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+Index shift(const Index &in_index, const Shape &shift_from)
+{
+ Index index = in_index;
+ assert(index.rank() == shift_from.rank());
+ for (int32_t d = 0; d < in_index.rank(); ++d)
+ {
+ index.at(d) = index.at(d) + shift_from.dim(d);
+ }
+ return index;
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Common.h b/compiler/mir-interpreter/src/ops/Common.h
new file mode 100644
index 000000000..43336216e
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Common.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_COMMON_
+#define _NNC_CORE_BACKEND_INTERPRETER_COMMON_
+
+#include "mir/Tensor.h"
+#include "mir/TensorVariant.h"
+#include "mir/DataType.h"
+#include "mir/Shape.h"
+#include "mir/Index.h"
+
+namespace mir_interpreter
+{
+
+template <template <typename> class F, typename... Args>
+void dispatch(mir::DataType dt, Args &&... args)
+{
+ switch (dt)
+ {
+ case mir::DataType::FLOAT32:
+ return F<float>::run(std::forward<Args>(args)...);
+ case mir::DataType::FLOAT64:
+ return F<double>::run(std::forward<Args>(args)...);
+ case mir::DataType::INT32:
+ return F<int32_t>::run(std::forward<Args>(args)...);
+ case mir::DataType::INT64:
+ return F<int64_t>::run(std::forward<Args>(args)...);
+ case mir::DataType::UINT8:
+ return F<uint8_t>::run(std::forward<Args>(args)...);
+ case mir::DataType::UNKNOWN:
+ throw std::runtime_error{"Unknown datatype met during operation execution"};
+ default:
+ throw std::runtime_error{"mir::DataType enum mismatch"};
+ }
+}
+
+template <typename T> void erase(mir::TensorVariant &tv)
+{
+ size_t element_count = tv.getShape().numElements();
+ for (size_t i = 0; i < element_count; ++i)
+ {
+ auto ptr = tv.atOffset(i);
+ *reinterpret_cast<T *>(ptr) = 0;
+ }
+}
+
+mir::Index shift(const mir::Index &in_index, const mir::Shape &shift_from);
+
+} // namespace mir_interpreter
+
+#endif // _NNC_CORE_BACKEND_INTERPRETER_COMMON_
diff --git a/compiler/mir-interpreter/src/ops/Concat.cpp b/compiler/mir-interpreter/src/ops/Concat.cpp
new file mode 100644
index 000000000..99fe00c31
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Concat.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2019 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 "Concat.h"
+#include "Common.h"
+
+#include <cmath>
+#include <cstring>
+
+namespace mir_interpreter
+{
+
+template <typename T> struct ConcatImpl
+{
+ static void run(const std::vector<std::reference_wrapper<const mir::TensorVariant>> &inputs,
+ int axis, mir::TensorVariant &output);
+};
+
+template <typename T>
+void ConcatImpl<T>::run(const std::vector<std::reference_wrapper<const mir::TensorVariant>> &inputs,
+ int axis, mir::TensorVariant &output)
+{
+ const auto &output_shape = output.getShape();
+ const size_t inputs_count = inputs.size();
+ const int32_t concat_dims = output_shape.rank();
+ int64_t concat_size = 0;
+ for (size_t i = 0; i < inputs_count; i++)
+ {
+ const auto &input_shape = inputs[i].get().getShape();
+ assert(input_shape.rank() == concat_dims);
+ for (int32_t j = 0; j < concat_dims; j++)
+ {
+ if (j != axis)
+ {
+ assert(input_shape.dim(j) == output_shape.dim(j));
+ }
+ }
+ concat_size += input_shape.dim(axis);
+ }
+ assert(concat_size == output_shape.dim(axis));
+ // Outer size before axis
+ int32_t outer_size = 1;
+ for (int32_t i = 0; i < axis; i++)
+ outer_size *= output_shape.dim(i);
+ // Inner size after axis
+ int32_t base_inner_size = 1;
+ for (int32_t i = axis + 1; i < concat_dims; i++)
+ base_inner_size *= output_shape.dim(i);
+ // flatten = outer_size * dim(axis) * base_inner_size;
+ std::vector<int32_t> copy_sizes;
+ std::vector<char *> input_ptrs;
+ for (size_t i = 0; i < inputs_count; i++)
+ {
+ const auto input_shape = inputs[i].get().getShape();
+ copy_sizes.push_back(input_shape.dim(axis) * base_inner_size);
+ input_ptrs.push_back(inputs[i].get().atOffset(0));
+ }
+
+ char *output_ptr = output.atOffset(0);
+ const size_t elem_size = inputs[0].get().getElementSize();
+ for (int32_t i = 0; i < outer_size; i++)
+ {
+ for (size_t j = 0; j < inputs_count; j++)
+ {
+ std::memcpy(output_ptr, input_ptrs[j], copy_sizes[j] * elem_size);
+ output_ptr += copy_sizes[j] * elem_size;
+ input_ptrs[j] += copy_sizes[j] * elem_size;
+ }
+ }
+}
+
+template <> struct ConcatImpl<uint8_t>
+{
+ static void run(const std::vector<std::reference_wrapper<const mir::TensorVariant>> &inputs,
+ int axis, mir::TensorVariant &output);
+};
+
+void ConcatImpl<uint8_t>::run(
+ const std::vector<std::reference_wrapper<const mir::TensorVariant>> &inputs, int axis,
+ mir::TensorVariant &output)
+{
+ const size_t inputs_count = inputs.size();
+ std::vector<int32_t> input_zeropoints(inputs_count);
+ std::vector<float> input_scales(inputs_count);
+ const auto &output_shape = output.getShape();
+ const int32_t concat_dimensions = output_shape.rank();
+ int64_t concat_size = 0;
+ for (size_t i = 0; i < inputs_count; i++)
+ {
+ const auto &input_type = inputs[i].get().getType();
+ assert(input_type.isQuantized());
+ assert(input_type.getElementType() == mir::DataType::UINT8);
+ const auto &input_shape = input_type.getShape();
+ assert(input_shape.rank() == concat_dimensions);
+
+ for (int32_t j = 0; j < concat_dimensions; j++)
+ if (j != axis)
+ assert(input_shape.dim(j) == output_shape.dim(j));
+
+ concat_size += input_shape.dim(axis);
+ input_zeropoints[i] = input_type.getQuantization().getZeroPoint();
+ input_scales[i] = input_type.getQuantization().getScale();
+ }
+ assert(concat_size == output_shape.dim(axis));
+
+ const auto &output_type = output.getType();
+ assert(output_type.isQuantized());
+ int32_t output_zeropoint = output_type.getQuantization().getZeroPoint();
+ float output_scale = output_type.getQuantization().getScale();
+
+ // Outer size before axis
+ int32_t outer_size = 1;
+ for (int32_t i = 0; i < axis; i++)
+ outer_size *= output_shape.dim(i);
+ // Inner size after axis
+ int32_t base_inner_size = 1;
+ for (int32_t i = axis + 1; i < concat_dimensions; i++)
+ base_inner_size *= output_shape.dim(i);
+ // flatten = outer_size * dim(axis) * base_inner_size;
+
+ uint8_t *output_ptr = reinterpret_cast<uint8_t *>(output.atOffset(0));
+
+ const float inverse_output_scale = 1.f / output_scale;
+ for (int k = 0; k < outer_size; k++)
+ {
+ for (size_t i = 0; i < inputs_count; ++i)
+ {
+ const mir::TensorVariant &input = inputs[i];
+ const int copy_size = input.getShape().dim(axis) * base_inner_size;
+ const char *input_data = input.atOffset(0) + k * copy_size;
+ const uint8_t *input_ptr = reinterpret_cast<const uint8_t *>(input_data);
+ if (input_zeropoints[i] == output_zeropoint && input_scales[i] == output_scale)
+ {
+ std::memcpy(output_ptr, input_ptr, copy_size);
+ }
+ else
+ {
+ const float scale = input_scales[i] * inverse_output_scale;
+ const float bias = -input_zeropoints[i] * scale;
+ for (int j = 0; j < copy_size; ++j)
+ {
+ const int32_t value =
+ static_cast<int32_t>(std::round(input_ptr[j] * scale + bias)) + output_zeropoint;
+ output_ptr[j] = static_cast<uint8_t>(std::max(std::min(255, value), 0));
+ }
+ }
+ output_ptr += copy_size;
+ }
+ }
+}
+
+void Concat(const std::vector<std::reference_wrapper<const mir::TensorVariant>> &inputs, int axis,
+ mir::TensorVariant &output)
+{
+ dispatch<ConcatImpl>(inputs[0].get().getElementType(), inputs, axis, output);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Concat.h b/compiler/mir-interpreter/src/ops/Concat.h
new file mode 100644
index 000000000..587a97809
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Concat.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_CONCAT_IMPL_
+#define _NNC_CORE_BACKEND_INTERPRETER_CONCAT_IMPL_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void Concat(const std::vector<std::reference_wrapper<const mir::TensorVariant>> &inputs, int axis,
+ mir::TensorVariant &output);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_CONCAT_IMPL_
diff --git a/compiler/mir-interpreter/src/ops/Conv2D.cpp b/compiler/mir-interpreter/src/ops/Conv2D.cpp
new file mode 100644
index 000000000..c9b98a56f
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Conv2D.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2018 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 "Conv2D.h"
+#include "QuantizationHelpers.h"
+#include "Common.h"
+
+#include "mir/Tensor.h"
+
+#include <cmath>
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+static std::int32_t calcOffset(const Shape &shape, std::int32_t i0, std::int32_t i1,
+ std::int32_t i2, std::int32_t i3)
+{
+ return ((i0 * shape.dim(1) + i1) * shape.dim(2) + i2) * shape.dim(3) + i3;
+}
+
+template <typename T> struct Conv2DImpl
+{
+ static void run(const TensorVariant &input, const TensorVariant &kernel,
+ const Conv2DOpAttributes &attributes, TensorVariant &result,
+ const TensorVariant *fused_bias);
+};
+
+template <typename T>
+void Conv2DImpl<T>::run(const TensorVariant &input, const TensorVariant &kernel,
+ const Conv2DOpAttributes &attributes, TensorVariant &result,
+ const TensorVariant *fused_bias)
+{
+ const auto *input_data = reinterpret_cast<const T *>(input.atOffset(0));
+ const auto *kernel_data = reinterpret_cast<const T *>(kernel.atOffset(0));
+ auto *result_data = reinterpret_cast<T *>(result.atOffset(0));
+
+ const Shape &input_shape = input.getShape();
+ const Shape &output_shape = result.getShape();
+ const Shape &kernel_shape = kernel.getShape();
+
+ const std::vector<std::int32_t> &strides = attributes.strides;
+ const std::vector<std::int32_t> &padding_before = attributes.padding_before;
+ const std::int32_t num_groups = attributes.num_groups;
+ assert(attributes.data_format == DataFormat::NHWC);
+
+ const std::int32_t batch_size = output_shape.dim(0);
+ const std::int32_t output_height = output_shape.dim(1);
+ const std::int32_t output_width = output_shape.dim(2);
+ const std::int32_t kernel_height = kernel_shape.dim(1);
+ const std::int32_t kernel_width = kernel_shape.dim(2);
+ const std::int32_t input_height = input_shape.dim(1);
+ const std::int32_t input_width = input_shape.dim(2);
+
+ const std::int32_t num_in_channels = input_shape.dim(3);
+ const std::int32_t num_out_channels = output_shape.dim(3);
+
+ assert(num_in_channels % num_groups == 0);
+ assert(num_out_channels % num_groups == 0);
+
+ const std::int32_t out_group_size = num_out_channels / num_groups;
+ const std::int32_t in_group_size = num_in_channels / num_groups;
+
+ assert(kernel_shape.dim(3) == in_group_size);
+ assert(kernel_shape.dim(0) == num_out_channels);
+
+ for (std::int32_t batch = 0; batch < batch_size; ++batch)
+ {
+ for (std::int32_t out_y = 0; out_y < output_height; ++out_y)
+ {
+ for (std::int32_t out_x = 0; out_x < output_width; ++out_x)
+ {
+ for (std::int32_t group = 0; group < num_groups; ++group)
+ {
+ const std::int32_t out_group_offset = group * out_group_size;
+ const std::int32_t in_group_offset = group * in_group_size;
+
+ for (std::int32_t out_c = 0; out_c < out_group_size; ++out_c)
+ {
+ const std::int32_t in_y_origin = (out_y * strides[0]) - padding_before[0];
+ const std::int32_t in_x_origin = (out_x * strides[1]) - padding_before[1];
+
+ T sum = 0.0f;
+
+ for (std::int32_t kernel_y = 0; kernel_y < kernel_height; ++kernel_y)
+ {
+ for (std::int32_t kernel_x = 0; kernel_x < kernel_width; ++kernel_x)
+ {
+ for (std::int32_t in_c = 0; in_c < in_group_size; ++in_c)
+ {
+ const std::int32_t in_y = in_y_origin + kernel_y;
+ const std::int32_t in_x = in_x_origin + kernel_x;
+
+ if ((in_y >= 0 && in_y < input_height) && (in_x >= 0 && in_x < input_width))
+ {
+ const std::int32_t in_offset =
+ calcOffset(input_shape, batch, in_y, in_x, in_group_offset + in_c);
+ const std::int32_t kernel_offset = calcOffset(
+ kernel_shape, out_group_offset + out_c, kernel_y, kernel_x, in_c);
+ const T input_val = input_data[in_offset];
+ const T kernel_val = kernel_data[kernel_offset];
+ sum += kernel_val * input_val;
+ }
+ }
+ }
+ }
+
+ const std::int32_t out_offset =
+ calcOffset(output_shape, batch, out_y, out_x, out_group_offset + out_c);
+ result_data[out_offset] = sum;
+ }
+ }
+ }
+ }
+ }
+}
+
+template <> struct Conv2DImpl<uint8_t>
+{
+ static void run(const TensorVariant &input, const TensorVariant &kernel,
+ const Conv2DOpAttributes &attributes, TensorVariant &result,
+ const TensorVariant *fused_bias);
+};
+
+void Conv2DImpl<uint8_t>::run(const TensorVariant &input, const TensorVariant &kernel,
+ const Conv2DOpAttributes &attributes, TensorVariant &result,
+ const TensorVariant *fused_bias)
+{
+ if (!fused_bias)
+ {
+ throw std::runtime_error{"Quantized Conv2D cannot be executed without fused bias"};
+ }
+
+ const auto &input_type = input.getType();
+ const auto &kernel_type = kernel.getType();
+ const auto &bias_type = fused_bias->getType();
+ const auto &output_type = result.getType();
+ (void)bias_type;
+
+ assert(input_type.isQuantized());
+ assert(kernel_type.isQuantized());
+ assert(bias_type.isQuantized());
+ assert(output_type.isQuantized());
+ assert(input_type.getElementType() == DataType::UINT8);
+ assert(kernel_type.getElementType() == DataType::UINT8);
+ assert(bias_type.getElementType() == DataType::INT32);
+ assert(output_type.getElementType() == DataType::UINT8);
+
+ int32_t input_offset = -input_type.getQuantization().getZeroPoint();
+ int32_t kernel_offset = -kernel_type.getQuantization().getZeroPoint();
+ int32_t output_offset = output_type.getQuantization().getZeroPoint();
+
+ double input_scale = input_type.getQuantization().getScale();
+ double kernel_scale = kernel_type.getQuantization().getScale();
+ double output_scale = output_type.getQuantization().getScale();
+
+ double real_multiplier = input_scale * kernel_scale / output_scale;
+ int32_t output_multiplier = 0;
+ int output_shift = 0;
+ QuantizeMultiplier(real_multiplier, &output_multiplier, &output_shift);
+
+ const Shape &in_shape = input.getShape();
+ const Shape &kernel_shape = kernel.getShape();
+ const Shape &out_shape = result.getShape();
+ const auto &strides = attributes.strides;
+ const std::vector<int32_t> &pads = attributes.padding_before;
+ assert(attributes.num_groups == 1);
+ assert(attributes.data_format == DataFormat::NHWC);
+
+ assert(in_shape.rank() == 4);
+ assert(kernel_shape.rank() == 4);
+ assert(kernel_shape.dim(3) == in_shape.dim(3));
+ assert(kernel_shape.dim(0) == out_shape.dim(3));
+ assert(strides.size() == 2);
+ assert(pads.size() == 2);
+
+ int32_t stride_height = strides[0];
+ int32_t stride_width = strides[1];
+
+ int32_t pad_height = pads[0];
+ int32_t pad_width = pads[1];
+
+ int32_t input_height = in_shape.dim(1);
+ int32_t input_width = in_shape.dim(2);
+
+ Tensor<uint8_t> input_accessor(input);
+ Tensor<uint8_t> kernel_accessor(kernel);
+ Tensor<int32_t> bias_accessor(*fused_bias);
+ Tensor<uint8_t> res_accessor(result);
+
+ int32_t output_min = std::numeric_limits<uint8_t>::min();
+ int32_t output_max = std::numeric_limits<uint8_t>::max();
+
+ for (int batch = 0; batch < out_shape.dim(0); ++batch)
+ {
+ for (int out_y = 0; out_y < out_shape.dim(1); ++out_y)
+ {
+ for (int out_x = 0; out_x < out_shape.dim(2); ++out_x)
+ {
+ for (int out_channel = 0; out_channel < out_shape.dim(3); ++out_channel)
+ {
+ 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 < kernel_shape.dim(1); ++filter_y)
+ {
+ for (int filter_x = 0; filter_x < kernel_shape.dim(2); ++filter_x)
+ {
+ for (int in_channel = 0; in_channel < kernel_shape.dim(3); ++in_channel)
+ {
+ const int in_x = in_x_origin + filter_x;
+ const int in_y = in_y_origin + 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))
+ {
+ Index in_index{batch, in_y, in_x, in_channel};
+ Index ker_index{out_channel, filter_y, filter_x, in_channel};
+ int32_t input_val = input_accessor.at(in_index);
+ int32_t kernel_val = kernel_accessor.at(ker_index);
+ acc += (kernel_val + kernel_offset) * (input_val + input_offset);
+ }
+ }
+ }
+ }
+ acc += bias_accessor.at(Index{out_channel});
+ acc = MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift);
+ acc += output_offset;
+ acc = std::max(acc, output_min);
+ acc = std::min(acc, output_max);
+ Index out_index{batch, out_y, out_x, out_channel};
+ res_accessor.at(out_index) = static_cast<uint8_t>(acc);
+ }
+ }
+ }
+ }
+}
+
+void Conv2D(const mir::TensorVariant &input, const mir::TensorVariant &kernel,
+ const mir::Conv2DOpAttributes &attributes, mir::TensorVariant &result,
+ const mir::TensorVariant *fused_bias)
+{
+ dispatch<Conv2DImpl>(result.getElementType(), input, kernel, attributes, result, fused_bias);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Conv2D.h b/compiler/mir-interpreter/src/ops/Conv2D.h
new file mode 100644
index 000000000..ebb550816
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Conv2D.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_CONV2D_IMPL_
+#define _NNC_CORE_BACKEND_INTERPRETER_CONV2D_IMPL_
+
+#include "mir/ops/Conv2DOp.h"
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Conv2D(const mir::TensorVariant &input, const mir::TensorVariant &kernel,
+ const mir::Conv2DOpAttributes &attributes, mir::TensorVariant &result,
+ const mir::TensorVariant *fused_bias);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_CONV2D_IMPL
diff --git a/compiler/mir-interpreter/src/ops/DeConv2D.cpp b/compiler/mir-interpreter/src/ops/DeConv2D.cpp
new file mode 100644
index 000000000..746d8c87c
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/DeConv2D.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2018 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 "DeConv2D.h"
+#include "Common.h"
+
+#include "mir/TensorUtil.h"
+
+#include <cstdint>
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+using std::int32_t;
+
+static int32_t calcOffset(const Shape &shape, int32_t i0, int32_t i1, int32_t i2, int32_t i3)
+{
+ return ((i0 * shape.dim(1) + i1) * shape.dim(2) + i2) * shape.dim(3) + i3;
+}
+
+template <typename T> struct DeConv2DImpl
+{
+ static void run(const TensorVariant &input, const TensorVariant &kernel,
+ const Deconv2DOpAttributes &attributes, TensorVariant &output);
+};
+
+template <typename T>
+void DeConv2DImpl<T>::run(const TensorVariant &input, const TensorVariant &kernel,
+ const Deconv2DOpAttributes &attributes, TensorVariant &output)
+{
+ // [H, W, Co, Ci] -> [Ci, H, W, Co]
+ TensorVariant transposed_kernel = transposeTensor<3, 0, 1, 2>(kernel);
+
+ const auto *input_data = reinterpret_cast<const T *>(input.atOffset(0));
+ const auto *kernel_data = reinterpret_cast<const T *>(transposed_kernel.atOffset(0));
+ auto *output_data = reinterpret_cast<T *>(output.atOffset(0));
+
+ const Shape &input_shape = input.getShape();
+ const Shape &output_shape = output.getShape();
+ const Shape &kernel_shape = transposed_kernel.getShape();
+
+ const std::vector<int32_t> &strides = attributes.strides;
+ const std::vector<int32_t> &padding_before = attributes.padding_before;
+ assert(attributes.data_format == DataFormat::NHWC);
+
+ const int32_t batch_size = output_shape.dim(0);
+ const int32_t output_height = output_shape.dim(1);
+ const int32_t output_width = output_shape.dim(2);
+ const int32_t kernel_height = kernel_shape.dim(1);
+ const int32_t kernel_width = kernel_shape.dim(2);
+ const int32_t input_height = input_shape.dim(1);
+ const int32_t input_width = input_shape.dim(2);
+
+ const int32_t num_in_channels = input_shape.dim(3);
+ const int32_t num_out_channels = output_shape.dim(3);
+
+ assert(kernel_shape.dim(0) == num_in_channels);
+ assert(kernel_shape.dim(3) == num_out_channels);
+
+ erase<T>(output);
+
+ for (int32_t batch = 0; batch < batch_size; ++batch)
+ {
+ for (int32_t in_y = 0; in_y < input_height; ++in_y)
+ {
+ for (int32_t in_x = 0; in_x < input_width; ++in_x)
+ {
+ for (int32_t in_c = 0; in_c < num_in_channels; ++in_c)
+ {
+ const T input_val = input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)];
+ const int32_t out_y_origin = in_y * strides[0] - padding_before[0];
+ const int32_t out_x_origin = in_x * strides[1] - padding_before[1];
+
+ for (int32_t kernel_y = 0; kernel_y < kernel_height; ++kernel_y)
+ {
+ for (int32_t kernel_x = 0; kernel_x < kernel_width; ++kernel_x)
+ {
+ const int32_t out_y = out_y_origin + kernel_y;
+ const int32_t out_x = out_x_origin + kernel_x;
+
+ if ((out_y >= 0 && out_y < output_height) && (out_x >= 0 && out_x < output_width))
+ {
+ for (int32_t out_c = 0; out_c < num_out_channels; ++out_c)
+ {
+ const int32_t kernel_offset =
+ calcOffset(kernel_shape, in_c, kernel_y, kernel_x, out_c);
+ const int32_t output_offset =
+ calcOffset(output_shape, batch, out_y, out_x, out_c);
+ const T kernel_val = kernel_data[kernel_offset];
+ output_data[output_offset] += input_val * kernel_val;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void DeConv2D(const TensorVariant &input, const TensorVariant &kernel,
+ const Deconv2DOpAttributes &attributes, TensorVariant &output)
+{
+ dispatch<DeConv2DImpl>(output.getElementType(), input, kernel, attributes, output);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/DeConv2D.h b/compiler/mir-interpreter/src/ops/DeConv2D.h
new file mode 100644
index 000000000..be797fcef
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/DeConv2D.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_DECONV2D_IMPL_
+#define _NNC_CORE_BACKEND_INTERPRETER_DECONV2D_IMPL_
+
+#include "mir/Attributes.h"
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+/**
+ * @brief Transposed convolution (or Deconvolution)
+ * @param input The Input tensor
+ * @param op The DeConvolution operation object
+ *
+ * This is basically the backward pass for the convolution operation,
+ * hence all the indexing can be deducted by expressing the input index
+ * of Conv in terms of it's output index.
+ */
+
+void DeConv2D(const mir::TensorVariant &input, const mir::TensorVariant &kernel,
+ const mir::Deconv2DOpAttributes &attributes, mir::TensorVariant &output);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_DECONV2D_IMPL_
diff --git a/compiler/mir-interpreter/src/ops/DepthwiseConv2D.cpp b/compiler/mir-interpreter/src/ops/DepthwiseConv2D.cpp
new file mode 100644
index 000000000..4b6df3478
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/DepthwiseConv2D.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2018 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 "DepthwiseConv2D.h"
+#include "QuantizationHelpers.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+#include <cmath>
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct DepthwiseConv2DImpl
+{
+ static void run(const mir::ops::DepthwiseConv2DOp &op, const mir::TensorVariant &inputv,
+ const mir::TensorVariant &kernelv, const mir::TensorVariant *biasv,
+ mir::TensorVariant &output);
+};
+
+template <typename T>
+void DepthwiseConv2DImpl<T>::run(const mir::ops::DepthwiseConv2DOp &op,
+ const mir::TensorVariant &inputv,
+ const mir::TensorVariant &kernelv, const mir::TensorVariant *biasv,
+ mir::TensorVariant &output)
+{
+ const Shape &in_shape = op.getInputShape(0);
+ const Shape &kernel_shape = op.getInputShape(1);
+ const Shape &out_shape = op.getOutputShape(0);
+ const auto &strides = op.getStrides();
+ const std::vector<int32_t> &pads = op.getPaddingBefore();
+
+ assert(in_shape.rank() == 4);
+ assert(kernel_shape.rank() == 4);
+ assert(kernel_shape.dim(2) == in_shape.dim(3));
+ assert(in_shape.dim(3) * kernel_shape.dim(3) == out_shape.dim(3));
+ assert(strides.size() == 2);
+ assert(pads.size() == 2);
+
+ int32_t channel_multiplier = kernel_shape.dim(3);
+
+ Tensor<T> res_accessor(output);
+ Tensor<T> input(inputv);
+ Tensor<T> bias(*biasv);
+ Tensor<T> kernel(kernelv);
+
+ ShapeRange in_range(in_shape);
+ ShapeRange kernel_range(kernel_shape);
+ ShapeRange out_range(Shape{out_shape.dim(0), out_shape.dim(1), out_shape.dim(2), 1});
+
+ Index in_index;
+ in_index.resize(4);
+
+ erase<T>(output);
+
+ for (const auto &out_index : out_range)
+ {
+ Index out_index_k = out_index;
+ for (const auto &kernel_index : kernel_range)
+ {
+ in_index.at(0) = out_index.at(0);
+ for (int i = 0; i < 2; ++i)
+ in_index.at(1 + i) = out_index.at(1 + i) * strides[i] + kernel_index.at(i) - pads[i];
+ in_index.at(3) = kernel_index.at(2);
+
+ if (in_range.contains(in_index))
+ {
+ out_index_k.at(3) = kernel_index.at(2) * channel_multiplier + kernel_index.at(3);
+ res_accessor.at(out_index_k) += input.at(in_index) * kernel.at(kernel_index);
+ }
+ }
+ }
+}
+
+template <> struct DepthwiseConv2DImpl<uint8_t>
+{
+ static void run(const mir::ops::DepthwiseConv2DOp &op, const mir::TensorVariant &inputv,
+ const mir::TensorVariant &kernelv, const mir::TensorVariant *biasv,
+ mir::TensorVariant &output);
+};
+
+void DepthwiseConv2DImpl<uint8_t>::run(const mir::ops::DepthwiseConv2DOp &op,
+ const mir::TensorVariant &inputv,
+ const mir::TensorVariant &kernelv,
+ const mir::TensorVariant *biasv, mir::TensorVariant &output)
+{
+ if (!biasv)
+ {
+ throw std::runtime_error{"Unsupported quantized DepthwiseConv2D without fused bias"};
+ }
+
+ const auto &input_type = inputv.getType();
+ const auto &kernel_type = kernelv.getType();
+ const auto &bias_type = biasv->getType();
+ const auto &output_type = op.getOutput(0)->getType();
+ (void)bias_type;
+
+ assert(input_type.isQuantized());
+ assert(kernel_type.isQuantized());
+ assert(bias_type.isQuantized());
+ assert(output_type.isQuantized());
+ assert(input_type.getElementType() == DataType::UINT8);
+ assert(kernel_type.getElementType() == DataType::UINT8);
+ assert(bias_type.getElementType() == DataType::INT32);
+
+ int32_t input_offset = -input_type.getQuantization().getZeroPoint();
+ int32_t kernel_offset = -kernel_type.getQuantization().getZeroPoint();
+ int32_t output_offset = output_type.getQuantization().getZeroPoint();
+
+ double input_scale = input_type.getQuantization().getScale();
+ double kernel_scale = kernel_type.getQuantization().getScale();
+ double output_scale = output_type.getQuantization().getScale();
+
+ double real_multiplier = input_scale * kernel_scale / output_scale;
+ int32_t output_multiplier = 0;
+ int output_shift = 0;
+ QuantizeMultiplier(real_multiplier, &output_multiplier, &output_shift);
+
+ const Shape &in_shape = inputv.getShape();
+ const Shape &kernel_shape = kernelv.getShape();
+ const Shape &out_shape = op.getOutputShape(0);
+ const auto &strides = op.getStrides();
+ const std::vector<int32_t> &pads = op.getPaddingBefore();
+
+ assert(in_shape.rank() == 4);
+ assert(kernel_shape.rank() == 4);
+ assert(kernel_shape.dim(2) == in_shape.dim(3)); // HWIO
+ assert(in_shape.dim(3) * kernel_shape.dim(3) == out_shape.dim(3));
+ assert(strides.size() == 2);
+ assert(pads.size() == 2);
+
+ int32_t stride_height = strides[0];
+ int32_t stride_width = strides[1];
+
+ int32_t pad_height = pads[0];
+ int32_t pad_width = pads[1];
+
+ int32_t input_height = in_shape.dim(1);
+ int32_t input_width = in_shape.dim(2);
+
+ Tensor<uint8_t> input_accessor(inputv);
+ Tensor<uint8_t> kernel_accessor(kernelv);
+ Tensor<int32_t> bias_accessor(*biasv);
+ Tensor<uint8_t> res_accessor(output);
+
+ int32_t output_min = std::numeric_limits<uint8_t>::min();
+ int32_t output_max = std::numeric_limits<uint8_t>::max();
+
+ int batches = out_shape.dim(0);
+ int output_height = out_shape.dim(1);
+ int output_width = out_shape.dim(2);
+ int input_depth = in_shape.dim(3);
+
+ int filter_height = kernel_shape.dim(0); // HWIO
+ int filter_width = kernel_shape.dim(1); // HWIO
+
+ for (int b = 0; b < batches; ++b)
+ {
+ for (int out_y = 0; out_y < output_height; ++out_y)
+ {
+ for (int out_x = 0; out_x < output_width; ++out_x)
+ {
+ for (int ic = 0; ic < input_depth; ++ic)
+ {
+ const int oc = ic;
+ 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 + filter_x;
+ const int in_y = in_y_origin + 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))
+ {
+ Index in_index{b, in_y, in_x, ic};
+ Index ker_index{filter_y, filter_x, oc, 0}; // HWIO
+ int32_t input_val = input_accessor.at(in_index);
+ int32_t kernel_val = kernel_accessor.at(ker_index);
+ acc += (kernel_val + kernel_offset) * (input_val + input_offset);
+ }
+ }
+ }
+ acc += bias_accessor.at(Index{oc});
+ acc = MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift);
+ acc += output_offset;
+ acc = std::max(acc, output_min);
+ acc = std::min(acc, output_max);
+ Index out_index{b, out_y, out_x, oc};
+ res_accessor.at(out_index) = static_cast<uint8_t>(acc);
+ }
+ }
+ }
+ }
+}
+
+void DepthwiseConv2D(const mir::ops::DepthwiseConv2DOp &op, const mir::TensorVariant &input,
+ const mir::TensorVariant &kernel, mir::TensorVariant &output,
+ const mir::TensorVariant *bias)
+{
+ dispatch<DepthwiseConv2DImpl>(output.getElementType(), op, input, kernel, bias, output);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/DepthwiseConv2D.h b/compiler/mir-interpreter/src/ops/DepthwiseConv2D.h
new file mode 100644
index 000000000..d89529fc9
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/DepthwiseConv2D.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_DEPTHWISE_CONV2D_IMPL_
+#define _NNC_CORE_BACKEND_INTERPRETER_DEPTHWISE_CONV2D_IMPL_
+
+#include "mir/ops/DepthwiseConv2DOp.h"
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void DepthwiseConv2D(const mir::ops::DepthwiseConv2DOp &op, const mir::TensorVariant &input,
+ const mir::TensorVariant &kernel, mir::TensorVariant &output,
+ const mir::TensorVariant *bias);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_DEPTHWISE_CONV2D_IMPL_
diff --git a/compiler/mir-interpreter/src/ops/Div.cpp b/compiler/mir-interpreter/src/ops/Div.cpp
new file mode 100644
index 000000000..00553e7e0
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Div.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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 "Div.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct DivImpl
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res);
+};
+
+template <typename T>
+void DivImpl<T>::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ TensorVariant broadcasted_lhs(lhs, res.getShape());
+ TensorVariant broadcasted_rhs(rhs, res.getShape());
+ Tensor<T> lhs_accessor(broadcasted_lhs);
+ Tensor<T> rhs_accessor(broadcasted_rhs);
+ Tensor<T> res_accessor(res);
+
+ for (const auto &index : ShapeRange(res.getShape()))
+ {
+ res_accessor.at(index) = lhs_accessor.at(index) / rhs_accessor.at(index);
+ }
+}
+
+template <> struct DivImpl<uint8_t>
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+ {
+ // No support for quantized elementwise div yet
+ throw std::runtime_error{"NYI"};
+ }
+};
+
+void Div(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ dispatch<DivImpl>(res.getElementType(), lhs, rhs, res);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Div.h b/compiler/mir-interpreter/src/ops/Div.h
new file mode 100644
index 000000000..558e299ec
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Div.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_DIV_
+#define _NNC_CORE_BACKEND_INTERPRETER_DIV_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Div(const mir::TensorVariant &lhs, const mir::TensorVariant &rhs, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_DIV_
diff --git a/compiler/mir-interpreter/src/ops/ELU.cpp b/compiler/mir-interpreter/src/ops/ELU.cpp
new file mode 100644
index 000000000..0cd76baf4
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/ELU.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReLU.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+#include <cmath>
+
+namespace mir_interpreter
+{
+
+template <typename T> struct ELUImpl
+{
+ static void run(const mir::TensorVariant &arg, float alpha, mir::TensorVariant &result);
+};
+
+template <typename T>
+void ELUImpl<T>::run(const mir::TensorVariant &arg, float alpha, mir::TensorVariant &result)
+{
+ mir::Tensor<T> arg_accessor(arg);
+ mir::Tensor<T> res_accessor(result);
+
+ for (const auto &index : mir::ShapeRange(result.getShape()))
+ {
+ const T x = arg_accessor.at(index);
+ res_accessor.at(index) = x < 0 ? alpha * (std::exp(x) - 1) : x;
+ }
+}
+
+void ELU(const mir::TensorVariant &arg, float alpha, mir::TensorVariant &result)
+{
+ dispatch<ELUImpl>(result.getElementType(), arg, alpha, result);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/ELU.h b/compiler/mir-interpreter/src/ops/ELU.h
new file mode 100644
index 000000000..c6ebae1a7
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/ELU.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_ELU_
+#define _NNC_CORE_BACKEND_INTERPRETER_ELU_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void ELU(const mir::TensorVariant &arg, float alpha, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_ELU_
diff --git a/compiler/mir-interpreter/src/ops/Equal.cpp b/compiler/mir-interpreter/src/ops/Equal.cpp
new file mode 100644
index 000000000..b75ea5543
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Equal.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Equal.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct EqualImpl
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res);
+};
+
+template <typename T>
+void EqualImpl<T>::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ TensorVariant broadcasted_lhs(lhs, res.getShape());
+ TensorVariant broadcasted_rhs(rhs, res.getShape());
+ Tensor<T> lhs_accessor(broadcasted_lhs);
+ Tensor<T> rhs_accessor(broadcasted_rhs);
+ Tensor<uint8_t> res_accessor(res);
+
+ for (const auto &index : ShapeRange(res.getShape()))
+ {
+ res_accessor.at(index) = (lhs_accessor.at(index) == rhs_accessor.at(index));
+ }
+}
+
+void Equal(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ if (lhs.getElementType() != rhs.getElementType())
+ {
+ throw std::runtime_error{"Equal with different input types is unsupported"};
+ }
+
+ dispatch<EqualImpl>(lhs.getElementType(), lhs, rhs, res);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Equal.h b/compiler/mir-interpreter/src/ops/Equal.h
new file mode 100644
index 000000000..2d112a2f1
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Equal.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_EQUAL_
+#define _NNC_CORE_BACKEND_INTERPRETER_EQUAL_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Equal(const mir::TensorVariant &lhs, const mir::TensorVariant &rhs, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_EQUAL_
diff --git a/compiler/mir-interpreter/src/ops/Fill.h b/compiler/mir-interpreter/src/ops/Fill.h
new file mode 100644
index 000000000..6dee25b8a
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Fill.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_FILL_
+#define _NNC_CORE_BACKEND_INTERPRETER_FILL_
+
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+template <typename T> struct FillImpl
+{
+ template <typename F> static void run(mir::TensorVariant &res, F f)
+ {
+ mir::Tensor<T> res_accessor(res);
+
+ for (const auto &index : mir::ShapeRange(res.getShape()))
+ {
+ res_accessor.at(index) = f(index);
+ }
+ }
+};
+
+template <typename F> void Fill(mir::TensorVariant &t, F f)
+{
+ dispatch<FillImpl>(t.getElementType(), t, f);
+}
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_FILL_
diff --git a/compiler/mir-interpreter/src/ops/FullyConnected.cpp b/compiler/mir-interpreter/src/ops/FullyConnected.cpp
new file mode 100644
index 000000000..9c6ef8dc8
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/FullyConnected.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2018 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 "FullyConnected.h"
+#include "Common.h"
+
+#include "QuantizationHelpers.h"
+
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+template <typename T>
+static void fullyConnected2D(const mir::TensorVariant &input, const mir::TensorVariant &weights,
+ mir::TensorVariant &output)
+{
+ assert(input.getShape().rank() == 2);
+ assert(weights.getShape().rank() == 2);
+ assert(input.getShape().dim(1) == weights.getShape().dim(0));
+
+ auto in_raw = reinterpret_cast<T *>(input.atOffset(0));
+ auto weight_raw = reinterpret_cast<T *>(weights.atOffset(0));
+ auto output_raw = reinterpret_cast<T *>(output.atOffset(0));
+
+ auto rows = output.getShape().dim(0);
+ auto cols = output.getShape().dim(1);
+ auto N = input.getShape().dim(1);
+ auto wcols = weights.getShape().dim(1);
+
+ for (int32_t r = 0; r < rows; ++r)
+ {
+ for (int32_t k = 0; k < N; ++k)
+ {
+ auto in = in_raw[r * N + k];
+
+ for (int32_t c = 0; c < cols; ++c)
+ {
+ output_raw[r * cols + c] += in * weight_raw[k * wcols + c];
+ }
+ }
+ }
+}
+
+template <typename T> struct FullyConnectedImpl
+{
+ static void run(const mir::TensorVariant &inputv, const mir::TensorVariant &weightsv,
+ const mir::ops::FullyConnectedOp &op, mir::TensorVariant &res,
+ const mir::TensorVariant *biasv);
+};
+
+template <typename T>
+void FullyConnectedImpl<T>::run(const mir::TensorVariant &inputv,
+ const mir::TensorVariant &weightsv,
+ const mir::ops::FullyConnectedOp &op, mir::TensorVariant &res,
+ const mir::TensorVariant *biasv)
+{
+ if (biasv)
+ {
+ throw std::runtime_error("non-quantized FullyConnected with fused bias is unsupported");
+ }
+
+ mir::Tensor<T> input{inputv};
+ mir::Tensor<T> weights{weightsv};
+
+ erase<T>(res);
+
+ if (input.getShape().rank() == 2 && weights.getShape().rank() == 2 && res.getShape().rank() == 2)
+ {
+ // optimized case for 2d matrix multiplication
+ fullyConnected2D<T>(inputv, weightsv, res);
+ return;
+ }
+
+ mir::Tensor<T> accessor(res);
+
+ const mir::Shape &in_shape = input.getShape();
+ int32_t in_rank = in_shape.rank();
+
+ const mir::Shape &w_shape = weights.getShape();
+ int32_t w_rank = w_shape.rank();
+
+ assert(in_shape.dim(in_rank - 1) == w_shape.dim(w_rank - 2));
+ (void)in_rank;
+
+ mir::ShapeRange out_range(res.getShape());
+
+ int32_t len = w_shape.dim(w_rank - 2);
+
+ for (auto &out_index : out_range)
+ {
+ mir::Index t_index = out_index;
+ T &output_element = accessor.at(out_index);
+ int32_t col = t_index.at(w_rank - 1);
+ int32_t row = t_index.at(w_rank - 2);
+ for (int32_t i = 0; i < len; ++i)
+ {
+ t_index.at(w_rank - 1) = i;
+ T in = input.at(t_index);
+ t_index.at(w_rank - 1) = col;
+ t_index.at(w_rank - 2) = i;
+ T w = weights.at(t_index);
+ t_index.at(w_rank - 2) = row;
+ output_element += in * w;
+ }
+ }
+}
+
+template <> struct FullyConnectedImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &inputv, const mir::TensorVariant &weightsv,
+ const mir::ops::FullyConnectedOp &op, mir::TensorVariant &res,
+ const mir::TensorVariant *biasv);
+};
+
+void FullyConnectedImpl<uint8_t>::run(const mir::TensorVariant &inputv,
+ const mir::TensorVariant &weightsv,
+ const mir::ops::FullyConnectedOp &op, mir::TensorVariant &res,
+ const mir::TensorVariant *biasv)
+{
+ if (!biasv)
+ {
+ throw std::runtime_error{"Quantized FullyConnected cannot be executed without fused bias"};
+ }
+
+ const auto &input_type = inputv.getType();
+ const auto &weights_type = weightsv.getType();
+ const auto &bias_type = biasv->getType();
+ const auto &output_type = op.getOutput(0)->getType();
+ (void)bias_type;
+
+ assert(input_type.isQuantized());
+ assert(weights_type.isQuantized());
+ assert(bias_type.isQuantized());
+ assert(output_type.isQuantized());
+ assert(input_type.getElementType() == mir::DataType::UINT8);
+ assert(weights_type.getElementType() == mir::DataType::UINT8);
+ assert(bias_type.getElementType() == mir::DataType::INT32);
+
+ int32_t input_offset = -input_type.getQuantization().getZeroPoint();
+ int32_t weights_offset = -weights_type.getQuantization().getZeroPoint();
+ int32_t output_offset = output_type.getQuantization().getZeroPoint();
+
+ double input_scale = input_type.getQuantization().getScale();
+ double weights_scale = weights_type.getQuantization().getScale();
+ double output_scale = output_type.getQuantization().getScale();
+
+ double real_multiplier = input_scale * weights_scale / output_scale;
+ int32_t output_multiplier = 0;
+ int output_shift = 0;
+ QuantizeMultiplier(real_multiplier, &output_multiplier, &output_shift);
+
+ const mir::Shape &in_shape = inputv.getShape();
+ const mir::Shape &weights_shape = weightsv.getShape();
+ const mir::Shape &out_shape = op.getOutputShape(0);
+
+ const int32_t batches = in_shape.dim(0);
+ assert(in_shape.rank() == 2);
+ assert(weights_shape.rank() == 2);
+ assert(in_shape.dim(1) == weights_shape.dim(0));
+ const int32_t accum_depth = weights_shape.dim(0);
+ const int32_t output_depth = weights_shape.dim(1);
+
+ uint8_t *input_data = reinterpret_cast<uint8_t *>(inputv.atOffset(0));
+ uint8_t *weights_data = reinterpret_cast<uint8_t *>(weightsv.atOffset(0));
+ int32_t *bias_data = reinterpret_cast<int32_t *>(biasv->atOffset(0));
+
+ uint8_t *output_data = reinterpret_cast<uint8_t *>(res.atOffset(0));
+
+ int32_t output_min = std::numeric_limits<uint8_t>::min();
+ int32_t output_max = std::numeric_limits<uint8_t>::max();
+
+ for (int32_t b = 0; b < batches; ++b)
+ {
+ for (int32_t out_c = 0; out_c < output_depth; ++out_c)
+ {
+ int32_t acc = 0;
+ for (int d = 0; d < accum_depth; ++d)
+ {
+ int32_t input_val = input_data[b * accum_depth + d];
+ int32_t weights_val = weights_data[d * output_depth + out_c];
+ acc += (weights_val + weights_offset) * (input_val + input_offset);
+ }
+ acc += bias_data[out_c];
+ acc = MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift);
+ acc += output_offset;
+ acc = std::max(acc, output_min);
+ acc = std::min(acc, output_max);
+ output_data[out_c + output_depth * b] = static_cast<uint8_t>(acc);
+ }
+ }
+}
+
+void FullyConnected(const mir::TensorVariant &input, const mir::TensorVariant &weights,
+ const mir::ops::FullyConnectedOp &op, mir::TensorVariant &res,
+ const mir::TensorVariant *bias)
+{
+ dispatch<FullyConnectedImpl>(res.getElementType(), input, weights, op, res, bias);
+}
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/FullyConnected.h b/compiler/mir-interpreter/src/ops/FullyConnected.h
new file mode 100644
index 000000000..fdfe64265
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/FullyConnected.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_FULLYCONNECTED_
+#define _NNC_CORE_BACKEND_INTERPRETER_FULLYCONNECTED_
+
+#include "mir/ops/FullyConnectedOp.h"
+#include "mir/ShapeRange.h"
+
+namespace mir_interpreter
+{
+
+void FullyConnected(const mir::TensorVariant &input, const mir::TensorVariant &weights,
+ const mir::ops::FullyConnectedOp &op, mir::TensorVariant &res,
+ const mir::TensorVariant *bias);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_FULLYCONNECTED_
diff --git a/compiler/mir-interpreter/src/ops/Gather.cpp b/compiler/mir-interpreter/src/ops/Gather.cpp
new file mode 100644
index 000000000..4328c26b2
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Gather.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Gather.h"
+#include "Common.h"
+
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T, typename IndicesT> struct GatherImpl
+{
+ static void run(const TensorVariant &datav, const TensorVariant &indicesv,
+ const ops::GatherOp &op, mir::TensorVariant &res);
+};
+
+template <typename T, typename IndicesT>
+void GatherImpl<T, IndicesT>::run(const TensorVariant &datav, const TensorVariant &indicesv,
+ const ops::GatherOp &op, TensorVariant &res)
+{
+ const auto &data_shape = datav.getShape();
+ const auto &indices_shape = indicesv.getShape();
+ Tensor<T> data(datav);
+ Tensor<T> output(res);
+ Tensor<IndicesT> indices(indicesv);
+
+ int32_t axis = op.getAxis();
+ if (axis < 0)
+ axis += data_shape.rank();
+ assert(axis >= 0 && axis < data_shape.rank());
+ int32_t axis_size = data_shape.dim(axis);
+ int32_t num_indices = indices_shape.numElements();
+
+ int32_t outer_size = 1;
+ for (int32_t i = 0; i < axis; ++i)
+ outer_size *= data_shape.dim(i);
+
+ int32_t inner_size = 1;
+ for (int32_t i = axis + 1; i < data_shape.rank(); ++i)
+ inner_size *= data_shape.dim(i);
+
+ for (int32_t outer = 0; outer < outer_size; ++outer)
+ {
+ for (int32_t i = 0; i < num_indices; ++i)
+ {
+ auto index = indices.atOffset(i);
+ assert(index >= 0 && index < axis_size);
+ for (int32_t inner = 0; inner < inner_size; inner++)
+ {
+ output.atOffset((outer * num_indices + i) * inner_size + inner) =
+ data.atOffset((outer * axis_size + index) * inner_size + inner);
+ }
+ }
+ }
+}
+
+// a hack to reuse dispath function
+template <typename T> struct GatherByT
+{
+
+ template <typename IndicesT> using GatherWithFixedT = GatherImpl<T, IndicesT>;
+
+ static void run(const TensorVariant &data, const TensorVariant &indices, const ops::GatherOp &op,
+ TensorVariant &res)
+ {
+ dispatch<GatherWithFixedT>(indices.getElementType(), data, indices, op, res);
+ }
+};
+
+void Gather(const TensorVariant &data, const TensorVariant &indices, const ops::GatherOp &op,
+ TensorVariant &res)
+{
+ dispatch<GatherByT>(data.getElementType(), data, indices, op, res);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Gather.h b/compiler/mir-interpreter/src/ops/Gather.h
new file mode 100644
index 000000000..0f9648323
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Gather.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_GATHER_
+#define _NNC_CORE_BACKEND_INTERPRETER_GATHER_
+
+#include "mir/ops/GatherOp.h"
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Gather(const mir::TensorVariant &data, const mir::TensorVariant &indices,
+ const mir::ops::GatherOp &op, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_GATHER_
diff --git a/compiler/mir-interpreter/src/ops/Greater.cpp b/compiler/mir-interpreter/src/ops/Greater.cpp
new file mode 100644
index 000000000..36400292f
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Greater.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Greater.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct GreaterImpl
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res);
+};
+
+template <typename T>
+void GreaterImpl<T>::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ TensorVariant broadcasted_lhs(lhs, res.getShape());
+ TensorVariant broadcasted_rhs(rhs, res.getShape());
+ Tensor<T> lhs_accessor(broadcasted_lhs);
+ Tensor<T> rhs_accessor(broadcasted_rhs);
+ Tensor<uint8_t> res_accessor(res);
+
+ for (const auto &index : ShapeRange(res.getShape()))
+ {
+ res_accessor.at(index) = (lhs_accessor.at(index) > rhs_accessor.at(index));
+ }
+}
+
+void Greater(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ if (lhs.getElementType() != rhs.getElementType())
+ {
+ throw std::runtime_error{"Greater with different input types is unsupported"};
+ }
+ dispatch<GreaterImpl>(lhs.getElementType(), lhs, rhs, res);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Greater.h b/compiler/mir-interpreter/src/ops/Greater.h
new file mode 100644
index 000000000..812245ecd
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Greater.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_GREATER_
+#define _NNC_CORE_BACKEND_INTERPRETER_GREATER_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Greater(const mir::TensorVariant &lhs, const mir::TensorVariant &rhs, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_GREATER_
diff --git a/compiler/mir-interpreter/src/ops/HardSwish.cpp b/compiler/mir-interpreter/src/ops/HardSwish.cpp
new file mode 100644
index 000000000..20f7820c2
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/HardSwish.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "HardSwish.h"
+#include "Common.h"
+
+namespace mir_interpreter
+{
+
+template <typename T> struct HardSwishImpl
+{
+ static void run(const mir::TensorVariant &input, mir::TensorVariant &result);
+};
+
+template <typename T>
+void HardSwishImpl<T>::run(const mir::TensorVariant &input, mir::TensorVariant &result)
+{
+ auto output_data = reinterpret_cast<T *>(result.atOffset(0));
+ auto input_data = reinterpret_cast<T *>(input.atOffset(0));
+ auto in_end = input_data + input.getShape().numElements();
+ for (; input_data < in_end; input_data++, output_data++)
+ {
+ const auto in = *input_data;
+ *output_data = in * std::min<T>(6.f, std::max<T>(0.f, in + 3.f)) / 6.f;
+ }
+}
+
+template <> struct HardSwishImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &input, mir::TensorVariant &result)
+ {
+ throw std::runtime_error{"NYI"};
+ }
+};
+
+void HardSwish(const mir::TensorVariant &input, mir::TensorVariant &result)
+{
+ dispatch<HardSwishImpl>(input.getElementType(), input, result);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/HardSwish.h b/compiler/mir-interpreter/src/ops/HardSwish.h
new file mode 100644
index 000000000..9b39bb164
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/HardSwish.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_HARDSWISH_IMPL_
+#define _NNC_CORE_BACKEND_INTERPRETER_HARDSWISH_IMPL_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void HardSwish(const mir::TensorVariant &input, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_HARDSWISH_IMPL_
diff --git a/compiler/mir-interpreter/src/ops/LeakyReLU.cpp b/compiler/mir-interpreter/src/ops/LeakyReLU.cpp
new file mode 100644
index 000000000..5b265f9f5
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/LeakyReLU.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReLU.h"
+#include "Common.h"
+
+#include <mir/ShapeRange.h>
+#include <mir/Tensor.h>
+
+namespace mir_interpreter
+{
+
+template <typename T> struct LeakyReLUImpl
+{
+ static void run(const mir::TensorVariant &arg, float alpha, mir::TensorVariant &result);
+};
+
+template <typename T>
+void LeakyReLUImpl<T>::run(const mir::TensorVariant &arg, float alpha, mir::TensorVariant &result)
+{
+ mir::Tensor<T> arg_accessor(arg);
+ mir::Tensor<T> res_accessor(result);
+
+ for (const auto &index : mir::ShapeRange(result.getShape()))
+ {
+ auto x = arg_accessor.at(index);
+ res_accessor.at(index) = x < 0 ? x * alpha : x;
+ }
+}
+
+void LeakyReLU(const mir::TensorVariant &arg, float alpha, mir::TensorVariant &result)
+{
+ dispatch<LeakyReLUImpl>(result.getElementType(), arg, alpha, result);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/LeakyReLU.h b/compiler/mir-interpreter/src/ops/LeakyReLU.h
new file mode 100644
index 000000000..6bf9b78ac
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/LeakyReLU.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_LEAKYRELU_
+#define _NNC_CORE_BACKEND_INTERPRETER_LEAKYRELU_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void LeakyReLU(const mir::TensorVariant &arg, float cap, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_LEAKYRELU_
diff --git a/compiler/mir-interpreter/src/ops/Less.cpp b/compiler/mir-interpreter/src/ops/Less.cpp
new file mode 100644
index 000000000..8da351915
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Less.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Less.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct LessImpl
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res);
+};
+
+template <typename T>
+void LessImpl<T>::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ TensorVariant broadcasted_lhs(lhs, res.getShape());
+ TensorVariant broadcasted_rhs(rhs, res.getShape());
+ Tensor<T> lhs_accessor(broadcasted_lhs);
+ Tensor<T> rhs_accessor(broadcasted_rhs);
+ Tensor<uint8_t> res_accessor(res);
+
+ for (const auto &index : ShapeRange(res.getShape()))
+ {
+ res_accessor.at(index) = (lhs_accessor.at(index) < rhs_accessor.at(index));
+ }
+}
+
+void Less(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ if (lhs.getElementType() != rhs.getElementType())
+ {
+ throw std::runtime_error{"Less with different input types is unsupported"};
+ }
+ dispatch<LessImpl>(lhs.getElementType(), lhs, rhs, res);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Less.h b/compiler/mir-interpreter/src/ops/Less.h
new file mode 100644
index 000000000..fa3edd2d0
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Less.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_LESS_
+#define _NNC_CORE_BACKEND_INTERPRETER_LESS_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Less(const mir::TensorVariant &lhs, const mir::TensorVariant &rhs, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_LESS_
diff --git a/compiler/mir-interpreter/src/ops/Max.cpp b/compiler/mir-interpreter/src/ops/Max.cpp
new file mode 100644
index 000000000..eb284c77c
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Max.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Max.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+#include <algorithm>
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct MaxImpl
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res);
+};
+
+template <typename T>
+void MaxImpl<T>::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ TensorVariant broadcasted_lhs(lhs, res.getShape());
+ TensorVariant broadcasted_rhs(rhs, res.getShape());
+ Tensor<T> lhs_accessor(broadcasted_lhs);
+ Tensor<T> rhs_accessor(broadcasted_rhs);
+ Tensor<T> res_accessor(res);
+
+ for (const auto &index : ShapeRange(res.getShape()))
+ {
+ res_accessor.at(index) = std::max(lhs_accessor.at(index), rhs_accessor.at(index));
+ }
+}
+template <> struct MaxImpl<uint8_t>
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+ {
+ throw std::runtime_error{"NYI"};
+ };
+};
+
+void Max(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ if (lhs.getElementType() != rhs.getElementType())
+ {
+ throw std::runtime_error{"Max with different input types is unsupported"};
+ }
+ dispatch<MaxImpl>(lhs.getElementType(), lhs, rhs, res);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Max.h b/compiler/mir-interpreter/src/ops/Max.h
new file mode 100644
index 000000000..b49d0602d
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Max.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_MAX_
+#define _NNC_CORE_BACKEND_INTERPRETER_MAX_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Max(const mir::TensorVariant &lhs, const mir::TensorVariant &rhs, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_MAX_
diff --git a/compiler/mir-interpreter/src/ops/MaxPool2D.cpp b/compiler/mir-interpreter/src/ops/MaxPool2D.cpp
new file mode 100644
index 000000000..cec2f5984
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/MaxPool2D.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MaxPool2D.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+#include <limits>
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct MaxPool2DImpl
+{
+ static void run(const mir::TensorVariant &inputv, const mir::ops::MaxPool2DOp &op,
+ mir::TensorVariant &result);
+};
+
+template <typename T>
+void MaxPool2DImpl<T>::run(const TensorVariant &inputv, const ops::MaxPool2DOp &op,
+ TensorVariant &result)
+{
+ const auto &input_shape = op.getInputShape(0);
+ const auto &output_shape = op.getOutputShape(0);
+ const auto &window_size = op.getWindowSize();
+ const auto &strides = op.getStrides();
+ const auto &padding_before = op.getPaddingBefore();
+ const auto &padding_after = op.getPaddingAfter();
+ (void)padding_after;
+
+ Tensor<T> input(inputv);
+
+ constexpr int num_spatial_dims = 2;
+ assert(input.getShape().rank() == 4);
+ assert(window_size.size() == num_spatial_dims);
+ assert(strides.size() == num_spatial_dims);
+ assert(padding_before.size() == num_spatial_dims);
+ assert(padding_after.size() == num_spatial_dims);
+
+ Tensor<T> res_accessor(result);
+
+ ShapeRange in_range(input_shape);
+ Index in_index(input_shape.rank());
+
+ for (const auto &out_index : ShapeRange(output_shape))
+ {
+ T result = std::numeric_limits<T>::lowest();
+
+ // Assuming NHWC format.
+ in_index.at(0) = out_index.at(0);
+ in_index.at(3) = out_index.at(3);
+
+ for (const auto &window_index : ShapeRange(Shape(window_size)))
+ {
+ // Assuming NHWC format.
+ for (int i = 0; i < num_spatial_dims; ++i)
+ in_index.at(1 + i) =
+ out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i];
+
+ if (in_range.contains(in_index))
+ {
+ result = std::max(result, input.at(in_index));
+ }
+ }
+
+ res_accessor.at(out_index) = result;
+ }
+}
+
+template <> struct MaxPool2DImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &input, const mir::ops::MaxPool2DOp &op,
+ mir::TensorVariant &result);
+};
+
+void MaxPool2DImpl<uint8_t>::run(const TensorVariant &input, const ops::MaxPool2DOp &op,
+ TensorVariant &result)
+{
+ const auto &input_type = input.getType();
+ const auto &output_type = op.getOutput(0)->getType();
+ (void)input_type;
+
+ assert(input_type.isQuantized());
+ assert(output_type.isQuantized());
+ assert(input_type.getElementType() == DataType::UINT8);
+
+ const auto &input_shape = op.getInputShape(0);
+ const auto &output_shape = op.getOutputShape(0);
+ const auto &window_size = op.getWindowSize();
+ const auto &strides = op.getStrides();
+ const auto &padding_before = op.getPaddingBefore();
+ const auto &padding_after = op.getPaddingAfter();
+ (void)padding_after;
+
+ constexpr int num_spatial_dims = 2;
+ assert(input.getShape().rank() == 4);
+ assert(window_size.size() == num_spatial_dims);
+ assert(strides.size() == num_spatial_dims);
+ assert(padding_before.size() == num_spatial_dims);
+ assert(padding_after.size() == num_spatial_dims);
+
+ Tensor<uint8_t> input_accessor(input);
+
+ TensorType res_type(mir::DataType::UINT8, output_shape, output_type.getQuantization());
+ TensorVariant res(res_type);
+ Tensor<uint8_t> res_accessor(res);
+
+ ShapeRange in_range(input_shape);
+ Index in_index(input_shape.rank());
+
+ for (const auto &out_index : ShapeRange(output_shape))
+ {
+ // Assuming NHWC format.
+ in_index.at(0) = out_index.at(0);
+ in_index.at(3) = out_index.at(3);
+
+ uint8_t result = 0;
+ for (const auto &window_index : ShapeRange(Shape(window_size)))
+ {
+ // Assuming NHWC format.
+ for (int i = 0; i < num_spatial_dims; ++i)
+ in_index.at(1 + i) =
+ out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i];
+
+ if (in_range.contains(in_index))
+ {
+ result = std::max(result, input_accessor.at(in_index));
+ }
+ }
+ res_accessor.at(out_index) = result;
+ }
+}
+
+void MaxPool2D(const mir::TensorVariant &input, const mir::ops::MaxPool2DOp &op,
+ mir::TensorVariant &result)
+{
+ dispatch<MaxPool2DImpl>(input.getElementType(), input, op, result);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/MaxPool2D.h b/compiler/mir-interpreter/src/ops/MaxPool2D.h
new file mode 100644
index 000000000..564def207
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/MaxPool2D.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_MAX_POOL_2D_
+#define _NNC_CORE_BACKEND_INTERPRETER_MAX_POOL_2D_
+
+#include "mir/ops/MaxPool2DOp.h"
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void MaxPool2D(const mir::TensorVariant &input, const mir::ops::MaxPool2DOp &op,
+ mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_MAX_POOL_2D_
diff --git a/compiler/mir-interpreter/src/ops/Mul.cpp b/compiler/mir-interpreter/src/ops/Mul.cpp
new file mode 100644
index 000000000..446577c58
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Mul.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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 "Mul.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct MulImpl
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res);
+};
+
+template <typename T>
+void MulImpl<T>::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ TensorVariant broadcasted_lhs(lhs, res.getShape());
+ TensorVariant broadcasted_rhs(rhs, res.getShape());
+ Tensor<T> lhs_accessor(broadcasted_lhs);
+ Tensor<T> rhs_accessor(broadcasted_rhs);
+ Tensor<T> res_accessor(res);
+
+ for (const auto &index : ShapeRange(res.getShape()))
+ {
+ res_accessor.at(index) = lhs_accessor.at(index) * rhs_accessor.at(index);
+ }
+}
+
+template <> struct MulImpl<uint8_t>
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+ {
+ throw std::runtime_error{"NYI"};
+ }
+};
+
+void Mul(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ dispatch<MulImpl>(lhs.getElementType(), lhs, rhs, res);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Mul.h b/compiler/mir-interpreter/src/ops/Mul.h
new file mode 100644
index 000000000..b2e71fa85
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Mul.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_MUL_
+#define _NNC_CORE_BACKEND_INTERPRETER_MUL_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Mul(const mir::TensorVariant &lhs, const mir::TensorVariant &rhs, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_MUL_
diff --git a/compiler/mir-interpreter/src/ops/Pad.cpp b/compiler/mir-interpreter/src/ops/Pad.cpp
new file mode 100644
index 000000000..054a1b68a
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Pad.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Pad.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct PadImpl
+{
+ static void run(const mir::TensorVariant &inputv, const mir::ops::PadOp &op,
+ mir::TensorVariant &result);
+};
+
+template <typename T>
+void PadImpl<T>::run(const TensorVariant &inputv, const ops::PadOp &op, TensorVariant &result)
+{
+ Tensor<T> result_accessor(result);
+ Tensor<T> input(inputv);
+
+ Shape out_shape = result_accessor.getShape();
+
+ ShapeRange out_range(out_shape);
+ const int rank = op.getInputShape(0).rank();
+ const auto &padding_before = op.getPaddingBefore();
+ const auto &padding_after = op.getPaddingAfter();
+
+ Index temp_index;
+ temp_index.resize(rank);
+
+ bool index_on_padding(false);
+ for (const Index &ind : out_range)
+ {
+ index_on_padding = false;
+
+ for (int32_t i = 0; i < rank; i++)
+ {
+ // index on input values
+ if (ind.at(i) >= padding_before[i] && ind.at(i) < out_shape.dim(i) - padding_after[i])
+ {
+ temp_index.at(i) = ind.at(i) - padding_before[i];
+ }
+ else
+ { // not in input
+ index_on_padding = true;
+ break;
+ }
+ }
+ if (index_on_padding)
+ {
+ result_accessor.at(ind) = op.getPaddingValue();
+ }
+ else
+ {
+ result_accessor.at(ind) = input.at(temp_index);
+ }
+ }
+}
+
+void Pad(const mir::TensorVariant &input, const mir::ops::PadOp &op, mir::TensorVariant &result)
+{
+ dispatch<PadImpl>(input.getElementType(), input, op, result);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Pad.h b/compiler/mir-interpreter/src/ops/Pad.h
new file mode 100644
index 000000000..cd72b8afd
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Pad.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_PAD_IMPL_
+#define _NNC_CORE_BACKEND_INTERPRETER_PAD_IMPL_
+
+#include "mir/ops/PadOp.h"
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+/**
+ * @brief Implements PadOp for interpreter backend
+ *
+ * This operation pads a tensor according to the paddings
+ * you specify. For each dimension of input add values
+ * before and after of contents.
+ */
+void Pad(const mir::TensorVariant &input, const mir::ops::PadOp &op, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif // _NNC_CORE_BACKEND_INTERPRETER_PAD_IMPL_
diff --git a/compiler/mir-interpreter/src/ops/Quantization.cpp b/compiler/mir-interpreter/src/ops/Quantization.cpp
new file mode 100644
index 000000000..283a7c751
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Quantization.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Quantization.h"
+#include "mir/Tensor.h"
+#include "mir/ShapeRange.h"
+
+#include <cmath>
+#include <limits>
+
+namespace mir_interpreter
+{
+using namespace mir;
+
+void Dequantize(const TensorVariant &input, TensorVariant &output)
+{
+ const TensorType &input_type = input.getType();
+ assert(input_type.isQuantized());
+ assert(input_type.getElementType() == DataType::UINT8);
+
+ const float scale = input_type.getQuantization().getScale();
+ const int32_t zero_point = input_type.getQuantization().getZeroPoint();
+
+ Tensor<uint8_t> input_accessor(input);
+ Tensor<float> res_accessor(output);
+
+ for (const auto &index : ShapeRange(output.getShape()))
+ {
+ const int32_t value = input_accessor.at(index);
+ res_accessor.at(index) = scale * static_cast<float>(value - zero_point);
+ }
+}
+
+void Quantize(const TensorVariant &input, TensorVariant &output)
+{
+ const TensorType &output_type = output.getType();
+ assert(output_type.isQuantized());
+ assert(input.getElementType() == DataType::FLOAT32);
+
+ const float scale = output_type.getQuantization().getScale();
+ const int32_t zero_point = output_type.getQuantization().getZeroPoint();
+
+ const int32_t min_val = std::numeric_limits<uint8_t>::min();
+ const int32_t max_val = std::numeric_limits<uint8_t>::max();
+
+ Tensor<float> input_accessor(input);
+ Tensor<uint8_t> res_accessor(output);
+
+ for (const auto &index : ShapeRange(output.getShape()))
+ {
+ const float value = input_accessor.at(index);
+ int32_t unclamped = static_cast<int32_t>(std::round(value / scale)) + zero_point;
+ int32_t clamped = std::min(std::max(unclamped, min_val), max_val);
+ res_accessor.at(index) = static_cast<uint8_t>(clamped);
+ }
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Quantization.h b/compiler/mir-interpreter/src/ops/Quantization.h
new file mode 100644
index 000000000..23388d4d8
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Quantization.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_QUANTIZATION_IMPL_
+#define _NNC_CORE_BACKEND_INTERPRETER_QUANTIZATION_IMPL_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+using namespace mir;
+
+void Dequantize(const TensorVariant &input, TensorVariant &output);
+
+void Quantize(const TensorVariant &input, TensorVariant &output);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_QUANTIZATION_IMPL_
diff --git a/compiler/mir-interpreter/src/ops/QuantizationHelpers.h b/compiler/mir-interpreter/src/ops/QuantizationHelpers.h
new file mode 100644
index 000000000..8faeffbd3
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/QuantizationHelpers.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_QUANTIZATION_HELPERS_
+#define _NNC_CORE_BACKEND_INTERPRETER_QUANTIZATION_HELPERS_
+
+#include <cmath>
+#include <limits>
+
+namespace mir_interpreter
+{
+
+inline void QuantizeMultiplier(double double_multiplier, int32_t *quantized_multiplier, int *shift)
+{
+ if (double_multiplier == 0.)
+ {
+ *quantized_multiplier = 0;
+ *shift = 0;
+ return;
+ }
+
+ const double q = std::frexp(double_multiplier, shift);
+ auto q_fixed = static_cast<int64_t>(round(q * (1ll << 31)));
+
+ assert(q_fixed <= (1ll << 31));
+ if (q_fixed == (1ll << 31))
+ {
+ q_fixed /= 2;
+ ++*shift;
+ }
+ assert(q_fixed <= std::numeric_limits<int32_t>::max());
+ // A shift amount smaller than -31 would cause all bits to be shifted out
+ // and thus all results would be zero. We implement that instead with
+ // q_fixed==0, so as to avoid hitting issues with right-shift
+ // operations with shift amounts greater than 31. Note that this happens
+ // roughly when abs(double_multiplier) < 2^-31 and the present handling means
+ // that we're effectively flushing tiny double_multiplier's to zero.
+ // We could conceivably handle values in the range (roughly) [32, 63]
+ // as 'denormals' i.e. (shift==0, q_fixed < 2^30). In that point of view
+ // the present handling is just doing 'flush denormals to zero'. We could
+ // reconsider and actually generate nonzero denormals if a need arises.
+ if (*shift < -31)
+ {
+ *shift = 0;
+ q_fixed = 0;
+ }
+ *quantized_multiplier = static_cast<int32_t>(q_fixed);
+}
+
+inline void QuantizeMultiplierSmallerThanOneExp(double double_multiplier,
+ int32_t *quantized_multiplier, int *left_shift)
+{
+ assert(double_multiplier < 1.0);
+ assert(double_multiplier > 0.0);
+ int shift;
+ QuantizeMultiplier(double_multiplier, quantized_multiplier, &shift);
+ assert(shift <= 0);
+ *left_shift = shift;
+}
+
+inline int32_t MaskIfNonZero(int32_t a)
+{
+ static const int32_t zero = 0;
+ return a ? ~zero : zero;
+}
+
+inline int32_t MaskIfZero(int32_t a) { return MaskIfNonZero(!a); }
+
+inline int32_t MaskIfLessThan(int32_t a, int32_t b) { return MaskIfNonZero(a < b); }
+
+inline int32_t MaskIfGreaterThan(int32_t a, int32_t b) { return MaskIfNonZero(a > b); }
+
+inline int32_t RoundingDivideByPOT(int32_t x, int exponent)
+{
+ assert(exponent >= 0);
+ assert(exponent <= 31);
+ const int32_t mask = (1ll << exponent) - 1;
+ const int32_t remainder = x & mask;
+ const int32_t threshold = (mask >> 1) + (MaskIfLessThan(x, 0) & 1);
+ return (x >> exponent) + (MaskIfGreaterThan(remainder, threshold) & 1);
+}
+
+inline std::int32_t SaturatingRoundingDoublingHighMul(std::int32_t a, std::int32_t b)
+{
+ bool overflow = a == b && a == std::numeric_limits<std::int32_t>::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<std::int32_t>((ab_64 + nudge) / (1ll << 31));
+ return overflow ? std::numeric_limits<std::int32_t>::max() : ab_x2_high32;
+}
+
+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);
+}
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_QUANTIZATION_HELPERS_
diff --git a/compiler/mir-interpreter/src/ops/ReLU.cpp b/compiler/mir-interpreter/src/ops/ReLU.cpp
new file mode 100644
index 000000000..92d3ded5e
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/ReLU.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReLU.h"
+#include "Common.h"
+
+#include <mir/ShapeRange.h>
+#include <mir/Tensor.h>
+
+#include <algorithm>
+
+namespace mir_interpreter
+{
+
+template <typename T> struct ReLUImpl
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result);
+};
+
+template <typename T>
+void ReLUImpl<T>::run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+{
+ mir::Tensor<T> arg_accessor(arg);
+ mir::Tensor<T> res_accessor(result);
+
+ for (const auto &index : mir::ShapeRange(result.getShape()))
+ {
+ res_accessor.at(index) = std::max(arg_accessor.at(index), static_cast<T>(0));
+ }
+}
+
+template <> struct ReLUImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+ {
+ throw std::runtime_error{"NYI"};
+ }
+};
+
+void ReLU(const mir::TensorVariant &arg, mir::TensorVariant &result)
+{
+ dispatch<ReLUImpl>(arg.getElementType(), arg, result);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/ReLU.h b/compiler/mir-interpreter/src/ops/ReLU.h
new file mode 100644
index 000000000..9edabb9d9
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/ReLU.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_RELU_
+#define _NNC_CORE_BACKEND_INTERPRETER_RELU_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void ReLU(const mir::TensorVariant &arg, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_RELU_
diff --git a/compiler/mir-interpreter/src/ops/ReduceMean.cpp b/compiler/mir-interpreter/src/ops/ReduceMean.cpp
new file mode 100644
index 000000000..ebaa3b48f
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/ReduceMean.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_REDUCE_MEAN_
+#define _NNC_CORE_BACKEND_INTERPRETER_REDUCE_MEAN_
+
+#include "ReduceMean.h"
+#include "Common.h"
+
+#include "mir/ops/ReduceMeanOp.h"
+#include "mir/Tensor.h"
+#include "mir/ShapeRange.h"
+
+namespace mir_interpreter
+{
+
+template <typename T> struct ReduceMeanImpl
+{
+ static void run(const mir::TensorVariant &inputv, const mir::ops::ReduceMeanOp &op,
+ mir::TensorVariant &output);
+};
+
+template <typename T>
+void ReduceMeanImpl<T>::run(const mir::TensorVariant &inputv, const mir::ops::ReduceMeanOp &op,
+ mir::TensorVariant &output)
+{
+ const auto &input_shape = op.getInputShape(0);
+ const auto &output_shape = op.getOutputShape(0);
+ const auto &reduction_dims = op.getReductionDims();
+ const bool keep_dims = op.getKeepDims();
+
+ const auto reductor = [](T result, T x) { return result + x; };
+
+ mir::Tensor<T> input(inputv);
+ mir::Tensor<T> res_accessor(output);
+
+ erase<T>(output);
+
+ // This mask contains 'true' for dimensions that should be reduced. For example, if we want
+ // to reduce dimensions 1 and 3 with total number of dimensions of 4, the mask will be
+ // [false, true, false, true].
+ std::vector<bool> reduction_dims_mask(input_shape.rank(), false);
+ for (const int dim : reduction_dims)
+ {
+ reduction_dims_mask[dim] = true;
+ }
+
+ mir::Index out_index(output_shape.rank());
+ for (const mir::Index &in_index : mir::ShapeRange(input_shape))
+ {
+ int out_index_dim = 0;
+ for (int dim = 0; dim < input_shape.rank(); ++dim)
+ {
+ if (keep_dims)
+ {
+ out_index.at(out_index_dim++) = reduction_dims_mask[dim] ? 0 : in_index.at(dim);
+ }
+ else
+ {
+ if (!reduction_dims_mask[dim])
+ {
+ out_index.at(out_index_dim++) = in_index.at(dim);
+ }
+ }
+ }
+ res_accessor.at(out_index) = reductor(res_accessor.at(out_index), input.at(in_index));
+ }
+
+ const std::int32_t reduction_factor = input_shape.numElements() / output_shape.numElements();
+
+ for (const auto &index : mir::ShapeRange(output_shape))
+ {
+ res_accessor.at(index) /= reduction_factor;
+ }
+}
+
+void ReduceMean(const mir::TensorVariant &input, const mir::ops::ReduceMeanOp &op,
+ mir::TensorVariant &output)
+{
+ dispatch<ReduceMeanImpl>(input.getElementType(), input, op, output);
+};
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_REDUCE_MEAN_
diff --git a/compiler/mir-interpreter/src/ops/ReduceMean.h b/compiler/mir-interpreter/src/ops/ReduceMean.h
new file mode 100644
index 000000000..178563b2c
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/ReduceMean.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_REDUCE_MEAN_
+#define _NNC_CORE_BACKEND_INTERPRETER_REDUCE_MEAN_
+
+#include "mir/ops/ReduceMeanOp.h"
+
+namespace mir_interpreter
+{
+
+void ReduceMean(const mir::TensorVariant &input, const mir::ops::ReduceMeanOp &op,
+ mir::TensorVariant &output);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_REDUCE_MEAN_
diff --git a/compiler/mir-interpreter/src/ops/Reshape.cpp b/compiler/mir-interpreter/src/ops/Reshape.cpp
new file mode 100644
index 000000000..f29b261ce
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Reshape.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Reshape.h"
+
+#include "mir/ShapeRange.h"
+
+#include <cstring>
+
+namespace mir_interpreter
+{
+
+void Reshape(const mir::TensorVariant &input, mir::TensorVariant &output)
+{
+ assert(input.getShape().numElements() == output.getShape().numElements());
+
+ mir::ShapeRange input_range(input.getShape());
+ auto in_iter = input_range.begin();
+ const size_t elem_size = input.getElementSize();
+
+ for (const auto &out_index : mir::ShapeRange(output.getShape()))
+ std::memcpy(output.at(out_index), input.at(*in_iter++), elem_size);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Reshape.h b/compiler/mir-interpreter/src/ops/Reshape.h
new file mode 100644
index 000000000..2da6411f6
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Reshape.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_RESHAPE_IMPL_
+#define _NNC_CORE_BACKEND_INTERPRETER_RESHAPE_IMPL_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Reshape(const mir::TensorVariant &input, mir::TensorVariant &output);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_RESHAPE_IMPL_
diff --git a/compiler/mir-interpreter/src/ops/Sigmoid.cpp b/compiler/mir-interpreter/src/ops/Sigmoid.cpp
new file mode 100644
index 000000000..23718f935
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Sigmoid.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Sigmoid.h"
+#include "Common.h"
+
+#include <mir/ShapeRange.h>
+#include <mir/Tensor.h>
+
+#include <cmath>
+
+namespace mir_interpreter
+{
+
+template <typename T> struct SigmoidImpl
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result);
+};
+
+template <typename T>
+void SigmoidImpl<T>::run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+{
+ mir::Tensor<T> arg_accessor(arg);
+ mir::Tensor<T> res_accessor(result);
+
+ for (const auto &index : mir::ShapeRange(result.getShape()))
+ {
+ res_accessor.at(index) = 1.0f / (1.0f + std::exp(-arg_accessor.at(index)));
+ }
+}
+
+template <> struct SigmoidImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+ {
+ throw std::runtime_error{"NYI"};
+ }
+};
+
+void Sigmoid(const mir::TensorVariant &arg, mir::TensorVariant &result)
+{
+ dispatch<SigmoidImpl>(arg.getElementType(), arg, result);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Sigmoid.h b/compiler/mir-interpreter/src/ops/Sigmoid.h
new file mode 100644
index 000000000..81c614c89
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Sigmoid.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_SIGMOID_
+#define _NNC_CORE_BACKEND_INTERPRETER_SIGMOID_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void Sigmoid(const mir::TensorVariant &arg, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_SIGMOID_
diff --git a/compiler/mir-interpreter/src/ops/Slice.cpp b/compiler/mir-interpreter/src/ops/Slice.cpp
new file mode 100644
index 000000000..df24d49cd
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Slice.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Slice.h"
+
+#include "Fill.h"
+#include "Common.h"
+
+#include "mir/Tensor.h"
+#include "mir/ShapeRange.h"
+
+namespace mir_interpreter
+{
+
+template <typename T> struct SliceImpl
+{
+ static void run(const mir::TensorVariant &arg, const mir::Shape &starts, mir::TensorVariant &res);
+};
+
+template <typename T>
+void SliceImpl<T>::run(const mir::TensorVariant &arg, const mir::Shape &starts,
+ mir::TensorVariant &res)
+{
+ mir::Tensor<T> input(arg);
+ mir::Tensor<T> output(res);
+
+ for (auto id : mir::ShapeRange(res.getShape()))
+ {
+ mir::Index idx = mir_interpreter::shift(id, starts);
+ output.at(id) = input.at(idx);
+ }
+}
+
+void Slice(const mir::TensorVariant &arg, const mir::Shape &starts, mir::TensorVariant &res)
+{
+ dispatch<SliceImpl>(arg.getElementType(), arg, starts, res);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Slice.h b/compiler/mir-interpreter/src/ops/Slice.h
new file mode 100644
index 000000000..9e5e3bb0e
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Slice.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_SLICE_
+#define _NNC_CORE_BACKEND_INTERPRETER_SLICE_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Slice(const mir::TensorVariant &arg, const mir::Shape &starts, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_DIV_
diff --git a/compiler/mir-interpreter/src/ops/Softmax.cpp b/compiler/mir-interpreter/src/ops/Softmax.cpp
new file mode 100644
index 000000000..f263f967d
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Softmax.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2019 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 "Softmax.h"
+#include "Common.h"
+#include "QuantizationHelpers.h"
+
+#include <mir/ShapeRange.h>
+#include <mir/Tensor.h>
+
+#include <cmath>
+
+namespace mir_interpreter
+{
+
+static inline void PopulateSoftmaxLookupTable(float *table, float input_scale, float beta)
+{
+ const float scale = -input_scale * beta;
+ const int32_t max_uint8 = std::numeric_limits<uint8_t>::max();
+ for (int32_t val = 0; val <= max_uint8; ++val)
+ table[max_uint8 - val] = expf(scale * val);
+}
+
+template <typename T> struct SoftmaxImpl
+{
+ static void run(const mir::TensorVariant &arg, int axis, mir::TensorVariant &result);
+};
+
+template <typename T>
+void SoftmaxImpl<T>::run(const mir::TensorVariant &arg, int axis, mir::TensorVariant &result)
+{
+ mir::Tensor<T> arg_accessor(arg);
+ mir::Tensor<T> res_accessor(result);
+
+ mir::Shape expsum_shape = arg.getShape();
+ expsum_shape.dim(axis) = 1;
+ mir::TensorType expsum_type(arg.getElementType(), expsum_shape);
+ mir::TensorVariant expsum(expsum_type);
+ mir::Tensor<T> expsum_accessor(expsum);
+
+ for (const auto &expsum_index : mir::ShapeRange(expsum_shape))
+ {
+ T sum = 0;
+ mir::Index arg_index = expsum_index;
+ std::int32_t axis_size = arg.getShape().dim(axis);
+ for (std::int32_t i = 0; i < axis_size; ++i)
+ {
+ arg_index.at(axis) = i;
+ sum += std::exp(arg_accessor.at(arg_index));
+ }
+ expsum_accessor.at(expsum_index) = sum;
+ }
+
+ for (const auto &res_index : mir::ShapeRange(result.getShape()))
+ {
+ mir::Index expsum_index = res_index;
+ expsum_index.at(axis) = 0;
+ res_accessor.at(res_index) =
+ std::exp(arg_accessor.at(res_index)) / expsum_accessor.at(expsum_index);
+ }
+}
+
+template <> struct SoftmaxImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &input, int axis, mir::TensorVariant &output);
+};
+
+void SoftmaxImpl<uint8_t>::run(const mir::TensorVariant &input, int axis,
+ mir::TensorVariant &output)
+{
+ const auto &input_type = input.getType();
+ const auto &output_type = output.getType();
+
+ assert(input_type.isQuantized());
+ assert(output_type.isQuantized());
+
+ const auto input_shape = input_type.getShape();
+
+ assert(input_type.getElementType() == mir::DataType::UINT8);
+ assert(axis == input_shape.rank() - 1); // supported only last dim axis
+ (void)axis;
+
+ double input_scale = input_type.getQuantization().getScale();
+ double output_scale = output_type.getQuantization().getScale();
+
+ const int trailing_dim = input_shape.rank() - 1;
+ int excluding_last_dim = 1;
+ for (int32_t i = 0; i < input_shape.rank() - 1; i++)
+ {
+ excluding_last_dim *= input_shape.dim(i);
+ }
+ const int last_dim = input_shape.dim(trailing_dim);
+
+ const int32_t clamp_max = std::numeric_limits<uint8_t>::max();
+ const int32_t clamp_min = std::numeric_limits<uint8_t>::min();
+
+ uint8_t *input_data = reinterpret_cast<uint8_t *>(input.atOffset(0));
+
+ float table[256];
+ PopulateSoftmaxLookupTable(table, input_scale, 1.f);
+
+ uint8_t *output_data = reinterpret_cast<uint8_t *>(output.atOffset(0));
+
+ for (int i = 0; i < excluding_last_dim; ++i)
+ {
+ int32_t max_val = std::numeric_limits<uint8_t>::min();
+ // Find max quantized value.
+ for (int j = 0; j < last_dim; ++j)
+ {
+ max_val = std::max(max_val, static_cast<int32_t>(input_data[j]));
+ }
+
+ float sum_exp = 0.0f;
+ const int32_t max_uint8 = std::numeric_limits<uint8_t>::max();
+ const float *table_offset = &table[max_uint8 - max_val];
+ // Calculate normalizer sum(exp(x)).
+ for (int j = 0; j < last_dim; ++j)
+ {
+ sum_exp += table_offset[input_data[j]];
+ }
+
+ const float inv_sum_exp = 1.0f / (sum_exp * output_scale);
+ // Normalize and quantize probabilities.
+ for (int j = 0; j < last_dim; ++j)
+ {
+ const float prob_rescaled = table_offset[input_data[j]] * inv_sum_exp;
+ const int32_t prob_quantized = static_cast<int32_t>(prob_rescaled + 0.5);
+ output_data[j] =
+ static_cast<uint8_t>(std::max(std::min(clamp_max, prob_quantized), clamp_min));
+ }
+ input_data += last_dim;
+ output_data += last_dim;
+ }
+}
+
+void Softmax(const mir::TensorVariant &arg, int axis, mir::TensorVariant &result)
+{
+ dispatch<SoftmaxImpl>(arg.getElementType(), arg, axis, result);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Softmax.h b/compiler/mir-interpreter/src/ops/Softmax.h
new file mode 100644
index 000000000..9c9818c70
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Softmax.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_SOFTMAX_
+#define _NNC_CORE_BACKEND_INTERPRETER_SOFTMAX_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void Softmax(const mir::TensorVariant &arg, int axis, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_SOFTMAX_
diff --git a/compiler/mir-interpreter/src/ops/Sqrt.cpp b/compiler/mir-interpreter/src/ops/Sqrt.cpp
new file mode 100644
index 000000000..7a2ca49c8
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Sqrt.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Sqrt.h"
+#include "Common.h"
+
+#include <mir/ShapeRange.h>
+#include <mir/Tensor.h>
+
+#include <cmath>
+
+namespace mir_interpreter
+{
+
+template <typename T> struct SqrtImpl
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result);
+};
+
+template <typename T>
+void SqrtImpl<T>::run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+{
+ mir::Tensor<T> arg_accessor(arg);
+ mir::Tensor<T> res_accessor(result);
+
+ for (const auto &index : mir::ShapeRange(result.getShape()))
+ {
+ res_accessor.at(index) = std::sqrt(arg_accessor.at(index));
+ }
+}
+
+template <> struct SqrtImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+ {
+ throw std::runtime_error{"NYI"};
+ }
+};
+
+void Sqrt(const mir::TensorVariant &arg, mir::TensorVariant &result)
+{
+ dispatch<SqrtImpl>(arg.getElementType(), arg, result);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Sqrt.h b/compiler/mir-interpreter/src/ops/Sqrt.h
new file mode 100644
index 000000000..fef2bf0fe
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Sqrt.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_SQRT_
+#define _NNC_CORE_BACKEND_INTERPRETER_SQRT_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void Sqrt(const mir::TensorVariant &arg, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_SQRT_
diff --git a/compiler/mir-interpreter/src/ops/Sub.cpp b/compiler/mir-interpreter/src/ops/Sub.cpp
new file mode 100644
index 000000000..6c03cff82
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Sub.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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 "Sub.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct SubImpl
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res);
+};
+
+template <typename T>
+void SubImpl<T>::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ TensorVariant broadcasted_lhs(lhs, res.getShape());
+ TensorVariant broadcasted_rhs(rhs, res.getShape());
+ Tensor<T> lhs_accessor(broadcasted_lhs);
+ Tensor<T> rhs_accessor(broadcasted_rhs);
+ Tensor<T> res_accessor(res);
+
+ for (const auto &index : ShapeRange(res.getShape()))
+ {
+ res_accessor.at(index) = lhs_accessor.at(index) - rhs_accessor.at(index);
+ }
+}
+
+template <> struct SubImpl<uint8_t>
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+ {
+ throw std::runtime_error{"NYI"};
+ }
+};
+
+void Sub(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ dispatch<SubImpl>(lhs.getElementType(), lhs, rhs, res);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Sub.h b/compiler/mir-interpreter/src/ops/Sub.h
new file mode 100644
index 000000000..53991596f
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Sub.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_SUB_
+#define _NNC_CORE_BACKEND_INTERPRETER_SUB_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Sub(const mir::TensorVariant &lhs, const mir::TensorVariant &rhs, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_SUB_
diff --git a/compiler/mir-interpreter/src/ops/Tanh.cpp b/compiler/mir-interpreter/src/ops/Tanh.cpp
new file mode 100644
index 000000000..49a3461bf
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Tanh.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Sigmoid.h"
+#include "Common.h"
+
+#include <mir/ShapeRange.h>
+#include <mir/Tensor.h>
+
+#include <cmath>
+
+namespace mir_interpreter
+{
+
+template <typename T> struct TanhImpl
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result);
+};
+
+template <typename T>
+void TanhImpl<T>::run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+{
+ mir::Tensor<T> arg_accessor(arg);
+ mir::Tensor<T> res_accessor(result);
+
+ for (const auto &index : mir::ShapeRange(result.getShape()))
+ {
+ res_accessor.at(index) = std::tanh(arg_accessor.at(index));
+ }
+}
+
+template <> struct TanhImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+ {
+ throw std::runtime_error{"NYI"};
+ }
+};
+
+void Tanh(const mir::TensorVariant &arg, mir::TensorVariant &result)
+{
+ dispatch<TanhImpl>(arg.getElementType(), arg, result);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Tanh.h b/compiler/mir-interpreter/src/ops/Tanh.h
new file mode 100644
index 000000000..2f376f5bd
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Tanh.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_TANH_
+#define _NNC_CORE_BACKEND_INTERPRETER_TANH_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void Tanh(const mir::TensorVariant &arg, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_TANH_
diff --git a/compiler/mir-interpreter/src/ops/Transpose.cpp b/compiler/mir-interpreter/src/ops/Transpose.cpp
new file mode 100644
index 000000000..1f0ad56c3
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Transpose.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 "Transpose.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+#include "Common.h"
+
+namespace mir_interpreter
+{
+
+template <typename T> struct TransposeImpl
+{
+ static void run(const mir::TensorVariant &input, const mir::ops::TransposeOp &op,
+ mir::TensorVariant &output);
+};
+
+template <typename T>
+void TransposeImpl<T>::run(const mir::TensorVariant &inputv, const mir::ops::TransposeOp &op,
+ mir::TensorVariant &outputv)
+{
+ const auto &output_shape = op.getOutputShape(0);
+ const auto &axis_order = op.getAxisOrder();
+ const int32_t num_axis = static_cast<int32_t>(axis_order.size());
+ assert(num_axis == inputv.getShape().rank());
+ assert(num_axis == output_shape.rank());
+
+ mir::Index output_index;
+ output_index.resize(num_axis);
+
+ mir::Tensor<T> input(inputv);
+ mir::Tensor<T> output(outputv);
+
+ for (auto &input_index : mir::ShapeRange(input.getShape()))
+ {
+ for (int32_t i = 0; i < num_axis; i++)
+ output_index.at(i) = input_index.at(axis_order[i]);
+
+ output.at(output_index) = input.at(input_index);
+ }
+}
+
+void Transpose(const mir::TensorVariant &input, const mir::ops::TransposeOp &op,
+ mir::TensorVariant &output)
+{
+ dispatch<TransposeImpl>(input.getElementType(), input, op, output);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Transpose.h b/compiler/mir-interpreter/src/ops/Transpose.h
new file mode 100644
index 000000000..f60ed7295
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Transpose.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NNC_CORE_BACKEND_INTERPRETER_TRANSPOSE_
+#define _NNC_CORE_BACKEND_INTERPRETER_TRANSPOSE_
+
+#include "mir/TensorVariant.h"
+#include "mir/ops/TransposeOp.h"
+
+namespace mir_interpreter
+{
+
+void Transpose(const mir::TensorVariant &input, const mir::ops::TransposeOp &op,
+ mir::TensorVariant &output);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_TRANSPOSE_
diff --git a/compiler/mir-onnx-importer/AttributeHelpers.h b/compiler/mir-onnx-importer/AttributeHelpers.h
deleted file mode 100644
index d5cc1501a..000000000
--- a/compiler/mir-onnx-importer/AttributeHelpers.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_ATTRIBUTE_HELPERS_H
-#define MIR_ONNX_ATTRIBUTE_HELPERS_H
-
-#include "onnx/onnx.pb.h"
-
-#include <algorithm>
-#include <cassert>
-#include <cstddef>
-#include <cstdint>
-#include <string>
-#include <utility>
-#include <vector>
-
-namespace mir_onnx
-{
-
-template <typename T> T getAttributeValue(const onnx::AttributeProto &attribute) = delete;
-
-template <> inline float getAttributeValue(const onnx::AttributeProto &attribute)
-{
- assert(attribute.type() == onnx::AttributeProto::FLOAT);
- return attribute.f();
-}
-
-template <> inline std::int64_t getAttributeValue(const onnx::AttributeProto &attribute)
-{
- assert(attribute.type() == onnx::AttributeProto::INT);
- return attribute.i();
-}
-
-template <> inline std::string getAttributeValue(const onnx::AttributeProto &attribute)
-{
- assert(attribute.type() == onnx::AttributeProto::STRING);
- return attribute.s();
-}
-
-template <> inline onnx::TensorProto getAttributeValue(const onnx::AttributeProto &attribute)
-{
- assert(attribute.type() == onnx::AttributeProto::TENSOR);
- return attribute.t();
-}
-
-template <>
-inline std::vector<std::int32_t> getAttributeValue(const onnx::AttributeProto &attribute)
-{
- assert(attribute.type() == onnx::AttributeProto::INTS);
- // TODO Check that values fit.
- return {attribute.ints().cbegin(), attribute.ints().cend()};
-}
-
-template <>
-inline std::vector<std::int64_t> getAttributeValue(const onnx::AttributeProto &attribute)
-{
- assert(attribute.type() == onnx::AttributeProto::INTS);
- return {attribute.ints().cbegin(), attribute.ints().cend()};
-}
-
-inline const onnx::AttributeProto *findAttribute(const onnx::NodeProto &node,
- const std::string &name)
-{
- const auto &attributes = node.attribute();
- const auto it = std::find_if(
- attributes.cbegin(), attributes.cend(),
- [&name](const onnx::AttributeProto &attribute) { return attribute.name() == name; });
- if (it == attributes.cend())
- return nullptr;
- return &*it;
-}
-
-template <typename T> T getAttributeValue(const onnx::NodeProto &node, const std::string &name)
-{
- const auto *attribute = findAttribute(node, name);
- if (attribute == nullptr)
- throw std::runtime_error("Cannot find attribute '" + name + "' in node '" + node.name() + "'.");
- return getAttributeValue<T>(*attribute);
-}
-
-template <typename T>
-T getAttributeValue(const onnx::NodeProto &node, const std::string &name, T default_value)
-{
- const auto *attribute = findAttribute(node, name);
- if (attribute == nullptr)
- return std::move(default_value);
- return getAttributeValue<T>(*attribute);
-}
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_ATTRIBUTE_HELPERS_H
diff --git a/compiler/mir-onnx-importer/CMakeLists.txt b/compiler/mir-onnx-importer/CMakeLists.txt
deleted file mode 100644
index 20b7c3489..000000000
--- a/compiler/mir-onnx-importer/CMakeLists.txt
+++ /dev/null
@@ -1,90 +0,0 @@
-nncc_find_package(ONNXSource EXACT 1.5.0 QUIET)
-nncc_find_package(Protobuf QUIET)
-
-if (NOT ONNXSource_FOUND)
- return()
-endif ()
-
-if (NOT Protobuf_FOUND)
- return()
-endif ()
-
-Protobuf_Generate(MIR_ONNX_PROTO
- ${CMAKE_CURRENT_BINARY_DIR}/generated
- ${ONNXSource_DIR}
- onnx/onnx.proto)
-
-add_library(mir_onnx_proto STATIC ${MIR_ONNX_PROTO_SOURCES})
-set_target_properties(mir_onnx_proto PROPERTIES POSITION_INDEPENDENT_CODE ON)
-target_include_directories(mir_onnx_proto PUBLIC ${MIR_ONNX_PROTO_INCLUDE_DIRS})
-target_link_libraries(mir_onnx_proto PUBLIC libprotobuf)
-
-set(MIR_ONNX_IMPORTER_SOURCES
- AttributeHelpers.h
- ConvPoolHelpers.cpp
- ConvPoolHelpers.h
- ONNXHelpers.cpp
- ONNXHelpers.h
- ONNXImporterImpl.cpp
- ONNXImporterImpl.h
- ONNXNodeConverterRegistry.h
- ONNXOpRegistration.h
- Op/Add.cpp
- Op/Add.h
- Op/AveragePool.cpp
- Op/AveragePool.h
- Op/BatchNormalization.cpp
- Op/BatchNormalization.h
- Op/Concat.cpp
- Op/Concat.h
- Op/Constant.cpp
- Op/Constant.h
- Op/Conv.cpp
- Op/Conv.h
- Op/Dropout.cpp
- Op/Dropout.h
- Op/Flatten.cpp
- Op/Flatten.h
- Op/Gather.cpp
- Op/Gather.h
- Op/Gemm.cpp
- Op/Gemm.h
- Op/Identity.cpp
- Op/Identity.h
- Op/MatMul.cpp
- Op/MatMul.h
- Op/GlobalAveragePool.cpp
- Op/GlobalAveragePool.h
- Op/Max.cpp
- Op/Max.h
- Op/MaxPool.cpp
- Op/MaxPool.h
- Op/Mul.cpp
- Op/Mul.h
- Op/Pad.cpp
- Op/Pad.h
- Op/ReduceMean.cpp
- Op/ReduceMean.h
- Op/Relu.cpp
- Op/Relu.h
- Op/Reshape.cpp
- Op/Reshape.h
- Op/Shape.cpp
- Op/Shape.h
- Op/Sigmoid.cpp
- Op/Sigmoid.h
- Op/Softmax.cpp
- Op/Softmax.h
- Op/Sum.cpp
- Op/Sum.h
- Op/Transpose.cpp
- Op/Transpose.h
- Op/Unsqueeze.cpp
- Op/Unsqueeze.h
- Op/Upsample.cpp
- Op/Upsample.h)
-
-add_library(mir_onnx_importer STATIC ${MIR_ONNX_IMPORTER_SOURCES})
-set_target_properties(mir_onnx_importer PROPERTIES POSITION_INDEPENDENT_CODE ON)
-target_include_directories(mir_onnx_importer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
-target_link_libraries(mir_onnx_importer PUBLIC mir_onnx_proto mir PRIVATE stdex)
diff --git a/compiler/mir-onnx-importer/ConvPoolHelpers.cpp b/compiler/mir-onnx-importer/ConvPoolHelpers.cpp
deleted file mode 100644
index 367aeff4a..000000000
--- a/compiler/mir-onnx-importer/ConvPoolHelpers.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ConvPoolHelpers.h"
-
-#include <algorithm>
-#include <cassert>
-
-namespace mir_onnx
-{
-
-void inferAutoPadding(const std::string &pad_type, const mir::Shape &input_shape,
- const std::vector<std::int32_t> &dilations,
- const std::vector<std::int32_t> &strides,
- const std::vector<std::int32_t> &window_size,
- std::vector<std::int32_t> &padding_before,
- std::vector<std::int32_t> &padding_after)
-{
- constexpr int num_spatial_dims = 2;
-
- if (pad_type == "NOTSET")
- {
- // Do nothing.
- }
- else if (pad_type == "VALID")
- {
- padding_before.assign(num_spatial_dims, 0);
- padding_after.assign(num_spatial_dims, 0);
- }
- else
- {
- padding_before.resize(num_spatial_dims);
- padding_after.resize(num_spatial_dims);
-
- assert(dilations.size() == num_spatial_dims);
- assert(strides.size() == num_spatial_dims);
- assert(window_size.size() == num_spatial_dims);
-
- for (int i = 0; i < num_spatial_dims; ++i)
- {
- const std::int32_t eff_window_size = (window_size[i] - 1) * dilations[i] + 1;
- // Assuming input has NCHW format.
- const std::int32_t residual = input_shape.dim(2 + i) % strides[i];
- const std::int32_t total_pad = std::max(
- INT32_C(0), residual == 0 ? eff_window_size - strides[i] : eff_window_size - residual);
- if (pad_type == "SAME_UPPER")
- {
- padding_before[i] = total_pad / 2;
- padding_after[i] = (total_pad + 1) / 2;
- }
- else
- {
- assert(pad_type == "SAME_LOWER");
- padding_before[i] = (total_pad + 1) / 2;
- padding_after[i] = total_pad / 2;
- }
- }
- }
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/ConvPoolHelpers.h b/compiler/mir-onnx-importer/ConvPoolHelpers.h
deleted file mode 100644
index fbe913381..000000000
--- a/compiler/mir-onnx-importer/ConvPoolHelpers.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_CONV_POOL_HELPERS_H
-#define MIR_ONNX_CONV_POOL_HELPERS_H
-
-#include "mir/Shape.h"
-
-#include <cstdint>
-#include <string>
-#include <vector>
-
-namespace mir_onnx
-{
-
-void inferAutoPadding(const std::string &pad_type, const mir::Shape &input_shape,
- const std::vector<std::int32_t> &dilations,
- const std::vector<std::int32_t> &strides,
- const std::vector<std::int32_t> &window_size,
- std::vector<std::int32_t> &padding_before,
- std::vector<std::int32_t> &padding_after);
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_CONV_POOL_HELPERS_H
diff --git a/compiler/mir-onnx-importer/ONNXHelpers.cpp b/compiler/mir-onnx-importer/ONNXHelpers.cpp
deleted file mode 100644
index 6059d3a94..000000000
--- a/compiler/mir-onnx-importer/ONNXHelpers.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ONNXHelpers.h"
-#include "AttributeHelpers.h"
-
-#include "mir/ShapeRange.h"
-
-namespace mir_onnx
-{
-
-mir::TensorVariant fixGroupedKernel(int groups, const mir::TensorVariant &folded_kernel)
-{
- const int kernel_in_chan_num = 2;
- const int kernel_out_chan_num = 3;
-
- const mir::Shape &kernel_shape = folded_kernel.getShape();
- auto kernel_in_channels = kernel_shape.dim(kernel_in_chan_num);
- auto kernel_out_channels = kernel_shape.dim(kernel_out_chan_num);
- auto in_channels = kernel_in_channels * groups;
-
- // Original kernel has shape [H, W, inputChannels/groups, outputChannels]
- // here creates unfolded kernel with shape [H, W, inputChannels, outputChannels]
- mir::Shape unfold_kernel_shape(kernel_shape);
- unfold_kernel_shape.dim(kernel_in_chan_num) = in_channels;
- size_t data_size = folded_kernel.getElementSize();
- mir::TensorVariant unfold_kernel(folded_kernel.getDataType(), unfold_kernel_shape);
-
- int in_group_size = kernel_in_channels;
- int out_group_size = kernel_out_channels / groups;
- assert(kernel_out_channels % groups == 0);
-
- // Iterate over "unfolded" kernel Shape and insert appropriate values into result kernel
- for (const mir::Index &idx : mir::ShapeRange(unfold_kernel_shape))
- {
- auto in_group_no = idx.at(kernel_in_chan_num) / in_group_size;
- auto out_group_no = idx.at(kernel_out_chan_num) / out_group_size;
- // check that input channel group fits output channel group
- if (in_group_no == out_group_no)
- {
- // compute index in original kernel that corresponds output index
- mir::Index folded_idx(idx);
- folded_idx.at(kernel_in_chan_num) %= in_group_size;
-
- std::copy(folded_kernel.at(folded_idx), folded_kernel.at(folded_idx) + data_size,
- unfold_kernel.at(idx));
- }
- else
- {
- // fill element of output kernel with zero element
- assert(folded_kernel.getDataType() == mir::DataType::FLOAT32 &&
- "unsupported data type, add appropriate zero element creation");
- auto elem = reinterpret_cast<float *>(unfold_kernel.at(idx));
- *elem = 0.0f;
- }
- }
- return unfold_kernel;
-}
-
-mir::TensorVariant createTensor(const onnx::TensorProto *tensor)
-{
- mir::DataType type;
- const void *src_data;
- mir::Shape shape(tensor->dims_size());
- for (int i = 0; i < tensor->dims_size(); ++i)
- {
- shape.dim(i) = tensor->dims(i);
- }
-
- if (tensor->float_data_size() != 0)
- {
- assert(tensor->data_type() == onnx::TensorProto::FLOAT);
- type = mir::DataType::FLOAT32;
- src_data = tensor->float_data().data();
- }
- else if (tensor->double_data_size() != 0)
- {
- assert(tensor->data_type() == onnx::TensorProto::DOUBLE);
- type = mir::DataType::FLOAT64;
- src_data = tensor->double_data().data();
- }
- else if (tensor->int32_data_size() != 0)
- {
- assert(tensor->data_type() == onnx::TensorProto::INT32);
- type = mir::DataType::INT32;
- src_data = tensor->int32_data().data();
- }
- else if (tensor->int64_data_size() != 0)
- {
- assert(tensor->data_type() == onnx::TensorProto::INT64);
- type = mir::DataType::INT64;
- src_data = tensor->int64_data().data();
- }
- else if (tensor->has_raw_data())
- {
- switch (tensor->data_type())
- {
- case onnx::TensorProto::FLOAT:
- type = mir::DataType::FLOAT32;
- break;
- case onnx::TensorProto::INT64:
- type = mir::DataType::INT64;
- break;
- default:
- throw std::runtime_error("Unsupported data type");
- }
- src_data = tensor->raw_data().data();
- }
- else
- {
- throw std::runtime_error("Invalid data in Proto file, investigate");
- }
-
- return mir::TensorVariant(type, shape, src_data);
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/ONNXHelpers.h b/compiler/mir-onnx-importer/ONNXHelpers.h
deleted file mode 100644
index 1784a4c15..000000000
--- a/compiler/mir-onnx-importer/ONNXHelpers.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MIR_ONNX_HELPERS_H__
-#define __MIR_ONNX_HELPERS_H__
-
-#include "mir/Graph.h"
-#include "mir/TensorVariant.h"
-#include "mir/ops/TransposeOp.h"
-
-#include "onnx/onnx.pb.h"
-
-namespace mir_onnx
-{
-
-mir::TensorVariant fixGroupedKernel(int groups, const mir::TensorVariant &folded_kernel);
-mir::TensorVariant createTensor(const onnx::TensorProto *tensor);
-
-template <typename OpType, typename... Types>
-mir::Operation *createOp(mir::Graph *graph, Types &&... args)
-{
- return graph->create<OpType>(std::forward<Types>(args)...);
-}
-
-} // namespace mir_onnx
-
-#endif // __MIR_ONNX_HELPERS_H__
diff --git a/compiler/mir-onnx-importer/ONNXImporterImpl.cpp b/compiler/mir-onnx-importer/ONNXImporterImpl.cpp
deleted file mode 100644
index 29d5b70ec..000000000
--- a/compiler/mir-onnx-importer/ONNXImporterImpl.cpp
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ONNXImporterImpl.h"
-#include "ONNXHelpers.h"
-#include "ONNXOpRegistration.h"
-
-#include "mir/Operation.h"
-#include "mir/Shape.h"
-#include "mir/TensorUtil.h"
-#include "mir/TensorVariant.h"
-
-#include "mir/ops/ConstantOp.h"
-
-#include <fcntl.h>
-
-#include <google/protobuf/io/zero_copy_stream_impl.h>
-#include <google/protobuf/io/coded_stream.h>
-#include <functional>
-#include <iostream>
-#include <stdex/Memory.h>
-#include <utility>
-
-namespace mir_onnx
-{
-
-ONNXImporterImpl::ONNXImporterImpl(std::string filename) : _modelFilename(std::move(filename))
-{
- registerSupportedOps();
-}
-
-ONNXImporterImpl::~ONNXImporterImpl() = default;
-
-static void loadModelFile(const std::string &filename, onnx::ModelProto *model)
-{
- GOOGLE_PROTOBUF_VERIFY_VERSION;
-
- int file_handle = open(filename.c_str(), O_RDONLY);
-
- if (file_handle == -1)
- throw std::runtime_error("Couldn't open file \"" + filename + "\": " + std::strerror(errno) +
- ".");
-
- google::protobuf::io::FileInputStream file_stream(file_handle);
- file_stream.SetCloseOnDelete(true);
-
- google::protobuf::io::CodedInputStream coded_stream(&file_stream);
- coded_stream.SetTotalBytesLimit(INT_MAX, INT_MAX);
-
- if (!model->ParseFromCodedStream(&coded_stream))
- throw std::runtime_error("Couldn't parse file \"" + filename + "\".");
-
- // If the file has not been consumed entirely, assume that the file is in the wrong format.
- if (!coded_stream.ConsumedEntireMessage())
- throw std::runtime_error("File \"" + filename + "\" has not been consumed entirely.");
-}
-
-void ONNXImporterImpl::import()
-{
- _model = stdex::make_unique<onnx::ModelProto>();
- loadModelFile(_modelFilename, _model.get());
-
- collectUnsupportedOps();
-}
-
-void ONNXImporterImpl::collectUnsupportedOps()
-{
- std::set<std::string> problems_op_set;
-
- for (int i = 0; i < _model->graph().node_size(); i++)
- {
- const auto &onnx_node = _model->graph().node(i);
- assert(onnx_node.has_op_type());
- const auto &op_type = onnx_node.op_type();
-
- const auto *converter = NodeConverterRegistry::getInstance().lookup(op_type);
- if (converter == nullptr)
- problems_op_set.insert(op_type);
- }
- if (!problems_op_set.empty())
- {
- std::cerr << "The following operators are not supported:\n";
- for (const auto &op : problems_op_set)
- std::cerr << op << std::endl;
- throw std::runtime_error("Unsupported operators found");
- }
-}
-
-void ONNXImporterImpl::createGraphInputs()
-{
- const auto &graph = _model->graph();
- const auto &initializer = graph.initializer();
- const auto &value_info = graph.value_info();
-
- // Create all initializer Tensors
- for (const auto &tensor : initializer)
- {
- const auto mir_tensor = createTensor(&tensor);
- auto *op = _graph->create<mir::ops::ConstantOp>(mir_tensor);
- _context->setOutput(tensor.name(), op->getOutput(0));
- }
-
- for (const auto &input : graph.input())
- {
- assert(input.has_name());
-
- if (_context->getOutput(input.name()) == nullptr)
- {
- const auto &onnx_input_shape = input.type().tensor_type().shape();
- mir::Shape shape(onnx_input_shape.dim_size());
- for (int i = 0; i < onnx_input_shape.dim_size(); i++)
- {
- assert(onnx_input_shape.dim(i).has_dim_value());
- shape.dim(i) = static_cast<int32_t>(onnx_input_shape.dim(i).dim_value());
- }
-
- auto *op = _graph->create<mir::ops::InputOp>(shape);
- _context->setOutput(input.name(), op->getOutput(0));
- }
- }
-}
-
-std::unique_ptr<mir::Graph> ONNXImporterImpl::createIR()
-{
- _graph = stdex::make_unique<mir::Graph>();
- _context = stdex::make_unique<ConverterContext>(_graph.get());
-
- if (_model->ir_version() > onnx::IR_VERSION)
- {
- throw std::runtime_error("IR version " + std::to_string(_model->ir_version()) +
- " is not supported yet.");
- }
-
- // Set Opset Version for each domain
- for (const auto &op_set : _model->opset_import())
- {
- _context->setOpsetVersion(op_set.domain(), op_set.version());
- }
-
- createGraphInputs();
-
- // Forming partially ordered computation graph
- for (const auto &onnx_node : _model->graph().node())
- {
- assert(onnx_node.has_op_type());
- auto &op_type = onnx_node.op_type();
- // Get converter
- auto *node_converter = NodeConverterRegistry::getInstance().lookup(op_type);
- assert(node_converter);
- node_converter->convert(onnx_node, _context.get());
- }
- // Set graph outputs
- const auto &outputs = _model->graph().output();
- for (const auto &output : outputs)
- {
- assert(output.has_name());
- auto mir_output = _context->getOutput(output.name());
- if (mir_output == nullptr)
- throw std::runtime_error("Bad output name!");
-
- _graph->create<mir::ops::OutputOp>(mir_output);
- }
-
- return std::move(_graph);
-}
-
-std::unique_ptr<mir::Graph> ONNXImporterImpl::importModel()
-{
- import();
- return createIR();
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/ONNXImporterImpl.h b/compiler/mir-onnx-importer/ONNXImporterImpl.h
deleted file mode 100644
index 3914961cc..000000000
--- a/compiler/mir-onnx-importer/ONNXImporterImpl.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _MIR_ONNX_IMPORTER_H
-#define _MIR_ONNX_IMPORTER_H
-
-#include "mir/Graph.h"
-#include "onnx/onnx.pb.h"
-
-#include <map>
-#include <memory>
-#include <string>
-
-namespace mir_onnx
-{
-class ConverterContext;
-
-class ONNXImporterImpl final
-{
-public:
- explicit ONNXImporterImpl(std::string filename);
- ~ONNXImporterImpl();
- /// @brief Load the model and convert it into a MIR Graph.
- std::unique_ptr<mir::Graph> importModel();
-
-private:
- void import();
- std::unique_ptr<mir::Graph> createIR();
- void createGraphInputs();
- void collectUnsupportedOps();
- // Maps ONNX tensor names to corresponding MIR operation outputs.
- std::string _modelFilename;
- std::unique_ptr<onnx::ModelProto> _model;
- std::unique_ptr<ConverterContext> _context;
- std::unique_ptr<mir::Graph> _graph;
-};
-} // namespace mir_onnx
-
-#endif // _MIR_ONNX_IMPORTER_H
diff --git a/compiler/mir-onnx-importer/ONNXNodeConverterRegistry.h b/compiler/mir-onnx-importer/ONNXNodeConverterRegistry.h
deleted file mode 100644
index da3a34c26..000000000
--- a/compiler/mir-onnx-importer/ONNXNodeConverterRegistry.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __ONNX_NODE_CONVERTER_REGISTRY_H__
-#define __ONNX_NODE_CONVERTER_REGISTRY_H__
-
-#include "onnx/onnx.pb.h"
-#include "mir/Graph.h"
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <stdex/Memory.h>
-
-namespace mir_onnx
-{
-
-class ConverterContext
-{
-public:
- explicit ConverterContext(mir::Graph *graph) : _graph(graph) {}
- ~ConverterContext() = default;
-
- void setOpsetVersion(const std::string &domain, const int64_t opset_version)
- {
- _domainToOpsetVersion.emplace(domain, opset_version);
- }
-
- int64_t getOpsetVersion(const std::string &domain) const
- {
- auto iter = _domainToOpsetVersion.find(domain);
- if (iter == _domainToOpsetVersion.end())
- throw std::runtime_error("Didn't have domain " + domain + "!");
- return iter->second;
- }
-
- void setOutput(const std::string &name, mir::Operation::Output *output)
- {
- output->setName(name);
- auto result = _tensorNameToOutput.emplace(name, output);
- if (!result.second)
- throw std::runtime_error("Name duplication: " + name);
- }
-
- mir::Operation::Output *getOutput(const std::string &name) const
- {
- auto iter = _tensorNameToOutput.find(name);
- if (iter == _tensorNameToOutput.end())
- return nullptr;
- else
- return iter->second;
- }
-
- std::vector<mir::Operation::Output *> getNodeInputs(const onnx::NodeProto &onnx_node) const
- {
- const auto &input_names = onnx_node.input();
- std::vector<mir::Operation::Output *> outputs;
-
- for (const auto &input_name : input_names)
- {
- if (!input_name.empty())
- {
- auto *mir_output = getOutput(input_name);
- assert(mir_output != nullptr);
- outputs.emplace_back(mir_output);
- }
- }
- return outputs;
- }
-
- void setNodeOutputs(const onnx::NodeProto &onnx_node,
- const std::vector<mir::Operation::Output *> &outputs)
- {
- assert(!outputs.empty());
- for (std::size_t i = 0; i < outputs.size(); ++i)
- {
- setOutput(onnx_node.output(i), outputs[i]);
- }
- }
-
- mir::Graph *getGraph() const { return _graph; }
-
-private:
- std::map<std::string, mir::Operation::Output *> _tensorNameToOutput;
- std::map<std::string, int64_t> _domainToOpsetVersion;
- mir::Graph *_graph;
-};
-
-class NodeConverter
-{
-public:
- virtual void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const = 0;
- virtual ~NodeConverter() = default;
-};
-
-class NodeConverterRegistry
-{
-public:
- NodeConverterRegistry() = default;
-
- const NodeConverter *lookup(const std::string &op_type) const
- {
- const auto converter_iter = _converter_map.find(op_type);
- if (converter_iter == _converter_map.end())
- return nullptr;
-
- return converter_iter->second.get();
- }
-
- static NodeConverterRegistry &getInstance()
- {
- static NodeConverterRegistry instance;
- return instance;
- }
-
- void registerConverter(const std::string &op_type, std::unique_ptr<NodeConverter> &&converter)
- {
- const auto converter_iter = _converter_map.find(op_type);
- if (converter_iter == _converter_map.end())
- {
- _converter_map.emplace(op_type, std::move(converter));
- }
- else
- {
- throw std::runtime_error("Converter registration twiсe on same node impossible!");
- }
- }
-
-private:
- std::map<const std::string, std::unique_ptr<NodeConverter>> _converter_map;
-};
-
-} // namespace mir_onnx
-
-#endif // __ONNX_NODE_CONVERTER_REGISTRY_H__
diff --git a/compiler/mir-onnx-importer/ONNXOpRegistration.h b/compiler/mir-onnx-importer/ONNXOpRegistration.h
deleted file mode 100644
index c3eb51f4f..000000000
--- a/compiler/mir-onnx-importer/ONNXOpRegistration.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __ONNX_OP_REGISTRATION_H__
-#define __ONNX_OP_REGISTRATION_H__
-
-#include "ONNXNodeConverterRegistry.h"
-
-#include "Op/Add.h"
-#include "Op/AveragePool.h"
-#include "Op/BatchNormalization.h"
-#include "Op/Concat.h"
-#include "Op/Constant.h"
-#include "Op/Conv.h"
-#include "Op/Dropout.h"
-#include "Op/Flatten.h"
-#include "Op/Gather.h"
-#include "Op/Gemm.h"
-#include "Op/GlobalAveragePool.h"
-#include "Op/Identity.h"
-#include "Op/MatMul.h"
-#include "Op/Max.h"
-#include "Op/MaxPool.h"
-#include "Op/Mul.h"
-#include "Op/Pad.h"
-#include "Op/ReduceMean.h"
-#include "Op/Relu.h"
-#include "Op/Reshape.h"
-#include "Op/Shape.h"
-#include "Op/Sigmoid.h"
-#include "Op/Softmax.h"
-#include "Op/Sum.h"
-#include "Op/Transpose.h"
-#include "Op/Unsqueeze.h"
-#include "Op/Upsample.h"
-
-namespace mir_onnx
-{
-
-inline void registerSupportedOps()
-{
- auto &registry = NodeConverterRegistry::getInstance();
- registry.registerConverter("Add", stdex::make_unique<AddNodeConverter>());
- registry.registerConverter("AveragePool", stdex::make_unique<AveragePoolNodeConverter>());
- registry.registerConverter("BatchNormalization",
- stdex::make_unique<BatchNormalizationNodeConverter>());
- registry.registerConverter("Concat", stdex::make_unique<ConcatNodeConverter>());
- registry.registerConverter("Constant", stdex::make_unique<ConstantNodeConverter>());
- registry.registerConverter("Conv", stdex::make_unique<ConvNodeConverter>());
- registry.registerConverter("Dropout", stdex::make_unique<DropoutNodeConverter>());
- registry.registerConverter("Flatten", stdex::make_unique<FlattenNodeConverter>());
- registry.registerConverter("Gather", stdex::make_unique<GatherNodeConverter>());
- registry.registerConverter("Gemm", stdex::make_unique<GemmNodeConverter>());
- registry.registerConverter("GlobalAveragePool",
- stdex::make_unique<GlobalAveragePoolNodeConverter>());
- registry.registerConverter("Identity", stdex::make_unique<IdentityNodeConverter>());
- registry.registerConverter("MatMul", stdex::make_unique<MatMulNodeConverter>());
- registry.registerConverter("Max", stdex::make_unique<MaxNodeConverter>());
- registry.registerConverter("MaxPool", stdex::make_unique<MaxPoolNodeConverter>());
- registry.registerConverter("Mul", stdex::make_unique<MulNodeConverter>());
- registry.registerConverter("Pad", stdex::make_unique<PadNodeConverter>());
- registry.registerConverter("ReduceMean", stdex::make_unique<ReduceMeanNodeConverter>());
- registry.registerConverter("Relu", stdex::make_unique<ReluNodeConverter>());
- registry.registerConverter("Reshape", stdex::make_unique<ReshapeNodeConverter>());
- registry.registerConverter("Shape", stdex::make_unique<ShapeNodeConverter>());
- registry.registerConverter("Sigmoid", stdex::make_unique<SigmoidNodeConverter>());
- registry.registerConverter("Softmax", stdex::make_unique<SoftmaxNodeConverter>());
- registry.registerConverter("Sum", stdex::make_unique<SumNodeConverter>());
- registry.registerConverter("Transpose", stdex::make_unique<TransposeNodeConverter>());
- registry.registerConverter("Unsqueeze", stdex::make_unique<UnsqueezeNodeConverter>());
- registry.registerConverter("Upsample", stdex::make_unique<UpsampleNodeConverter>());
-}
-
-} // namespace mir_onnx
-
-#endif // __ONNX_OP_REGISTRATION_H__
diff --git a/compiler/mir-onnx-importer/Op/Add.cpp b/compiler/mir-onnx-importer/Op/Add.cpp
deleted file mode 100644
index e00e4f4ff..000000000
--- a/compiler/mir-onnx-importer/Op/Add.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Add.h"
-
-#include "ONNXHelpers.h"
-#include "AttributeHelpers.h"
-
-#include "mir/ops/AddOp.h"
-
-namespace mir_onnx
-{
-
-void AddNodeConverter::convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const
-{
- const auto opset_version = context->getOpsetVersion(onnx_node.domain());
- if (opset_version >= 7)
- convertV7(onnx_node, context);
- else if (opset_version >= 6)
- convertV6(onnx_node, context);
- else if (opset_version >= 1)
- convertV1(onnx_node, context);
- else
- throw std::runtime_error("Not supported opset version on Add operation!");
-}
-
-void AddNodeConverter::convertV1(const onnx::NodeProto &onnx_node, ConverterContext *context) const
-{
- // consumed_inputs attribute not used
- convertV6(onnx_node, context);
-}
-
-void AddNodeConverter::convertV6(const onnx::NodeProto &onnx_node, ConverterContext *context) const
-{
- // broadcast attribute not used
- const auto *axis = findAttribute(onnx_node, "axis");
- if (axis != nullptr)
- throw std::runtime_error("Not supported axis attribute in Add operation!");
-
- convertV7(onnx_node, context);
-}
-
-void AddNodeConverter::convertV7(const onnx::NodeProto &onnx_node, ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
-
- auto result = createOp<mir::ops::AddOp>(graph, inputs[0], inputs[1])->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Add.h b/compiler/mir-onnx-importer/Op/Add.h
deleted file mode 100644
index 079815ce8..000000000
--- a/compiler/mir-onnx-importer/Op/Add.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_ADD_H
-#define MIR_ONNX_OP_ADD_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class AddNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-
-private:
- void convertV1(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV6(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV7(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_ADD_H
diff --git a/compiler/mir-onnx-importer/Op/AveragePool.cpp b/compiler/mir-onnx-importer/Op/AveragePool.cpp
deleted file mode 100644
index cc9e647e9..000000000
--- a/compiler/mir-onnx-importer/Op/AveragePool.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "AveragePool.h"
-
-#include "ONNXHelpers.h"
-#include "AttributeHelpers.h"
-#include "ConvPoolHelpers.h"
-
-#include "mir/ops/AvgPool2DOp.h"
-
-namespace mir_onnx
-{
-
-void AveragePoolNodeConverter::convert(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto opset_version = context->getOpsetVersion(onnx_node.domain());
- if (opset_version >= 10)
- convertV10(onnx_node, context);
- else if (opset_version >= 7)
- convertV7(onnx_node, context);
- else if (opset_version >= 1)
- convertV1(onnx_node, context);
- else
- throw std::runtime_error("Not supported opset version on Add operation!");
-}
-
-void AveragePoolNodeConverter::convertV1(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
-
- assert(inputs.size() == 1);
- auto input = inputs[0];
-
- const auto &input_shape = input->getShape();
- if (input_shape.rank() != 4)
- throw std::runtime_error("AveragePool: only 2-D input is supported.");
-
- constexpr int num_spatial_dims = 2;
-
- const auto strides =
- getAttributeValue(onnx_node, "strides", std::vector<std::int32_t>(num_spatial_dims, 1));
- if (strides.size() != num_spatial_dims)
- throw std::runtime_error("AveragePool: attribute 'strides' has incorrect size.");
-
- const auto kernel_shape = getAttributeValue<std::vector<std::int32_t>>(onnx_node, "kernel_shape");
- if (kernel_shape.size() != num_spatial_dims)
- throw std::runtime_error("AveragePool: attribute 'kernel_shape' has incorrect size.");
-
- std::vector<std::int32_t> padding_before(num_spatial_dims, 0);
- std::vector<std::int32_t> padding_after(num_spatial_dims, 0);
- if (const auto *pads_attr = findAttribute(onnx_node, "pads"))
- {
- const auto pads = getAttributeValue<std::vector<std::int32_t>>(*pads_attr);
- if (pads.size() != num_spatial_dims * 2)
- throw std::runtime_error("AveragePool: attribute 'pads' has incorrect size.");
- padding_before.assign(pads.cbegin(), std::next(pads.cbegin(), num_spatial_dims));
- padding_after.assign(std::next(pads.cbegin(), num_spatial_dims), pads.cend());
- }
- else
- {
- const auto auto_pad = getAttributeValue<std::string>(onnx_node, "auto_pad", "NOTSET");
- const std::vector<std::int32_t> dilations(num_spatial_dims, 1);
- inferAutoPadding(auto_pad, input_shape, dilations, strides, kernel_shape, padding_before,
- padding_after);
- }
-
- auto result = createOp<mir::ops::AvgPool2DOp>(graph, input, kernel_shape, strides, padding_before,
- padding_after, false, mir::DataFormat::NCHW)
- ->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-void AveragePoolNodeConverter::convertV7(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto count_include_pad = getAttributeValue<int64_t>(onnx_node, "count_include_pad", 0);
- if (count_include_pad != 0)
- throw std::runtime_error("Not supported count_include_pad attribute!");
-
- convertV1(onnx_node, context);
-}
-
-void AveragePoolNodeConverter::convertV10(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto ceil_mode = getAttributeValue<int64_t>(onnx_node, "ceil_mode", 0);
- if (ceil_mode != 0)
- throw std::runtime_error("Not supported ceil_mode attribute!");
-
- convertV7(onnx_node, context);
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/AveragePool.h b/compiler/mir-onnx-importer/Op/AveragePool.h
deleted file mode 100644
index 607d01226..000000000
--- a/compiler/mir-onnx-importer/Op/AveragePool.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_AVERAGE_POOL_H
-#define MIR_ONNX_OP_AVERAGE_POOL_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class AveragePoolNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-
-private:
- void convertV1(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV7(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV10(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_AVERAGE_POOL_H
diff --git a/compiler/mir-onnx-importer/Op/BatchNormalization.cpp b/compiler/mir-onnx-importer/Op/BatchNormalization.cpp
deleted file mode 100644
index 67b7203e3..000000000
--- a/compiler/mir-onnx-importer/Op/BatchNormalization.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "BatchNormalization.h"
-
-#include "ONNXHelpers.h"
-#include "AttributeHelpers.h"
-
-#include "mir/ShapeRange.h"
-#include "mir/Tensor.h"
-
-#include "mir/ops/AddOp.h"
-#include "mir/ops/ConstantOp.h"
-#include "mir/ops/MulOp.h"
-#include "mir/ops/ReshapeOp.h"
-
-#include <cmath>
-
-namespace mir_onnx
-{
-
-void BatchNormalizationNodeConverter::convert(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto opset_version = context->getOpsetVersion(onnx_node.domain());
- if (opset_version >= 9)
- convertV9(onnx_node, context);
- else if (opset_version >= 7)
- convertV7(onnx_node, context);
- else if (opset_version >= 6)
- convertV6(onnx_node, context);
- else if (opset_version >= 1)
- convertV1(onnx_node, context);
- else
- throw std::runtime_error("Not supported opset version on BatchNormalization operation!");
-}
-
-void BatchNormalizationNodeConverter::convertV1(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- // consumed_inputs attribute not used
- convertV6(onnx_node, context);
-}
-
-void BatchNormalizationNodeConverter::convertV6(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto is_test = getAttributeValue<std::int64_t>(onnx_node, "is_test", 0);
- if (is_test == 0)
- throw std::runtime_error("Not supported is_test attribute!");
-
- convertV7(onnx_node, context);
-}
-
-void BatchNormalizationNodeConverter::convertV7(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- // spatial attribute used only for learning
-
- convertV9(onnx_node, context);
-}
-
-void BatchNormalizationNodeConverter::convertV9(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- // momentum attrribute used only for learning
-
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
-
- assert(inputs.size() == 5);
- auto input = inputs[0];
- auto scale = inputs[1];
- auto bias = inputs[2];
- auto mean = inputs[3];
- auto var = inputs[4];
-
- // 1e-05f is the default epsilon.
- const auto epsilon = getAttributeValue<float>(onnx_node, "epsilon", 1e-05f);
-
- // Y = (X - mean) * scale / sqrt(var + epsilon) + bias =
- // = (X + C1) * C2 + bias
- // We need these to be constants since we are going to change them.
- // TODO Implement the formula using ops and let the optimizer constant-fold them.
- auto scale_op = dynamic_cast<mir::ops::ConstantOp *>(scale->getNode());
- auto mean_op = dynamic_cast<mir::ops::ConstantOp *>(mean->getNode());
- auto var_op = dynamic_cast<mir::ops::ConstantOp *>(var->getNode());
-
- if (scale_op == nullptr || mean_op == nullptr || var_op == nullptr)
- throw std::runtime_error(
- "BatchNormalization: only constant 'scale', 'mean' and 'variance' inputs are supported.");
-
- mir::Tensor<float> scale_accessor(scale_op->getValue());
- mir::Tensor<float> mean_accessor(mean_op->getValue());
- mir::Tensor<float> var_accessor(var_op->getValue());
-
- // C1 = -mean
- for (const auto &idx : mir::ShapeRange(mean_accessor.getShape()))
- mean_accessor.at(idx) *= -1;
-
- // C2 = scale / sqrt(var + epsilon)
- for (const auto &idx : mir::ShapeRange(scale_accessor.getShape()))
- scale_accessor.at(idx) /= std::sqrt(var_accessor.at(idx) + epsilon);
-
- assert(mean_accessor.getShape().rank() == 1);
- assert(input->getShape().rank() == 4 && "Supported only 4D input");
- mir::Shape new_shape{1, 1, 1, 1};
- new_shape.dim(1) = mean_accessor.getShape().dim(0); // set channel dim
-
- auto reshaped_mean = createOp<mir::ops::ReshapeOp>(graph, mean, new_shape)->getOutput(0);
- auto reshaped_scale = createOp<mir::ops::ReshapeOp>(graph, scale, new_shape)->getOutput(0);
- auto reshaped_bias = createOp<mir::ops::ReshapeOp>(graph, bias, new_shape)->getOutput(0);
-
- // Y = (X + C1) * C2 + bias
- auto result = createOp<mir::ops::AddOp>(graph, input, reshaped_mean)->getOutput(0);
- result = createOp<mir::ops::MulOp>(graph, result, reshaped_scale)->getOutput(0);
- result = createOp<mir::ops::AddOp>(graph, result, reshaped_bias)->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/BatchNormalization.h b/compiler/mir-onnx-importer/Op/BatchNormalization.h
deleted file mode 100644
index 2e20c577c..000000000
--- a/compiler/mir-onnx-importer/Op/BatchNormalization.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_BATCH_NORMALIZATION_H
-#define MIR_ONNX_OP_BATCH_NORMALIZATION_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class BatchNormalizationNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-
-private:
- void convertV1(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV6(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV7(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV9(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_BATCH_NORMALIZATION_H
diff --git a/compiler/mir-onnx-importer/Op/Concat.cpp b/compiler/mir-onnx-importer/Op/Concat.cpp
deleted file mode 100644
index 229cb76d8..000000000
--- a/compiler/mir-onnx-importer/Op/Concat.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Concat.h"
-
-#include "ONNXHelpers.h"
-#include "AttributeHelpers.h"
-
-#include "mir/ops/ConcatOp.h"
-
-namespace mir_onnx
-{
-
-void ConcatNodeConverter::convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const
-{
- const auto opset_version = context->getOpsetVersion(onnx_node.domain());
- if (opset_version >= 4)
- convertV4(onnx_node, context);
- else if (opset_version >= 1)
- convertV1(onnx_node, context);
- else
- throw std::runtime_error("Not supported opset version on Add operation!");
-}
-
-void ConcatNodeConverter::convertV1(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
-
- const auto axis = getAttributeValue<int64_t>(onnx_node, "axis", 1);
-
- auto result = createOp<mir::ops::ConcatOp>(graph, inputs, axis)->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-void ConcatNodeConverter::convertV4(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
- // From version 4 axis attribute is required
- auto attr = findAttribute(onnx_node, "axis");
- if (!attr)
- throw std::runtime_error("Attribute axis is required!");
- int32_t axis = attr->i();
-
- auto result = createOp<mir::ops::ConcatOp>(graph, inputs, axis)->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Concat.h b/compiler/mir-onnx-importer/Op/Concat.h
deleted file mode 100644
index 906919d27..000000000
--- a/compiler/mir-onnx-importer/Op/Concat.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_CONCAT_H
-#define MIR_ONNX_OP_CONCAT_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class ConcatNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-
-private:
- void convertV1(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV4(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_CONCAT_H
diff --git a/compiler/mir-onnx-importer/Op/Constant.cpp b/compiler/mir-onnx-importer/Op/Constant.cpp
deleted file mode 100644
index 8cf3f4dd2..000000000
--- a/compiler/mir-onnx-importer/Op/Constant.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Constant.h"
-
-#include "ONNXHelpers.h"
-#include "AttributeHelpers.h"
-
-#include "mir/TensorVariant.h"
-#include "mir/ops/ConstantOp.h"
-
-namespace mir_onnx
-{
-
-void ConstantNodeConverter::convert(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto opset_version = context->getOpsetVersion(onnx_node.domain());
- if (opset_version >= 11)
- convertV11(onnx_node, context);
- else if (opset_version >= 9)
- convertV9(onnx_node, context);
- else if (opset_version >= 1)
- convertV1(onnx_node, context);
- else
- throw std::runtime_error("Not supported opset version on Constant operation!");
-}
-
-void ConstantNodeConverter::convertV1(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
-
- const auto onnx_tensor = getAttributeValue<onnx::TensorProto>(onnx_node, "value");
- auto mir_tensor = createTensor(&onnx_tensor);
-
- auto result = graph->create<mir::ops::ConstantOp>(mir_tensor)->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-void ConstantNodeConverter::convertV9(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- // Since version 9 Constant operation support other types contained in tensor
- convertV1(onnx_node, context);
-}
-
-void ConstantNodeConverter::convertV11(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto *value_attr = findAttribute(onnx_node, "value");
- const auto *sparse_value_attr = findAttribute(onnx_node, "sparse_value");
- if (value_attr == nullptr && sparse_value_attr == nullptr)
- throw std::runtime_error("Not enough attributes in Constant operation!");
-
- if (value_attr != nullptr)
- return convertV9(onnx_node, context);
-
- if (sparse_value_attr != nullptr)
- throw std::runtime_error("Not supported sparse_tensor in Constant operation!");
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Constant.h b/compiler/mir-onnx-importer/Op/Constant.h
deleted file mode 100644
index 8fc56f6ca..000000000
--- a/compiler/mir-onnx-importer/Op/Constant.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_CONSTANT_H
-#define MIR_ONNX_OP_CONSTANT_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class ConstantNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-
-private:
- void convertV1(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV9(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV11(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_CONSTANT_H
diff --git a/compiler/mir-onnx-importer/Op/Conv.cpp b/compiler/mir-onnx-importer/Op/Conv.cpp
deleted file mode 100644
index aba6fe68f..000000000
--- a/compiler/mir-onnx-importer/Op/Conv.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Conv.h"
-
-#include "ONNXHelpers.h"
-#include "AttributeHelpers.h"
-#include "ConvPoolHelpers.h"
-
-#include "mir/TensorUtil.h"
-
-#include "mir/ops/AddOp.h"
-#include "mir/ops/ConstantOp.h"
-#include "mir/ops/Conv2DOp.h"
-#include "mir/ops/DepthwiseConv2DOp.h"
-
-namespace mir_onnx
-{
-
-void ConvNodeConverter::convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const
-{
- const auto opset_version = context->getOpsetVersion(onnx_node.domain());
- if (opset_version >= 1)
- convertV1(onnx_node, context);
- else
- throw std::runtime_error("Not supported opset version on Conv operation!");
-}
-
-void ConvNodeConverter::convertV1(const onnx::NodeProto &onnx_node, ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
-
- assert(inputs.size() >= 2);
- auto input = inputs[0];
- auto kernel = inputs[1];
-
- const auto &input_shape = input->getShape();
- if (input_shape.rank() != 4)
- throw std::runtime_error("Conv: only 2-D input is supported.");
-
- constexpr int num_spatial_dims = 2;
-
- const auto dilations =
- getAttributeValue(onnx_node, "dilations", std::vector<std::int32_t>(num_spatial_dims, 1));
- if (dilations.size() != num_spatial_dims)
- throw std::runtime_error("Conv: attribute 'dilations' has incorrect size.");
- if (!std::all_of(dilations.cbegin(), dilations.cend(), [](std::int32_t x) { return x == 1; }))
- throw std::runtime_error("Conv: attribute 'dilations' has unsupported value.");
-
- const auto strides =
- getAttributeValue(onnx_node, "strides", std::vector<std::int32_t>(num_spatial_dims, 1));
- if (strides.size() != num_spatial_dims)
- throw std::runtime_error("Conv: attribute 'strides' has incorrect size.");
-
- // Assuming kernel has OIHW format.
- assert(kernel->getShape().rank() == 4);
- const auto kernel_shape = getAttributeValue(
- onnx_node, "kernel_shape",
- std::vector<std::int32_t>{kernel->getShape().dim(2), kernel->getShape().dim(3)});
- if (kernel_shape.size() != num_spatial_dims)
- throw std::runtime_error("Conv: attribute 'kernel_shape' has incorrect size.");
-
- std::vector<std::int32_t> padding_before(num_spatial_dims, 0);
- std::vector<std::int32_t> padding_after(num_spatial_dims, 0);
- if (const auto *pads_attr = findAttribute(onnx_node, "pads"))
- {
- const auto pads = getAttributeValue<std::vector<std::int32_t>>(*pads_attr);
- if (pads.size() != num_spatial_dims * 2)
- throw std::runtime_error("Conv: attribute 'pads' has incorrect size.");
- padding_before.assign(pads.cbegin(), std::next(pads.cbegin(), num_spatial_dims));
- padding_after.assign(std::next(pads.cbegin(), num_spatial_dims), pads.cend());
- }
- else
- {
- const auto auto_pad = getAttributeValue<std::string>(onnx_node, "auto_pad", "NOTSET");
- inferAutoPadding(auto_pad, input_shape, dilations, strides, kernel_shape, padding_before,
- padding_after);
- }
-
- // FIXME: It can be non-constant value.
- auto *in_weights = dynamic_cast<mir::ops::ConstantOp *>(kernel->getNode());
- assert(in_weights && "Weights could be a constant tensor only");
- const auto &in_weights_tensor = in_weights->getValue();
- // We should transpose ONNX MC(IO)HW to HWOI
- auto kernel_tensor = mir::transposeTensor<2, 3, 1, 0>(in_weights_tensor);
- auto in_group_size = kernel_tensor.getShape().dim(2);
- auto out_channels = kernel_tensor.getShape().dim(3);
-
- // 1 is the default number of groups.
- const auto group = getAttributeValue<std::int64_t>(onnx_node, "group", 1);
- bool is_depthwise = (group != 1) && (in_group_size == 1) && (out_channels == group);
-
- mir::Operation::Output *result;
- if (is_depthwise)
- {
- // TODO handle properly kernel with layer multiplier
- auto transposed_tensor = mir::transposeTensor<0, 1, 3, 2>(kernel_tensor);
- kernel = createOp<mir::ops::ConstantOp>(graph, transposed_tensor)->getOutput(0);
- result = createOp<mir::ops::DepthwiseConv2DOp>(graph, input, kernel, strides, padding_before,
- padding_after, mir::DataFormat::NCHW)
- ->getOutput(0);
- }
- else
- {
- // first we need to convert kernel of grouped convolution to appropriate ordinary kernel
- if (group != 1)
- kernel_tensor = fixGroupedKernel(group, kernel_tensor);
- kernel_tensor = mir::transposeTensor<3, 0, 1, 2>(kernel_tensor);
- kernel = createOp<mir::ops::ConstantOp>(graph, kernel_tensor)->getOutput(0);
- result = createOp<mir::ops::Conv2DOp>(graph, input, kernel, strides, padding_before,
- padding_after, mir::DataFormat::NCHW)
- ->getOutput(0);
- }
-
- if (inputs.size() > 2)
- {
- auto bias = inputs[2];
- result = createOp<mir::ops::AddOp>(graph, result, bias)->getOutput(0);
- }
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Conv.h b/compiler/mir-onnx-importer/Op/Conv.h
deleted file mode 100644
index 59974267e..000000000
--- a/compiler/mir-onnx-importer/Op/Conv.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_CONV_H
-#define MIR_ONNX_OP_CONV_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class ConvNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-
-private:
- void convertV1(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_CONV_H
diff --git a/compiler/mir-onnx-importer/Op/Dropout.cpp b/compiler/mir-onnx-importer/Op/Dropout.cpp
deleted file mode 100644
index c81c5ff3b..000000000
--- a/compiler/mir-onnx-importer/Op/Dropout.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Dropout.h"
-
-#include "AttributeHelpers.h"
-
-namespace mir_onnx
-{
-
-void DropoutNodeConverter::convert(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto opset_version = context->getOpsetVersion(onnx_node.domain());
- if (opset_version >= 10)
- convertV10(onnx_node, context);
- else if (opset_version >= 7)
- convertV7(onnx_node, context);
- else if (opset_version >= 6)
- convertV6(onnx_node, context);
- else if (opset_version >= 1)
- convertV1(onnx_node, context);
- else
- throw std::runtime_error("Not supported opset version on Dropout operation!");
-}
-
-void DropoutNodeConverter::convertV1(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- // consumed_inputs attribute not used
- convertV6(onnx_node, context);
-}
-
-void DropoutNodeConverter::convertV6(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto is_test = getAttributeValue<std::int64_t>(onnx_node, "is_test", 0);
- if (is_test == 0)
- throw std::runtime_error("Not supported is_test attribute!");
-
- convertV10(onnx_node, context);
-}
-
-void DropoutNodeConverter::convertV7(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- convertV10(onnx_node, context);
-}
-
-void DropoutNodeConverter::convertV10(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
-
- // ratio attribute not used
-
- // This is a no-op in inference mode.
- context->setNodeOutputs(onnx_node, {inputs[0]});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Dropout.h b/compiler/mir-onnx-importer/Op/Dropout.h
deleted file mode 100644
index 56291c9aa..000000000
--- a/compiler/mir-onnx-importer/Op/Dropout.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_DROPOUT_H
-#define MIR_ONNX_OP_DROPOUT_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class DropoutNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-
-private:
- void convertV1(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV6(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV7(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV10(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_DROPOUT_H
diff --git a/compiler/mir-onnx-importer/Op/Flatten.cpp b/compiler/mir-onnx-importer/Op/Flatten.cpp
deleted file mode 100644
index b0da6651f..000000000
--- a/compiler/mir-onnx-importer/Op/Flatten.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Flatten.h"
-
-#include "ONNXHelpers.h"
-#include "AttributeHelpers.h"
-
-#include "mir/ops/ReshapeOp.h"
-
-namespace mir_onnx
-{
-
-void FlattenNodeConverter::convert(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto opset_version = context->getOpsetVersion(onnx_node.domain());
- if (opset_version >= 9)
- convertV9(onnx_node, context);
- else if (opset_version >= 1)
- convertV1(onnx_node, context);
- else
- throw std::runtime_error("Not supported opset version on Flatten operation!");
-}
-
-void FlattenNodeConverter::convertV1(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
-
- const auto axis = getAttributeValue<int64_t>(onnx_node, "axis", 1);
- assert(inputs.size() == 1);
- const auto &in_shape = inputs[0]->getShape();
- assert(axis <= in_shape.rank()); // A tensor of rank >= axis
- int32_t first_dim = 1, second_dim = 1;
- int32_t dim = 0;
-
- for (; dim < axis; dim++)
- first_dim *= in_shape.dim(dim);
-
- for (; dim < in_shape.rank(); dim++)
- second_dim *= in_shape.dim(dim);
-
- mir::Shape out_shape({first_dim, second_dim}); // Output 2D tensor
-
- auto result = createOp<mir::ops::ReshapeOp>(graph, inputs[0], out_shape)->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-void FlattenNodeConverter::convertV9(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- // Other type constraints
- convertV1(onnx_node, context);
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Flatten.h b/compiler/mir-onnx-importer/Op/Flatten.h
deleted file mode 100644
index ee438ca92..000000000
--- a/compiler/mir-onnx-importer/Op/Flatten.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_FLATTEN_H
-#define MIR_ONNX_OP_FLATTEN_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class FlattenNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-
-private:
- void convertV1(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV9(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_FLATTEN_H
diff --git a/compiler/mir-onnx-importer/Op/Gather.cpp b/compiler/mir-onnx-importer/Op/Gather.cpp
deleted file mode 100644
index 517007ed0..000000000
--- a/compiler/mir-onnx-importer/Op/Gather.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Gather.h"
-
-#include "ONNXHelpers.h"
-#include "AttributeHelpers.h"
-
-#include "mir/ops/GatherOp.h"
-
-namespace mir_onnx
-{
-
-void GatherNodeConverter::convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
-
- // 0 is the default axis number.
- const auto axis = getAttributeValue<std::int64_t>(onnx_node, "axis", 0);
-
- auto result = createOp<mir::ops::GatherOp>(graph, inputs[0], inputs[1], axis)->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Gather.h b/compiler/mir-onnx-importer/Op/Gather.h
deleted file mode 100644
index 0269560a2..000000000
--- a/compiler/mir-onnx-importer/Op/Gather.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_GATHER_H
-#define MIR_ONNX_OP_GATHER_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class GatherNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_GATHER_H
diff --git a/compiler/mir-onnx-importer/Op/Gemm.cpp b/compiler/mir-onnx-importer/Op/Gemm.cpp
deleted file mode 100644
index 279bb1c20..000000000
--- a/compiler/mir-onnx-importer/Op/Gemm.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Gemm.h"
-
-#include "ONNXHelpers.h"
-#include "AttributeHelpers.h"
-
-#include "mir/TensorUtil.h"
-
-#include "mir/ops/AddOp.h"
-#include "mir/ops/ConstantOp.h"
-#include "mir/ops/FullyConnectedOp.h"
-#include "mir/ops/MulOp.h"
-#include "mir/ops/ReshapeOp.h"
-#include "mir/ops/TransposeOp.h"
-
-namespace mir_onnx
-{
-
-void GemmNodeConverter::convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
-
- assert(inputs.size() == 3);
- auto a = inputs[0];
- auto b = inputs[1];
- auto c = inputs[2];
-
- // 1.0f is the default factor.
- const auto alpha_val = getAttributeValue<float>(onnx_node, "alpha", 1.0f);
- const auto beta_val = getAttributeValue<float>(onnx_node, "beta", 1.0f);
-
- // 0 means that no transpose is needed. It is the default value.
- const auto trans_a = getAttributeValue<std::int64_t>(onnx_node, "transA", 0);
- const auto trans_b = getAttributeValue<std::int64_t>(onnx_node, "transB", 0);
-
- // Transpose the A and B matrices as needed.
- if (trans_a)
- a = createOp<mir::ops::TransposeOp>(graph, a, std::vector<std::size_t>{1, 0})->getOutput(0);
- if (trans_b)
- {
- // TODO Remove that when optimization would be done
- if (auto *weights = dynamic_cast<mir::ops::ConstantOp *>(b->getNode()))
- {
- const auto &weights_tensor = weights->getValue();
- auto transposed_weights = mir::transposeTensor<1, 0>(weights_tensor);
- b = createOp<mir::ops::ConstantOp>(graph, transposed_weights)->getOutput(0);
- }
- else
- {
- b = createOp<mir::ops::TransposeOp>(graph, b, std::vector<std::size_t>{1, 0})->getOutput(0);
- }
- }
-
- // Calculate A * B.
- auto ab = createOp<mir::ops::FullyConnectedOp>(graph, a, b)->getOutput(0);
-
- // Multiply A * B by the constant factor.
- if (alpha_val != 1.0f)
- {
- mir::TensorVariant alpha_tensor(mir::DataType::FLOAT32, {1}, &alpha_val);
- auto alpha = createOp<mir::ops::ConstantOp>(graph, alpha_tensor)->getOutput(0);
- ab = createOp<mir::ops::MulOp>(graph, alpha, ab)->getOutput(0);
- }
-
- // Multiply C by the constant factor.
- if (beta_val != 1.0f)
- {
- mir::TensorVariant beta_tensor(mir::DataType::FLOAT32, {1}, &beta_val);
- auto beta = createOp<mir::ops::ConstantOp>(graph, beta_tensor)->getOutput(0);
- c = createOp<mir::ops::MulOp>(graph, beta, c)->getOutput(0);
- }
-
- // Calculate the result: alpha * A * B + beta * C.
- auto result = createOp<mir::ops::AddOp>(graph, ab, c)->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Gemm.h b/compiler/mir-onnx-importer/Op/Gemm.h
deleted file mode 100644
index 3246b9a83..000000000
--- a/compiler/mir-onnx-importer/Op/Gemm.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_GEMM_H
-#define MIR_ONNX_OP_GEMM_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class GemmNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_GEMM_H
diff --git a/compiler/mir-onnx-importer/Op/GlobalAveragePool.cpp b/compiler/mir-onnx-importer/Op/GlobalAveragePool.cpp
deleted file mode 100644
index fc161e1f1..000000000
--- a/compiler/mir-onnx-importer/Op/GlobalAveragePool.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "GlobalAveragePool.h"
-
-#include "ONNXHelpers.h"
-
-#include "mir/ops/AvgPool2DOp.h"
-
-namespace mir_onnx
-{
-
-void GlobalAveragePoolNodeConverter::convert(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
-
- assert(inputs.size() == 1);
- auto input = inputs[0];
-
- const auto &input_shape = input->getShape();
- if (input_shape.rank() != 4)
- throw std::runtime_error("GlobalAveragePool: only 2-D input is supported.");
-
- // GlobalAveragePool is equivalent to AveragePool with kernel size equal
- // to the spatial dimension of input tensor.
- const std::vector<std::int32_t> window_size{input->getShape().dim(2), input->getShape().dim(3)};
- const std::vector<std::int32_t> strides{1, 1};
- const std::vector<std::int32_t> padding_before{0, 0};
- const std::vector<std::int32_t> padding_after{0, 0};
-
- auto result = createOp<mir::ops::AvgPool2DOp>(graph, input, window_size, strides, padding_before,
- padding_after, true, mir::DataFormat::NCHW)
- ->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/GlobalAveragePool.h b/compiler/mir-onnx-importer/Op/GlobalAveragePool.h
deleted file mode 100644
index 47164940a..000000000
--- a/compiler/mir-onnx-importer/Op/GlobalAveragePool.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_GLOBAL_AVERAGE_POOL_H
-#define MIR_ONNX_OP_GLOBAL_AVERAGE_POOL_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class GlobalAveragePoolNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_GLOBAL_AVERAGE_POOL_H
diff --git a/compiler/mir-onnx-importer/Op/Identity.cpp b/compiler/mir-onnx-importer/Op/Identity.cpp
deleted file mode 100644
index 713444b5b..000000000
--- a/compiler/mir-onnx-importer/Op/Identity.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Identity.h"
-
-namespace mir_onnx
-{
-
-void IdentityNodeConverter::convert(const onnx::NodeProto &onnx_node,
- mir_onnx::ConverterContext *context) const
-{
- const auto opset_version = context->getOpsetVersion(onnx_node.domain());
-
- if (opset_version >= 1)
- convertV1(onnx_node, context);
- else
- throw std::runtime_error("Not supported opset version on Identity operation!");
-}
-
-void IdentityNodeConverter::convertV1(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto inputs = context->getNodeInputs(onnx_node);
- assert(inputs.size() == 1);
-
- context->setNodeOutputs(onnx_node, {inputs[0]});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Identity.h b/compiler/mir-onnx-importer/Op/Identity.h
deleted file mode 100644
index 51a02f614..000000000
--- a/compiler/mir-onnx-importer/Op/Identity.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_IDENTITY_H
-#define MIR_ONNX_OP_IDENTITY_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class IdentityNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-
-private:
- void convertV1(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_IDENTITY_H
diff --git a/compiler/mir-onnx-importer/Op/MatMul.cpp b/compiler/mir-onnx-importer/Op/MatMul.cpp
deleted file mode 100644
index b2107bc8e..000000000
--- a/compiler/mir-onnx-importer/Op/MatMul.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "MatMul.h"
-
-#include "ONNXHelpers.h"
-
-#include "mir/ops/FullyConnectedOp.h"
-
-namespace mir_onnx
-{
-
-void MatMulNodeConverter::convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const
-{
- const auto opset_version = context->getOpsetVersion(onnx_node.domain());
- if (opset_version >= 9)
- convertV9(onnx_node, context);
- else if (opset_version >= 1)
- convertV1(onnx_node, context);
- else
- throw std::runtime_error("Not supported opset version on MatMul operation!");
-}
-
-void MatMulNodeConverter::convertV1(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
-
- assert(inputs.size() == 2);
- auto A = inputs[0];
- auto B = inputs[1];
- // MatMul multiply N-dimentional matrix
- // FullyConnected layer multiply only 2-dimentional matrix
- if (A->getShape().rank() != 2 || B->getShape().rank() != 2)
- throw std::runtime_error("Supported only 2D matrix multiplying!");
- // Calculate A * B.
- auto result = createOp<mir::ops::FullyConnectedOp>(graph, A, B)->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-void MatMulNodeConverter::convertV9(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- // Other type constraints
- convertV1(onnx_node, context);
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/MatMul.h b/compiler/mir-onnx-importer/Op/MatMul.h
deleted file mode 100644
index f418da8a8..000000000
--- a/compiler/mir-onnx-importer/Op/MatMul.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_MATMUL_H
-#define MIR_ONNX_OP_MATMUL_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class MatMulNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-
-private:
- void convertV1(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV9(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_MATMUL_H
diff --git a/compiler/mir-onnx-importer/Op/Max.cpp b/compiler/mir-onnx-importer/Op/Max.cpp
deleted file mode 100644
index 158820ceb..000000000
--- a/compiler/mir-onnx-importer/Op/Max.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Max.h"
-
-#include "ONNXHelpers.h"
-
-#include "mir/ops/MaxOp.h"
-
-namespace mir_onnx
-{
-
-void MaxNodeConverter::convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
- auto result = createOp<mir::ops::MaxOp>(graph, inputs[0], inputs[1])->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Max.h b/compiler/mir-onnx-importer/Op/Max.h
deleted file mode 100644
index c0ac00e40..000000000
--- a/compiler/mir-onnx-importer/Op/Max.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_MAX_H
-#define MIR_ONNX_OP_MAX_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class MaxNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_MAX_H
diff --git a/compiler/mir-onnx-importer/Op/MaxPool.cpp b/compiler/mir-onnx-importer/Op/MaxPool.cpp
deleted file mode 100644
index a8fa5208a..000000000
--- a/compiler/mir-onnx-importer/Op/MaxPool.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "MaxPool.h"
-
-#include "ONNXHelpers.h"
-#include "AttributeHelpers.h"
-#include "ConvPoolHelpers.h"
-
-#include "mir/ops/MaxPool2DOp.h"
-
-namespace mir_onnx
-{
-
-void MaxPoolNodeConverter::convert(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto opset_version = context->getOpsetVersion(onnx_node.domain());
- if (opset_version >= 10)
- convertV10(onnx_node, context);
- else if (opset_version >= 8)
- convertV8(onnx_node, context);
- else if (opset_version >= 1)
- convertV1(onnx_node, context);
- else
- throw std::runtime_error("Not supported opset version on MaxPool operation!");
-}
-
-void MaxPoolNodeConverter::convertV1(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
-
- assert(inputs.size() == 1);
- auto input = inputs[0];
-
- const auto &input_shape = input->getShape();
- if (input_shape.rank() != 4)
- throw std::runtime_error("MaxPool: only 2-D input is supported.");
-
- constexpr int num_spatial_dims = 2;
-
- const auto strides =
- getAttributeValue(onnx_node, "strides", std::vector<std::int32_t>(num_spatial_dims, 1));
- if (strides.size() != num_spatial_dims)
- throw std::runtime_error("MaxPool: attribute 'strides' has incorrect size.");
-
- const auto kernel_shape = getAttributeValue<std::vector<std::int32_t>>(onnx_node, "kernel_shape");
- if (kernel_shape.size() != num_spatial_dims)
- throw std::runtime_error("MaxPool: attribute 'kernel_shape' has incorrect size.");
-
- std::vector<std::int32_t> padding_before;
- std::vector<std::int32_t> padding_after;
- if (const auto *pads_attr = findAttribute(onnx_node, "pads"))
- {
- const auto pads = getAttributeValue<std::vector<std::int32_t>>(*pads_attr);
- if (pads.size() != num_spatial_dims * 2)
- throw std::runtime_error("MaxPool: attribute 'pads' has incorrect size.");
- padding_before.assign(pads.cbegin(), std::next(pads.cbegin(), num_spatial_dims));
- padding_after.assign(std::next(pads.cbegin(), num_spatial_dims), pads.cend());
- }
- else
- {
- const auto auto_pad = getAttributeValue<std::string>(onnx_node, "auto_pad", "NOTSET");
- const std::vector<std::int32_t> dilations(num_spatial_dims, 1);
- inferAutoPadding(auto_pad, input_shape, dilations, strides, kernel_shape, padding_before,
- padding_after);
- }
-
- auto result = createOp<mir::ops::MaxPool2DOp>(graph, input, kernel_shape, strides, padding_before,
- padding_after, mir::DataFormat::NCHW)
- ->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-void MaxPoolNodeConverter::convertV8(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto storage_order = getAttributeValue<int64_t>(onnx_node, "storage_order", 0);
- if (storage_order != 0)
- throw std::runtime_error("Not supported storage order attribute!");
-
- convertV1(onnx_node, context);
-}
-
-void MaxPoolNodeConverter::convertV10(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto ceil_mode = getAttributeValue<int64_t>(onnx_node, "ceil_mode", 0);
- if (ceil_mode != 0)
- throw std::runtime_error("Not supported ceil_mode attribute!");
-
- const auto *dilations = findAttribute(onnx_node, "dilations");
- if (dilations != nullptr)
- {
- // check default (=1) dilations on each spatial axis
- for (auto index = 0; index < dilations->ints_size(); index++)
- if (dilations->ints(index) != 1)
- throw std::runtime_error("Not supported dilations in MaxPool operation!");
- }
-
- convertV8(onnx_node, context);
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/MaxPool.h b/compiler/mir-onnx-importer/Op/MaxPool.h
deleted file mode 100644
index fc8372142..000000000
--- a/compiler/mir-onnx-importer/Op/MaxPool.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_MAX_POOL_H
-#define MIR_ONNX_OP_MAX_POOL_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class MaxPoolNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-
-private:
- void convertV1(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV8(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV10(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_MAX_POOL_H
diff --git a/compiler/mir-onnx-importer/Op/Mul.cpp b/compiler/mir-onnx-importer/Op/Mul.cpp
deleted file mode 100644
index 68358c719..000000000
--- a/compiler/mir-onnx-importer/Op/Mul.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Mul.h"
-
-#include "ONNXHelpers.h"
-
-#include "mir/ops/MulOp.h"
-
-namespace mir_onnx
-{
-
-void MulNodeConverter::convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
- auto result = createOp<mir::ops::MulOp>(graph, inputs[0], inputs[1])->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Mul.h b/compiler/mir-onnx-importer/Op/Mul.h
deleted file mode 100644
index c09677416..000000000
--- a/compiler/mir-onnx-importer/Op/Mul.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_MUL_H
-#define MIR_ONNX_OP_MUL_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class MulNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_MUL_H
diff --git a/compiler/mir-onnx-importer/Op/Pad.cpp b/compiler/mir-onnx-importer/Op/Pad.cpp
deleted file mode 100644
index 5015c4168..000000000
--- a/compiler/mir-onnx-importer/Op/Pad.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Pad.h"
-
-#include "ONNXHelpers.h"
-#include "AttributeHelpers.h"
-
-#include "mir/ops/PadOp.h"
-
-namespace mir_onnx
-{
-
-void PadNodeConverter::convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const
-{
- const auto opset_version = context->getOpsetVersion(onnx_node.domain());
- if (opset_version >= 2)
- convertPadAttrName("paddings", onnx_node, context);
- else if (opset_version >= 1)
- convertPadAttrName("pads", onnx_node, context);
- else
- throw std::runtime_error("Not supported opset version on Pad operation!");
-}
-
-void PadNodeConverter::convertPadAttrName(const std::string &pad_attr_name,
- const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
-
- assert(inputs.size() == 1);
- auto input = inputs[0];
-
- // 0.0f is the default value to be filled into padded cells.
- const auto value = getAttributeValue<float>(onnx_node, "value", 0.0f);
- const auto pads = getAttributeValue<std::vector<std::int64_t>>(onnx_node, pad_attr_name);
- // "constant" is the default mode.
- const auto mode = getAttributeValue<std::string>(onnx_node, "mode", "constant");
- if (mode != "constant")
- throw std::runtime_error("Not supported Pad mode attribute!");
-
- const int num_dims = input->getShape().rank();
- assert(pads.size() == num_dims * 2);
- std::vector<std::int32_t> padding_before(num_dims);
- std::vector<std::int32_t> padding_after(num_dims);
- for (int i = 0; i < num_dims; i++)
- {
- padding_before[i] = pads[i];
- padding_after[i] = pads[num_dims + i];
- }
-
- auto result =
- createOp<mir::ops::PadOp>(graph, input, padding_before, padding_after, value)->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Pad.h b/compiler/mir-onnx-importer/Op/Pad.h
deleted file mode 100644
index 7ab23877c..000000000
--- a/compiler/mir-onnx-importer/Op/Pad.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_PAD_H
-#define MIR_ONNX_OP_PAD_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class PadNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-
-private:
- void convertPadAttrName(const std::string &pad_attr_name, const onnx::NodeProto &onnx_node,
- ConverterContext *context) const;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_PAD_H
diff --git a/compiler/mir-onnx-importer/Op/ReduceMean.cpp b/compiler/mir-onnx-importer/Op/ReduceMean.cpp
deleted file mode 100644
index bb717d8ef..000000000
--- a/compiler/mir-onnx-importer/Op/ReduceMean.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ReduceMean.h"
-
-#include "ONNXHelpers.h"
-#include "AttributeHelpers.h"
-
-#include "mir/ops/ReduceMeanOp.h"
-
-#include <numeric>
-
-namespace mir_onnx
-{
-
-void ReduceMeanNodeConverter::convert(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto opset_version = context->getOpsetVersion(onnx_node.domain());
-
- if (opset_version >= 1)
- convertV1(onnx_node, context);
- else
- throw std::runtime_error("Not supported opset version on ReduceMean operation!");
-}
-
-void ReduceMeanNodeConverter::convertV1(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto inputs = context->getNodeInputs(onnx_node);
- assert(inputs.size() == 1);
-
- const auto axes = getAttributeValue<std::vector<std::int64_t>>(onnx_node, "axes");
- const auto keepdims = getAttributeValue<int64_t>(onnx_node, "keepdims", 1);
-
- std::vector<int32_t> reduce_dims;
- if (axes.empty())
- { // reduce over all dimensions
- reduce_dims.resize(inputs[0]->getShape().rank());
- std::iota(reduce_dims.begin(), reduce_dims.end(), 0);
- }
- else
- {
- reduce_dims.resize(axes.size());
- std::copy(axes.begin(), axes.end(), reduce_dims.begin());
- }
- // Keep the reduced dimension or not, default 1 mean keep reduced dimension.
- bool keep_dims = static_cast<bool>(keepdims);
-
- mir::Graph *graph = context->getGraph();
- auto result =
- createOp<mir::ops::ReduceMeanOp>(graph, inputs[0], reduce_dims, keep_dims)->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/ReduceMean.h b/compiler/mir-onnx-importer/Op/ReduceMean.h
deleted file mode 100644
index f03903a85..000000000
--- a/compiler/mir-onnx-importer/Op/ReduceMean.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_REDUCEMEAN_H
-#define MIR_ONNX_OP_REDUCEMEAN_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class ReduceMeanNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-
-private:
- void convertV1(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_REDUCEMEAN_H
diff --git a/compiler/mir-onnx-importer/Op/Relu.cpp b/compiler/mir-onnx-importer/Op/Relu.cpp
deleted file mode 100644
index 8037c7a42..000000000
--- a/compiler/mir-onnx-importer/Op/Relu.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Relu.h"
-
-#include "ONNXHelpers.h"
-
-#include "mir/ops/ReluOp.h"
-
-namespace mir_onnx
-{
-
-void ReluNodeConverter::convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
- assert(inputs.size() == 1);
- auto result = createOp<mir::ops::ReluOp>(graph, inputs[0])->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Relu.h b/compiler/mir-onnx-importer/Op/Relu.h
deleted file mode 100644
index c7f516e67..000000000
--- a/compiler/mir-onnx-importer/Op/Relu.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_RELU_H
-#define MIR_ONNX_OP_RELU_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class ReluNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_RELU_H
diff --git a/compiler/mir-onnx-importer/Op/Reshape.cpp b/compiler/mir-onnx-importer/Op/Reshape.cpp
deleted file mode 100644
index ac6a16d1a..000000000
--- a/compiler/mir-onnx-importer/Op/Reshape.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Reshape.h"
-
-#include "ONNXHelpers.h"
-#include "AttributeHelpers.h"
-
-#include "mir/Tensor.h"
-#include "mir/ShapeRange.h"
-
-#include "mir/ops/ConstantOp.h"
-#include "mir/ops/ReshapeOp.h"
-
-namespace mir_onnx
-{
-
-void ReshapeNodeConverter::convert(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto opset_version = context->getOpsetVersion(onnx_node.domain());
- if (opset_version >= 5)
- convertV5(onnx_node, context);
- else if (opset_version >= 1)
- convertV1(onnx_node, context);
- else
- throw std::runtime_error("Not supported opset version on Reshape operation!");
-}
-
-void ReshapeNodeConverter::convertV1(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
- // consumed_inputs attribute not used
- const auto *shape_attr = findAttribute(onnx_node, "shape");
- if (shape_attr && shape_attr->ints_size() > 0)
- {
- mir::Shape in_shape = inputs[0]->getShape();
- mir::Shape out_shape(shape_attr->ints_size());
- for (int32_t index = 0; index < out_shape.rank(); index++)
- {
- const auto dim_value = shape_attr->ints(index);
- if (dim_value == 0)
- out_shape.dim(index) = in_shape.dim(index);
- else
- out_shape.dim(index) = dim_value;
- }
-
- auto result = createOp<mir::ops::ReshapeOp>(graph, inputs[0], out_shape)->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
- }
- else // dimension value is unchanged
- {
- context->setNodeOutputs(onnx_node, {inputs[0]});
- }
-}
-
-void ReshapeNodeConverter::convertV5(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
- // The original shape
- const auto &in_shape = inputs[0]->getShape();
-
- // Input tensor describing the new shape
- auto *op = dynamic_cast<mir::ops::ConstantOp *>(inputs[1]->getNode());
- assert(op && "We support only constant shape input");
- auto shape_tensor = op->getValue();
- mir::Shape shape_tensor_shape = (shape_tensor).getShape();
- assert(shape_tensor_shape.rank() == 1);
- // The rank of the new shape
- auto cnt = shape_tensor_shape.numElements();
- // The vector to build the new shape from
- std::vector<int32_t> shape_vector(cnt);
- mir::ShapeRange out_range(shape_tensor_shape);
- mir::Tensor<int64_t> tensor_accessor(shape_tensor);
-
- int i = 0;
- for (auto idx : out_range)
- {
- if (tensor_accessor.at(idx) == 0)
- shape_vector[i] = in_shape.dim(i);
- else if (tensor_accessor.at(idx) == -1)
- shape_vector[i] = mir::Shape::autoDim;
- else
- shape_vector[i] = tensor_accessor.at(idx);
- i++;
- }
- auto out_shape = mir::Shape(shape_vector);
- auto result = createOp<mir::ops::ReshapeOp>(graph, inputs[0], out_shape)->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Reshape.h b/compiler/mir-onnx-importer/Op/Reshape.h
deleted file mode 100644
index f9f94faff..000000000
--- a/compiler/mir-onnx-importer/Op/Reshape.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_RESHAPE_H
-#define MIR_ONNX_OP_RESHAPE_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class ReshapeNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-
-private:
- void convertV1(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV5(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_RESHAPE_H
diff --git a/compiler/mir-onnx-importer/Op/Shape.cpp b/compiler/mir-onnx-importer/Op/Shape.cpp
deleted file mode 100644
index 1d4968cd1..000000000
--- a/compiler/mir-onnx-importer/Op/Shape.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Shape.h"
-
-#include "ONNXHelpers.h"
-
-#include "mir/TensorVariant.h"
-
-#include "mir/ops/ConstantOp.h"
-
-namespace mir_onnx
-{
-
-void ShapeNodeConverter::convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
- const auto &input_shape = inputs[0]->getShape();
- int size = input_shape.rank();
- mir::Shape output_shape{size};
- std::vector<float> data(static_cast<std::size_t>(size));
- for (int i = 0; i < size; i++)
- {
- data[i] = input_shape.dim(i);
- }
- mir::TensorVariant tensor(mir::DataType::FLOAT32, output_shape, data.data());
- auto result = createOp<mir::ops::ConstantOp>(graph, tensor)->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Shape.h b/compiler/mir-onnx-importer/Op/Shape.h
deleted file mode 100644
index b622dcd25..000000000
--- a/compiler/mir-onnx-importer/Op/Shape.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_SHAPE_H
-#define MIR_ONNX_OP_SHAPE_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class ShapeNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_SHAPE_H
diff --git a/compiler/mir-onnx-importer/Op/Sigmoid.cpp b/compiler/mir-onnx-importer/Op/Sigmoid.cpp
deleted file mode 100644
index aa80592a0..000000000
--- a/compiler/mir-onnx-importer/Op/Sigmoid.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Sigmoid.h"
-
-#include "ONNXHelpers.h"
-
-#include "mir/ops/SigmoidOp.h"
-
-namespace mir_onnx
-{
-
-void SigmoidNodeConverter::convert(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
- assert(inputs.size() == 1);
- auto result = createOp<mir::ops::SigmoidOp>(graph, inputs[0])->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Sigmoid.h b/compiler/mir-onnx-importer/Op/Sigmoid.h
deleted file mode 100644
index be7fc1192..000000000
--- a/compiler/mir-onnx-importer/Op/Sigmoid.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_SIGMOID_H
-#define MIR_ONNX_OP_SIGMOID_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class SigmoidNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_SIGMOID_H
diff --git a/compiler/mir-onnx-importer/Op/Softmax.cpp b/compiler/mir-onnx-importer/Op/Softmax.cpp
deleted file mode 100644
index 84db24ea9..000000000
--- a/compiler/mir-onnx-importer/Op/Softmax.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Softmax.h"
-
-#include "ONNXHelpers.h"
-#include "AttributeHelpers.h"
-
-#include "mir/ops/SoftmaxOp.h"
-
-namespace mir_onnx
-{
-
-void SoftmaxNodeConverter::convert(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
-
- // 1 is the default axis number.
- const auto axis = getAttributeValue<std::int64_t>(onnx_node, "axis", 1);
-
- auto result = createOp<mir::ops::SoftmaxOp>(graph, inputs[0], axis)->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Softmax.h b/compiler/mir-onnx-importer/Op/Softmax.h
deleted file mode 100644
index 77e9f6650..000000000
--- a/compiler/mir-onnx-importer/Op/Softmax.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_SOFTMAX_H
-#define MIR_ONNX_OP_SOFTMAX_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class SoftmaxNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_SOFTMAX_H
diff --git a/compiler/mir-onnx-importer/Op/Sum.cpp b/compiler/mir-onnx-importer/Op/Sum.cpp
deleted file mode 100644
index ddade1bd4..000000000
--- a/compiler/mir-onnx-importer/Op/Sum.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Sum.h"
-
-#include "ONNXHelpers.h"
-
-#include "mir/ops/AddOp.h"
-
-namespace mir_onnx
-{
-
-void SumNodeConverter::convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
- assert(inputs.size() >= 1);
-
- auto result = inputs[0];
- for (int i = 1; i < static_cast<int>(inputs.size()); ++i)
- {
- result = createOp<mir::ops::AddOp>(graph, result, inputs[i])->getOutput(0);
- }
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Sum.h b/compiler/mir-onnx-importer/Op/Sum.h
deleted file mode 100644
index 41455ca10..000000000
--- a/compiler/mir-onnx-importer/Op/Sum.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_SUM_H
-#define MIR_ONNX_OP_SUM_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class SumNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_SUM_H
diff --git a/compiler/mir-onnx-importer/Op/Transpose.cpp b/compiler/mir-onnx-importer/Op/Transpose.cpp
deleted file mode 100644
index 74987daa5..000000000
--- a/compiler/mir-onnx-importer/Op/Transpose.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Transpose.h"
-#include "ONNXHelpers.h"
-#include "AttributeHelpers.h"
-
-#include "mir/ops/TransposeOp.h"
-
-#include <numeric>
-
-namespace mir_onnx
-{
-
-void TransposeNodeConverter::convert(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto opset_version = context->getOpsetVersion(onnx_node.domain());
-
- if (opset_version >= 1)
- convertV1(onnx_node, context);
- else
- throw std::runtime_error("Not supported opset version on Transpose operation!");
-}
-
-void TransposeNodeConverter::convertV1(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
-
- assert(inputs.size() == 1);
- auto input = inputs[0];
-
- const auto num_axes = input->getShape().rank();
- std::vector<std::size_t> axis_order(num_axes);
- const auto *perm_attr = findAttribute(onnx_node, "perm");
-
- if (perm_attr == nullptr)
- {
- // Reverse the dimensions.
- std::iota(axis_order.rbegin(), axis_order.rend(), 0);
- }
- else
- {
- const auto perm = getAttributeValue<std::vector<std::int64_t>>(*perm_attr);
- assert(perm.size() == num_axes);
- std::copy(perm.cbegin(), perm.cend(), axis_order.begin());
- }
-
- auto result = createOp<mir::ops::TransposeOp>(graph, input, axis_order)->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Transpose.h b/compiler/mir-onnx-importer/Op/Transpose.h
deleted file mode 100644
index 7c9eb5208..000000000
--- a/compiler/mir-onnx-importer/Op/Transpose.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_TRANSPOSE_H
-#define MIR_ONNX_OP_TRANSPOSE_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class TransposeNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-
-private:
- void convertV1(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_TRANSPOSE_H
diff --git a/compiler/mir-onnx-importer/Op/Unsqueeze.cpp b/compiler/mir-onnx-importer/Op/Unsqueeze.cpp
deleted file mode 100644
index e1a80619b..000000000
--- a/compiler/mir-onnx-importer/Op/Unsqueeze.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Unsqueeze.h"
-
-#include "ONNXHelpers.h"
-#include "AttributeHelpers.h"
-
-#include "mir/ops/ReshapeOp.h"
-
-namespace mir_onnx
-{
-
-void UnsqueezeNodeConverter::convert(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
- const auto axes = getAttributeValue<std::vector<std::int64_t>>(onnx_node, "axes");
- assert(!axes.empty());
- const mir::Shape &input_shape = inputs[0]->getShape();
- const int out_rank = input_shape.rank() + static_cast<int>(axes.size());
- mir::Shape out_shape(out_rank);
- auto ints_iterator = axes.cbegin();
- int j = 0;
- for (int i = 0; i < out_rank; i++)
- {
- if (ints_iterator < axes.cend() && i == *ints_iterator)
- {
- out_shape.dim(i) = 1;
- ints_iterator++;
- }
- else
- {
- out_shape.dim(i) = input_shape.dim(j);
- j++;
- }
- }
- auto result = createOp<mir::ops::ReshapeOp>(graph, inputs[0], out_shape)->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Unsqueeze.h b/compiler/mir-onnx-importer/Op/Unsqueeze.h
deleted file mode 100644
index b84c6e9c6..000000000
--- a/compiler/mir-onnx-importer/Op/Unsqueeze.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_UNSQUEEZE_H
-#define MIR_ONNX_OP_UNSQUEEZE_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class UnsqueezeNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_UNSQUEEZE_H
diff --git a/compiler/mir-onnx-importer/Op/Upsample.cpp b/compiler/mir-onnx-importer/Op/Upsample.cpp
deleted file mode 100644
index 3c8e3141b..000000000
--- a/compiler/mir-onnx-importer/Op/Upsample.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Upsample.h"
-
-#include "ONNXHelpers.h"
-#include "AttributeHelpers.h"
-
-#include "mir/Tensor.h"
-
-#include "mir/ops/ConstantOp.h"
-#include "mir/ops/ResizeOp.h"
-
-namespace mir_onnx
-{
-
-void UpsampleNodeConverter::convert(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- const auto opset_version = context->getOpsetVersion(onnx_node.domain());
- if (opset_version >= 10)
- convertV10(onnx_node, context);
- else if (opset_version >= 9)
- convertV9(onnx_node, context);
- else if (opset_version >= 7)
- convertV7(onnx_node, context);
- else if (opset_version >= 1)
- convertV1(onnx_node, context);
- else
- throw std::runtime_error("Not supported opset version on Upsample operation!");
-}
-
-void UpsampleNodeConverter::convertV1(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
-
- // "nearest" is the default mode.
- std::string mode = getAttributeValue<std::string>(onnx_node, "mode", "nearest");
- assert(mode == "nearest" && "Unsupported upscale mode!");
-
- const float h_scale = getAttributeValue<float>(onnx_node, "height_scale", 0.0f); // required
- const float w_scale = getAttributeValue<float>(onnx_node, "width_scale", 0.0f); // required
- if (h_scale < 1.0f || w_scale < 1.0f)
- throw std::runtime_error("Wrong scale attributes!");
-
- assert(inputs[0]->getShape().rank() == 4 && "Only rank 4 is supported");
- std::vector<float> scales_vector(4);
- // NCHW
- scales_vector.at(0) = 1.0f;
- scales_vector.at(1) = 1.0f;
- scales_vector.at(2) = h_scale;
- scales_vector.at(3) = w_scale;
-
- auto result =
- createOp<mir::ops::ResizeOp>(graph, inputs[0],
- mir::ops::ResizeOp::ResizeMethod::nearestNeighbor, scales_vector)
- ->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-void UpsampleNodeConverter::convertV7(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
-
- // "nearest" is the default mode.
- std::string mode = getAttributeValue<std::string>(onnx_node, "mode", "nearest");
- assert(mode == "nearest" && "Unsupported upscale mode!");
-
- const auto *scales_attr = findAttribute(onnx_node, "scales");
- if (!scales_attr)
- throw std::runtime_error("Not enough required scales attribute!");
-
- if (scales_attr->floats_size() != inputs[0]->getShape().rank())
- throw std::runtime_error(
- "Number of elements of scales should be the same as the rank of input");
-
- assert(inputs[0]->getShape().rank() == 4 && "Only rank 4 is supported");
- std::vector<float> scales_vector(4);
- // NCHW
- scales_vector.at(0) = scales_attr->floats(0);
- scales_vector.at(1) = scales_attr->floats(1);
- scales_vector.at(2) = scales_attr->floats(2);
- scales_vector.at(3) = scales_attr->floats(3);
-
- auto result =
- createOp<mir::ops::ResizeOp>(graph, inputs[0],
- mir::ops::ResizeOp::ResizeMethod::nearestNeighbor, scales_vector)
- ->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-void UpsampleNodeConverter::convertV9(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
- mir::Graph *graph = context->getGraph();
-
- // "nearest" is the default mode.
- const auto mode = getAttributeValue<std::string>(onnx_node, "mode", "nearest");
- assert(mode == "nearest" && "Unsupported upscale mode!");
-
- // relies on attributes being lifted to constants (ONNX optimization pass)
- assert(inputs.size() > 1);
- auto *scales = dynamic_cast<mir::ops::ConstantOp *>(inputs[1]->getNode());
- assert(scales && "Weights could be a constant tensor only");
- auto scales_tensor = mir::Tensor<float>(scales->getValue());
- int rank = inputs[0]->getShape().rank();
- assert(scales_tensor.getShape().numElements() == rank &&
- "The number of elements of 'scales' should be the same as the rank of input 'X'");
- assert(rank == 4 && "Only rank 4 is supported");
- std::vector<float> scales_vector(4);
- assert(scales_tensor.getShape().rank() == 1 && "Scales are a 1d tensor");
- for (int i = 0; i < scales_tensor.getShape().numElements(); i++)
- scales_vector[i] = scales_tensor.atOffset(i);
-
- auto result =
- createOp<mir::ops::ResizeOp>(graph, inputs[0],
- mir::ops::ResizeOp::ResizeMethod::nearestNeighbor, scales_vector)
- ->getOutput(0);
-
- context->setNodeOutputs(onnx_node, {result});
-}
-
-void UpsampleNodeConverter::convertV10(const onnx::NodeProto &onnx_node,
- ConverterContext *context) const
-{
- throw std::runtime_error("Version 10 of Upsample operation is deprecated!");
-}
-
-} // namespace mir_onnx
diff --git a/compiler/mir-onnx-importer/Op/Upsample.h b/compiler/mir-onnx-importer/Op/Upsample.h
deleted file mode 100644
index dd1ccb7c2..000000000
--- a/compiler/mir-onnx-importer/Op/Upsample.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_ONNX_OP_UPSAMPLE_H
-#define MIR_ONNX_OP_UPSAMPLE_H
-
-#include "ONNXNodeConverterRegistry.h"
-
-namespace mir_onnx
-{
-
-class UpsampleNodeConverter : public NodeConverter
-{
-public:
- void convert(const onnx::NodeProto &onnx_node, ConverterContext *context) const override;
-
-private:
- void convertV1(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV7(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV9(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
- void convertV10(const onnx::NodeProto &onnx_node, ConverterContext *context) const;
-};
-
-} // namespace mir_onnx
-
-#endif // MIR_ONNX_OP_UPSAMPLE_H
diff --git a/compiler/mir-onnx-importer/requires.cmake b/compiler/mir-onnx-importer/requires.cmake
deleted file mode 100644
index 1059c50d3..000000000
--- a/compiler/mir-onnx-importer/requires.cmake
+++ /dev/null
@@ -1 +0,0 @@
-require("mir")
diff --git a/compiler/mir-tflite-importer/CMakeLists.txt b/compiler/mir-tflite-importer/CMakeLists.txt
deleted file mode 100644
index 104f7b6d4..000000000
--- a/compiler/mir-tflite-importer/CMakeLists.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-nncc_find_package(FlatBuffers REQUIRED)
-
-if (NOT FlatBuffers_FOUND)
- return()
-endif ()
-
-FlatBuffers_Target(mir_tflite_schema
- OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated/schema"
- SCHEMA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/schema"
- SCHEMA_FILES schema.fbs)
-
-
-set(MIR_TFLITE_IMPORTER_SOURCES
- tflite_importer.cpp
- tflite_importer.h
- tflite_op_creator.cpp
- tflite_op_creator.h)
-
-add_library(mir_tflite_importer STATIC ${MIR_TFLITE_IMPORTER_SOURCES})
-set_target_properties(mir_tflite_importer PROPERTIES POSITION_INDEPENDENT_CODE ON)
-target_include_directories(mir_tflite_importer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
-target_link_libraries(mir_tflite_importer PUBLIC mir mir_tflite_schema PRIVATE stdex)
diff --git a/compiler/mir-tflite-importer/requires.cmake b/compiler/mir-tflite-importer/requires.cmake
deleted file mode 100644
index 1059c50d3..000000000
--- a/compiler/mir-tflite-importer/requires.cmake
+++ /dev/null
@@ -1 +0,0 @@
-require("mir")
diff --git a/compiler/mir-tflite-importer/schema/schema.fbs b/compiler/mir-tflite-importer/schema/schema.fbs
deleted file mode 100644
index 3ece383d2..000000000
--- a/compiler/mir-tflite-importer/schema/schema.fbs
+++ /dev/null
@@ -1,794 +0,0 @@
-// 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.
-
-// Revision History
-// Version 0: Initial version.
-// Version 1: Add subgraphs to schema.
-// Version 2: Rename operators to conform to NN API.
-// Version 3: Move buffer data from Model.Subgraph.Tensors to Model.Buffers.
-
-namespace tflite;
-
-// This corresponds to the version.
-file_identifier "TFL3";
-// File extension of any written files.
-file_extension "tflite";
-
-// IMPORTANT: All new members of tables, enums and unions must be added at the
-// end to ensure backwards compatibility.
-
-// The type of data stored in a tensor.
-enum TensorType : byte {
- FLOAT32 = 0,
- FLOAT16 = 1,
- INT32 = 2,
- UINT8 = 3,
- INT64 = 4,
- STRING = 5,
- BOOL = 6,
- INT16 = 7,
- COMPLEX64 = 8,
- INT8 = 9,
-}
-
-// Custom quantization parameters for experimenting with new quantization
-// techniques.
-table CustomQuantization {
- custom:[ubyte] (force_align: 16);
-}
-
-// Represents a specific quantization technique's parameters.
-union QuantizationDetails {
- CustomQuantization,
-}
-
-// Parameters for converting a quantized tensor back to float.
-table QuantizationParameters {
- // These four parameters are the asymmetric linear quantization parameters.
- // Given a quantized value q, the corresponding float value f should be:
- // f = scale * (q - zero_point)
- // For other quantization types, the QuantizationDetails below is used.
- min:[float]; // For importing back into tensorflow.
- max:[float]; // For importing back into tensorflow.
- scale:[float]; // For dequantizing the tensor's values.
- zero_point:[long];
-
- // If this is not none, the quantization parameters above are ignored and the
- // value of the QuantizationDetails union below should be used.
- details:QuantizationDetails;
-}
-
-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
- // Tensorflow's NHWC).
- shape:[int];
- type:TensorType;
- // An index that refers to the buffers table at the root of the model. Or,
- // if there is no data buffer associated (i.e. intermediate results), then
- // this is 0 (which refers to an always existent empty buffer).
- //
- // The data_buffer itself is an opaque container, with the assumption that the
- // target device is little-endian. In addition, all builtin operators assume
- // the memory is ordered such that if `shape` is [4, 3, 2], then index
- // [i, j, k] maps to data_buffer[i*3*2 + j*2 + k].
- buffer:uint;
- name:string; // For debugging and importing back into tensorflow.
- quantization:QuantizationParameters; // Optional.
-
- is_variable:bool = false;
-}
-
-// A list of builtin operators. Builtin operators are slightly faster than custom
-// ones, but not by much. Moreover, while custom operators accept an opaque
-// object containing configuration parameters, builtins have a predetermined
-// set of acceptable options.
-enum BuiltinOperator : byte {
- ADD = 0,
- AVERAGE_POOL_2D = 1,
- CONCATENATION = 2,
- CONV_2D = 3,
- DEPTHWISE_CONV_2D = 4,
- // DEPTH_TO_SPACE = 5,
- DEQUANTIZE = 6,
- EMBEDDING_LOOKUP = 7,
- FLOOR = 8,
- FULLY_CONNECTED = 9,
- HASHTABLE_LOOKUP = 10,
- L2_NORMALIZATION = 11,
- L2_POOL_2D = 12,
- LOCAL_RESPONSE_NORMALIZATION = 13,
- LOGISTIC = 14,
- LSH_PROJECTION = 15,
- LSTM = 16,
- MAX_POOL_2D = 17,
- MUL = 18,
- RELU = 19,
- // NOTE(aselle): RELU_N1_TO_1 used to be called RELU1, but it was renamed
- // since different model developers use RELU1 in different ways. Never
- // create another op called RELU1.
- RELU_N1_TO_1 = 20,
- RELU6 = 21,
- RESHAPE = 22,
- RESIZE_BILINEAR = 23,
- RNN = 24,
- SOFTMAX = 25,
- SPACE_TO_DEPTH = 26,
- SVDF = 27,
- TANH = 28,
- // TODO(aselle): Consider rename to CONCATENATE_EMBEDDINGS
- CONCAT_EMBEDDINGS = 29,
- SKIP_GRAM = 30,
- CALL = 31,
- CUSTOM = 32,
- EMBEDDING_LOOKUP_SPARSE = 33,
- PAD = 34,
- UNIDIRECTIONAL_SEQUENCE_RNN = 35,
- GATHER = 36,
- BATCH_TO_SPACE_ND = 37,
- SPACE_TO_BATCH_ND = 38,
- TRANSPOSE = 39,
- MEAN = 40,
- SUB = 41,
- DIV = 42,
- SQUEEZE = 43,
- UNIDIRECTIONAL_SEQUENCE_LSTM = 44,
- STRIDED_SLICE = 45,
- BIDIRECTIONAL_SEQUENCE_RNN = 46,
- EXP = 47,
- TOPK_V2 = 48,
- SPLIT = 49,
- LOG_SOFTMAX = 50,
- // DELEGATE is a special op type for the operations which are delegated to
- // other backends.
- // WARNING: Experimental interface, subject to change
- DELEGATE = 51,
- BIDIRECTIONAL_SEQUENCE_LSTM = 52,
- CAST = 53,
- PRELU = 54,
- MAXIMUM = 55,
- ARG_MAX = 56,
- MINIMUM = 57,
- LESS = 58,
- NEG = 59,
- PADV2 = 60,
- GREATER = 61,
- GREATER_EQUAL = 62,
- LESS_EQUAL = 63,
- SELECT = 64,
- SLICE = 65,
- SIN = 66,
- TRANSPOSE_CONV = 67,
- SPARSE_TO_DENSE = 68,
- TILE = 69,
- EXPAND_DIMS = 70,
- EQUAL = 71,
- NOT_EQUAL = 72,
- LOG = 73,
- SUM = 74,
- SQRT = 75,
- RSQRT = 76,
- SHAPE = 77,
- POW = 78,
- ARG_MIN = 79,
- FAKE_QUANT = 80,
- REDUCE_PROD = 81,
- REDUCE_MAX = 82,
- PACK = 83,
- LOGICAL_OR = 84,
- ONE_HOT = 85,
- LOGICAL_AND = 86,
- LOGICAL_NOT = 87,
- UNPACK = 88,
- REDUCE_MIN = 89,
- FLOOR_DIV = 90,
- REDUCE_ANY = 91,
- SQUARE = 92,
- ZEROS_LIKE = 93,
- FILL = 94,
- FLOOR_MOD = 95,
- RANGE = 96,
- RESIZE_NEAREST_NEIGHBOR = 97,
- LEAKY_RELU = 98,
- SQUARED_DIFFERENCE = 99,
- MIRROR_PAD = 100,
- ABS = 101,
- SPLIT_V = 102,
-}
-
-// Options for the builtin operators.
-union BuiltinOptions {
- 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,
-}
-
-enum Padding : byte { SAME, VALID }
-
-enum ActivationFunctionType : byte {
- NONE = 0,
- RELU = 1,
- RELU_N1_TO_1 = 2,
- RELU6 = 3,
- TANH = 4,
- SIGN_BIT = 5,
-}
-
-table Conv2DOptions {
- padding:Padding;
- stride_w:int;
- stride_h:int;
- fused_activation_function:ActivationFunctionType;
- dilation_w_factor:int = 1;
- dilation_h_factor:int = 1;
-}
-
-table Pool2DOptions {
- padding:Padding;
- stride_w:int;
- stride_h:int;
- filter_width:int;
- filter_height:int;
- fused_activation_function:ActivationFunctionType;
-}
-
-table DepthwiseConv2DOptions {
- // Parameters for DepthwiseConv version 1 or above.
- padding:Padding;
- stride_w:int;
- stride_h:int;
- depth_multiplier:int;
- fused_activation_function:ActivationFunctionType;
- // Parameters for DepthwiseConv version 2 or above.
- dilation_w_factor:int = 1;
- dilation_h_factor:int = 1;
-}
-
-table ConcatEmbeddingsOptions {
- num_channels:int;
- num_columns_per_channel:[int];
- embedding_dim_per_channel:[int]; // This could be inferred from parameters.
-}
-
-enum LSHProjectionType: byte {
- UNKNOWN = 0,
- SPARSE = 1,
- DENSE = 2,
-}
-
-table LSHProjectionOptions {
- type: LSHProjectionType;
-}
-
-table SVDFOptions {
- rank:int;
- fused_activation_function:ActivationFunctionType;
-}
-
-// An implementation of TensorFlow RNNCell.
-table RNNOptions {
- fused_activation_function:ActivationFunctionType;
-}
-
-// An implementation of TensorFlow dynamic_rnn with RNNCell.
-table SequenceRNNOptions {
- time_major:bool;
- fused_activation_function:ActivationFunctionType;
-}
-
-// An implementation of TensorFlow bidrectional_dynamic_rnn with RNNCell.
-table BidirectionalSequenceRNNOptions {
- time_major:bool;
- fused_activation_function:ActivationFunctionType;
- merge_outputs: bool;
-}
-
-enum FullyConnectedOptionsWeightsFormat: byte {
- DEFAULT = 0,
- SHUFFLED4x16INT8 = 1,
-}
-
-// An implementation of TensorFlow fully_connected (a.k.a Dense) layer.
-table FullyConnectedOptions {
- // Parameters for FullyConnected version 1 or above.
- fused_activation_function:ActivationFunctionType;
-
- // Parameters for FullyConnected version 2 or above.
- weights_format:FullyConnectedOptionsWeightsFormat = DEFAULT;
-}
-
-table SoftmaxOptions {
- beta: float;
-}
-
-// An implementation of TensorFlow concat.
-table ConcatenationOptions {
- axis:int;
- fused_activation_function:ActivationFunctionType;
-}
-
-table AddOptions {
- fused_activation_function:ActivationFunctionType;
-}
-
-table MulOptions {
- fused_activation_function:ActivationFunctionType;
-}
-
-table L2NormOptions {
- fused_activation_function:ActivationFunctionType;
-}
-
-table LocalResponseNormalizationOptions {
- radius:int;
- bias:float;
- alpha:float;
- beta:float;
-}
-
-enum LSTMKernelType : byte {
- // Full LSTM kernel which supports peephole and projection.
- FULL = 0,
- // Basic LSTM kernels. Equivalent to TensorFlow BasicLSTMCell.
- BASIC = 1,
-}
-
-// An implementation of TensorFlow LSTMCell and CoupledInputForgetGateLSTMCell
-table LSTMOptions {
- // Parameters for LSTM version 1 or above.
- fused_activation_function:ActivationFunctionType;
- cell_clip: float; // Optional, 0.0 means no clipping
- proj_clip: float; // Optional, 0.0 means no clipping
-
- // Parameters for LSTM version 2 or above.
- // Basic kernel is only supported in version 2 or above.
- kernel_type: LSTMKernelType = FULL;
-}
-
-// An implementation of TensorFlow dynamic_rnn with LSTMCell.
-table UnidirectionalSequenceLSTMOptions {
- fused_activation_function:ActivationFunctionType;
- cell_clip: float; // Optional, 0.0 means no clipping
- proj_clip: float; // Optional, 0.0 means no clipping
-
- // If true then first dimension is sequence, otherwise batch.
- time_major:bool;
-}
-
-table BidirectionalSequenceLSTMOptions {
- fused_activation_function:ActivationFunctionType;
- cell_clip: float; // Optional, 0.0 means no clipping
- proj_clip: float; // Optional, 0.0 means no clipping
-
- // If true, store the outputs of both directions into the first output.
- merge_outputs: bool;
-}
-
-table ResizeBilinearOptions {
- new_height: int (deprecated);
- new_width: int (deprecated);
- align_corners: bool;
-}
-
-table ResizeNearestNeighborOptions {
- align_corners: bool;
-}
-
-// A call operation options
-table CallOptions {
- // The subgraph index that needs to be called.
- subgraph:uint;
-}
-
-table PadOptions {
-}
-
-table PadV2Options {
-}
-
-table ReshapeOptions {
- new_shape:[int];
-}
-
-table SpaceToBatchNDOptions {
-}
-
-table BatchToSpaceNDOptions {
-}
-
-table SkipGramOptions {
- ngram_size: int;
- max_skip_size: int;
- include_all_ngrams: bool;
-}
-
-table SpaceToDepthOptions {
- block_size: int;
-}
-
-table SubOptions {
- fused_activation_function:ActivationFunctionType;
-}
-
-table DivOptions {
- fused_activation_function:ActivationFunctionType;
-}
-
-table TopKV2Options {
-}
-
-enum CombinerType : byte {
- SUM = 0,
- MEAN = 1,
- SQRTN = 2,
-}
-
-table EmbeddingLookupSparseOptions {
- combiner:CombinerType;
-}
-
-table GatherOptions {
- axis: int;
-}
-
-table TransposeOptions {
-}
-
-table ExpOptions {
-}
-
-table ReducerOptions {
- keep_dims: bool;
-}
-
-table SqueezeOptions {
- squeeze_dims:[int];
-}
-
-table SplitOptions {
- num_splits: int;
-}
-
-table SplitVOptions {
- num_splits: int;
-}
-
-table StridedSliceOptions {
- begin_mask: int;
- end_mask: int;
- ellipsis_mask: int;
- new_axis_mask: int;
- shrink_axis_mask: int;
-}
-
-table LogSoftmaxOptions {
-}
-
-table CastOptions {
- in_data_type: TensorType;
- out_data_type: TensorType;
-}
-
-table DequantizeOptions {
-}
-
-table MaximumMinimumOptions {
-}
-
-table TileOptions {
-}
-
-table ArgMaxOptions {
- output_type : TensorType;
-}
-
-table ArgMinOptions {
- output_type : TensorType;
-}
-
-table GreaterOptions {
-}
-
-table GreaterEqualOptions {
-}
-
-table LessOptions {
-}
-
-table LessEqualOptions {
-}
-
-table NegOptions {
-}
-
-table SelectOptions {
-}
-
-table SliceOptions {
-}
-
-table TransposeConvOptions {
- padding:Padding;
- stride_w:int;
- stride_h:int;
-}
-
-table ExpandDimsOptions {
-}
-
-table SparseToDenseOptions {
- validate_indices:bool;
-}
-
-table EqualOptions {
-}
-
-table NotEqualOptions {
-}
-
-table ShapeOptions {
- // Optional output type of the operation (int32 or int64). Defaults to int32.
- out_type : TensorType;
-}
-
-table PowOptions {
-}
-
-table FakeQuantOptions {
- // Parameters supported by version 1:
- min:float;
- max:float;
- num_bits:int;
-
- // Parameters supported by version 2:
- narrow_range:bool;
-}
-
-table PackOptions {
- values_count:int;
- axis:int;
-}
-
-table LogicalOrOptions {
-}
-
-table OneHotOptions {
- axis:int;
-}
-
-table AbsOptions {
-}
-
-
-table LogicalAndOptions {
-}
-
-table LogicalNotOptions {
-}
-
-table UnpackOptions {
- num:int;
- axis:int;
-}
-
-table FloorDivOptions {
-}
-
-table SquareOptions {
-}
-
-table ZerosLikeOptions {
-}
-
-table FillOptions {
-}
-
-table FloorModOptions {
-}
-
-table RangeOptions {
-}
-
-table LeakyReluOptions {
- alpha:float;
-}
-
-table SquaredDifferenceOptions {
-}
-
-enum MirrorPadMode : byte {
- // Doesn't include borders.
- REFLECT = 0,
- // Includes borders.
- SYMMETRIC = 1,
-}
-
-table MirrorPadOptions {
- mode:MirrorPadMode;
-}
-
-// An OperatorCode can be an enum value (BuiltinOperator) if the operator is a
-// builtin, or a string if the operator is custom.
-table OperatorCode {
- builtin_code:BuiltinOperator;
- custom_code:string;
-
- // The version of the operator. The version need to be bumped whenever new
- // parameters are introduced into an op.
- version:int = 1;
-}
-
-enum CustomOptionsFormat : byte {
- FLEXBUFFERS = 0,
-}
-
-// An operator takes tensors as inputs and outputs. The type of operation being
-// performed is determined by an index into the list of valid OperatorCodes,
-// while the specifics of each operations is configured using builtin_options
-// or custom_options.
-table Operator {
- // Index into the operator_codes array. Using an integer here avoids
- // complicate map lookups.
- opcode_index:uint;
-
- // Optional input and output tensors are indicated by -1.
- inputs:[int];
- outputs:[int];
-
- builtin_options:BuiltinOptions;
- custom_options:[ubyte];
- custom_options_format:CustomOptionsFormat;
-
- // A list of booleans indicating the input tensors which are being mutated by
- // this operator.(e.g. used by RNN and LSTM).
- // For example, if the "inputs" array refers to 5 tensors and the second and
- // fifth are mutable variables, then this list will contain
- // [false, true, false, false, true].
- //
- // If the list is empty, no variable is mutated in this operator.
- // The list either has the same length as `inputs`, or is empty.
- mutating_variable_inputs:[bool];
-}
-
-// The root type, defining a subgraph, which typically represents an entire
-// model.
-table SubGraph {
- // A list of all tensors used in this subgraph.
- tensors:[Tensor];
-
- // Indices of the tensors that are inputs into this subgraph. Note this is
- // the list of non-static tensors that feed into the subgraph for inference.
- inputs:[int];
-
- // Indices of the tensors that are outputs out of this subgraph. Note this is
- // the list of output tensors that are considered the product of the
- // subgraph's inference.
- outputs:[int];
-
- // All operators, in execution order.
- operators:[Operator];
-
- // Name of this subgraph (used for debugging).
- name:string;
-}
-
-// Table of raw data buffers (used for constant tensors). Referenced by tensors
-// by index. The generous alignment accommodates mmap-friendly data structures.
-table Buffer {
- data:[ubyte] (force_align: 16);
-}
-
-table Model {
- // Version of the schema.
- version:uint;
-
- // A list of all operator codes used in this model. This is
- // kept in order because operators carry an index into this
- // vector.
- operator_codes:[OperatorCode];
-
- // All the subgraphs of the model. The 0th is assumed to be the main
- // model.
- subgraphs:[SubGraph];
-
- // A description of the model.
- description:string;
-
- // Buffers of the model.
- // Note the 0th entry of this array must be an empty buffer (sentinel).
- // This is a convention so that tensors without a buffer can provide 0 as
- // their buffer.
- buffers:[Buffer];
-
- // Metadata about the model. Indirects into the existings buffers list.
- metadata_buffer:[int];
-}
-
-root_type Model; \ No newline at end of file
diff --git a/compiler/mir-tflite-importer/schema/schema.meta b/compiler/mir-tflite-importer/schema/schema.meta
deleted file mode 100644
index 72f9afd13..000000000
--- a/compiler/mir-tflite-importer/schema/schema.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-REPO=https://github.com/tensorflow/tensorflow.git
-COMMIT=49123b0
diff --git a/compiler/mir-tflite-importer/tflite_importer.cpp b/compiler/mir-tflite-importer/tflite_importer.cpp
deleted file mode 100644
index 4a43a0d8d..000000000
--- a/compiler/mir-tflite-importer/tflite_importer.cpp
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "tflite_importer.h"
-#include "tflite_op_creator.h"
-
-#include "mir/ops/ConstantOp.h"
-#include "mir/ops/OutputOp.h"
-
-#include <fstream>
-#include <stdex/Memory.h>
-#include <utility>
-
-using namespace ::tflite;
-
-namespace mir_tflite
-{
-
-TfliteImporter::TfliteImporter(std::string filename) : _filename(std::move(filename))
-{
- _graph = stdex::make_unique<mir::Graph>();
- _opCreator = stdex::make_unique<TFLiteOpCreator>(_graph.get());
-}
-
-TfliteImporter::~TfliteImporter() = default;
-
-void TfliteImporter::import()
-{
- std::ifstream stream(_filename, std::ios::in | std::ios::binary);
- if (stream.fail())
- throw std::runtime_error("Couldn't open file \"" + _filename + "\".");
-
- stream.seekg(0, std::ios::end);
- std::streampos end = stream.tellg();
- stream.seekg(0, std::ios::beg);
- std::streampos begin = stream.tellg();
- const auto file_size = end - begin;
-
- _modelRaw = std::unique_ptr<char[]>(new char[file_size]);
- stream.read(_modelRaw.get(), file_size);
- if (stream.fail())
- throw std::runtime_error("Couldn't read file \"" + _filename + "\".");
-
- auto verifier =
- flatbuffers::Verifier(reinterpret_cast<const uint8_t *>(_modelRaw.get()), file_size);
-
- if (!VerifyModelBuffer(verifier))
- throw std::runtime_error("Could not load model: " + _filename + "\n");
-
- _modelPacked = GetModel(_modelRaw.get());
-
- _opcodes = _modelPacked->operator_codes();
- _buffers = _modelPacked->buffers();
- collectUnsupportedOps();
-}
-
-static const std::set<tflite::BuiltinOperator> supportedOperators = {
- BuiltinOperator_ADD,
- BuiltinOperator_AVERAGE_POOL_2D,
- BuiltinOperator_CONCATENATION,
- BuiltinOperator_CONV_2D,
- BuiltinOperator_DEPTHWISE_CONV_2D,
- BuiltinOperator_DIV,
- BuiltinOperator_FULLY_CONNECTED,
- BuiltinOperator_LEAKY_RELU,
- BuiltinOperator_LOGISTIC,
- BuiltinOperator_MAX_POOL_2D,
- BuiltinOperator_MAXIMUM,
- BuiltinOperator_MEAN,
- BuiltinOperator_MUL,
- BuiltinOperator_PAD,
- BuiltinOperator_RELU,
- BuiltinOperator_RELU6,
- BuiltinOperator_RESHAPE,
- BuiltinOperator_RESIZE_NEAREST_NEIGHBOR,
- BuiltinOperator_SHAPE,
- BuiltinOperator_SLICE,
- BuiltinOperator_SOFTMAX,
- BuiltinOperator_SQRT,
- BuiltinOperator_SQUARED_DIFFERENCE,
- BuiltinOperator_SQUEEZE,
- BuiltinOperator_STRIDED_SLICE,
- BuiltinOperator_SUB,
- BuiltinOperator_TANH,
- BuiltinOperator_TRANSPOSE,
- BuiltinOperator_TRANSPOSE_CONV,
-};
-
-void TfliteImporter::collectUnsupportedOps()
-{
- std::set<std::string> errors;
- for (auto sub_graph : *(_modelPacked->subgraphs()))
- for (auto op : *(sub_graph->operators()))
- {
- BuiltinOperator opcode = (*_opcodes)[op->opcode_index()]->builtin_code();
- if (supportedOperators.find(opcode) == supportedOperators.end())
- {
- if (opcode <= BuiltinOperator_MAX)
- errors.insert(std::string(EnumNameBuiltinOperator(opcode)) + ": unsupported operator");
- else
- errors.insert(std::to_string(opcode) + ": unsuppored in tflite custom opcode");
- }
- }
-
- if (!errors.empty())
- {
- std::string msg("NNC can't load model. Detected problems:");
- for (const auto &e : errors)
- msg.append("\n * " + e);
- throw std::runtime_error(msg);
- }
-}
-
-std::unique_ptr<mir::Graph> TfliteImporter::createIR()
-{
- walkGraphAndCreateMIR();
- return std::move(_graph);
-}
-
-std::unique_ptr<mir::Graph> TfliteImporter::importModel()
-{
- import();
- return createIR();
-}
-
-void TfliteImporter::walkGraphAndCreateMIR()
-{
- walkModel(_modelPacked);
- setGraphOutputs();
-}
-
-void TfliteImporter::walkModel(const Model * /*m*/)
-{
- for (auto sub_graph : *(_modelPacked->subgraphs()))
- walkSubGraph(sub_graph);
-}
-
-void TfliteImporter::walkSubGraph(const SubGraph *s)
-{
- _tensors = s->tensors();
-
- _graphInputs.assign(s->inputs()->begin(), s->inputs()->end());
- _graphOutputs.assign(s->outputs()->begin(), s->outputs()->end());
-
- for (auto i : *s->inputs())
- {
- const Tensor *t = (*_tensors)[i];
-
- mir::Shape input_shape(t->shape()->size());
- for (int dim = 0; dim < t->shape()->size(); ++dim)
- {
- input_shape.dim(dim) = t->shape()->Get(dim);
- }
-
- auto input = _graph->create<mir::ops::InputOp>(input_shape)->getOutput(0);
- input->setName(t->name()->c_str());
- assert(_tensorMap.find(i) == _tensorMap.cend());
- _tensorMap[i] = input;
- }
-
- for (auto op : *(s->operators()))
- walkOperator(op);
-}
-
-void TfliteImporter::walkOperator(const Operator *op)
-{
- std::vector<mir::Operation::Output *> inputs = getMIRInputsForOperator(op);
- std::vector<mir::Operation::Output *> outputs;
-
- BuiltinOperator opcode = (*_opcodes)[op->opcode_index()]->builtin_code();
- switch (opcode)
- {
- case BuiltinOperator_CONV_2D:
- outputs = _opCreator->convertConv2D(op->builtin_options_as<Conv2DOptions>(), inputs);
- break;
- case BuiltinOperator_DEPTHWISE_CONV_2D:
- outputs = _opCreator->convertDepthwiseConv2D(op->builtin_options_as<DepthwiseConv2DOptions>(),
- inputs);
- break;
- case BuiltinOperator_MAX_POOL_2D:
- outputs = _opCreator->convertMaxPool2D(op->builtin_options_as<Pool2DOptions>(), inputs);
- break;
- case BuiltinOperator_AVERAGE_POOL_2D:
- outputs = _opCreator->convertAveragePool2D(op->builtin_options_as<Pool2DOptions>(), inputs);
- break;
- case BuiltinOperator_CONCATENATION:
- outputs =
- _opCreator->convertConcatenation(op->builtin_options_as<ConcatenationOptions>(), inputs);
- break;
- case BuiltinOperator_RESHAPE:
- outputs = _opCreator->convertReshape(op->builtin_options_as<ReshapeOptions>(), inputs);
- break;
- case BuiltinOperator_RESIZE_NEAREST_NEIGHBOR:
- outputs = _opCreator->convertResizeNearestNeighbor(
- op->builtin_options_as<ResizeNearestNeighborOptions>(), inputs);
- break;
- case BuiltinOperator_MEAN:
- outputs = _opCreator->convertMean(op->builtin_options_as<ReducerOptions>(), inputs);
- break;
- case BuiltinOperator_FULLY_CONNECTED:
- outputs = _opCreator->convertFullyConnected(op->builtin_options_as<FullyConnectedOptions>(),
- inputs);
- break;
- case BuiltinOperator_SOFTMAX:
- outputs = _opCreator->convertSoftmax(op->builtin_options_as<SoftmaxOptions>(), inputs);
- break;
- case BuiltinOperator_SLICE:
- outputs = _opCreator->convertSlice(op->builtin_options_as<SliceOptions>(), inputs);
- break;
- case BuiltinOperator_SQUEEZE:
- outputs = _opCreator->convertSqueeze(op->builtin_options_as<SqueezeOptions>(), inputs);
- break;
- case BuiltinOperator_LOGISTIC:
- outputs = _opCreator->convertLogistic(inputs);
- break;
- case BuiltinOperator_SQRT:
- outputs = _opCreator->convertSqrt(inputs);
- break;
- case BuiltinOperator_ADD:
- outputs = _opCreator->convertAdd(op->builtin_options_as<AddOptions>(), inputs);
- break;
- case BuiltinOperator_SUB:
- outputs = _opCreator->convertSub(op->builtin_options_as<SubOptions>(), inputs);
- break;
- case BuiltinOperator_MUL:
- outputs = _opCreator->convertMul(op->builtin_options_as<MulOptions>(), inputs);
- break;
- case BuiltinOperator_DIV:
- outputs = _opCreator->convertDiv(op->builtin_options_as<DivOptions>(), inputs);
- break;
- case BuiltinOperator_MAXIMUM:
- outputs = _opCreator->convertMax(inputs);
- break;
- case BuiltinOperator_SQUARED_DIFFERENCE:
- outputs = _opCreator->convertSquaredDifference(inputs);
- break;
- case BuiltinOperator_TRANSPOSE_CONV:
- outputs =
- _opCreator->convertTransposeConv(op->builtin_options_as<TransposeConvOptions>(), inputs);
- break;
- case BuiltinOperator_PAD:
- outputs = _opCreator->convertPad(op->builtin_options_as<PadOptions>(), inputs);
- break;
- case BuiltinOperator_TANH:
- outputs = _opCreator->convertTanh(inputs);
- break;
- case BuiltinOperator_RELU:
- outputs = _opCreator->convertReLU(inputs);
- break;
- case BuiltinOperator_RELU6:
- outputs = _opCreator->convertReLU6(inputs);
- break;
- case BuiltinOperator_TRANSPOSE:
- outputs = _opCreator->convertTranspose(op->builtin_options_as<TransposeOptions>(), inputs);
- break;
- case BuiltinOperator_STRIDED_SLICE:
- outputs =
- _opCreator->convertStridedSlice(op->builtin_options_as<StridedSliceOptions>(), inputs);
- break;
- case BuiltinOperator_LEAKY_RELU:
- outputs = _opCreator->convertLeakyReLU(op->builtin_options_as<LeakyReluOptions>(), inputs);
- break;
- case BuiltinOperator_SHAPE:
- outputs = _opCreator->convertShape(op->builtin_options_as<ShapeOptions>(), inputs);
- break;
- default:
- assert(false && "All unsupported types should have been found before this pass.");
- }
-
- assert(outputs.size() == op->outputs()->size());
- for (size_t i = 0; i < op->outputs()->size(); ++i)
- {
- const int32_t tensor_index = (*op->outputs())[i];
- const Tensor *tensor = (*_tensors)[tensor_index];
-
- outputs[i]->setName(tensor->name()->c_str());
- assert(_tensorMap.find(tensor_index) == _tensorMap.cend());
- _tensorMap[tensor_index] = outputs[i];
- }
-}
-
-std::vector<mir::Operation::Output *> TfliteImporter::getMIRInputsForOperator(const Operator *op)
-{
- std::vector<mir::Operation::Output *> inputs;
-
- try
- {
- for (auto i : *(op->inputs()))
- {
- const Tensor *tensor = (*_tensors)[i];
- const Buffer *buffer = (*_buffers)[tensor->buffer()];
- if (buffer->data() != nullptr)
- {
- assert(_tensorMap.find(i) == _tensorMap.end());
- mir::TensorVariant mir_tensor = createTensor(tensor, buffer);
- inputs.emplace_back(_graph->create<mir::ops::ConstantOp>(mir_tensor)->getOutput(0));
- }
- else
- {
- // By this point every input for the operation "op" should have corresponding
- // Model IR operations that output its inputs. This assumption is provided by the fact
- // that TFLite format specifies all operations in the execution order.
- inputs.emplace_back(_tensorMap.at(i));
- }
- }
- }
- catch (const std::out_of_range &e)
- {
- throw std::runtime_error("Found a TFLite operator with an input tensor for which "
- "a corresponding Model IR node that outputs it was not created.");
- }
-
- return inputs;
-}
-
-mir::TensorVariant TfliteImporter::createTensor(const Tensor *t, const Buffer *b)
-{
- assert(b->data() != nullptr);
-
- mir::DataType type;
- switch (t->type())
- {
- case TensorType_INT32:
- type = mir::DataType::INT32;
- break;
- case TensorType_FLOAT32:
- type = mir::DataType::FLOAT32;
- break;
- case TensorType_INT64:
- type = mir::DataType::INT64;
- break;
- default:
- throw std::runtime_error(std::string("Unsupported tensor type: ") +
- EnumNameTensorType(t->type()));
- }
-
- mir::Shape shape(t->shape()->size());
- for (int i = 0; i < t->shape()->size(); ++i)
- {
- shape.dim(i) = t->shape()->Get(i);
- }
- return mir::TensorVariant(type, shape, b->data()->Data());
-}
-
-void TfliteImporter::setGraphOutputs()
-{
- for (auto output_idx : _graphOutputs)
- {
- auto output = _tensorMap[output_idx];
- _graph->create<mir::ops::OutputOp>(output);
- }
-}
-
-} // namespace mir_tflite
diff --git a/compiler/mir-tflite-importer/tflite_importer.h b/compiler/mir-tflite-importer/tflite_importer.h
deleted file mode 100644
index c324d53d0..000000000
--- a/compiler/mir-tflite-importer/tflite_importer.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_TFLITE_IMPORTER_H
-#define MIR_TFLITE_IMPORTER_H
-
-#include "schema_generated.h"
-#include "mir/Graph.h"
-#include "mir/TensorVariant.h"
-
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-
-namespace mir_tflite
-{
-
-class TFLiteOpCreator;
-class TfliteImporter
-{
-public:
- explicit TfliteImporter(std::string filename);
-
- /// @brief Load the model and convert it into a MIR Graph.
- std::unique_ptr<mir::Graph> importModel();
-
- ~TfliteImporter();
-
-private:
- std::string _filename;
- std::unique_ptr<char[]> _modelRaw;
- std::unique_ptr<::tflite::ModelT> _model;
- const ::tflite::Model *_modelPacked = nullptr;
-
- std::unique_ptr<mir::Graph> _graph;
- std::unique_ptr<TFLiteOpCreator> _opCreator;
-
- const flatbuffers::Vector<flatbuffers::Offset<::tflite::OperatorCode>> *_opcodes = nullptr;
- const flatbuffers::Vector<flatbuffers::Offset<::tflite::Tensor>> *_tensors = nullptr;
- const flatbuffers::Vector<flatbuffers::Offset<::tflite::Buffer>> *_buffers = nullptr;
-
- std::vector<int32_t> _graphInputs;
- std::vector<int32_t> _graphOutputs;
-
- // Maps TFLite tensors indices to corresponding MIR operation outputs.
- // FIXME Can the same id represent different tensors in different subgraphs?
- std::map<int, mir::Operation::Output *> _tensorMap;
-
- void import();
- std::unique_ptr<mir::Graph> createIR();
-
- /**
- * @brief Pass through tflite graph and create MIR graph
- */
- void walkGraphAndCreateMIR();
-
- void walkModel(const ::tflite::Model *m);
-
- void walkSubGraph(const ::tflite::SubGraph *s);
-
- void walkOperator(const ::tflite::Operator *op);
-
- /**
- * @brief Pass through tflite graph and collect operators unsupported by NNC
- * @throw PassException with message, containing detected problems
- */
- void collectUnsupportedOps();
-
- /**
- * @brief Mark output MIR nodes
- */
- void setGraphOutputs();
-
- /**
- * @brief Returns MIR operation outputs corresponding to the inputs of the given operator.
- */
- std::vector<mir::Operation::Output *> getMIRInputsForOperator(const ::tflite::Operator *op);
-
- mir::TensorVariant createTensor(const ::tflite::Tensor *t, const ::tflite::Buffer *b);
-};
-
-} // namespace mir_tflite
-
-#endif // MIR_TFLITE_IMPORTER_H
diff --git a/compiler/mir-tflite-importer/tflite_op_creator.cpp b/compiler/mir-tflite-importer/tflite_op_creator.cpp
deleted file mode 100644
index 207f137de..000000000
--- a/compiler/mir-tflite-importer/tflite_op_creator.cpp
+++ /dev/null
@@ -1,676 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "tflite_op_creator.h"
-#include "schema_generated.h"
-
-#include "mir/ops/AddOp.h"
-#include "mir/ops/AvgPool2DOp.h"
-#include "mir/ops/CappedReluOp.h"
-#include "mir/ops/ConcatOp.h"
-#include "mir/ops/ConstantOp.h"
-#include "mir/ops/Conv2DOp.h"
-#include "mir/ops/Deconv2DOp.h"
-#include "mir/ops/DepthwiseConv2DOp.h"
-#include "mir/ops/DivOp.h"
-#include "mir/ops/FullyConnectedOp.h"
-#include "mir/ops/LeakyReluOp.h"
-#include "mir/ops/MaxOp.h"
-#include "mir/ops/MaxPool2DOp.h"
-#include "mir/ops/MulOp.h"
-#include "mir/ops/PadOp.h"
-#include "mir/ops/ReduceMeanOp.h"
-#include "mir/ops/ReluOp.h"
-#include "mir/ops/ReshapeOp.h"
-#include "mir/ops/ResizeOp.h"
-#include "mir/ops/SigmoidOp.h"
-#include "mir/ops/SliceOp.h"
-#include "mir/ops/SoftmaxOp.h"
-#include "mir/ops/SqrtOp.h"
-#include "mir/ops/SqueezeOp.h"
-#include "mir/ops/SubOp.h"
-#include "mir/ops/TanhOp.h"
-#include "mir/ops/TransposeOp.h"
-
-#include "mir/Shape.h"
-#include "mir/ShapeRange.h"
-#include "mir/Tensor.h"
-
-using namespace ::tflite;
-
-namespace mir_tflite
-{
-
-namespace ops = mir::ops;
-using mir::Shape;
-
-static mir::ops::PaddingType convertPadding(tflite::Padding padding)
-{
- switch (padding)
- {
- case tflite::Padding_VALID:
- return mir::ops::PaddingType::Valid;
- case tflite::Padding_SAME:
- return mir::ops::PaddingType::SameUpper;
- default:
- assert(false);
- }
-}
-
-// TODO Move this to MIR?
-static void calculatePadding(mir::ops::PaddingType padding_type, const mir::Shape &input_shape,
- const std::vector<std::int32_t> &window_size,
- const std::vector<std::int32_t> &strides,
- std::vector<std::int32_t> &padding_before,
- std::vector<std::int32_t> &padding_after)
-{
- constexpr int num_spatial_dims = 2;
- assert(window_size.size() == num_spatial_dims);
- assert(strides.size() == num_spatial_dims);
- assert(padding_before.size() == num_spatial_dims);
- assert(padding_after.size() == num_spatial_dims);
-
- switch (padding_type)
- {
- case mir::ops::PaddingType::SameUpper:
- for (int i = 0; i < num_spatial_dims; ++i)
- {
- // Assuming NHWC format.
- const std::int32_t total_padding =
- (input_shape.dim(1 + i) % strides[i] == 0)
- ? std::max(0, window_size[i] - strides[i])
- : std::max(0, window_size[i] - input_shape.dim(1 + i) % strides[i]);
- padding_before[i] = total_padding / 2;
- padding_after[i] = total_padding - padding_before[i];
- }
- break;
- case mir::ops::PaddingType::Valid:
- for (int i = 0; i < num_spatial_dims; ++i)
- {
- padding_before[i] = 0;
- padding_after[i] = 0;
- }
- break;
- default:
- assert(false);
- }
-}
-
-template <typename VectorT>
-static std::vector<VectorT> convertIntTensorToVector(const mir::Tensor<int32_t> &tensor)
-{
- std::vector<VectorT> v;
- for (const auto &i : mir::ShapeRange(tensor.getShape()))
- v.emplace_back(static_cast<VectorT>(tensor.at(i)));
- return v;
-}
-
-static const mir::TensorVariant &extractTensor(const mir::Operation::Output *output)
-{
- auto constant_op = dynamic_cast<const ops::ConstantOp *>(output->getNode());
- if (constant_op == nullptr)
- throw std::runtime_error("Non-constant input is not supported.");
- return constant_op->getValue();
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertConv2D(const Conv2DOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto input = inputs.at(0);
- auto kernel = inputs.at(1);
- auto bias = inputs.at(2);
-
- kernel = createOp<ops::ConstantOp>(extractTensor(kernel))->getOutput(0);
-
- const std::vector<std::int32_t> strides{opts->stride_h(), opts->stride_w()};
- std::vector<std::int32_t> padding_before(2);
- std::vector<std::int32_t> padding_after(2);
-
- const auto padding_type = convertPadding(opts->padding());
- const auto &input_shape = input->getShape();
- const auto &kernel_shape = kernel->getShape();
- std::vector<std::int32_t> kernel_size{kernel_shape.dim(1), kernel_shape.dim(2)};
- calculatePadding(padding_type, input_shape, kernel_size, strides, padding_before, padding_after);
-
- auto result = createOp<ops::Conv2DOp>(input, kernel, strides, padding_before, padding_after,
- mir::DataFormat::NHWC)
- ->getOutput(0);
- result = createOp<ops::AddOp>(result, bias)->getOutput(0);
- return {addFusedActivation(result, opts->fused_activation_function())};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertDepthwiseConv2D(const DepthwiseConv2DOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto input = inputs.at(0);
- auto kernel = inputs.at(1);
- auto bias = inputs.at(2);
-
- // OHWI -> HWIO
- const std::vector<std::size_t> axis_order{1, 2, 3, 0};
- kernel = createOp<ops::TransposeOp>(kernel, axis_order)->getOutput(0);
-
- const std::vector<std::int32_t> strides{opts->stride_h(), opts->stride_w()};
- std::vector<std::int32_t> padding_before(2);
- std::vector<std::int32_t> padding_after(2);
-
- const auto padding_type = convertPadding(opts->padding());
- const auto &input_shape = input->getShape();
- const auto &kernel_shape = kernel->getShape();
- std::vector<std::int32_t> kernel_size{kernel_shape.dim(0), kernel_shape.dim(1)};
- calculatePadding(padding_type, input_shape, kernel_size, strides, padding_before, padding_after);
-
- auto result = createOp<ops::DepthwiseConv2DOp>(input, kernel, strides, padding_before,
- padding_after, mir::DataFormat::NHWC)
- ->getOutput(0);
- result = createOp<ops::AddOp>(result, bias)->getOutput(0);
- return {addFusedActivation(result, opts->fused_activation_function())};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertConcatenation(const ::tflite::ConcatenationOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto result = createOp<ops::ConcatOp>(inputs, opts->axis());
- return {addFusedActivation(result->getOutput(0), opts->fused_activation_function())};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertMaxPool2D(const ::tflite::Pool2DOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto input = inputs.at(0);
-
- const auto &input_shape = input->getShape();
- const std::vector<std::int32_t> window_size{opts->filter_height(), opts->filter_width()};
- const std::vector<std::int32_t> strides{opts->stride_h(), opts->stride_w()};
- std::vector<std::int32_t> padding_before(2);
- std::vector<std::int32_t> padding_after(2);
-
- const auto padding_type = convertPadding(opts->padding());
- calculatePadding(padding_type, input_shape, window_size, strides, padding_before, padding_after);
-
- auto result = createOp<ops::MaxPool2DOp>(input, window_size, strides, padding_before,
- padding_after, mir::DataFormat::NHWC);
- return {addFusedActivation(result->getOutput(0), opts->fused_activation_function())};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertAveragePool2D(const ::tflite::Pool2DOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto input = inputs.at(0);
-
- const auto &input_shape = input->getShape();
- const std::vector<std::int32_t> window_size{opts->filter_height(), opts->filter_width()};
- const std::vector<std::int32_t> strides{opts->stride_h(), opts->stride_w()};
- std::vector<std::int32_t> padding_before(2);
- std::vector<std::int32_t> padding_after(2);
-
- const auto padding_type = convertPadding(opts->padding());
- calculatePadding(padding_type, input_shape, window_size, strides, padding_before, padding_after);
-
- auto result = createOp<ops::AvgPool2DOp>(input, window_size, strides, padding_before,
- padding_after, false, mir::DataFormat::NHWC);
- return {addFusedActivation(result->getOutput(0), opts->fused_activation_function())};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertSoftmax(const ::tflite::SoftmaxOptions * /*opts*/,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto input = inputs.at(0);
-
- // Softmax in TFLite is always 2-D.
- assert(input->getShape().rank() == 2);
- const int32_t axis = 1;
- auto result = createOp<ops::SoftmaxOp>(input, axis);
- return {result->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertSlice(const ::tflite::SliceOptions * /*opts*/,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto input = inputs.at(0);
- mir::Tensor<int32_t> begin_tensor(extractTensor(inputs.at(1)));
- mir::Tensor<int32_t> size_tensor(extractTensor(inputs.at(2)));
-
- Shape starts(convertIntTensorToVector<int32_t>(begin_tensor));
- Shape sizes(convertIntTensorToVector<int32_t>(size_tensor));
- auto result = createOp<ops::SliceOp>(input, starts, sizes);
- return {result->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertReshape(const ::tflite::ReshapeOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto input = inputs.at(0);
-
- // TODO: we should also support "-1" values in new_shape, which means that correct
- // shape values must be calculated. Better do it in the shape inference module.
- Shape new_shape(opts->new_shape()->size());
- for (int i = 0; i < opts->new_shape()->size(); ++i)
- {
- new_shape.dim(i) = opts->new_shape()->Get(i);
- }
- auto result = createOp<ops::ReshapeOp>(input, new_shape);
- return {result->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertTransposeConv(const ::tflite::TransposeConvOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- mir::Tensor<int32_t> output_shape_tensor(extractTensor(inputs.at(0)));
- auto kernel = inputs.at(1);
- auto input = inputs.at(2);
-
- const std::vector<std::int32_t> strides{opts->stride_h(), opts->stride_w()};
- Shape output_shape(convertIntTensorToVector<int32_t>(output_shape_tensor));
-
- // OHWI -> HWOI
- const std::vector<std::size_t> axis_order{1, 2, 0, 3};
- kernel = createOp<ops::TransposeOp>(kernel, axis_order)->getOutput(0);
-
- const auto padding_type = convertPadding(opts->padding());
- auto result = createOp<ops::DeConv2DOp>(input, kernel, strides, padding_type, output_shape,
- mir::DataFormat::NHWC)
- ->getOutput(0);
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertResizeNearestNeighbor(const ::tflite::ResizeNearestNeighborOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- if (opts->align_corners())
- throw std::runtime_error("'align_corners' is not currently supported");
-
- auto input = inputs.at(0);
- mir::Tensor<int32_t> size_tensor(extractTensor(inputs.at(1)));
-
- const auto &input_shape = input->getShape();
- Shape res_shape{input_shape.dim(0), size_tensor.at(mir::Index{0}), size_tensor.at(mir::Index{1}),
- input_shape.dim(3)};
- auto result =
- createOp<ops::ResizeOp>(input, ops::ResizeOp::ResizeMethod::nearestNeighbor, res_shape);
- return {result->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertAdd(const ::tflite::AddOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- assert(inputs.size() == 2);
- auto result = createOp<ops::AddOp>(inputs[0], inputs[1])->getOutput(0);
- return {addFusedActivation(result, opts->fused_activation_function())};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertSub(const ::tflite::SubOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- assert(inputs.size() == 2);
- auto result = createOp<ops::SubOp>(inputs[0], inputs[1])->getOutput(0);
- return {addFusedActivation(result, opts->fused_activation_function())};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertMul(const ::tflite::MulOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- assert(inputs.size() == 2);
- // Try to constant fold the operation in some cases.
- if (inputs[0]->getShape() == inputs[1]->getShape() &&
- opts->fused_activation_function() == ActivationFunctionType_NONE)
- {
- auto constant1_op = dynamic_cast<const ops::ConstantOp *>(inputs[0]->getNode());
- auto constant2_op = dynamic_cast<const ops::ConstantOp *>(inputs[1]->getNode());
- if (constant1_op != nullptr && constant2_op != nullptr)
- {
- const auto &input1_tensor = constant1_op->getValue();
- const auto &input2_tensor = constant2_op->getValue();
- if (input1_tensor.getDataType() == mir::DataType::INT32 &&
- input2_tensor.getDataType() == mir::DataType::INT32)
- {
- const auto &output_shape = inputs[0]->getShape();
- mir::TensorVariant res_tensor(mir::DataType::INT32, output_shape);
-
- mir::Tensor<int32_t> input1_accessor(input1_tensor);
- mir::Tensor<int32_t> input2_accessor(input2_tensor);
- mir::Tensor<int32_t> res_accessor(res_tensor);
-
- for (const auto &idx : mir::ShapeRange(output_shape))
- {
- res_accessor.at(idx) = input1_accessor.at(idx) * input2_accessor.at(idx);
- }
-
- return {createOp<ops::ConstantOp>(res_tensor)->getOutput(0)};
- }
- }
- }
-
- auto result = createOp<ops::MulOp>(inputs[0], inputs[1])->getOutput(0);
- return {addFusedActivation(result, opts->fused_activation_function())};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertDiv(const ::tflite::DivOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- assert(inputs.size() == 2);
- auto result = createOp<ops::DivOp>(inputs[0], inputs[1])->getOutput(0);
- return {addFusedActivation(result, opts->fused_activation_function())};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertMax(const std::vector<mir::Operation::Output *> &inputs)
-{
- assert(inputs.size() == 2);
- auto result = createOp<ops::MaxOp>(inputs[0], inputs[1])->getOutput(0);
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertSquaredDifference(const std::vector<mir::Operation::Output *> &inputs)
-{
- assert(inputs.size() == 2);
- auto result = createOp<ops::SubOp>(inputs[0], inputs[1])->getOutput(0);
- result = createOp<ops::MulOp>(result, result)->getOutput(0);
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertMean(const ::tflite::ReducerOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto input = inputs.at(0);
- mir::Tensor<int32_t> axes_tensor(extractTensor(inputs.at(1)));
-
- std::vector<int32_t> axes = convertIntTensorToVector<int32_t>(axes_tensor);
- auto result = createOp<ops::ReduceMeanOp>(input, axes, opts->keep_dims());
- return {result->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertFullyConnected(const ::tflite::FullyConnectedOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto input = inputs.at(0);
- auto weights = inputs.at(1);
- auto bias = inputs.at(2);
-
- // Flatten input to 2-D shape.
- const auto &input_shape = input->getShape();
- int32_t outer_size = input_shape.dim(0);
- int32_t inner_size = input_shape.numElements() / outer_size;
- auto flatten = createOp<ops::ReshapeOp>(input, Shape{outer_size, inner_size});
-
- // Transpose the weights.
- const std::vector<std::size_t> axis_order{1, 0};
- weights = createOp<ops::TransposeOp>(weights, axis_order)->getOutput(0);
-
- auto result = createOp<ops::FullyConnectedOp>(flatten->getOutput(0), weights)->getOutput(0);
- result = createOp<ops::AddOp>(result, bias)->getOutput(0);
- return {addFusedActivation(result, opts->fused_activation_function())};
-}
-
-mir::Operation::Output *TFLiteOpCreator::addFusedActivation(mir::Operation::Output *input,
- ActivationFunctionType activation_type)
-{
- switch (activation_type)
- {
- case ActivationFunctionType_NONE:
- return input;
- case ActivationFunctionType_RELU:
- return createOp<ops::ReluOp>(input)->getOutput(0);
- case ActivationFunctionType_RELU6:
- return createOp<ops::CappedReluOp>(input, 6)->getOutput(0);
- case ActivationFunctionType_TANH:
- return createOp<ops::TanhOp>(input)->getOutput(0);
- default:
- throw std::runtime_error(std::string("Unsupported activation type: ") +
- tflite::EnumNameActivationFunctionType(activation_type));
- }
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertSqueeze(const ::tflite::SqueezeOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto input = inputs.at(0);
-
- std::vector<int32_t> squeeze_dims(opts->squeeze_dims()->begin(), opts->squeeze_dims()->end());
- auto result = createOp<ops::SqueezeOp>(input, squeeze_dims);
- return {result->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertPad(const ::tflite::PadOptions * /*opts*/,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto input = inputs.at(0);
- mir::Tensor<int32_t> paddings_tensor(extractTensor(inputs.at(1)));
-
- const auto &input_shape = input->getShape();
- const int num_dims = input_shape.rank();
-
- std::vector<std::int32_t> padding_before(num_dims);
- std::vector<std::int32_t> padding_after(num_dims);
- for (int i = 0; i < num_dims; i++)
- {
- padding_before[i] = paddings_tensor.at(mir::Index({i, 0}));
- padding_after[i] = paddings_tensor.at(mir::Index({i, 1}));
- }
-
- const float padding_value = 0.0f;
-
- auto result =
- createOp<ops::PadOp>(input, padding_before, padding_after, padding_value)->getOutput(0);
- return {result};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertTanh(const std::vector<mir::Operation::Output *> &inputs)
-{
- auto input = inputs.at(0);
-
- auto result = createOp<ops::TanhOp>(input);
- return {result->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertReLU(const std::vector<mir::Operation::Output *> &inputs)
-{
- auto input = inputs.at(0);
-
- auto result = createOp<ops::ReluOp>(input);
- return {result->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertReLU6(const std::vector<mir::Operation::Output *> &inputs)
-{
- auto input = inputs.at(0);
-
- auto result = createOp<ops::CappedReluOp>(input, 6);
- return {result->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertSqrt(const std::vector<mir::Operation::Output *> &inputs)
-{
- auto input = inputs.at(0);
-
- auto result = createOp<ops::SqrtOp>(input);
- return {result->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertLogistic(const std::vector<mir::Operation::Output *> &inputs)
-{
- auto input = inputs.at(0);
-
- auto result = createOp<ops::SigmoidOp>(input);
- return {result->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertTranspose(const ::tflite::TransposeOptions * /*opts*/,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto input = inputs.at(0);
- mir::Tensor<int32_t> perm_tensor(extractTensor(inputs.at(1)));
-
- std::vector<std::size_t> axis_order = convertIntTensorToVector<std::size_t>(perm_tensor);
- auto result = createOp<ops::TransposeOp>(input, axis_order);
- return {result->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertStridedSlice(const ::tflite::StridedSliceOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- if (opts->ellipsis_mask() != 0)
- throw std::runtime_error("StridedSlice: parameter 'ellipsis_mask' is not supported.");
-
- if (opts->new_axis_mask() != 0)
- throw std::runtime_error("StridedSlice: parameter 'new_axis_mask' is not supported.");
-
- auto input = inputs.at(0);
- mir::Tensor<int32_t> begin_tensor(extractTensor(inputs.at(1)));
- mir::Tensor<int32_t> end_tensor(extractTensor(inputs.at(2)));
- mir::Tensor<int32_t> strides_tensor(extractTensor(inputs.at(3)));
-
- std::vector<int32_t> begin = convertIntTensorToVector<int32_t>(begin_tensor);
- std::vector<int32_t> end = convertIntTensorToVector<int32_t>(end_tensor);
- std::vector<int32_t> strides = convertIntTensorToVector<int32_t>(strides_tensor);
-
- int32_t begin_mask = opts->begin_mask();
- int32_t end_mask = opts->end_mask();
- int32_t shrink_axis_mask = opts->shrink_axis_mask();
-
- const auto &input_shape = input->getShape();
- int32_t num_dims = input_shape.rank();
-
- for (int32_t stride : strides)
- {
- if (stride != 1)
- throw std::runtime_error("StridedSlice: parameter 'strides' is not supported");
- }
-
- Shape start(num_dims);
- Shape size(num_dims);
- std::vector<int32_t> squeeze_dims;
- for (int axis = 0; axis < num_dims; axis++)
- {
- if (static_cast<uint32_t>(begin_mask) & (1u << static_cast<uint32_t>(axis)))
- start.dim(axis) = 0;
- else
- start.dim(axis) = begin.at(static_cast<uint64_t>(axis));
-
- if (static_cast<uint32_t>(end_mask) & (1u << static_cast<uint32_t>(axis)))
- size.dim(axis) = input_shape.dim(axis) - start.dim(axis);
- else
- size.dim(axis) = end.at(static_cast<uint64_t>(axis)) - start.dim(axis);
-
- if (static_cast<uint32_t>(shrink_axis_mask) & (1u << static_cast<uint32_t>(axis)))
- squeeze_dims.push_back(axis);
- }
-
- // Try to constant fold the operation in some cases.
- if (shrink_axis_mask == 0)
- {
- auto constant_op = dynamic_cast<const ops::ConstantOp *>(input->getNode());
- if (constant_op != nullptr)
- {
- const auto &input_tensor = constant_op->getValue();
- if (input_tensor.getDataType() == mir::DataType::INT32)
- {
- mir::Shape output_shape(num_dims);
- for (int32_t i = 0; i < num_dims; ++i)
- {
- if (size.dim(i) == -1)
- {
- output_shape.dim(i) = input_shape.dim(i) - start.dim(i);
- }
- else
- {
- output_shape.dim(i) = size.dim(i);
- }
- }
-
- mir::TensorVariant res_tensor(mir::DataType::INT32, output_shape);
- mir::Tensor<int32_t> input_accessor(input_tensor);
- mir::Tensor<int32_t> res_accessor(res_tensor);
-
- mir::Index in_idx(static_cast<std::size_t>(num_dims));
- for (const auto &out_idx : mir::ShapeRange(output_shape))
- {
- for (int32_t i = 0; i < num_dims; ++i)
- {
- in_idx.at(i) = out_idx.at(i) + start.dim(i);
- }
- res_accessor.at(out_idx) = input_accessor.at(in_idx);
- }
-
- return {createOp<ops::ConstantOp>(res_tensor)->getOutput(0)};
- }
- }
- }
-
- auto result = createOp<ops::SliceOp>(input, start, size);
- result = createOp<ops::SqueezeOp>(result->getOutput(0), squeeze_dims);
- return {result->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertLeakyReLU(const ::tflite::LeakyReluOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- auto input = inputs.at(0);
-
- auto result = createOp<ops::LeakyReluOp>(input, opts->alpha());
- return {result->getOutput(0)};
-}
-
-std::vector<mir::Operation::Output *>
-TFLiteOpCreator::convertShape(const ::tflite::ShapeOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs)
-{
- if (opts->out_type() != TensorType_INT32)
- {
- throw std::runtime_error(std::string("SHAPE: Unsupported tensor type: ") +
- EnumNameTensorType(opts->out_type()));
- }
-
- const auto &input_shape = inputs[0]->getShape();
- int32_t rank = input_shape.rank();
- Shape output_shape{rank};
- std::vector<int32_t> data;
- data.reserve(static_cast<uint64_t>(rank));
- for (int32_t i = 0; i < rank; i++)
- data.emplace_back(input_shape.dim(i));
- mir::TensorVariant tensor(mir::DataType::INT32, output_shape, data.data());
- auto result = createOp<ops::ConstantOp>(tensor);
- return {result->getOutput(0)};
-}
-
-} // namespace mir_tflite
diff --git a/compiler/mir-tflite-importer/tflite_op_creator.h b/compiler/mir-tflite-importer/tflite_op_creator.h
deleted file mode 100644
index 5f0b48b68..000000000
--- a/compiler/mir-tflite-importer/tflite_op_creator.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MIR_TFLITE_OP_CREATOR_H
-#define MIR_TFLITE_OP_CREATOR_H
-
-#include "schema_generated.h"
-
-#include "mir/Graph.h"
-
-#include <utility>
-#include <vector>
-
-namespace mir_tflite
-{
-
-class TFLiteOpCreator
-{
-public:
- explicit TFLiteOpCreator(mir::Graph *g) : _graph(g) {}
-
- std::vector<mir::Operation::Output *>
- convertConv2D(const ::tflite::Conv2DOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertDepthwiseConv2D(const ::tflite::DepthwiseConv2DOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertConcatenation(const ::tflite::ConcatenationOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertMaxPool2D(const ::tflite::Pool2DOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertAveragePool2D(const ::tflite::Pool2DOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertMean(const ::tflite::ReducerOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertSoftmax(const ::tflite::SoftmaxOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertSlice(const ::tflite::SliceOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertReshape(const ::tflite::ReshapeOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertFullyConnected(const ::tflite::FullyConnectedOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertResizeNearestNeighbor(const ::tflite::ResizeNearestNeighborOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertLogistic(const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertSqrt(const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertSqueeze(const ::tflite::SqueezeOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertAdd(const ::tflite::AddOptions *opts, const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertSub(const ::tflite::SubOptions *opts, const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertMul(const ::tflite::MulOptions *opts, const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertDiv(const ::tflite::DivOptions *opts, const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertMax(const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertSquaredDifference(const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertTanh(const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertReLU(const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertReLU6(const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertTransposeConv(const ::tflite::TransposeConvOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertPad(const ::tflite::PadOptions *opts, const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertTranspose(const ::tflite::TransposeOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertStridedSlice(const ::tflite::StridedSliceOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertLeakyReLU(const ::tflite::LeakyReluOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs);
-
- std::vector<mir::Operation::Output *>
- convertShape(const ::tflite::ShapeOptions *opts,
- const std::vector<mir::Operation::Output *> &inputs);
-
-private:
- mir::Graph *_graph;
-
- mir::Operation::Output *addFusedActivation(mir::Operation::Output *input,
- ::tflite::ActivationFunctionType activation_type);
-
- template <typename OpType, typename... Types> mir::Operation *createOp(Types &&... args);
-};
-
-template <typename OpType, typename... Types>
-mir::Operation *TFLiteOpCreator::createOp(Types &&... args)
-{
- return _graph->create<OpType>(std::forward<Types>(args)...);
-}
-
-} // namespace mir_tflite
-
-#endif // MIR_TFLITE_OP_CREATOR_H
diff --git a/compiler/mir/CMakeLists.txt b/compiler/mir/CMakeLists.txt
index 9f6db34c0..4c89893cc 100644
--- a/compiler/mir/CMakeLists.txt
+++ b/compiler/mir/CMakeLists.txt
@@ -1,6 +1,7 @@
set(MIR_SOURCES
src/ops/AvgPool2DOp.cpp
src/ops/BinaryElementwiseOp.cpp
+ src/ops/BroadcastOp.cpp
src/ops/ConcatOp.cpp
src/ops/Conv2DOp.cpp
src/ops/DeConv2DOp.cpp
@@ -35,3 +36,8 @@ set_target_properties(mir PROPERTIES POSITION_INDEPENDENT_CODE ON)
set_target_properties(mir PROPERTIES LINKER_LANGUAGE CXX)
add_subdirectory(unittests)
+
+add_subdirectory(src/mir_caffe_importer)
+add_subdirectory(src/mir_caffe2_importer)
+add_subdirectory(src/mir_tflite_importer)
+add_subdirectory(src/mir_onnx_importer)
diff --git a/compiler/mir/Readme.md b/compiler/mir/Readme.md
index a7a3c107c..9fb1348a5 100644
--- a/compiler/mir/Readme.md
+++ b/compiler/mir/Readme.md
@@ -33,4 +33,4 @@ Can be included as a `CMake` target.
### Dependencies
Mir depends on `adtitas` library, which provides the `small_vector` data type.
- \ No newline at end of file
+
diff --git a/compiler/mir/include/mir/Attributes.h b/compiler/mir/include/mir/Attributes.h
new file mode 100644
index 000000000..64a4e0f46
--- /dev/null
+++ b/compiler/mir/include/mir/Attributes.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OP_ATTRIBUTES_H
+#define OP_ATTRIBUTES_H
+
+#include <vector>
+#include "mir/DataFormat.h"
+#include "mir/ops/PaddingType.h"
+
+namespace mir
+{
+
+struct Conv2DOpAttributes
+{
+ Conv2DOpAttributes() = default;
+
+ std::vector<std::int32_t> strides{1, 1};
+ std::vector<std::int32_t> padding_before{0, 0};
+ std::vector<std::int32_t> padding_after{0, 0};
+ std::int32_t num_groups{1};
+ DataFormat data_format{DataFormat::NHWC};
+};
+
+struct AvgPool2DOpAttributes
+{
+ AvgPool2DOpAttributes() = default;
+
+ std::vector<std::int32_t> window{1, 1};
+ std::vector<std::int32_t> strides{1, 1};
+ std::vector<std::int32_t> padding_before{0, 0};
+ std::vector<std::int32_t> padding_after{0, 0};
+ DataFormat data_format{DataFormat::NHWC};
+ bool include_pad{true};
+};
+
+struct MaxPool2DOpAttributes
+{
+ MaxPool2DOpAttributes() = default;
+
+ std::vector<std::int32_t> window{1, 1};
+ std::vector<std::int32_t> strides{1, 1};
+ std::vector<std::int32_t> padding_before{0, 0};
+ std::vector<std::int32_t> padding_after{0, 0};
+ DataFormat data_format{DataFormat::NHWC};
+};
+
+struct Deconv2DOpAttributes
+{
+ Deconv2DOpAttributes() = default;
+
+ std::vector<std::int32_t> strides{1, 1};
+ std::vector<std::int32_t> padding_before{0, 0};
+ std::vector<std::int32_t> padding_after{0, 0};
+ DataFormat data_format{DataFormat::NHWC};
+ ops::PaddingType padding_type{ops::PaddingType::Explicit};
+};
+
+struct PadOpAttributes
+{
+ PadOpAttributes() : padding_value(0.0) {}
+ PadOpAttributes(unsigned dims) : padding_before(dims), padding_after(dims), padding_value(0.0) {}
+
+ std::vector<std::int32_t> padding_before;
+ std::vector<std::int32_t> padding_after;
+ float padding_value;
+};
+} // namespace mir
+
+#endif
diff --git a/compiler/mir/include/mir/Common.h b/compiler/mir/include/mir/Common.h
index acdb8f10c..340622e99 100644
--- a/compiler/mir/include/mir/Common.h
+++ b/compiler/mir/include/mir/Common.h
@@ -17,16 +17,19 @@
#ifndef _MIR_COMMON_H_
#define _MIR_COMMON_H_
+#include <cstddef>
+#include <cstdint>
+
namespace mir
{
/**
* @brief maximum number of dimensions what an Index, Shape or Tensor can have
*/
-constexpr size_t MAX_DIMENSION_COUNT = 8;
+constexpr std::size_t MAX_DIMENSION_COUNT = 8;
-inline constexpr size_t wrap_index(int32_t index, size_t limit) noexcept
+inline constexpr std::size_t wrap_index(std::int32_t index, std::size_t limit) noexcept
{
- return static_cast<size_t>(index >= 0 ? index : limit + index);
+ return static_cast<std::size_t>(index >= 0 ? index : limit + index);
}
} // namespace mir
diff --git a/compiler/mir/include/mir/DataType.h b/compiler/mir/include/mir/DataType.h
index b333b3761..4d99be3c1 100644
--- a/compiler/mir/include/mir/DataType.h
+++ b/compiler/mir/include/mir/DataType.h
@@ -17,6 +17,9 @@
#ifndef _MIR_DATA_TYPE_H_
#define _MIR_DATA_TYPE_H_
+#include <cassert>
+#include <cstdint>
+
namespace mir
{
@@ -26,9 +29,30 @@ enum class DataType
FLOAT32,
FLOAT64,
INT32,
- INT64
+ INT64,
+ UINT8
};
+inline std::size_t getDataTypeSize(DataType type)
+{
+ switch (type)
+ {
+ case DataType::FLOAT32:
+ return sizeof(float);
+ case DataType::FLOAT64:
+ return sizeof(double);
+ case DataType::INT32:
+ return sizeof(int32_t);
+ case DataType::INT64:
+ return sizeof(int64_t);
+ case DataType::UINT8:
+ return sizeof(uint8_t);
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
} // namespace mir
#endif //_MIR_DATA_TYPE_H_
diff --git a/compiler/mir/include/mir/Graph.h b/compiler/mir/include/mir/Graph.h
index 4a5b21a27..bf94cfb14 100644
--- a/compiler/mir/include/mir/Graph.h
+++ b/compiler/mir/include/mir/Graph.h
@@ -91,21 +91,6 @@ public:
*/
void replaceNode(Operation *op, Operation *with);
- /**
- * @brief Replaces referenced node with input(VariableOp) node
- * @param op Node to replace
- * @return Input node which is placed in graph instead of passed node
- * @warning deletes passed node
- */
- ops::InputOp *replaceWithInputNode(Operation *op);
-
- /**
- * @brief Change graph inputs to nodes with names in newInputs
- * @param new_inputs names of nodes to be made into input nodes
- * @warning Input node order is not preserved and may differ from newInputs vector
- */
- void replaceInputNodes(const std::vector<std::string> &new_inputs);
-
private:
void registerOp(Operation *op);
@@ -116,6 +101,11 @@ private:
std::vector<ops::OutputOp *> _outputs;
};
+/**
+ * @brief Returns nodes of the graph sorted topologically.
+ */
+std::vector<Operation *> getSortedNodes(Graph *graph);
+
} // namespace mir
#endif //_MIR_GRAPH_H_
diff --git a/compiler/mir/include/mir/OpDefs.h b/compiler/mir/include/mir/OpDefs.h
index 8af91d062..f7351ab26 100644
--- a/compiler/mir/include/mir/OpDefs.h
+++ b/compiler/mir/include/mir/OpDefs.h
@@ -17,25 +17,33 @@
#ifndef _MIR_OPDEFS_H_
#define _MIR_OPDEFS_H_
+#include "mir/ops/AbsOp.h"
#include "mir/ops/AddOp.h"
#include "mir/ops/AvgPool2DOp.h"
+#include "mir/ops/BroadcastOp.h"
#include "mir/ops/CappedReluOp.h"
#include "mir/ops/ConcatOp.h"
#include "mir/ops/ConstantOp.h"
#include "mir/ops/Conv2DOp.h"
#include "mir/ops/Deconv2DOp.h"
#include "mir/ops/DepthwiseConv2DOp.h"
+#include "mir/ops/DequantizeOp.h"
#include "mir/ops/DivOp.h"
#include "mir/ops/EluOp.h"
+#include "mir/ops/EqualOp.h"
#include "mir/ops/FullyConnectedOp.h"
#include "mir/ops/GatherOp.h"
+#include "mir/ops/GreaterOp.h"
+#include "mir/ops/HardSwishOp.h"
#include "mir/ops/InputOp.h"
#include "mir/ops/LeakyReluOp.h"
+#include "mir/ops/LessOp.h"
#include "mir/ops/MaxOp.h"
#include "mir/ops/MaxPool2DOp.h"
#include "mir/ops/MulOp.h"
#include "mir/ops/OutputOp.h"
#include "mir/ops/PadOp.h"
+#include "mir/ops/QuantizeOp.h"
#include "mir/ops/ReduceMeanOp.h"
#include "mir/ops/ReluOp.h"
#include "mir/ops/ReshapeOp.h"
diff --git a/compiler/mir/include/mir/Operation.h b/compiler/mir/include/mir/Operation.h
index dfce02c45..37af6d2a8 100644
--- a/compiler/mir/include/mir/Operation.h
+++ b/compiler/mir/include/mir/Operation.h
@@ -17,12 +17,12 @@
#ifndef _MIR_OPERATION_H_
#define _MIR_OPERATION_H_
-#include "mir/Shape.h"
+#include "mir/TensorType.h"
#include <deque>
#include <string>
-#include <unordered_set>
#include <limits>
+#include <vector>
namespace mir
{
@@ -39,7 +39,21 @@ public:
#undef HANDLE_OP
};
- class Input;
+ /// @brief Represents a use of an operation output.
+ struct Use
+ {
+ Use(Operation *node, std::size_t index) : _node(node), _index(index) {}
+
+ Operation *getNode() const { return _node; }
+
+ std::size_t getIndex() const { return _index; }
+
+ bool operator==(const Use &other) { return _node == other._node && _index == other._index; }
+
+ private:
+ Operation *_node;
+ std::size_t _index;
+ };
/// @brief Represents an output of a node.
class Output
@@ -58,71 +72,50 @@ public:
Operation *getNode() { return _node; }
const Operation *getNode() const { return _node; }
- /// @brief Returns the index of this output among all the ouptputs of the node.
+ /// @brief Returns the index of this output among all the outputs of the node.
std::size_t getIndex() const { return _index; }
/// @brief Returns the inputs that consume this output.
- const std::unordered_set<Input *> &getConsumers() const { return _consumers; }
-
- /// @brief Adds the specified input to the consumers of this output.
- void addConsumer(Input *consumer) { _consumers.emplace(consumer); }
-
- /// @brief Removes the specified input from the consumers of this output.
- void removeConsumer(Input *consumer) { _consumers.erase(consumer); }
+ const std::vector<Use> &getUses() const { return _uses; }
- const Shape &getShape() const { return _shape; }
- void setShape(const Shape &shape) { _shape = shape; }
+ /// @brief Adds the specified use to the uses of this output.
+ void addUse(Use use) { _uses.push_back(use); }
- const std::string &getName() const { return _name; }
- void setName(const std::string &name) { _name = name; }
-
- private:
- Operation *_node;
- std::size_t _index;
- std::unordered_set<Input *> _consumers;
- Shape _shape;
- std::string _name;
- };
+ /// @brief Removes the specified use from the uses of this output.
+ void removeUse(Use use);
- /// @brief Represents an input of a node.
- class Input
- {
- public:
- Input(Operation *node, std::size_t index, Output *producer)
- : _node(node), _index(index), _producer(producer)
- {
- _producer->addConsumer(this);
- }
+ /// @brief Replace the defs of all uses of this output with the specified def.
+ void replaceAllUsesWith(Output *new_def);
- ~Input() = default;
+ /// @brief Gets the type of this output.
+ const TensorType &getType() const { return _type; }
- Input(const Input &) = delete;
- Input(Input &&) = delete;
- Input &operator=(const Input &) = delete;
- Input &operator=(Input &&) = delete;
+ /// @brief Sets the type of this output.
+ /// @warning Use with caution, because it can make the IR inconsistent.
+ void setType(const TensorType &type) { _type = type; }
- /// @brief Returns the node this is the input of.
- Operation *getNode() { return _node; }
- const Operation *getNode() const { return _node; }
+ // Convenient accessors.
+ DataType getElementType() const { return getType().getElementType(); }
+ const Shape &getShape() const { return getType().getShape(); }
- /// @brief Returns the index of this output among all the inputs of the node.
- std::size_t getIndex() const { return _index; }
+ // TODO Remove in favor of `setType`.
+ void setShape(const Shape &shape) { setType(TensorType(_type.getElementType(), shape)); }
- /// @brief Returns the output that produces data for this input.
- Output *getProducer() const { return _producer; }
+ const std::string &getName() const { return _name; }
+ void setName(const std::string &name) { _name = name; }
- /// @brief Replaces the output that produces data for this input with the specified one.
- void replaceProducer(Output *producer)
+ /// @brief Set AffineQuantization to Ouput
+ void setQuantization(const mir::AffineQuantization &quant)
{
- _producer->removeConsumer(this);
- producer->addConsumer(this);
- _producer = producer;
+ setType(TensorType(getElementType(), getShape(), quant));
}
private:
Operation *_node;
std::size_t _index;
- Output *_producer;
+ std::vector<Use> _uses;
+ TensorType _type;
+ std::string _name;
};
virtual ~Operation() = default;
@@ -135,22 +128,22 @@ public:
std::size_t getNumInputs() const { return _inputs.size(); }
std::size_t getNumOutputs() const { return _outputs.size(); }
- std::deque<Input> &getInputs() { return _inputs; }
- const std::deque<Input> &getInputs() const { return _inputs; }
+ std::deque<Output *> &getInputs() { return _inputs; }
+ const std::deque<Output *> &getInputs() const { return _inputs; }
std::deque<Output> &getOutputs() { return _outputs; }
const std::deque<Output> &getOutputs() const { return _outputs; }
- Input *getInput(std::size_t index)
+ Output *getInput(std::size_t index)
{
assert(index < _inputs.size());
- return &_inputs[index];
+ return _inputs[index];
}
- const Input *getInput(std::size_t index) const
+ const Output *getInput(std::size_t index) const
{
assert(index < _inputs.size());
- return &_inputs[index];
+ return _inputs[index];
}
Output *getOutput(std::size_t index)
@@ -165,10 +158,7 @@ public:
return &_outputs[index];
}
- const Shape &getInputShape(std::size_t index) const
- {
- return getInput(index)->getProducer()->getShape();
- }
+ const Shape &getInputShape(std::size_t index) const { return getInput(index)->getShape(); }
const Shape &getOutputShape(std::size_t index) const { return getOutput(index)->getShape(); }
@@ -179,12 +169,12 @@ public:
protected:
Operation(Type type, const std::vector<Output *> &inputs, std::size_t num_outputs = 1);
- void setOutputShape(std::size_t index, const Shape &shape) { getOutput(index)->setShape(shape); }
+ void setOutputType(std::size_t index, const TensorType &type) { getOutput(index)->setType(type); }
private:
Type _type;
std::size_t _id = std::numeric_limits<std::size_t>::max();
- std::deque<Input> _inputs;
+ std::deque<Output *> _inputs;
std::deque<Output> _outputs;
};
diff --git a/compiler/mir/include/mir/Operations.inc b/compiler/mir/include/mir/Operations.inc
index e9ccd1aae..d5736cb9b 100644
--- a/compiler/mir/include/mir/Operations.inc
+++ b/compiler/mir/include/mir/Operations.inc
@@ -18,25 +18,33 @@
#error "You should define HANDLE_OP before including this file"
#endif // HANDLE_OP
+HANDLE_OP(abs, AbsOp)
HANDLE_OP(add, AddOp)
HANDLE_OP(avgPool2D, AvgPool2DOp)
+HANDLE_OP(broadcast, BroadcastOp)
HANDLE_OP(cappedReLU, CappedReluOp)
HANDLE_OP(concat, ConcatOp)
HANDLE_OP(constant, ConstantOp)
HANDLE_OP(conv2D, Conv2DOp)
HANDLE_OP(deConv2D, DeConv2DOp)
HANDLE_OP(depthwiseConv, DepthwiseConv2DOp)
+HANDLE_OP(dequantize, DequantizeOp)
HANDLE_OP(div, DivOp)
HANDLE_OP(ELU, EluOp)
+HANDLE_OP(equal, EqualOp)
HANDLE_OP(fullyConnected, FullyConnectedOp)
HANDLE_OP(gather, GatherOp)
+HANDLE_OP(greater, GreaterOp)
+HANDLE_OP(hardswish, HardSwishOp)
HANDLE_OP(input, InputOp)
HANDLE_OP(leakyReLU, LeakyReluOp)
+HANDLE_OP(less, LessOp)
HANDLE_OP(max, MaxOp)
HANDLE_OP(maxPool2D, MaxPool2DOp)
HANDLE_OP(mul, MulOp)
HANDLE_OP(output, OutputOp)
HANDLE_OP(pad, PadOp)
+HANDLE_OP(quantize, QuantizeOp)
HANDLE_OP(reduceMean, ReduceMeanOp)
HANDLE_OP(ReLU, ReluOp)
HANDLE_OP(reshape, ReshapeOp)
diff --git a/compiler/mir/include/mir/Quantization.h b/compiler/mir/include/mir/Quantization.h
new file mode 100644
index 000000000..d266ee00d
--- /dev/null
+++ b/compiler/mir/include/mir/Quantization.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MIR_QUANTIZATION_H_
+#define _MIR_QUANTIZATION_H_
+
+namespace mir
+{
+
+class AffineQuantization
+{
+public:
+ AffineQuantization() = default;
+
+ AffineQuantization(float scale, int zero_point)
+ : _scale(scale), _zero_point(zero_point), _empty(false)
+ {
+ }
+
+ float getScale() const { return _scale; }
+
+ int getZeroPoint() const { return _zero_point; }
+
+ bool empty() const { return _empty; }
+
+private:
+ float _scale = 0.f;
+ int _zero_point = 0;
+ bool _empty = true;
+};
+
+} // namespace mir
+
+#endif //_MIR_QUANTIZATION_H_
diff --git a/compiler/mir/include/mir/Shape.h b/compiler/mir/include/mir/Shape.h
index fbd4951a1..cb33e6784 100644
--- a/compiler/mir/include/mir/Shape.h
+++ b/compiler/mir/include/mir/Shape.h
@@ -66,6 +66,8 @@ private:
adt::small_vector<int32_t, MAX_DIMENSION_COUNT> _dims;
};
+Shape broadcastShapes(const Shape &lhs_shape, const Shape &rhs_shape);
+
std::string toString(const Shape &shape);
} // namespace mir
diff --git a/compiler/mir/include/mir/Tensor.h b/compiler/mir/include/mir/Tensor.h
index c2ac74268..5e1f3f236 100644
--- a/compiler/mir/include/mir/Tensor.h
+++ b/compiler/mir/include/mir/Tensor.h
@@ -17,7 +17,6 @@
#ifndef _MIR_TENSOR_H_
#define _MIR_TENSOR_H_
-#include "mir/Shape.h"
#include "mir/ExternalRegion.h"
#include "mir/TensorVariant.h"
@@ -27,9 +26,7 @@ namespace mir
template <typename T> class Tensor final
{
public:
- Tensor() = delete;
-
- explicit Tensor(const TensorVariant &t) : _proxy(t), _shape(t.getShape()) {}
+ explicit Tensor(const TensorVariant &t) : _proxy(t) {}
T at(const Index &id) const { return *reinterpret_cast<T *>(this->_proxy.at(id)); }
@@ -42,17 +39,16 @@ public:
ExternalRegion<T> getRegion(const Index &idx)
{
// Only last dimension is safe to process continiously
- auto last_dim = _shape.rank() - 1;
+ auto last_dim = getShape().rank() - 1;
auto base = reinterpret_cast<T *>(_proxy.at(idx));
- auto length = _shape.dim(last_dim) - idx.at(last_dim);
+ auto length = getShape().dim(last_dim) - idx.at(last_dim);
return ExternalRegion<T>(base, length);
}
- virtual const Shape &getShape() const { return _proxy.getShape(); };
+ const Shape &getShape() const { return _proxy.getShape(); };
private:
const TensorVariant &_proxy;
- const Shape &_shape;
};
extern template class Tensor<float>;
diff --git a/compiler/mir/include/mir/TensorType.h b/compiler/mir/include/mir/TensorType.h
new file mode 100644
index 000000000..98797d687
--- /dev/null
+++ b/compiler/mir/include/mir/TensorType.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MIR_TENSOR_TYPE_H_
+#define _MIR_TENSOR_TYPE_H_
+
+#include "mir/DataType.h"
+#include "mir/Quantization.h"
+#include "mir/Shape.h"
+
+namespace mir
+{
+
+class TensorType final
+{
+public:
+ TensorType() = default;
+
+ TensorType(DataType element_type, const Shape &shape) : _element_type(element_type), _shape(shape)
+ {
+ }
+
+ TensorType(DataType element_type, const Shape &shape, const AffineQuantization &quant)
+ : _element_type(element_type), _shape(shape), _quantization(quant)
+ {
+ }
+
+ DataType getElementType() const { return _element_type; }
+
+ const Shape &getShape() const { return _shape; }
+
+ const AffineQuantization &getQuantization() const { return _quantization; }
+ void setQuantization(const AffineQuantization &quant) { _quantization = quant; }
+ bool isQuantized() const { return !_quantization.empty(); }
+
+private:
+ DataType _element_type = DataType::UNKNOWN;
+ Shape _shape;
+ AffineQuantization _quantization;
+};
+
+} // namespace mir
+#endif // _MIR_TENSOR_TYPE_H_
diff --git a/compiler/mir/include/mir/TensorUtil.h b/compiler/mir/include/mir/TensorUtil.h
index 031eccc9d..757f99f0b 100644
--- a/compiler/mir/include/mir/TensorUtil.h
+++ b/compiler/mir/include/mir/TensorUtil.h
@@ -17,13 +17,11 @@
#ifndef _MIR_TENSOR_UTIL_H_
#define _MIR_TENSOR_UTIL_H_
-#include <cstring>
-#include <memory>
-
-#include "mir/TensorVariant.h"
-#include "mir/Shape.h"
#include "mir/Index.h"
#include "mir/ShapeRange.h"
+#include "mir/TensorVariant.h"
+
+#include <cstring>
namespace mir
{
@@ -39,10 +37,13 @@ template <unsigned int... Ints> TensorVariant transposeTensor(const TensorVarian
const auto &shape = tensor.getShape();
Shape transposed_shape{shape.dim(Ints)...};
- auto elem_type = tensor.getDataType();
+ auto elem_type = tensor.getElementType();
auto elem_size = tensor.getElementSize();
+ TensorType transposed_type(elem_type, transposed_shape);
+ if (tensor.getType().isQuantized())
+ transposed_type.setQuantization(tensor.getType().getQuantization());
- TensorVariant transposed_tensor(elem_type, transposed_shape);
+ TensorVariant transposed_tensor(transposed_type);
for (const auto &index : ShapeRange(shape))
{
diff --git a/compiler/mir/include/mir/TensorVariant.h b/compiler/mir/include/mir/TensorVariant.h
index 6f2c59aef..921fd4468 100644
--- a/compiler/mir/include/mir/TensorVariant.h
+++ b/compiler/mir/include/mir/TensorVariant.h
@@ -17,14 +17,14 @@
#ifndef _MIR_TENSOR_VARIANT_H_
#define _MIR_TENSOR_VARIANT_H_
-#include <utility>
-#include <memory>
-#include <cassert>
-
-#include "mir/Index.h"
-#include "mir/Shape.h"
-#include "mir/DataType.h"
#include "mir/Common.h"
+#include "mir/Index.h"
+#include "mir/TensorType.h"
+
+#include <adtidas/SmallVector.h>
+
+#include <cassert>
+#include <memory>
namespace mir
{
@@ -32,9 +32,15 @@ namespace mir
class TensorVariant
{
public:
- TensorVariant(DataType data_type, const Shape &shape);
+ explicit TensorVariant(const TensorType &type);
+
+ TensorVariant(const TensorType &type, const void *data);
- TensorVariant(DataType data_type, const Shape &shape, const void *data);
+ // TODO Remove as deprecated.
+ TensorVariant(DataType element_type, const Shape &shape);
+
+ // TODO Remove as deprecated.
+ TensorVariant(DataType element_type, const Shape &shape, const void *data);
TensorVariant(const TensorVariant &t_old, const Shape &shape);
@@ -52,20 +58,25 @@ public:
{
assert(idx.rank() == getShape().rank());
std::size_t offset = 0;
- for (int i = 0; i < _shape.rank(); ++i)
+ for (int i = 0; i < getShape().rank(); ++i)
offset += idx.at(i) * _strides[i];
return offset;
}
- const Shape &getShape() const { return _shape; }
- DataType getDataType() const { return _data_type; }
+ const TensorType &getType() const { return _type; }
+
+ DataType getElementType() const { return _type.getElementType(); }
+ const Shape &getShape() const { return _type.getShape(); }
+
+ // TODO Replace uses with `getElementType` and remove.
+ DataType getDataType() const { return _type.getElementType(); }
+ // FIXME This should not be a member of this class.
size_t getElementSize() const { return _element_size; }
private:
- DataType _data_type;
+ TensorType _type;
std::shared_ptr<char> _data;
adt::small_vector<int_fast32_t, MAX_DIMENSION_COUNT> _strides;
- Shape _shape;
size_t _element_size;
};
diff --git a/compiler/mir/include/mir/ops/AbsOp.h b/compiler/mir/include/mir/ops/AbsOp.h
new file mode 100644
index 000000000..d7dbd1622
--- /dev/null
+++ b/compiler/mir/include/mir/ops/AbsOp.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MIR_OPS_ABS_OP_H_
+#define _MIR_OPS_ABS_OP_H_
+
+#include "mir/Operation.h"
+
+namespace mir
+{
+namespace ops
+{
+
+class AbsOp : public Operation
+{
+public:
+ AbsOp(Output *arg) : Operation(Type::abs, {arg})
+ {
+ // Infer output shape.
+ setOutputType(0, {arg->getElementType(), arg->getShape()});
+ }
+
+ Operation *copyWithInputs(const std::vector<Output *> &inputs) override
+ {
+ return new AbsOp(inputs[0]);
+ }
+};
+
+} // namespace ops
+} // namespace mir
+
+#endif //_MIR_OPS_ABS_OP_H_
diff --git a/compiler/mir/include/mir/ops/AvgPool2DOp.h b/compiler/mir/include/mir/ops/AvgPool2DOp.h
index c53cd7930..47fe058ee 100644
--- a/compiler/mir/include/mir/ops/AvgPool2DOp.h
+++ b/compiler/mir/include/mir/ops/AvgPool2DOp.h
@@ -18,7 +18,7 @@
#define _MIR_OPS_AVG_POOL_OP_H_
#include "mir/Operation.h"
-#include "mir/DataFormat.h"
+#include "mir/Attributes.h"
#include <cstdint>
#include <vector>
@@ -31,45 +31,35 @@ namespace ops
class AvgPool2DOp : public Operation
{
public:
- AvgPool2DOp(Output *arg, const std::vector<std::int32_t> &window_size,
- const std::vector<std::int32_t> &strides,
- const std::vector<std::int32_t> &padding_before,
- const std::vector<std::int32_t> &padding_after, bool include_pad,
- DataFormat data_format)
- : Operation(Type::avgPool2D, {arg}), _window_size(window_size), _strides(strides),
- _padding_before(padding_before), _padding_after(padding_after), _include_pad(include_pad),
- _data_format(data_format)
+ AvgPool2DOp(Output *arg, const AvgPool2DOpAttributes &attributes)
+ : Operation(Type::avgPool2D, {arg}), _attributes(attributes)
{
- inferOutputShapes();
+ inferOutputTypes();
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
{
- return new AvgPool2DOp(inputs[0], _window_size, _strides, _padding_before, _padding_after,
- _include_pad, _data_format);
+ return new AvgPool2DOp(inputs[0], _attributes);
};
- const std::vector<std::int32_t> &getWindowSize() const { return _window_size; }
+ const std::vector<std::int32_t> &getWindowSize() const { return _attributes.window; }
- const std::vector<std::int32_t> &getStrides() const { return _strides; }
+ const std::vector<std::int32_t> &getStrides() const { return _attributes.strides; }
- const std::vector<std::int32_t> &getPaddingBefore() const { return _padding_before; }
+ const std::vector<std::int32_t> &getPaddingBefore() const { return _attributes.padding_before; }
- const std::vector<std::int32_t> &getPaddingAfter() const { return _padding_after; }
+ const std::vector<std::int32_t> &getPaddingAfter() const { return _attributes.padding_after; }
- bool getIncludePad() const { return _include_pad; }
+ bool getIncludePad() const { return _attributes.include_pad; }
- DataFormat getDataFormat() const { return _data_format; }
+ DataFormat getDataFormat() const { return _attributes.data_format; }
+
+ const AvgPool2DOpAttributes &getAttributes() const { return _attributes; }
private:
- void inferOutputShapes();
-
- std::vector<std::int32_t> _window_size;
- std::vector<std::int32_t> _strides;
- std::vector<std::int32_t> _padding_before;
- std::vector<std::int32_t> _padding_after;
- bool _include_pad;
- DataFormat _data_format;
+ void inferOutputTypes();
+
+ AvgPool2DOpAttributes _attributes;
};
} // namespace ops
diff --git a/compiler/mir/include/mir/ops/BinaryElementwiseOp.h b/compiler/mir/include/mir/ops/BinaryElementwiseOp.h
index 51f73b05d..8f344185a 100644
--- a/compiler/mir/include/mir/ops/BinaryElementwiseOp.h
+++ b/compiler/mir/include/mir/ops/BinaryElementwiseOp.h
@@ -27,19 +27,13 @@ namespace ops
class BinaryElementwiseOp : public Operation
{
protected:
- BinaryElementwiseOp(Type type, Output *lhs, Output *rhs)
- : Operation(type, {lhs, rhs}), _needs_broadcast(false)
+ BinaryElementwiseOp(Type type, Output *lhs, Output *rhs) : Operation(type, {lhs, rhs})
{
- inferOutputShapes();
- };
-
-public:
- bool getBroadcast() const { return _needs_broadcast; }
+ inferOutputTypes();
+ }
private:
- void inferOutputShapes();
-
- bool _needs_broadcast;
+ void inferOutputTypes();
};
} // namespace ops
diff --git a/compiler/mir/include/mir/ops/BroadcastOp.h b/compiler/mir/include/mir/ops/BroadcastOp.h
new file mode 100644
index 000000000..9d1cc221b
--- /dev/null
+++ b/compiler/mir/include/mir/ops/BroadcastOp.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MIR_OPS_BROADCAST_OP_H_
+#define _MIR_OPS_BROADCAST_OP_H_
+
+#include "mir/Operation.h"
+
+namespace mir
+{
+namespace ops
+{
+
+class BroadcastOp : public Operation
+{
+public:
+ BroadcastOp(Output *input, const Shape &target_shape) : Operation(Type::broadcast, {input})
+ {
+ inferOutputTypes(target_shape);
+ }
+
+ Operation *copyWithInputs(const std::vector<Output *> &inputs) override
+ {
+ return new BroadcastOp(inputs[0], getOutputShape(0));
+ }
+
+private:
+ void inferOutputTypes(const Shape &target_shape);
+};
+
+} // namespace ops
+} // namespace mir
+
+#endif //_MIR_OPS_BINARY_BROADCAST_OP_H_
diff --git a/compiler/mir/include/mir/ops/CappedReluOp.h b/compiler/mir/include/mir/ops/CappedReluOp.h
index e4b30b6c3..efd06c99c 100644
--- a/compiler/mir/include/mir/ops/CappedReluOp.h
+++ b/compiler/mir/include/mir/ops/CappedReluOp.h
@@ -30,7 +30,7 @@ public:
CappedReluOp(Output *arg, float cap) : Operation(Type::cappedReLU, {arg}), _cap(cap)
{
// Infer output shape.
- setOutputShape(0, getInputShape(0));
+ setOutputType(0, {arg->getElementType(), arg->getShape()});
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
diff --git a/compiler/mir/include/mir/ops/ConcatOp.h b/compiler/mir/include/mir/ops/ConcatOp.h
index dd0a3443b..4f46d4449 100644
--- a/compiler/mir/include/mir/ops/ConcatOp.h
+++ b/compiler/mir/include/mir/ops/ConcatOp.h
@@ -33,7 +33,7 @@ public:
ConcatOp(const std::vector<Output *> &args, int32_t axis)
: Operation(Type::concat, args), _axis(axis)
{
- inferOutputShapes();
+ inferOutputTypes();
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
@@ -55,7 +55,7 @@ public:
}
private:
- void inferOutputShapes();
+ void inferOutputTypes();
/// @brief The axis along which to concatenate, may be negative to index from the end
int32_t _axis;
diff --git a/compiler/mir/include/mir/ops/ConstantOp.h b/compiler/mir/include/mir/ops/ConstantOp.h
index eaad3cee9..ab2a592d9 100644
--- a/compiler/mir/include/mir/ops/ConstantOp.h
+++ b/compiler/mir/include/mir/ops/ConstantOp.h
@@ -30,7 +30,7 @@ class ConstantOp : public Operation
public:
explicit ConstantOp(const TensorVariant &value) : Operation(Type::constant, {}), _value(value)
{
- setOutputShape(0, _value.getShape());
+ setOutputType(0, _value.getType());
}
const TensorVariant &getValue() const { return _value; }
diff --git a/compiler/mir/include/mir/ops/Conv2DOp.h b/compiler/mir/include/mir/ops/Conv2DOp.h
index 602905b36..ec818dae5 100644
--- a/compiler/mir/include/mir/ops/Conv2DOp.h
+++ b/compiler/mir/include/mir/ops/Conv2DOp.h
@@ -18,7 +18,7 @@
#define _MIR_OPS_CONV_2D_OP_H_
#include "mir/Operation.h"
-#include "mir/DataFormat.h"
+#include "mir/Attributes.h"
#include <vector>
namespace mir
@@ -29,36 +29,42 @@ namespace ops
class Conv2DOp : public Operation
{
public:
- Conv2DOp(Output *input, Output *kernel, const std::vector<std::int32_t> &strides,
- const std::vector<std::int32_t> &padding_before,
- const std::vector<std::int32_t> &padding_after, DataFormat data_format)
- : Operation(Type::conv2D, {input, kernel}), _strides(strides),
- _padding_before(padding_before), _padding_after(padding_after), _data_format(data_format)
+ Conv2DOp(Output *input, Output *kernel, const Conv2DOpAttributes &attributes)
+ : Operation(Type::conv2D, {input, kernel}), _attributes(attributes)
{
- inferOutputShapes();
+ inferOutputTypes();
+ }
+
+ Conv2DOp(Output *input, Output *kernel, Output *bias, const Conv2DOpAttributes &attributes)
+ : Operation(Type::conv2D, {input, kernel, bias}), _attributes(attributes)
+ {
+ inferOutputTypes();
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
{
- return new Conv2DOp(inputs[0], inputs[1], _strides, _padding_before, _padding_after,
- _data_format);
+ if (inputs.size() == 2)
+ return new Conv2DOp(inputs[0], inputs[1], _attributes);
+ else
+ return new Conv2DOp(inputs[0], inputs[1], inputs[2], _attributes);
};
- const std::vector<std::int32_t> &getStrides() const { return _strides; }
+ const std::vector<std::int32_t> &getStrides() const { return _attributes.strides; }
+
+ const std::vector<std::int32_t> &getPaddingBefore() const { return _attributes.padding_before; }
+
+ const std::vector<std::int32_t> &getPaddingAfter() const { return _attributes.padding_after; }
- const std::vector<std::int32_t> &getPaddingBefore() const { return _padding_before; }
+ std::int32_t getNumGroups() const { return _attributes.num_groups; }
- const std::vector<std::int32_t> &getPaddingAfter() const { return _padding_after; }
+ DataFormat getDataFormat() const { return _attributes.data_format; }
- DataFormat getDataFormat() const { return _data_format; }
+ const Conv2DOpAttributes &getAttributes() const { return _attributes; }
private:
- void inferOutputShapes();
+ void inferOutputTypes();
- std::vector<std::int32_t> _strides;
- std::vector<std::int32_t> _padding_before;
- std::vector<std::int32_t> _padding_after;
- DataFormat _data_format;
+ Conv2DOpAttributes _attributes;
};
} // namespace ops
diff --git a/compiler/mir/include/mir/ops/Deconv2DOp.h b/compiler/mir/include/mir/ops/Deconv2DOp.h
index 68b179741..a7b548028 100644
--- a/compiler/mir/include/mir/ops/Deconv2DOp.h
+++ b/compiler/mir/include/mir/ops/Deconv2DOp.h
@@ -18,7 +18,7 @@
#define _MIR_OPS_DECONV_2D_OP_H_
#include "mir/Operation.h"
-#include "mir/DataFormat.h"
+#include "mir/Attributes.h"
#include "mir/ops/PaddingType.h"
#include <cstdint>
@@ -32,58 +32,50 @@ namespace ops
class DeConv2DOp : public Operation
{
public:
- DeConv2DOp(Output *input, Output *kernel, const std::vector<std::int32_t> &strides,
- const std::vector<std::int32_t> &padding_before,
- const std::vector<std::int32_t> &padding_after, DataFormat data_format)
- : Operation(Type::deConv2D, {input, kernel}), _strides(strides),
- _padding_type(PaddingType::Explicit), _padding_before(padding_before),
- _padding_after(padding_after), _data_format(data_format)
+ DeConv2DOp(Output *input, Output *kernel, const Deconv2DOpAttributes &attributes)
+ : Operation(Type::deConv2D, {input, kernel}), _attributes(attributes)
{
- inferOutputShapes();
+ inferOutputTypes();
}
- DeConv2DOp(Output *input, Output *kernel, const std::vector<std::int32_t> &strides,
- PaddingType padding_type, const Shape &output_shape, DataFormat data_format)
- : Operation(Type::deConv2D, {input, kernel}), _strides(strides), _padding_type(padding_type),
- _padding_before(2), _padding_after(2), _data_format(data_format)
+ DeConv2DOp(Output *input, Output *kernel, const Deconv2DOpAttributes &attributes,
+ const Shape &output_shape)
+ : Operation(Type::deConv2D, {input, kernel}), _attributes(attributes)
{
- setOutputShape(0, output_shape);
+ assert(input->getElementType() == kernel->getElementType());
+ setOutputType(0, {input->getElementType(), output_shape});
inferPaddings();
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
{
if (getPaddingType() == PaddingType::Explicit)
- return new DeConv2DOp(inputs[0], inputs[1], getStrides(), getPaddingBefore(),
- getPaddingAfter(), getDataFormat());
+ return new DeConv2DOp(inputs[0], inputs[1], _attributes);
else
- return new DeConv2DOp(inputs[0], inputs[1], getStrides(), getPaddingType(), getOutputShape(0),
- getDataFormat());
+ return new DeConv2DOp(inputs[0], inputs[1], _attributes, getOutputShape(0));
}
- const std::vector<std::int32_t> &getStrides() const { return _strides; }
+ const std::vector<std::int32_t> &getStrides() const { return _attributes.strides; }
- PaddingType getPaddingType() const { return _padding_type; }
+ PaddingType getPaddingType() const { return _attributes.padding_type; }
- const std::vector<std::int32_t> &getPaddingBefore() const { return _padding_before; }
+ const std::vector<std::int32_t> &getPaddingBefore() const { return _attributes.padding_before; }
- const std::vector<std::int32_t> &getPaddingAfter() const { return _padding_after; }
+ const std::vector<std::int32_t> &getPaddingAfter() const { return _attributes.padding_after; }
- DataFormat getDataFormat() const { return _data_format; }
+ DataFormat getDataFormat() const { return _attributes.data_format; }
+
+ const Deconv2DOpAttributes &getAttributes() const { return _attributes; }
private:
- void inferOutputShapes();
+ void inferOutputTypes();
/**
* @brief Compute paddings based on input shape, kernel shape and strides
*/
void inferPaddings();
- std::vector<std::int32_t> _strides;
- PaddingType _padding_type;
- std::vector<std::int32_t> _padding_before;
- std::vector<std::int32_t> _padding_after;
- DataFormat _data_format;
+ Deconv2DOpAttributes _attributes;
};
} // namespace ops
diff --git a/compiler/mir/include/mir/ops/DepthwiseConv2DOp.h b/compiler/mir/include/mir/ops/DepthwiseConv2DOp.h
index 3d31f6940..347b8e94f 100644
--- a/compiler/mir/include/mir/ops/DepthwiseConv2DOp.h
+++ b/compiler/mir/include/mir/ops/DepthwiseConv2DOp.h
@@ -18,7 +18,7 @@
#define _MIR_OPS_DEPTHWISE_CONV_2D_OP_H_
#include "mir/Operation.h"
-#include "mir/DataFormat.h"
+#include "mir/Attributes.h"
#include <vector>
namespace mir
@@ -29,36 +29,41 @@ namespace ops
class DepthwiseConv2DOp : public Operation
{
public:
- DepthwiseConv2DOp(Output *input, Output *kernel, const std::vector<std::int32_t> &strides,
- const std::vector<std::int32_t> &padding_before,
- const std::vector<std::int32_t> &padding_after, DataFormat data_format)
- : Operation(Type::depthwiseConv, {input, kernel}), _strides(strides),
- _padding_before(padding_before), _padding_after(padding_after), _data_format(data_format)
+ DepthwiseConv2DOp(Output *input, Output *kernel, const Conv2DOpAttributes &attributes)
+ : Operation(Type::depthwiseConv, {input, kernel}), _attributes(attributes)
{
- inferOutputShapes();
+ inferOutputTypes();
+ }
+
+ DepthwiseConv2DOp(Output *input, Output *kernel, Output *bias,
+ const Conv2DOpAttributes &attributes)
+ : Operation(Type::depthwiseConv, {input, kernel, bias}), _attributes(attributes)
+ {
+ inferOutputTypes();
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
{
- return new DepthwiseConv2DOp(inputs[0], inputs[1], _strides, _padding_before, _padding_after,
- _data_format);
+ if (inputs.size() == 2)
+ return new DepthwiseConv2DOp(inputs[0], inputs[1], _attributes);
+ else
+ return new DepthwiseConv2DOp(inputs[0], inputs[1], inputs[2], _attributes);
}
- const std::vector<std::int32_t> &getStrides() const { return _strides; }
+ const std::vector<std::int32_t> &getStrides() const { return _attributes.strides; }
+
+ const std::vector<std::int32_t> &getPaddingBefore() const { return _attributes.padding_before; }
- const std::vector<std::int32_t> &getPaddingBefore() const { return _padding_before; }
+ const std::vector<std::int32_t> &getPaddingAfter() const { return _attributes.padding_after; }
- const std::vector<std::int32_t> &getPaddingAfter() const { return _padding_after; }
+ DataFormat getDataFormat() const { return _attributes.data_format; }
- DataFormat getDataFormat() const { return _data_format; }
+ const Conv2DOpAttributes &getAttributes() const { return _attributes; }
private:
- void inferOutputShapes();
+ void inferOutputTypes();
- std::vector<std::int32_t> _strides;
- std::vector<std::int32_t> _padding_before;
- std::vector<std::int32_t> _padding_after;
- DataFormat _data_format;
+ mir::Conv2DOpAttributes _attributes;
};
} // namespace ops
diff --git a/compiler/mir/include/mir/ops/DequantizeOp.h b/compiler/mir/include/mir/ops/DequantizeOp.h
new file mode 100644
index 000000000..0b412235e
--- /dev/null
+++ b/compiler/mir/include/mir/ops/DequantizeOp.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MIR_OPS_DEQUANTIZE_OP_H_
+#define _MIR_OPS_DEQUANTIZE_OP_H_
+
+#include "mir/Operation.h"
+#include "mir/DataFormat.h"
+#include <vector>
+
+namespace mir
+{
+namespace ops
+{
+
+class DequantizeOp : public Operation
+{
+public:
+ explicit DequantizeOp(Output *input) : Operation(Type::dequantize, {input})
+ {
+ setOutputType(0, {input->getElementType(), input->getShape()});
+ }
+
+ Operation *copyWithInputs(const std::vector<Output *> &inputs) override
+ {
+ return new DequantizeOp(inputs[0]);
+ }
+};
+
+} // namespace ops
+} // namespace mir
+
+#endif //_MIR_OPS_DEQUANTIZE_OP_H_
diff --git a/compiler/mir/include/mir/ops/EluOp.h b/compiler/mir/include/mir/ops/EluOp.h
index 135f59bcc..c13b40251 100644
--- a/compiler/mir/include/mir/ops/EluOp.h
+++ b/compiler/mir/include/mir/ops/EluOp.h
@@ -29,7 +29,7 @@ class EluOp : public Operation
public:
EluOp(Output *arg, float alpha) : Operation(Type::ELU, {arg}), _alpha(alpha)
{
- setOutputShape(0, getInputShape(0));
+ setOutputType(0, {arg->getElementType(), arg->getShape()});
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
diff --git a/compiler/mir/include/mir/ops/EqualOp.h b/compiler/mir/include/mir/ops/EqualOp.h
new file mode 100644
index 000000000..964c2e809
--- /dev/null
+++ b/compiler/mir/include/mir/ops/EqualOp.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MIR_OPS_EQUAL_OP_H_
+#define _MIR_OPS_EQUAL_OP_H_
+
+#include "mir/ops/BinaryElementwiseOp.h"
+
+namespace mir
+{
+namespace ops
+{
+
+class EqualOp : public BinaryElementwiseOp
+{
+public:
+ EqualOp(Output *arg1, Output *arg2) : BinaryElementwiseOp(Type::equal, arg1, arg2)
+ {
+ setOutputType(0, {DataType::UINT8, getInputShape(0)});
+ }
+
+ Operation *copyWithInputs(const std::vector<Output *> &inputs) override
+ {
+ return new EqualOp(inputs[0], inputs[1]);
+ }
+};
+
+} // namespace ops
+} // namespace mir
+
+#endif //_MIR_OPS_EQUAL_OP_H_
diff --git a/compiler/mir/include/mir/ops/FullyConnectedOp.h b/compiler/mir/include/mir/ops/FullyConnectedOp.h
index 8299924fe..589c42df9 100644
--- a/compiler/mir/include/mir/ops/FullyConnectedOp.h
+++ b/compiler/mir/include/mir/ops/FullyConnectedOp.h
@@ -28,18 +28,28 @@ namespace ops
class FullyConnectedOp : public Operation
{
public:
- FullyConnectedOp(Output *arg1, Output *arg2) : Operation(Type::fullyConnected, {arg1, arg2})
+ FullyConnectedOp(Output *input, Output *weights)
+ : Operation(Type::fullyConnected, {input, weights})
{
- inferOutputShapes();
+ inferOutputTypes();
+ }
+
+ FullyConnectedOp(Output *input, Output *weights, Output *bias)
+ : Operation(Type::fullyConnected, {input, weights, bias})
+ {
+ inferOutputTypes();
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
{
- return new FullyConnectedOp(inputs[0], inputs[1]);
+ if (inputs.size() == 2)
+ return new FullyConnectedOp(inputs[0], inputs[1]);
+ else
+ return new FullyConnectedOp(inputs[0], inputs[1], inputs[2]);
}
private:
- void inferOutputShapes();
+ void inferOutputTypes();
};
} // namespace ops
diff --git a/compiler/mir/include/mir/ops/GatherOp.h b/compiler/mir/include/mir/ops/GatherOp.h
index 7f173fb5f..899c9f169 100644
--- a/compiler/mir/include/mir/ops/GatherOp.h
+++ b/compiler/mir/include/mir/ops/GatherOp.h
@@ -35,7 +35,7 @@ public:
GatherOp(Output *data, Output *indices, int32_t axis)
: Operation(Type::gather, {data, indices}), _axis(axis)
{
- inferOutputShapes();
+ inferOutputTypes();
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
@@ -46,7 +46,7 @@ public:
int32_t getAxis() const { return _axis; }
private:
- void inferOutputShapes();
+ void inferOutputTypes();
int32_t _axis;
};
diff --git a/compiler/mir/include/mir/ops/GreaterOp.h b/compiler/mir/include/mir/ops/GreaterOp.h
new file mode 100644
index 000000000..35ede7757
--- /dev/null
+++ b/compiler/mir/include/mir/ops/GreaterOp.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MIR_OPS_GREATER_OP_H_
+#define _MIR_OPS_GREATER_OP_H_
+
+#include "mir/ops/BinaryElementwiseOp.h"
+
+namespace mir
+{
+namespace ops
+{
+
+class GreaterOp : public BinaryElementwiseOp
+{
+public:
+ GreaterOp(Output *arg1, Output *arg2) : BinaryElementwiseOp(Type::greater, arg1, arg2)
+ {
+ setOutputType(0, {DataType::UINT8, getInputShape(0)});
+ }
+
+ Operation *copyWithInputs(const std::vector<Output *> &inputs) override
+ {
+ return new GreaterOp(inputs[0], inputs[1]);
+ }
+};
+
+} // namespace ops
+} // namespace mir
+
+#endif //_MIR_OPS_GREATER_OP_H_
diff --git a/compiler/mir/include/mir/ops/HardSwishOp.h b/compiler/mir/include/mir/ops/HardSwishOp.h
new file mode 100644
index 000000000..48a18bf58
--- /dev/null
+++ b/compiler/mir/include/mir/ops/HardSwishOp.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MIR_OPS_HARD_SWISH_H_
+#define _MIR_OPS_HARD_SWISH_H_
+
+#include "mir/Operation.h"
+
+namespace mir
+{
+namespace ops
+{
+
+class HardSwishOp : public Operation
+{
+public:
+ HardSwishOp(Output *arg) : Operation(Type::hardswish, {arg})
+ {
+ setOutputType(0, {arg->getElementType(), arg->getShape()});
+ }
+
+ Operation *copyWithInputs(const std::vector<Output *> &inputs) override
+ {
+ return new HardSwishOp(inputs[0]);
+ }
+};
+
+} // namespace ops
+} // namespace mir
+
+#endif //_MIR_OPS_HARD_SWISH_H_
diff --git a/compiler/mir/include/mir/ops/InputOp.h b/compiler/mir/include/mir/ops/InputOp.h
index e86619689..ed576aeb8 100644
--- a/compiler/mir/include/mir/ops/InputOp.h
+++ b/compiler/mir/include/mir/ops/InputOp.h
@@ -27,7 +27,13 @@ namespace ops
class InputOp : public Operation
{
public:
- explicit InputOp(const Shape &shape) : Operation(Type::input, {}) { setOutputShape(0, shape); }
+ // @brief Deprecated [use TensorType for creation of Input]
+ explicit InputOp(const Shape &shape) : Operation(Type::input, {})
+ {
+ setOutputType(0, {mir::DataType::UNKNOWN, shape});
+ }
+
+ explicit InputOp(const TensorType &type) : Operation(Type::input, {}) { setOutputType(0, type); }
Operation *copyWithInputs(const std::vector<Output *> &input) override
{
diff --git a/compiler/mir/include/mir/ops/LeakyReluOp.h b/compiler/mir/include/mir/ops/LeakyReluOp.h
index d330dea4d..5294778ac 100644
--- a/compiler/mir/include/mir/ops/LeakyReluOp.h
+++ b/compiler/mir/include/mir/ops/LeakyReluOp.h
@@ -30,7 +30,7 @@ public:
explicit LeakyReluOp(Output *arg, float alpha) : Operation(Type::leakyReLU, {arg}), _alpha(alpha)
{
// Infer output shape.
- setOutputShape(0, getInputShape(0));
+ setOutputType(0, {arg->getElementType(), arg->getShape()});
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
diff --git a/compiler/mir/include/mir/ops/LessOp.h b/compiler/mir/include/mir/ops/LessOp.h
new file mode 100644
index 000000000..7e5fb2666
--- /dev/null
+++ b/compiler/mir/include/mir/ops/LessOp.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MIR_OPS_LESS_OP_H_
+#define _MIR_OPS_LESS_OP_H_
+
+#include "mir/ops/BinaryElementwiseOp.h"
+
+namespace mir
+{
+namespace ops
+{
+
+class LessOp : public BinaryElementwiseOp
+{
+public:
+ LessOp(Output *arg1, Output *arg2) : BinaryElementwiseOp(Type::less, arg1, arg2)
+ {
+ setOutputType(0, TensorType(DataType::UINT8, getInputShape(0)));
+ }
+
+ Operation *copyWithInputs(const std::vector<Output *> &inputs) override
+ {
+ return new LessOp(inputs[0], inputs[1]);
+ }
+};
+
+} // namespace ops
+} // namespace mir
+
+#endif //_MIR_OPS_LESS_OP_H_
diff --git a/compiler/mir/include/mir/ops/MaxPool2DOp.h b/compiler/mir/include/mir/ops/MaxPool2DOp.h
index 2a9d55c33..7c5df4a53 100644
--- a/compiler/mir/include/mir/ops/MaxPool2DOp.h
+++ b/compiler/mir/include/mir/ops/MaxPool2DOp.h
@@ -18,7 +18,7 @@
#define _MIR_OPS_MAX_POOL_OP_H_
#include "mir/Operation.h"
-#include "mir/DataFormat.h"
+#include "mir/Attributes.h"
#include <cstdint>
#include <vector>
@@ -31,40 +31,33 @@ namespace ops
class MaxPool2DOp : public Operation
{
public:
- MaxPool2DOp(Output *arg, const std::vector<std::int32_t> &window_size,
- const std::vector<std::int32_t> &strides,
- const std::vector<std::int32_t> &padding_before,
- const std::vector<std::int32_t> &padding_after, DataFormat data_format)
- : Operation(Type::maxPool2D, {arg}), _window_size(window_size), _strides(strides),
- _padding_before(padding_before), _padding_after(padding_after), _data_format(data_format)
+ MaxPool2DOp(Output *arg, const MaxPool2DOpAttributes &attributes)
+ : Operation(Type::maxPool2D, {arg}), _attributes(attributes)
{
- inferOutputShapes();
+ inferOutputTypes();
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
{
- return new MaxPool2DOp(inputs[0], _window_size, _strides, _padding_before, _padding_after,
- _data_format);
+ return new MaxPool2DOp(inputs[0], _attributes);
};
- const std::vector<std::int32_t> &getWindowSize() const { return _window_size; }
+ const std::vector<std::int32_t> &getWindowSize() const { return _attributes.window; }
- const std::vector<std::int32_t> &getStrides() const { return _strides; }
+ const std::vector<std::int32_t> &getStrides() const { return _attributes.strides; }
- const std::vector<std::int32_t> &getPaddingBefore() const { return _padding_before; }
+ const std::vector<std::int32_t> &getPaddingBefore() const { return _attributes.padding_before; }
- const std::vector<std::int32_t> &getPaddingAfter() const { return _padding_after; }
+ const std::vector<std::int32_t> &getPaddingAfter() const { return _attributes.padding_after; }
- DataFormat getDataFormat() const { return _data_format; }
+ DataFormat getDataFormat() const { return _attributes.data_format; }
+
+ const MaxPool2DOpAttributes &getAttributes() const { return _attributes; }
private:
- void inferOutputShapes();
+ void inferOutputTypes();
- std::vector<std::int32_t> _window_size;
- std::vector<std::int32_t> _strides;
- std::vector<std::int32_t> _padding_before;
- std::vector<std::int32_t> _padding_after;
- DataFormat _data_format;
+ MaxPool2DOpAttributes _attributes;
};
} // namespace ops
diff --git a/compiler/mir/include/mir/ops/PadOp.h b/compiler/mir/include/mir/ops/PadOp.h
index 1b109e5af..76453acec 100644
--- a/compiler/mir/include/mir/ops/PadOp.h
+++ b/compiler/mir/include/mir/ops/PadOp.h
@@ -18,6 +18,7 @@
#define _MIR_OPS_PAD_OP_H_
#include "mir/Operation.h"
+#include "mir/Attributes.h"
namespace mir
{
@@ -27,36 +28,28 @@ namespace ops
class PadOp : public Operation
{
public:
- /// @param arg The tensor to be padded.
- /// @param padding_before The padding to be added before the tensor.
- /// @param padding_after The padding to be added after the tensor.
- /// @param padding_value The value to be used for padding.
- PadOp(Output *arg, const std::vector<std::int32_t> &padding_before,
- const std::vector<std::int32_t> &padding_after, float padding_value)
- : Operation(Type::pad, {arg}), _padding_before(padding_before), _padding_after(padding_after),
- _padding_value(padding_value)
+ PadOp(Output *arg, const PadOpAttributes &attributes)
+ : Operation(Type::pad, {arg}), _attributes(attributes)
{
- assert(_padding_before.size() == _padding_after.size());
- inferOutputShapes();
+ assert(_attributes.padding_before.size() == _attributes.padding_after.size());
+ inferOutputTypes();
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
{
- return new PadOp(inputs[0], _padding_before, _padding_after, _padding_value);
+ return new PadOp(inputs[0], _attributes);
}
- const std::vector<std::int32_t> &getPaddingBefore() const { return _padding_before; }
+ const std::vector<std::int32_t> &getPaddingBefore() const { return _attributes.padding_before; }
- const std::vector<std::int32_t> &getPaddingAfter() const { return _padding_after; }
+ const std::vector<std::int32_t> &getPaddingAfter() const { return _attributes.padding_after; }
- float getPaddingValue() const { return _padding_value; }
+ float getPaddingValue() const { return _attributes.padding_value; }
private:
- void inferOutputShapes();
+ void inferOutputTypes();
- std::vector<std::int32_t> _padding_before;
- std::vector<std::int32_t> _padding_after;
- float _padding_value;
+ PadOpAttributes _attributes;
};
} // namespace ops
diff --git a/compiler/mir/include/mir/ops/QuantizeOp.h b/compiler/mir/include/mir/ops/QuantizeOp.h
new file mode 100644
index 000000000..7e9216e3e
--- /dev/null
+++ b/compiler/mir/include/mir/ops/QuantizeOp.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MIR_OPS_QUANTIZE_OP_H_
+#define _MIR_OPS_QUANTIZE_OP_H_
+
+#include "mir/Operation.h"
+
+namespace mir
+{
+namespace ops
+{
+
+class QuantizeOp : public Operation
+{
+public:
+ explicit QuantizeOp(Output *input) : Operation(Type::quantize, {input})
+ {
+ setOutputType(0, {input->getElementType(), input->getShape()});
+ }
+
+ Operation *copyWithInputs(const std::vector<Output *> &inputs) override
+ {
+ return new QuantizeOp(inputs[0]);
+ }
+};
+
+} // namespace ops
+} // namespace mir
+
+#endif //_MIR_OPS_QUANTIZE_OP_H_
diff --git a/compiler/mir/include/mir/ops/ReduceOp.h b/compiler/mir/include/mir/ops/ReduceOp.h
index f8a2de82a..0f46a4596 100644
--- a/compiler/mir/include/mir/ops/ReduceOp.h
+++ b/compiler/mir/include/mir/ops/ReduceOp.h
@@ -31,7 +31,7 @@ protected:
ReduceOp(Type type, Output *arg, const std::vector<int> &reduction_dims, bool keep_dims)
: Operation(type, {arg}), _reduction_dims(reduction_dims), _keep_dims(keep_dims)
{
- inferOutputShapes();
+ inferOutputTypes();
}
public:
@@ -40,7 +40,7 @@ public:
bool getKeepDims() const { return _keep_dims; };
private:
- void inferOutputShapes();
+ void inferOutputTypes();
std::vector<int> _reduction_dims;
bool _keep_dims;
diff --git a/compiler/mir/include/mir/ops/ReluOp.h b/compiler/mir/include/mir/ops/ReluOp.h
index d1ef2c72d..fd1cc3c85 100644
--- a/compiler/mir/include/mir/ops/ReluOp.h
+++ b/compiler/mir/include/mir/ops/ReluOp.h
@@ -30,7 +30,7 @@ public:
explicit ReluOp(Output *arg) : Operation(Type::ReLU, {arg})
{
// Infer output shape.
- setOutputShape(0, getInputShape(0));
+ setOutputType(0, {arg->getElementType(), arg->getShape()});
}
Operation *copyWithInputs(const std::vector<Output *> &arg) override
diff --git a/compiler/mir/include/mir/ops/ReshapeOp.h b/compiler/mir/include/mir/ops/ReshapeOp.h
index 7b9645361..7271cff65 100644
--- a/compiler/mir/include/mir/ops/ReshapeOp.h
+++ b/compiler/mir/include/mir/ops/ReshapeOp.h
@@ -49,7 +49,7 @@ public:
dim = static_cast<int32_t>(in_elements_num / out_elements_num);
}
- setOutputShape(0, output_shape);
+ setOutputType(0, {arg->getElementType(), output_shape});
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
diff --git a/compiler/mir/include/mir/ops/ResizeOp.h b/compiler/mir/include/mir/ops/ResizeOp.h
index d4ef4a2b4..51e1b0b76 100644
--- a/compiler/mir/include/mir/ops/ResizeOp.h
+++ b/compiler/mir/include/mir/ops/ResizeOp.h
@@ -52,7 +52,7 @@ public:
output_shape.dim(i) = static_cast<int32_t>(lroundf(_scales.at(i) * input_shape.dim(i)));
}
- setOutputShape(0, output_shape);
+ setOutputType(0, {getInput(0)->getElementType(), output_shape});
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
@@ -66,7 +66,7 @@ public:
// Calculate scales based on given shape.
auto &input_shape = getInputShape(0);
assert(input_shape.rank() == 4 && output_shape.rank() == 4);
- setOutputShape(0, output_shape);
+ setOutputType(0, {getInput(0)->getElementType(), output_shape});
_scales = {1.0f, static_cast<float>(output_shape.dim(1)) / input_shape.dim(1),
static_cast<float>(output_shape.dim(2)) / input_shape.dim(2), 1.0f};
}
diff --git a/compiler/mir/include/mir/ops/SigmoidOp.h b/compiler/mir/include/mir/ops/SigmoidOp.h
index 87d047d83..8655baca0 100644
--- a/compiler/mir/include/mir/ops/SigmoidOp.h
+++ b/compiler/mir/include/mir/ops/SigmoidOp.h
@@ -30,7 +30,7 @@ public:
explicit SigmoidOp(Output *arg) : Operation(Type::sigmoid, {arg})
{
// Infer output shape.
- setOutputShape(0, getInputShape(0));
+ setOutputType(0, {arg->getElementType(), arg->getShape()});
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
diff --git a/compiler/mir/include/mir/ops/SliceOp.h b/compiler/mir/include/mir/ops/SliceOp.h
index 5c2639339..6370de4fa 100644
--- a/compiler/mir/include/mir/ops/SliceOp.h
+++ b/compiler/mir/include/mir/ops/SliceOp.h
@@ -30,7 +30,7 @@ public:
SliceOp(Output *arg, const Shape &starts, const Shape &sizes)
: Operation(Type::slice, {arg}), _starts(starts), _sizes(sizes)
{
- inferOutputShapes();
+ inferOutputTypes();
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
@@ -43,7 +43,7 @@ public:
const Shape &getSizes() { return _sizes; }
private:
- void inferOutputShapes();
+ void inferOutputTypes();
Shape _starts;
Shape _sizes;
diff --git a/compiler/mir/include/mir/ops/SoftmaxOp.h b/compiler/mir/include/mir/ops/SoftmaxOp.h
index f6f1259a0..ca05f593c 100644
--- a/compiler/mir/include/mir/ops/SoftmaxOp.h
+++ b/compiler/mir/include/mir/ops/SoftmaxOp.h
@@ -32,7 +32,7 @@ class SoftmaxOp : public Operation
public:
SoftmaxOp(Output *arg, int32_t axis) : Operation(Type::softmax, {arg}), _axis(axis)
{
- setOutputShape(0, getInputShape(0));
+ setOutputType(0, {arg->getElementType(), arg->getShape()});
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
diff --git a/compiler/mir/include/mir/ops/SqrtOp.h b/compiler/mir/include/mir/ops/SqrtOp.h
index 33bde55cf..a029f7634 100644
--- a/compiler/mir/include/mir/ops/SqrtOp.h
+++ b/compiler/mir/include/mir/ops/SqrtOp.h
@@ -29,7 +29,7 @@ class SqrtOp : public Operation
public:
explicit SqrtOp(Output *arg) : Operation(Type::sqrt, {arg})
{
- setOutputShape(0, getInputShape(0));
+ setOutputType(0, {arg->getElementType(), arg->getShape()});
};
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
diff --git a/compiler/mir/include/mir/ops/SqueezeOp.h b/compiler/mir/include/mir/ops/SqueezeOp.h
index 23e119ed1..8ef2a78bb 100644
--- a/compiler/mir/include/mir/ops/SqueezeOp.h
+++ b/compiler/mir/include/mir/ops/SqueezeOp.h
@@ -32,7 +32,7 @@ public:
: Operation(Type::squeeze, {arg}), _dims_to_squeeze(dims_to_squeeze)
{
// Infer output shape.
- inferOutputShapes();
+ inferOutputTypes();
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
@@ -40,7 +40,7 @@ public:
return new SqueezeOp(inputs[0], _dims_to_squeeze);
}
- void inferOutputShapes();
+ void inferOutputTypes();
int32_t getNumSqueezeDims() const { return static_cast<int32_t>(_dims_to_squeeze.size()); }
diff --git a/compiler/mir/include/mir/ops/TanhOp.h b/compiler/mir/include/mir/ops/TanhOp.h
index e2d3762de..d49261310 100644
--- a/compiler/mir/include/mir/ops/TanhOp.h
+++ b/compiler/mir/include/mir/ops/TanhOp.h
@@ -30,7 +30,7 @@ public:
explicit TanhOp(Output *arg) : Operation(Type::tanh, {arg})
{
// Infer output shape.
- setOutputShape(0, getInputShape(0));
+ setOutputType(0, {arg->getElementType(), arg->getShape()});
}
Operation *copyWithInputs(const std::vector<Output *> &inputs) override
diff --git a/compiler/mir/include/mir/ops/TransposeOp.h b/compiler/mir/include/mir/ops/TransposeOp.h
index 53fa084fa..235a901f1 100644
--- a/compiler/mir/include/mir/ops/TransposeOp.h
+++ b/compiler/mir/include/mir/ops/TransposeOp.h
@@ -43,7 +43,7 @@ public:
}
private:
- void inferOutputShapes();
+ void inferOutputTypes();
std::vector<std::size_t> _axis_order;
};
diff --git a/compiler/mir/include/mir_caffe2_importer/caffe2_importer.h b/compiler/mir/include/mir_caffe2_importer/caffe2_importer.h
new file mode 100644
index 000000000..213fbe98d
--- /dev/null
+++ b/compiler/mir/include/mir_caffe2_importer/caffe2_importer.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_CAFFE2_IMPORTER_H
+#define MIR_CAFFE2_IMPORTER_H
+
+#include <string>
+#include <memory>
+#include <vector>
+
+#include "mir/Graph.h"
+
+namespace mir_caffe2
+{
+
+std::unique_ptr<mir::Graph> loadModel(std::string predict_net, std::string init_net,
+ const std::vector<std::vector<int>> &input_shapes);
+
+} // namespace mir_caffe2
+
+#endif // MIR_CAFFE2_IMPORTER_H
diff --git a/compiler/mir/include/mir_caffe_importer/caffe_importer.h b/compiler/mir/include/mir_caffe_importer/caffe_importer.h
new file mode 100644
index 000000000..cf2c055bc
--- /dev/null
+++ b/compiler/mir/include/mir_caffe_importer/caffe_importer.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_CAFFE_IMPORTER_H
+#define MIR_CAFFE_IMPORTER_H
+
+#include <string>
+#include <memory>
+
+#include "mir/Graph.h"
+
+namespace mir_caffe
+{
+
+std::unique_ptr<mir::Graph> importModelFromBinaryFile(const std::string &filename);
+std::unique_ptr<mir::Graph> importModelFromTextFile(const std::string &filename);
+// TODO Remove after changing all uses.
+std::unique_ptr<mir::Graph> loadModel(const std::string &filename);
+
+} // namespace mir_caffe
+
+#endif // MIR_CAFFE_IMPORTER_H
diff --git a/compiler/mir/include/mir_onnx_importer/ONNXImporterImpl.h b/compiler/mir/include/mir_onnx_importer/ONNXImporterImpl.h
new file mode 100644
index 000000000..02a49b330
--- /dev/null
+++ b/compiler/mir/include/mir_onnx_importer/ONNXImporterImpl.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MIR_ONNX_IMPORTER_H
+#define _MIR_ONNX_IMPORTER_H
+
+#include "mir/Graph.h"
+
+#include <memory>
+#include <string>
+
+namespace mir_onnx
+{
+
+std::unique_ptr<mir::Graph> importModelFromBinaryFile(const std::string &filename);
+std::unique_ptr<mir::Graph> importModelFromTextFile(const std::string &filename);
+// TODO Remove after changing all uses.
+std::unique_ptr<mir::Graph> loadModel(const std::string &filename);
+
+} // namespace mir_onnx
+
+#endif // _MIR_ONNX_IMPORTER_H
diff --git a/compiler/mir/include/mir_tflite_importer/tflite_importer.h b/compiler/mir/include/mir_tflite_importer/tflite_importer.h
new file mode 100644
index 000000000..85cd01ee9
--- /dev/null
+++ b/compiler/mir/include/mir_tflite_importer/tflite_importer.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_TFLITE_IMPORTER_H
+#define MIR_TFLITE_IMPORTER_H
+
+#include "mir/Graph.h"
+
+#include <memory>
+#include <string>
+
+namespace mir_tflite
+{
+
+std::unique_ptr<mir::Graph> loadModel(std::string filename);
+
+} // namespace mir_tflite
+
+#endif // MIR_TFLITE_IMPORTER_H
diff --git a/compiler/mir/src/DotNodeBuilder.cpp b/compiler/mir/src/DotNodeBuilder.cpp
index 0afaa374d..abe7af63a 100644
--- a/compiler/mir/src/DotNodeBuilder.cpp
+++ b/compiler/mir/src/DotNodeBuilder.cpp
@@ -78,6 +78,7 @@ void DotNodeBuilder::visit(ops::Conv2DOp &op)
addAttribute("Strides", toString(op.getStrides()));
addAttribute("Padding before", toString(op.getPaddingBefore()));
addAttribute("Padding after", toString(op.getPaddingAfter()));
+ addAttribute("Num groups", std::to_string(op.getNumGroups()));
addAttribute("Data format", toString(op.getDataFormat()));
}
@@ -140,6 +141,7 @@ void DotNodeBuilder::visit(ops::ReduceMeanOp &op)
void DotNodeBuilder::visit(ops::ResizeOp &op)
{
assert(op.getMode() == ops::ResizeOp::ResizeMethod::nearestNeighbor);
+ (void)op;
addAttribute("Interpolation mode", "nearestNeighbor");
}
diff --git a/compiler/mir/src/Graph.cpp b/compiler/mir/src/Graph.cpp
index 4cf57dd60..0eccdac2b 100644
--- a/compiler/mir/src/Graph.cpp
+++ b/compiler/mir/src/Graph.cpp
@@ -16,9 +16,9 @@
#include "mir/Graph.h"
-#include <deque>
-#include <set>
#include <algorithm>
+#include <deque>
+#include <unordered_map>
namespace mir
{
@@ -34,61 +34,52 @@ static void replaceUsages(Operation *op, Operation *with)
assert(op->getNumOutputs() == with->getNumOutputs());
for (std::size_t i = 0; i < op->getNumOutputs(); ++i)
{
- auto *output = op->getOutput(i);
- // The copy is intended here.
- const auto consumers =
- output->getConsumers(); // NOLINT(performance-unnecessary-copy-initialization)
- for (auto *consumer : consumers)
- {
- consumer->replaceProducer(with->getOutput(i));
- }
+ Operation::Output *output = op->getOutput(i);
+ output->replaceAllUsesWith(with->getOutput(i));
}
}
-void Graph::accept(IVisitor *visitor)
+std::vector<Operation *> getSortedNodes(Graph *graph)
{
- std::deque<Operation *> q;
- std::set<Operation *> known_ops;
+ std::deque<Operation *> ready_nodes;
+ std::unordered_map<Operation *, std::size_t> num_visited_input_edges;
- for (auto *op : _ops)
+ for (Operation *op : graph->getNodes())
{
if (op->getNumInputs() == 0)
{
- q.emplace_back(op);
- known_ops.insert(op);
+ ready_nodes.push_back(op);
}
}
- // BFS
- while (!q.empty())
+ std::vector<Operation *> sorted_nodes;
+ while (!ready_nodes.empty())
{
- Operation *src_node = q.front();
- q.pop_front();
- src_node->accept(visitor);
- for (auto &src_output : src_node->getOutputs())
+ Operation *src_node = ready_nodes.front();
+ ready_nodes.pop_front();
+ sorted_nodes.push_back(src_node);
+ for (Operation::Output &output : src_node->getOutputs())
{
- for (auto *consumer : src_output.getConsumers())
+ for (const auto use : output.getUses())
{
- Operation *dst_node = consumer->getNode();
- if (known_ops.count(dst_node) == 0)
+ Operation *dst_node = use.getNode();
+ if (++num_visited_input_edges[dst_node] == dst_node->getNumInputs())
{
- bool all_inputs_resolved = true;
- for (auto &dst_input : dst_node->getInputs())
- {
- if (known_ops.count(dst_input.getProducer()->getNode()) == 0)
- {
- all_inputs_resolved = false;
- }
- }
- if (all_inputs_resolved)
- {
- known_ops.insert(dst_node);
- q.push_back(dst_node);
- }
+ ready_nodes.push_back(dst_node);
}
}
}
}
+
+ return sorted_nodes;
+}
+
+void Graph::accept(IVisitor *visitor)
+{
+ for (Operation *node : getSortedNodes(this))
+ {
+ node->accept(visitor);
+ }
}
Graph::~Graph()
@@ -116,49 +107,18 @@ void Graph::replaceNode(Operation *op, Operation *with)
removeNode(op);
}
-ops::InputOp *Graph::replaceWithInputNode(Operation *op)
-{
- assert(op->getNumOutputs() == 1 &&
- "Only operations with single output value can be replaced with input node");
-
- auto in = create<ops::InputOp>(op->getOutputShape(0));
- replaceNode(op, in);
-
- return dynamic_cast<ops::InputOp *>(in);
-}
-
-void Graph::replaceInputNodes(const std::vector<std::string> &new_inputs)
-{
- std::vector<Operation *> ops_to_replace;
-
- std::set<std::string> new_input_set(new_inputs.begin(), new_inputs.end());
-
- for (auto &op : _ops)
- {
- if (op->getNumOutputs() == 1 && new_input_set.count(op->getOutput(0)->getName()) != 0)
- {
- ops_to_replace.push_back(op);
- }
- }
-
- for (auto &op : ops_to_replace)
- {
- replaceWithInputNode(op);
- }
-}
-
void Graph::removeNode(Operation *op)
{
#ifndef NDEBUG
for (const auto &output : op->getOutputs())
{
- assert(output.getConsumers().empty() && "Trying to remove a node that has uses.");
+ assert(output.getUses().empty() && "Trying to remove a node that has uses.");
}
#endif
- for (auto &input : op->getInputs())
+ for (std::size_t i = 0; i < op->getNumInputs(); ++i)
{
- input.getProducer()->removeConsumer(&input);
+ op->getInput(i)->removeUse(Operation::Use(op, i));
}
if (op->getType() == Operation::Type::input)
diff --git a/compiler/mir/src/GraphPatternMatcher.cpp b/compiler/mir/src/GraphPatternMatcher.cpp
index 31b8551f4..78ea1fa02 100644
--- a/compiler/mir/src/GraphPatternMatcher.cpp
+++ b/compiler/mir/src/GraphPatternMatcher.cpp
@@ -33,9 +33,9 @@ GraphPatternMatcher::matchEdge(GraphPatternMatcher::Predicate p1, GraphPatternMa
{
for (auto &out : start->getOutputs())
{
- for (auto *consumer : out.getConsumers())
+ for (auto use : out.getUses())
{
- Operation *end = consumer->getNode();
+ Operation *end = use.getNode();
if (p2(end))
{
matches.emplace_back(std::make_pair(start, end));
@@ -57,16 +57,15 @@ GraphPatternMatcher::matchUpBush(mir::GraphPatternMatcher::Predicate p1,
{
if (p2(root))
{
- auto &prev_nodes = root->getInputs();
- if (std::all_of(prev_nodes.begin(), prev_nodes.end(), [p1](const Operation::Input &input) {
- return p1(input.getProducer()->getNode());
- }))
+ const auto &inputs = root->getInputs();
+ if (std::all_of(inputs.begin(), inputs.end(),
+ [p1](const Operation::Output *input) { return p1(input->getNode()); }))
{
std::vector<Operation *> tops;
- tops.reserve(prev_nodes.size());
- for (auto &pr : prev_nodes)
+ tops.reserve(inputs.size());
+ for (Operation::Output *pr : inputs)
{
- tops.emplace_back(pr.getProducer()->getNode());
+ tops.emplace_back(pr->getNode());
}
matches.emplace_back(std::make_pair(tops, root));
}
diff --git a/compiler/mir/src/IrDotDumper.cpp b/compiler/mir/src/IrDotDumper.cpp
index e40255cfa..0c3f4dfb0 100644
--- a/compiler/mir/src/IrDotDumper.cpp
+++ b/compiler/mir/src/IrDotDumper.cpp
@@ -29,9 +29,9 @@ void dumpGraph(const Graph *graph, std::ostream &stream)
for (const auto *node : graph->getNodes())
{
dot_graph.addNode(DotNodeBuilder(*node).getDotNode());
- for (const auto &input : node->getInputs())
+ for (const Operation::Output *input : node->getInputs())
{
- dot_graph.addEdge({input.getProducer()->getNode()->getId(), node->getId()});
+ dot_graph.addEdge({input->getNode()->getId(), node->getId()});
}
}
diff --git a/compiler/mir/src/Operation.cpp b/compiler/mir/src/Operation.cpp
index d926705b9..6f72acbf6 100644
--- a/compiler/mir/src/Operation.cpp
+++ b/compiler/mir/src/Operation.cpp
@@ -18,15 +18,34 @@
#include "mir/Visitor.h"
#include "mir/OpDefs.h"
+#include <algorithm>
+
namespace mir
{
+void Operation::Output::removeUse(Operation::Use use)
+{
+ auto it = std::remove(_uses.begin(), _uses.end(), use);
+ _uses.erase(it);
+}
+
+void Operation::Output::replaceAllUsesWith(mir::Operation::Output *new_def)
+{
+ for (auto use : _uses)
+ {
+ use.getNode()->_inputs[use.getIndex()] = new_def;
+ new_def->addUse(use);
+ }
+ _uses.clear();
+}
+
Operation::Operation(Type type, const std::vector<Output *> &inputs, std::size_t num_outputs)
: _type(type)
{
for (std::size_t i = 0; i < inputs.size(); ++i)
{
- _inputs.emplace_back(this, i, inputs[i]);
+ inputs[i]->addUse(Use(this, i));
+ _inputs.push_back(inputs[i]);
}
for (std::size_t i = 0; i < num_outputs; ++i)
{
diff --git a/compiler/mir/src/Shape.cpp b/compiler/mir/src/Shape.cpp
index d6250e2f7..825420cd6 100644
--- a/compiler/mir/src/Shape.cpp
+++ b/compiler/mir/src/Shape.cpp
@@ -40,6 +40,31 @@ int32_t Shape::numElements() const
return res;
}
+Shape broadcastShapes(const Shape &lhs_shape, const Shape &rhs_shape)
+{
+ const int num_dims = std::max(lhs_shape.rank(), rhs_shape.rank());
+ Shape result_shape(num_dims);
+
+ for (int i = 0; i < num_dims; ++i)
+ {
+ const std::int32_t lhs_dim =
+ (i >= num_dims - lhs_shape.rank()) ? lhs_shape.dim(i - (num_dims - lhs_shape.rank())) : 1;
+ const std::int32_t rhs_dim =
+ (i >= num_dims - rhs_shape.rank()) ? rhs_shape.dim(i - (num_dims - rhs_shape.rank())) : 1;
+ if (lhs_dim == 1)
+ {
+ result_shape.dim(i) = rhs_dim;
+ }
+ else
+ {
+ assert(rhs_dim == 1 || rhs_dim == lhs_dim);
+ result_shape.dim(i) = lhs_dim;
+ }
+ }
+
+ return result_shape;
+}
+
std::string toString(const Shape &shape)
{
std::stringstream ss;
diff --git a/compiler/mir/src/TensorVariant.cpp b/compiler/mir/src/TensorVariant.cpp
index 595ae7d86..9e57dbaf0 100644
--- a/compiler/mir/src/TensorVariant.cpp
+++ b/compiler/mir/src/TensorVariant.cpp
@@ -20,44 +20,36 @@
namespace mir
{
-TensorVariant::TensorVariant(DataType data_type, const Shape &shape)
- : _data_type(data_type), _strides(shape.rank()), _shape(shape)
+TensorVariant::TensorVariant(const TensorType &type) : _type(type), _strides(type.getShape().rank())
{
- switch (data_type)
- {
- case DataType::FLOAT32:
- _element_size = sizeof(float);
- break;
- case DataType::FLOAT64:
- _element_size = sizeof(double);
- break;
- case DataType::INT32:
- _element_size = sizeof(int32_t);
- break;
- case DataType::INT64:
- _element_size = sizeof(int64_t);
- break;
- default:
- assert(false);
- }
- std::size_t data_size = _shape.numElements() * _element_size;
+ _element_size = getDataTypeSize(getElementType());
+ std::size_t data_size = getShape().numElements() * _element_size;
_data.reset(new char[data_size], std::default_delete<char[]>());
int stride = 1;
- for (int d = _shape.rank() - 1; d >= 0; --d)
+ for (int d = getShape().rank() - 1; d >= 0; --d)
{
_strides[d] = stride;
- stride *= _shape.dim(d);
+ stride *= getShape().dim(d);
}
}
-TensorVariant::TensorVariant(DataType data_type, const Shape &shape, const void *data)
- : TensorVariant(data_type, shape)
+TensorVariant::TensorVariant(DataType element_type, const Shape &shape)
+ : TensorVariant(TensorType(element_type, shape))
{
- std::size_t data_size = _shape.numElements() * _element_size;
+}
+
+TensorVariant::TensorVariant(const TensorType &type, const void *data) : TensorVariant(type)
+{
+ std::size_t data_size = getShape().numElements() * _element_size;
std::memcpy(_data.get(), data, data_size);
}
+TensorVariant::TensorVariant(DataType element_type, const Shape &shape, const void *data)
+ : TensorVariant(TensorType(element_type, shape), data)
+{
+}
+
/**
* @brief Construct a TensorVariant from t_old that has strides with 0 where dim = 1
* Used for broadcasting
@@ -65,15 +57,15 @@ TensorVariant::TensorVariant(DataType data_type, const Shape &shape, const void
* @param shape shape to broadcast to
*/
TensorVariant::TensorVariant(const TensorVariant &t_old, const Shape &shape)
- : _data_type(t_old._data_type), _data(t_old._data), _strides(static_cast<size_t>(shape.rank())),
- _shape(shape), _element_size(t_old._element_size)
+ : _type(t_old.getType().getElementType(), shape), _data(t_old._data),
+ _strides(static_cast<size_t>(shape.rank())), _element_size(t_old._element_size)
{
- int axis_old = t_old._shape.rank() - 1;
+ int axis_old = t_old.getShape().rank() - 1;
for (int d = shape.rank() - 1; d >= 0; d--)
{
if (axis_old == -1)
break;
- if (t_old._shape.dim(axis_old) != 1)
+ if (t_old.getShape().dim(axis_old) != 1)
_strides[d] = t_old._strides[axis_old];
axis_old--;
}
diff --git a/compiler/mir/src/mir_caffe2_importer/CMakeLists.txt b/compiler/mir/src/mir_caffe2_importer/CMakeLists.txt
new file mode 100644
index 000000000..a5537815d
--- /dev/null
+++ b/compiler/mir/src/mir_caffe2_importer/CMakeLists.txt
@@ -0,0 +1,28 @@
+nnas_find_package(PytorchSource QUIET)
+nnas_find_package(Protobuf QUIET)
+
+if (NOT PytorchSource_FOUND OR NOT Protobuf_FOUND)
+ return()
+endif()
+
+Protobuf_Generate(CAFFE2_PROTO "${CMAKE_CURRENT_BINARY_DIR}/generated/caffe2"
+ "${PytorchSource_DIR}" "caffe2/proto/caffe2.proto")
+
+add_library(caffe2proto STATIC ${CAFFE2_PROTO_SOURCES})
+set_target_properties(caffe2proto PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(caffe2proto PUBLIC ${CAFFE2_PROTO_INCLUDE_DIRS})
+target_link_libraries(caffe2proto PUBLIC libprotobuf)
+
+
+set(MIR_CAFFE2_IMPORTER_SOURCES
+ caffe2_importer.cpp
+ caffe2_op_creator.cpp
+ caffe2_op_creator.h
+ caffe2_op_types.h
+ caffe2_proto_helper.cpp
+ caffe2_proto_helper.h)
+
+add_library(mir_caffe2_importer STATIC ${MIR_CAFFE2_IMPORTER_SOURCES})
+set_target_properties(mir_caffe2_importer PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(mir_caffe2_importer PUBLIC ../../include/mir_caffe2_importer)
+target_link_libraries(mir_caffe2_importer PUBLIC mir PRIVATE caffe2proto nncc_common)
diff --git a/compiler/mir/src/mir_caffe2_importer/caffe2_importer.cpp b/compiler/mir/src/mir_caffe2_importer/caffe2_importer.cpp
new file mode 100644
index 000000000..812fcc5cc
--- /dev/null
+++ b/compiler/mir/src/mir_caffe2_importer/caffe2_importer.cpp
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "caffe2_importer.h"
+#include "caffe2/proto/caffe2.pb.h"
+#include "caffe2_op_types.h"
+#include "caffe2_op_creator.h"
+#include "caffe2_proto_helper.h"
+
+#include "mir/ops/InputOp.h"
+#include "mir/ops/OutputOp.h"
+
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/io/coded_stream.h>
+
+#include <fcntl.h>
+
+#include <cassert>
+#include <cerrno>
+#include <cstring>
+#include <memory>
+#include <stdexcept>
+#include <utility>
+#include <set>
+
+namespace
+{
+
+using namespace mir_caffe2;
+
+class Caffe2Importer
+{
+public:
+ explicit Caffe2Importer(std::string predict_net, std::string init_net,
+ const std::vector<std::vector<int>> &input_shapes);
+
+ /// @brief Load the model and convert it into a MIR Graph.
+ std::unique_ptr<mir::Graph> importModel();
+
+ ~Caffe2Importer();
+
+private:
+ std::string _predictNet;
+ std::string _initNet;
+ std::unique_ptr<mir::Graph> _graph;
+ std::unique_ptr<caffe2::NetDef> _predict_net;
+ std::unique_ptr<caffe2::NetDef> _init_net;
+ std::unique_ptr<Caffe2OpCreator> _opCreator;
+ std::vector<mir::Shape> _inputShapes;
+
+ static const std::map<std::string, SupportedCaffe2OpType> _operatorTypes;
+
+ // Maps Caffe2 operator input names to corresponding MIR operation outputs.
+ std::unordered_map<std::string, mir::Operation::Output *> _blobNameToOutput;
+
+ void import();
+ std::unique_ptr<mir::Graph> createIR();
+
+ /**
+ * @brief Pass through caffe2 graph and collect ops unsupported by NNC
+ * @throw PassException with message, containing detected problems
+ */
+ void collectUnsupportedOps();
+
+ /**
+ * @brief Creating MIR node from single caffe2 operator
+ */
+ void createMIRNodesFromOp(const ::caffe2::OperatorDef &op);
+
+ /**
+ * @brief Returns MIR operation outputs corresponding to the inputs of the given operator.
+ */
+ std::vector<mir::Operation::Output *> getInputMIROps(const ::caffe2::OperatorDef &op);
+
+ void setOutputForTensor(const std::string &tensor_name, Operation::Output *output);
+ mir::Operation::Output *getOutputForTensor(const std::string &name) const;
+
+ /**
+ * @brief Mark output MIR nodes
+ */
+ void setGraphOutputs();
+};
+
+using namespace ::caffe2;
+using mir::Shape;
+
+Caffe2Importer::Caffe2Importer(std::string predict_net, std::string init_net,
+ const std::vector<std::vector<int>> &input_shapes)
+ : _predictNet(std::move(predict_net)), _initNet(std::move(init_net))
+{
+ for (auto &shape : input_shapes)
+ _inputShapes.emplace_back(shape);
+
+ _graph = std::make_unique<mir::Graph>();
+ _opCreator = std::make_unique<Caffe2OpCreator>(_graph.get());
+}
+
+Caffe2Importer::~Caffe2Importer() = default;
+
+static void loadModelFile(const std::string &filename, caffe2::NetDef *net)
+{
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+ int file_handle = open(filename.c_str(), O_RDONLY);
+
+ if (file_handle == -1)
+ throw std::runtime_error("Couldn't open file \"" + filename + "\": " + std::strerror(errno) +
+ ".");
+
+ google::protobuf::io::FileInputStream file_stream(file_handle);
+ file_stream.SetCloseOnDelete(true);
+
+ google::protobuf::io::CodedInputStream coded_stream(&file_stream);
+ coded_stream.SetTotalBytesLimit(INT_MAX, INT_MAX);
+
+ if (!net->ParseFromCodedStream(&coded_stream))
+ throw std::runtime_error("Couldn't parse file \"" + filename + "\".");
+
+ // If the file has not been consumed entirely, assume that the file is in the wrong format.
+ if (!coded_stream.ConsumedEntireMessage())
+ throw std::runtime_error("File \"" + filename + "\" has not been consumed entirely.");
+}
+
+void Caffe2Importer::import()
+{
+ _predict_net = std::make_unique<NetDef>();
+ loadModelFile(_predictNet, _predict_net.get());
+
+ _init_net = std::make_unique<NetDef>();
+ loadModelFile(_initNet, _init_net.get());
+
+ collectUnsupportedOps();
+}
+
+std::unique_ptr<mir::Graph> Caffe2Importer::createIR()
+{
+ // Load initializers.
+ for (const auto &op : _init_net->op())
+ createMIRNodesFromOp(op);
+
+ // Create inputs. This has to be done after processing initializers, because they may contain
+ // fake inputs.
+ // TODO Caffe2 does not provide a way to detect model inputs and outputs. For now assume that:
+ // - there is exactly one input;
+ // - the input is for the first layer;
+ // - the input has 'float' element type.
+ const auto &input_name = _predict_net->op(0).input(0);
+ mir::TensorType input_type(mir::DataType::FLOAT32, _inputShapes[0]);
+ auto input = _graph->create<mir::ops::InputOp>(input_type)->getOutput(0);
+ setOutputForTensor(input_name, input);
+
+ for (const auto &op : _predict_net->op())
+ createMIRNodesFromOp(op);
+
+ setGraphOutputs();
+
+ return std::move(_graph);
+}
+
+std::unique_ptr<mir::Graph> Caffe2Importer::importModel()
+{
+ import();
+ return createIR();
+}
+
+void Caffe2Importer::collectUnsupportedOps()
+{
+ std::set<std::string> unsupportedOps;
+ for (const auto &op : _predict_net->op())
+ {
+ if (_operatorTypes.find(op.type()) == _operatorTypes.end())
+ unsupportedOps.insert(op.type());
+ }
+
+ if (!unsupportedOps.empty())
+ {
+ std::string exceptionMsg("Can't load model, unsupported operators:");
+ for (const auto &op : unsupportedOps)
+ exceptionMsg.append("\n * " + op);
+ throw std::runtime_error(exceptionMsg);
+ }
+}
+
+void Caffe2Importer::createMIRNodesFromOp(const OperatorDef &op)
+{
+ std::vector<mir::Operation::Output *> outputs;
+
+ auto inputs = getInputMIROps(op);
+
+ SupportedCaffe2OpType opType = _operatorTypes.at(op.type());
+ switch (opType)
+ {
+ case SupportedCaffe2OpType::constantFill:
+ case SupportedCaffe2OpType::givenTensorFill:
+ case SupportedCaffe2OpType::givenTensorInt64Fill:
+ outputs = _opCreator->convertConstant(inputs, op);
+ break;
+ case SupportedCaffe2OpType::add:
+ outputs = _opCreator->convertAdd(inputs, op);
+ break;
+ case SupportedCaffe2OpType::averagePool:
+ outputs = _opCreator->convertAveragePool(inputs, op);
+ break;
+ case SupportedCaffe2OpType::conv:
+ outputs = _opCreator->convertConv(inputs, op);
+ break;
+ case SupportedCaffe2OpType::concat:
+ outputs = _opCreator->convertConcat(inputs, op);
+ break;
+ case SupportedCaffe2OpType::dropout:
+ outputs = _opCreator->convertDropout(inputs, op);
+ break;
+ case SupportedCaffe2OpType::FC:
+ outputs = _opCreator->convertFC(inputs, op);
+ break;
+ case SupportedCaffe2OpType::maxPool:
+ outputs = _opCreator->convertMaxPool(inputs, op);
+ break;
+ case SupportedCaffe2OpType::mul:
+ outputs = _opCreator->convertMul(inputs, op);
+ break;
+ case SupportedCaffe2OpType::relu:
+ outputs = _opCreator->convertRelu(inputs);
+ break;
+ case SupportedCaffe2OpType::resizeNearest:
+ outputs = _opCreator->convertResizeNearest(inputs, op);
+ break;
+ case SupportedCaffe2OpType::sigmoid:
+ outputs = _opCreator->convertSigmoid(inputs);
+ break;
+ case SupportedCaffe2OpType::softmax:
+ outputs = _opCreator->convertSoftmax(inputs, op);
+ break;
+ case SupportedCaffe2OpType::spatialBN:
+ outputs = _opCreator->convertSpatialBN(inputs, op);
+ break;
+ case SupportedCaffe2OpType::sum:
+ outputs = _opCreator->convertSum(inputs);
+ break;
+ case SupportedCaffe2OpType::clip:
+ outputs = _opCreator->convertClip(inputs, op);
+ break;
+ case SupportedCaffe2OpType::reshape:
+ outputs = _opCreator->convertReshape(inputs, op);
+ break;
+ default:
+ assert(false && "All unsupported types should have been found before this pass.");
+ }
+
+ for (size_t i = 0; i < outputs.size(); ++i)
+ {
+ setOutputForTensor(op.output(i), outputs[i]);
+ }
+}
+
+std::vector<mir::Operation::Output *> Caffe2Importer::getInputMIROps(const OperatorDef &op)
+{
+ std::vector<mir::Operation::Output *> inputs;
+
+ for (const auto &input_name : op.input())
+ {
+ inputs.push_back(getOutputForTensor(input_name));
+ }
+
+ return inputs;
+}
+
+void Caffe2Importer::setOutputForTensor(const std::string &tensor_name, Operation::Output *output)
+{
+ auto it = _blobNameToOutput.find(tensor_name);
+ if (it != _blobNameToOutput.cend())
+ {
+ // caffe2 input blob name could be same as output blob name, and next line will overwrite
+ // '_blobNameToOpOutput' element, but in all networks that I saw it was not a problem
+ it->second->setName("");
+ }
+ output->setName(tensor_name);
+ _blobNameToOutput[tensor_name] = output;
+}
+
+mir::Operation::Output *Caffe2Importer::getOutputForTensor(const std::string &name) const
+{
+ return _blobNameToOutput.at(name);
+}
+
+void Caffe2Importer::setGraphOutputs()
+{
+ // Create outputs.
+ // TODO Caffe2 does not provide a way to detect model inputs and outputs. For now assume that:
+ // - there is exactly one output;
+ // - the output is from the last layer.
+ const auto &output_name = _predict_net->op().rbegin()->output(0);
+ auto output = getOutputForTensor(output_name);
+ _graph->create<mir::ops::OutputOp>(output);
+}
+
+const std::map<std::string, SupportedCaffe2OpType> Caffe2Importer::_operatorTypes = {
+ {"Add", SupportedCaffe2OpType::add},
+ {"AveragePool", SupportedCaffe2OpType::averagePool},
+ {"Conv", SupportedCaffe2OpType::conv},
+ {"Concat", SupportedCaffe2OpType::concat},
+ {"ConstantFill", SupportedCaffe2OpType::constantFill},
+ {"Dropout", SupportedCaffe2OpType::dropout},
+ {"FC", SupportedCaffe2OpType::FC},
+ {"GivenTensorFill", SupportedCaffe2OpType::givenTensorFill},
+ {"MaxPool", SupportedCaffe2OpType::maxPool},
+ {"Mul", SupportedCaffe2OpType::mul},
+ {"Relu", SupportedCaffe2OpType::relu},
+ {"ResizeNearest", SupportedCaffe2OpType::resizeNearest},
+ {"Sigmoid", SupportedCaffe2OpType::sigmoid},
+ {"Softmax", SupportedCaffe2OpType::softmax},
+ {"SpatialBN", SupportedCaffe2OpType::spatialBN},
+ {"Sum", SupportedCaffe2OpType::sum},
+ {"Clip", SupportedCaffe2OpType::clip},
+ {"Reshape", SupportedCaffe2OpType::reshape},
+ {"GivenTensorInt64Fill", SupportedCaffe2OpType::givenTensorInt64Fill},
+};
+}
+
+namespace mir_caffe2
+{
+
+std::unique_ptr<mir::Graph> loadModel(std::string predict_net, std::string init_net,
+ const std::vector<std::vector<int>> &input_shapes)
+{
+ Caffe2Importer importer(std::move(predict_net), std::move(init_net), input_shapes);
+ return importer.importModel();
+}
+
+} // namespace mir_caffe2
diff --git a/compiler/mir/src/mir_caffe2_importer/caffe2_op_creator.cpp b/compiler/mir/src/mir_caffe2_importer/caffe2_op_creator.cpp
new file mode 100644
index 000000000..3390f4482
--- /dev/null
+++ b/compiler/mir/src/mir_caffe2_importer/caffe2_op_creator.cpp
@@ -0,0 +1,551 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "caffe2_op_creator.h"
+#include "caffe2_proto_helper.h"
+
+#include "mir/ops/AddOp.h"
+#include "mir/ops/AvgPool2DOp.h"
+#include "mir/ops/CappedReluOp.h"
+#include "mir/ops/ConcatOp.h"
+#include "mir/ops/ConstantOp.h"
+#include "mir/ops/Conv2DOp.h"
+#include "mir/ops/FullyConnectedOp.h"
+#include "mir/ops/MaxPool2DOp.h"
+#include "mir/ops/MulOp.h"
+#include "mir/ops/ReluOp.h"
+#include "mir/ops/ReshapeOp.h"
+#include "mir/ops/ResizeOp.h"
+#include "mir/ops/SigmoidOp.h"
+#include "mir/ops/SoftmaxOp.h"
+#include "mir/ops/TransposeOp.h"
+
+#include "mir/Index.h"
+#include "mir/Shape.h"
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+#include "mir/TensorUtil.h"
+
+#include <cmath>
+#include <stdexcept>
+#include <vector>
+
+namespace mir_caffe2
+{
+
+using namespace ::caffe2;
+using namespace mir;
+
+//
+// Helper functions
+//
+
+static std::pair<std::vector<int32_t>, std::vector<int32_t>>
+getPadding(const ::caffe2::OperatorDef &op)
+{
+
+ if (hasArgument(op.arg(), "pads"))
+ {
+ // pads order: t l b r
+ auto pads_arg = findArgumentByName(op.arg(), "pads");
+
+ std::vector<int32_t> paddings;
+ for (const auto &pad : pads_arg.ints())
+ paddings.push_back(static_cast<int32_t>(pad));
+
+ assert(paddings.size() == 4);
+
+ int32_t pad_t = paddings[0];
+ int32_t pad_l = paddings[1];
+ int32_t pad_b = paddings[2];
+ int32_t pad_r = paddings[3];
+
+ std::vector<int32_t> padding_before{pad_t, pad_l};
+ std::vector<int32_t> padding_after{pad_b, pad_r};
+ return {padding_before, padding_after};
+ }
+
+ bool has_custom_pad = hasArgument(op.arg(), "pad_l") || hasArgument(op.arg(), "pad_r") ||
+ hasArgument(op.arg(), "pad_t") || hasArgument(op.arg(), "pad_b");
+
+ if (has_custom_pad)
+ {
+ int32_t pad_l = getSingleArgument(op, "pad_l", 0);
+ int32_t pad_t = getSingleArgument(op, "pad_t", 0);
+ int32_t pad_r = getSingleArgument(op, "pad_r", 0);
+ int32_t pad_b = getSingleArgument(op, "pad_b", 0);
+
+ std::vector<int32_t> padding_before{pad_t, pad_l};
+ std::vector<int32_t> padding_after{pad_b, pad_r};
+ return {padding_before, padding_after};
+ }
+
+ int32_t pad = getSingleArgument(op, "pad", 0);
+ return {{pad, pad}, {pad, pad}};
+}
+
+static std::vector<std::int32_t> getStrides(const ::caffe2::OperatorDef &op)
+{
+ std::vector<std::int32_t> strides;
+
+ if (hasArgument(op.arg(), "stride"))
+ {
+ std::int32_t stride = getSingleArgument(op, "stride", 1);
+ strides = {stride, stride};
+ }
+
+ if (hasArgument(op.arg(), "strides"))
+ {
+ // strides order: h w
+ auto strides_arg = findArgumentByName(op.arg(), "strides");
+ for (const auto &s : strides_arg.ints())
+ strides.push_back(s);
+ }
+
+ assert(!strides.empty() && "Strides not found");
+
+ return strides;
+}
+
+static std::vector<std::int32_t> getWindowSize(const ::caffe2::OperatorDef &op,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ int is_global_pooling = getSingleArgument(op, "global_pooling", 0);
+ bool has_custom_kernel_size =
+ hasArgument(op.arg(), "kernel_h") || hasArgument(op.arg(), "kernel_w");
+ bool has_custom_kernels_size = hasArgument(op.arg(), "kernels");
+
+ int kernel_h(0), kernel_w(0);
+ if (is_global_pooling)
+ {
+ const auto &input_shape = inputs[0]->getShape();
+ assert(input_shape.rank() == 4 && "getWindowSize() inputs must be of rank 4");
+ kernel_h = input_shape.dim(2);
+ kernel_w = input_shape.dim(3);
+ }
+ else
+ {
+ if (has_custom_kernel_size)
+ {
+ kernel_h = getSingleArgument(op, "kernel_h", 0);
+ kernel_w = getSingleArgument(op, "kernel_w", 0);
+ }
+ else
+ {
+ if (has_custom_kernels_size)
+ {
+ // kernels order: h w
+ std::vector<int32_t> kernels;
+ auto kernels_arg = findArgumentByName(op.arg(), "kernels");
+ for (const auto &ker : kernels_arg.ints())
+ kernels.push_back(static_cast<int32_t>(ker));
+ assert(kernels.size() == 2);
+ kernel_h = kernels[0];
+ kernel_w = kernels[1];
+ }
+ else
+ {
+ kernel_h = kernel_w = getSingleArgument(op, "kernel", 0);
+ }
+ }
+ }
+ return {kernel_h, kernel_w};
+}
+
+//
+// Check functions
+//
+
+static void checkLayout(const OperatorDef &op)
+{
+ if (getSingleArgument(op, "order", "NCHW") != "NCHW")
+ throw std::runtime_error(op.type() + ": only 'NCHW' axis order is supported");
+}
+
+static void checkConvLikeOp(const ::caffe2::OperatorDef &op)
+{
+ checkLayout(op);
+
+ // Padding
+ bool has_custom_pad = hasArgument(op.arg(), "pad_l") || hasArgument(op.arg(), "pad_r") ||
+ hasArgument(op.arg(), "pad_t") || hasArgument(op.arg(), "pad_b");
+
+ if (has_custom_pad && hasArgument(op.arg(), "pad"))
+ throw std::runtime_error("Custom pad can't be combined with overall pad");
+
+ if (has_custom_pad &&
+ !(hasArgument(op.arg(), "pad_l") && hasArgument(op.arg(), "pad_r") &&
+ hasArgument(op.arg(), "pad_t") && hasArgument(op.arg(), "pad_b")))
+ throw std::runtime_error("If one custom pad specified - all custom pads must be specified");
+
+ // Kernel size
+ bool has_custom_kernel_size =
+ hasArgument(op.arg(), "kernel_h") || hasArgument(op.arg(), "kernel_w");
+
+ if (has_custom_kernel_size && hasArgument(op.arg(), "kernel"))
+ throw std::runtime_error("Custom kernel size can't be combined with overall kernel size");
+
+ if (has_custom_kernel_size &&
+ !(hasArgument(op.arg(), "kernel_h") && hasArgument(op.arg(), "kernel_w")))
+ throw std::runtime_error(
+ "If one custom kernel size specified - all custom kernel sizes must be specified");
+}
+
+static mir::TensorVariant createTensor(const OperatorDef &op)
+{
+ assert(hasArgument(op.arg(), "shape") && hasArgument(op.arg(), "values"));
+
+ const auto &shape = findArgumentByName(op.arg(), "shape");
+ const auto &values = findArgumentByName(op.arg(), "values");
+
+ mir::DataType element_type;
+ const void *src_data;
+ // if values on floats
+ if (!values.floats().empty())
+ {
+ element_type = mir::DataType::FLOAT32;
+ src_data = values.floats().data();
+ }
+ else
+ {
+ assert(!values.ints().empty());
+ if (op.type() == "GivenTensorInt64Fill")
+ {
+ element_type = mir::DataType::INT64;
+ }
+ else
+ {
+ element_type = mir::DataType::INT32;
+ }
+ src_data = values.ints().data();
+ }
+
+ mir::Shape tensor_shape(shape.ints_size());
+
+ for (int i = 0; i < shape.ints_size(); ++i)
+ {
+ tensor_shape.dim(i) = shape.ints(i);
+ }
+
+ return mir::TensorVariant({element_type, tensor_shape}, src_data);
+}
+
+//
+// Convert functions
+//
+
+std::vector<mir::Operation::Output *>
+Caffe2OpCreator::convertConstant(const std::vector<mir::Operation::Output *> &,
+ const ::caffe2::OperatorDef &op)
+{
+ // Constant may not contain any data if it is a fake input.
+ if (!hasArgument(op.arg(), "values"))
+ return {};
+
+ return {createOp<ops::ConstantOp>(createTensor(op))->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+Caffe2OpCreator::convertAdd(const std::vector<mir::Operation::Output *> &inputs,
+ const ::caffe2::OperatorDef &op)
+{
+ assert(inputs.size() == 2);
+ auto lhs = inputs[0];
+ auto rhs = inputs[1];
+
+ if (getSingleArgument(op, "broadcast", 0) != 0)
+ {
+ // FIXME This only works when 'axis' == 1 and the second input is 1-D.
+ rhs = createOp<ops::ReshapeOp>(rhs, Shape{1, rhs->getShape().dim(0), 1, 1})->getOutput(0);
+ auto result = createOp<ops::AddOp>(lhs, rhs)->getOutput(0);
+ return {result};
+ }
+
+ auto result = createOp<ops::AddOp>(lhs, rhs)->getOutput(0);
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+Caffe2OpCreator::convertAveragePool(const std::vector<mir::Operation::Output *> &inputs,
+ const OperatorDef &op)
+{
+ checkConvLikeOp(op);
+
+ assert(inputs.size() == 1);
+ auto input = inputs[0];
+
+ AvgPool2DOpAttributes attributes;
+ std::tie(attributes.padding_before, attributes.padding_after) = getPadding(op);
+ attributes.window = getWindowSize(op, inputs);
+ attributes.strides = getStrides(op);
+ attributes.include_pad = false;
+ attributes.data_format = DataFormat::NCHW;
+ auto result = createOp<ops::AvgPool2DOp>(input, attributes)->getOutput(0);
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+Caffe2OpCreator::convertConv(const std::vector<mir::Operation::Output *> &inputs,
+ const ::caffe2::OperatorDef &op)
+{
+ // dilation order: h w (not used)
+ mir::Conv2DOpAttributes attributes;
+ attributes.strides = getStrides(op);
+ std::tie(attributes.padding_before, attributes.padding_after) = getPadding(op);
+ attributes.num_groups = getSingleArgument(op, "group", 1);
+ attributes.data_format = DataFormat::NCHW;
+
+ std::vector<std::size_t> perm{0, 2, 3, 1}; // OIHW -> OHWI
+ auto kernel = createOp<ops::TransposeOp>(inputs[1], perm)->getOutput(0);
+ auto result = createOp<ops::Conv2DOp>(inputs[0], kernel, attributes)->getOutput(0);
+
+ if (op.input_size() > 2)
+ {
+ auto bias = inputs[2];
+ bias = createOp<ops::ReshapeOp>(bias, Shape{1, bias->getShape().dim(0), 1, 1})->getOutput(0);
+ result = createOp<ops::AddOp>(result, bias)->getOutput(0);
+ }
+
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+Caffe2OpCreator::convertConcat(const std::vector<mir::Operation::Output *> &inputs,
+ const ::caffe2::OperatorDef &op)
+{
+ checkLayout(op);
+
+ // `1` corresponds to the default (channels) axis.
+ int axis = getSingleArgument(op, "axis", 1);
+ auto result = createOp<ops::ConcatOp>(inputs, axis);
+ return {result->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+Caffe2OpCreator::convertDropout(const std::vector<mir::Operation::Output *> &inputs,
+ const ::caffe2::OperatorDef &)
+{
+ // This is a no-op in inference mode.
+ return {inputs[0]};
+}
+
+std::vector<mir::Operation::Output *>
+Caffe2OpCreator::convertFC(const std::vector<mir::Operation::Output *> &inputs,
+ const ::caffe2::OperatorDef &op)
+{
+ for (auto &s : {"axis", "axis_w", "float16_compute"})
+ if (hasArgument(op.arg(), s))
+ throw std::runtime_error(std::string("FC: only default '") + s + "' value is supported");
+
+ const auto &input_shape = inputs[0]->getShape();
+ // Transform input into 2-D tensor by flattening axes
+ Shape shape{input_shape.dim(0), input_shape.numElements() / input_shape.dim(0)};
+
+ auto reshape = createOp<ops::ReshapeOp>(inputs[0], shape)->getOutput(0);
+ auto weights =
+ createOp<ops::TransposeOp>(inputs[1], std::vector<std::size_t>{1, 0})->getOutput(0);
+ auto result = createOp<ops::FullyConnectedOp>(reshape, weights)->getOutput(0);
+ result = createOp<ops::AddOp>(result, inputs[2])->getOutput(0);
+
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+Caffe2OpCreator::convertMaxPool(const std::vector<mir::Operation::Output *> &inputs,
+ const OperatorDef &op)
+{
+ checkConvLikeOp(op);
+
+ assert(inputs.size() == 1);
+ auto input = inputs[0];
+
+ MaxPool2DOpAttributes attributes;
+ std::tie(attributes.padding_before, attributes.padding_after) = getPadding(op);
+ attributes.window = getWindowSize(op, inputs);
+ attributes.strides = getStrides(op);
+ attributes.data_format = DataFormat::NCHW;
+ auto result = createOp<ops::MaxPool2DOp>(input, attributes)->getOutput(0);
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+Caffe2OpCreator::convertMul(const std::vector<mir::Operation::Output *> &inputs,
+ const ::caffe2::OperatorDef &op)
+{
+ assert(inputs.size() == 2);
+ auto lhs = inputs[0];
+ auto rhs = inputs[1];
+
+ if (getSingleArgument(op, "broadcast", 0) != 0)
+ {
+ // FIXME This only works when `axis` == 1 and the second input is 1-D.
+ rhs = createOp<ops::ReshapeOp>(rhs, Shape{1, rhs->getShape().dim(0), 1, 1})->getOutput(0);
+ auto result = createOp<ops::MulOp>(lhs, rhs)->getOutput(0);
+ return {result};
+ }
+
+ auto result = createOp<ops::MulOp>(lhs, rhs)->getOutput(0);
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+Caffe2OpCreator::convertRelu(const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto relu = createOp<ops::ReluOp>(inputs[0]);
+ return {relu->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+Caffe2OpCreator::convertResizeNearest(const std::vector<mir::Operation::Output *> &inputs,
+ const ::caffe2::OperatorDef &op)
+{
+ std::vector<float> scales(4);
+ assert(inputs[0]->getShape().rank() == 4 && "only 4d tensors is supported");
+ // Assuming NCHW format.
+ scales[0] = 1.0f;
+ scales[1] = 1.0f;
+ scales[2] = getSingleArgument(op, "height_scale", 1.0f);
+ scales[3] = getSingleArgument(op, "width_scale", 1.0f);
+ auto result =
+ createOp<ops::ResizeOp>(inputs[0], ops::ResizeOp::ResizeMethod::nearestNeighbor, scales)
+ ->getOutput(0);
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+Caffe2OpCreator::convertSigmoid(const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto result = createOp<ops::SigmoidOp>(inputs[0]);
+ return {result->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+Caffe2OpCreator::convertSoftmax(const std::vector<mir::Operation::Output *> &inputs,
+ const ::caffe2::OperatorDef &op)
+{
+ int axis = getSingleArgument(op, "axis", 1);
+ auto softmax = createOp<ops::SoftmaxOp>(inputs[0], axis);
+ return {softmax->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+Caffe2OpCreator::convertSpatialBN(const std::vector<mir::Operation::Output *> &inputs,
+ const ::caffe2::OperatorDef &op)
+{
+ checkLayout(op);
+
+ // Sanity checks
+ if (op.input_size() != 5)
+ throw std::runtime_error(
+ "SpatialBN must have exactly 5 inputs ('sums' and 'sumsq' are not supported yet)");
+ if (getSingleArgument(op, "is_test", 1) != 1)
+ throw std::runtime_error("SpatialBN: only test mode supported");
+
+ // overall_res = (X - mean) / sqrt(var + epsilon) * scale + bias
+
+ auto scale_op = dynamic_cast<mir::ops::ConstantOp *>(inputs[1]->getNode());
+ auto bias_op = dynamic_cast<mir::ops::ConstantOp *>(inputs[2]->getNode());
+ auto mean_op = dynamic_cast<mir::ops::ConstantOp *>(inputs[3]->getNode());
+ auto var_op = dynamic_cast<mir::ops::ConstantOp *>(inputs[4]->getNode());
+ if (scale_op == nullptr || bias_op == nullptr || mean_op == nullptr || var_op == nullptr)
+ throw std::runtime_error(
+ "SpatialBN: non-constant 'scale', 'bias', 'mean' and 'var' inputs are not supported yet.");
+
+ const auto &scale_tensor = scale_op->getValue();
+ const auto &bias_tensor = bias_op->getValue();
+ const auto &mean_tensor = mean_op->getValue();
+ const auto &var_tensor = var_op->getValue();
+ float eps = getSingleArgument(op, "epsilon", 1e-5f);
+
+ // res1 = X - mean
+ Tensor<float> bias_data(mean_tensor);
+ for (auto &idx : ShapeRange(bias_data.getShape()))
+ bias_data.at(idx) *= -1;
+
+ auto mean = createOp<ops::ConstantOp>(mean_tensor)->getOutput(0);
+ mean = createOp<ops::ReshapeOp>(mean, Shape{1, mean->getShape().dim(0), 1, 1})->getOutput(0);
+ auto result = createOp<ops::AddOp>(inputs[0], mean)->getOutput(0);
+
+ // res2 = res1 * scale / (var + epsilon)
+ Tensor<float> multiplier(scale_tensor);
+ for (auto &idx : ShapeRange(scale_tensor.getShape()))
+ multiplier.at(idx) /= std::sqrt(*reinterpret_cast<float *>(var_tensor.at(idx)) + eps);
+ auto scale = createOp<ops::ConstantOp>(scale_tensor)->getOutput(0);
+ scale = createOp<ops::ReshapeOp>(scale, Shape{1, scale->getShape().dim(0), 1, 1})->getOutput(0);
+ result = createOp<ops::MulOp>(result, scale)->getOutput(0);
+
+ // overall_res = res2 + bias
+ auto bias = createOp<ops::ConstantOp>(bias_tensor)->getOutput(0);
+ bias = createOp<ops::ReshapeOp>(bias, Shape{1, bias->getShape().dim(0), 1, 1})->getOutput(0);
+ result = createOp<ops::AddOp>(result, bias)->getOutput(0);
+
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+Caffe2OpCreator::convertSum(const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto result = createOp<ops::AddOp>(inputs[0], inputs[1])->getOutput(0);
+ for (int i = 2; i < static_cast<int>(inputs.size()); ++i)
+ {
+ result = createOp<ops::AddOp>(result, inputs[i])->getOutput(0);
+ }
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+Caffe2OpCreator::convertClip(const std::vector<mir::Operation::Output *> &inputs,
+ const ::caffe2::OperatorDef &op)
+{
+
+ float max = getSingleArgument(op, "max", float(0));
+ float min = getSingleArgument(op, "min", float(0));
+
+ if (min != 0.0f)
+ throw std::runtime_error("Clip: min != 0 is not supported.");
+ if (max <= min)
+ throw std::runtime_error("Clip: max <= min is not supported.");
+ auto cap_relu = createOp<ops::CappedReluOp>(inputs[0], max);
+
+ return {cap_relu->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+Caffe2OpCreator::convertReshape(const std::vector<mir::Operation::Output *> &inputs,
+ const ::caffe2::OperatorDef &)
+{
+ auto shape_op = dynamic_cast<mir::ops::ConstantOp *>(inputs[1]->getNode());
+ if (shape_op == nullptr)
+ throw std::runtime_error("Reshape: non-constant shape is not supported yet.");
+
+ const auto &shape_tensor = shape_op->getValue();
+
+ Tensor<int64_t> out_shape_tensor(shape_tensor);
+
+ ShapeRange range(out_shape_tensor.getShape());
+ std::vector<int32_t> shape_vec;
+ for (const auto &index : range)
+ {
+ shape_vec.push_back(static_cast<int32_t>(out_shape_tensor.at(index)));
+ }
+ Shape out_shape(shape_vec);
+
+ auto reshape = createOp<ops::ReshapeOp>(inputs[0], out_shape);
+
+ return {reshape->getOutput(0)};
+}
+
+} // namespace mir_caffe2
diff --git a/compiler/mir-caffe2-importer/caffe2_op_creator.h b/compiler/mir/src/mir_caffe2_importer/caffe2_op_creator.h
index 2b29378e9..2b29378e9 100644
--- a/compiler/mir-caffe2-importer/caffe2_op_creator.h
+++ b/compiler/mir/src/mir_caffe2_importer/caffe2_op_creator.h
diff --git a/compiler/mir-caffe2-importer/caffe2_op_types.h b/compiler/mir/src/mir_caffe2_importer/caffe2_op_types.h
index b5e7e7631..b5e7e7631 100644
--- a/compiler/mir-caffe2-importer/caffe2_op_types.h
+++ b/compiler/mir/src/mir_caffe2_importer/caffe2_op_types.h
diff --git a/compiler/mir/src/mir_caffe2_importer/caffe2_proto_helper.cpp b/compiler/mir/src/mir_caffe2_importer/caffe2_proto_helper.cpp
new file mode 100644
index 000000000..7bb9cf06d
--- /dev/null
+++ b/compiler/mir/src/mir_caffe2_importer/caffe2_proto_helper.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "caffe2_proto_helper.h"
+
+namespace mir_caffe2
+{
+
+const ::caffe2::Argument &findArgumentByName(RepArgument args, const std::string &name)
+{
+ for (auto &arg : args)
+ if (arg.name() == name)
+ return arg;
+ throw std::runtime_error("Can't find argument with name: " + name);
+}
+
+bool hasArgument(RepArgument args, const std::string &name)
+{
+ for (auto &arg : args)
+ if (arg.name() == name)
+ return true;
+ return false;
+}
+
+int getSingleArgument(const ::caffe2::OperatorDef &op, const std::string &argument_name,
+ const int default_value)
+{
+ if (hasArgument(op.arg(), argument_name))
+ return static_cast<int>(findArgumentByName(op.arg(), argument_name).i());
+ return default_value;
+}
+
+float getSingleArgument(const ::caffe2::OperatorDef &op, const std::string &argument_name,
+ const float default_value)
+{
+ if (hasArgument(op.arg(), argument_name))
+ return findArgumentByName(op.arg(), argument_name).f();
+ return default_value;
+}
+
+std::string getSingleArgument(const ::caffe2::OperatorDef &op, const std::string &argument_name,
+ const std::string &default_value)
+{
+ if (hasArgument(op.arg(), argument_name))
+ return findArgumentByName(op.arg(), argument_name).s();
+ return default_value;
+}
+
+} // namespace mir_caffe2
diff --git a/compiler/mir/src/mir_caffe2_importer/caffe2_proto_helper.h b/compiler/mir/src/mir_caffe2_importer/caffe2_proto_helper.h
new file mode 100644
index 000000000..4f5c4ef39
--- /dev/null
+++ b/compiler/mir/src/mir_caffe2_importer/caffe2_proto_helper.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_CAFFE2_PROTO_HELPER_H
+#define MIR_CAFFE2_PROTO_HELPER_H
+
+#include "caffe2/proto/caffe2.pb.h"
+
+namespace mir_caffe2
+{
+
+using RepArgument = const ::google::protobuf::RepeatedPtrField<::caffe2::Argument> &;
+
+const ::caffe2::Argument &findArgumentByName(RepArgument args, const std::string &name);
+
+bool hasArgument(RepArgument args, const std::string &name);
+
+int getSingleArgument(const ::caffe2::OperatorDef &op, const std::string &argument_name,
+ int default_value);
+float getSingleArgument(const ::caffe2::OperatorDef &op, const std::string &argument_name,
+ float default_value);
+std::string getSingleArgument(const ::caffe2::OperatorDef &op, const std::string &argument_name,
+ const std::string &default_value);
+
+} // namespace mir_caffe2
+
+#endif // MIR_CAFFE2_PROTO_HELPER_H
diff --git a/compiler/mir/src/mir_caffe_importer/CMakeLists.txt b/compiler/mir/src/mir_caffe_importer/CMakeLists.txt
new file mode 100644
index 000000000..34be520fc
--- /dev/null
+++ b/compiler/mir/src/mir_caffe_importer/CMakeLists.txt
@@ -0,0 +1,16 @@
+nnas_find_package(CaffeProto QUIET)
+
+if (NOT CaffeProto_FOUND)
+ return()
+endif ()
+
+set(MIR_CAFFE_IMPORTER_SOURCES
+ caffe_importer.cpp
+ caffe_op_creator.cpp
+ caffe_op_creator.h
+ caffe_op_types.h)
+
+add_library(mir_caffe_importer STATIC ${MIR_CAFFE_IMPORTER_SOURCES})
+set_target_properties(mir_caffe_importer PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(mir_caffe_importer PUBLIC ../../include/mir_caffe_importer)
+target_link_libraries(mir_caffe_importer PUBLIC mir PRIVATE caffeproto nncc_common)
diff --git a/compiler/mir/src/mir_caffe_importer/caffe_importer.cpp b/compiler/mir/src/mir_caffe_importer/caffe_importer.cpp
new file mode 100644
index 000000000..49f13fbd8
--- /dev/null
+++ b/compiler/mir/src/mir_caffe_importer/caffe_importer.cpp
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "caffe_importer.h"
+#include "caffe/proto/caffe.pb.h"
+#include "caffe_op_creator.h"
+#include "caffe_op_types.h"
+
+#include "mir/ops/OutputOp.h"
+
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/text_format.h>
+
+#include <fcntl.h>
+
+#include <cassert>
+#include <cerrno>
+#include <cstring>
+#include <memory>
+#include <stdexcept>
+#include <utility>
+#include <vector>
+#include <set>
+
+namespace mir_caffe
+{
+
+namespace
+{
+
+class CaffeImporter
+{
+public:
+ /// @brief Load the model and convert it into a MIR Graph.
+ std::unique_ptr<mir::Graph> importModelFromBinaryFile(const std::string &filename);
+ std::unique_ptr<mir::Graph> importModelFromTextFile(const std::string &filename);
+
+private:
+ std::unique_ptr<mir::Graph> importModel();
+
+ std::unique_ptr<caffe::NetParameter> _net;
+ std::unique_ptr<CaffeOpCreator> _opCreator;
+
+ // Maps Caffe blob names to corresponding MIR operation outputs.
+ std::map<std::string, mir::Operation::Output *> _blobNameToOpOutput;
+
+ static const std::map<std::string, CaffeOpType> _operatorTypes;
+
+ /**
+ * @brief Mark output MIR nodes
+ */
+ void setGraphOutputs(mir::Graph *graph);
+
+ /**
+ * @brief Pass through caffe graph and collect unsupported by NNC layers
+ * @throw PassException with message, containing detected problems
+ */
+ void collectUnsupportedLayers();
+
+ /**
+ * @brief Create MIR node from single caffe layer
+ */
+ void createMIRNodesFromLayer(const caffe::LayerParameter &layer);
+
+ mir::Operation::Output *getOutputForBlob(const std::string &blob_name) const;
+ void setOutputForBlob(const std::string &blob_name, mir::Operation::Output *output);
+
+ /**
+ * @brief Collect unsupported parts of caffe layer
+ */
+ void collectUnsupportedOp(const caffe::LayerParameter &layer, std::set<std::string> &problems);
+
+ /**
+ * @brief Returns MIR operation outputs corresponding to the inputs of the given layer.
+ */
+ std::vector<mir::Operation::Output *> getMIRInputsForLayer(const caffe::LayerParameter &layer);
+
+ void processDeprecatedInput();
+};
+
+void loadModelFromBinaryFile(const std::string &filename, caffe::NetParameter *net)
+{
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+ int file_handle = open(filename.c_str(), O_RDONLY);
+
+ if (file_handle == -1)
+ throw std::runtime_error("Couldn't open file \"" + filename + "\": " + std::strerror(errno) +
+ ".");
+
+ google::protobuf::io::FileInputStream file_stream(file_handle);
+ file_stream.SetCloseOnDelete(true);
+
+ google::protobuf::io::CodedInputStream coded_stream(&file_stream);
+ coded_stream.SetTotalBytesLimit(INT_MAX, INT_MAX);
+
+ if (!net->ParseFromCodedStream(&coded_stream))
+ throw std::runtime_error("Couldn't parse file \"" + filename + "\".");
+
+ // If the file has not been consumed entirely, assume that the file is in the wrong format.
+ if (!coded_stream.ConsumedEntireMessage())
+ throw std::runtime_error("File \"" + filename + "\" has not been consumed entirely.");
+}
+
+void loadModelFromTextFile(const std::string &filename, caffe::NetParameter *net)
+{
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+ int file_handle = open(filename.c_str(), O_RDONLY);
+
+ if (file_handle == -1)
+ throw std::runtime_error("Couldn't open file \"" + filename + "\": " + std::strerror(errno) +
+ ".");
+
+ google::protobuf::io::FileInputStream file_stream(file_handle);
+ file_stream.SetCloseOnDelete(true);
+
+ if (!google::protobuf::TextFormat::Parse(&file_stream, net))
+ throw std::runtime_error("Couldn't parse file \"" + filename + "\".");
+}
+
+std::unique_ptr<mir::Graph> CaffeImporter::importModel()
+{
+ auto graph = std::make_unique<mir::Graph>();
+ _opCreator = std::make_unique<CaffeOpCreator>(graph.get());
+
+ collectUnsupportedLayers();
+
+ for (int i = 0; i < _net->layer_size(); ++i)
+ createMIRNodesFromLayer(_net->layer(i));
+
+ setGraphOutputs(graph.get());
+
+ return graph;
+}
+
+std::unique_ptr<mir::Graph> CaffeImporter::importModelFromBinaryFile(const std::string &filename)
+{
+ _net = std::make_unique<caffe::NetParameter>();
+ loadModelFromBinaryFile(filename, _net.get());
+
+ return importModel();
+}
+
+std::unique_ptr<mir::Graph> CaffeImporter::importModelFromTextFile(const std::string &filename)
+{
+ _net = std::make_unique<caffe::NetParameter>();
+ loadModelFromTextFile(filename, _net.get());
+
+ return importModel();
+}
+
+void CaffeImporter::collectUnsupportedLayers()
+{
+ processDeprecatedInput();
+
+ std::set<std::string> problems;
+
+ for (const caffe::LayerParameter &layer : _net->layer())
+ collectUnsupportedOp(layer, problems);
+
+ if (!problems.empty())
+ {
+ std::string msg("NNC can't load model. Detected problems:");
+ for (const auto &problemStr : problems)
+ msg.append("\n * " + problemStr);
+ throw std::runtime_error(msg);
+ }
+}
+
+void CaffeImporter::createMIRNodesFromLayer(const caffe::LayerParameter &layer)
+{
+ std::vector<mir::Operation::Output *> inputs = getMIRInputsForLayer(layer);
+ std::vector<mir::Operation::Output *> outputs;
+
+ switch (_operatorTypes.at(layer.type()))
+ {
+ case CaffeOpType::input:
+ outputs = _opCreator->convertInput(layer);
+ break;
+ case CaffeOpType::convolution:
+ outputs = _opCreator->convertConvolution(layer, inputs);
+ break;
+ case CaffeOpType::innerProduct:
+ outputs = _opCreator->convertInnerProduct(layer, inputs);
+ break;
+ case CaffeOpType::pooling:
+ outputs = _opCreator->convertPooling(layer, inputs);
+ break;
+ case CaffeOpType::concat:
+ outputs = _opCreator->convertConcat(layer, inputs);
+ break;
+ case CaffeOpType::reshape:
+ outputs = _opCreator->convertReshape(layer, inputs);
+ break;
+ case CaffeOpType::ReLU:
+ outputs = _opCreator->convertReLU(layer, inputs);
+ break;
+ case CaffeOpType::softmax:
+ outputs = _opCreator->convertSoftmax(layer, inputs);
+ break;
+ case CaffeOpType::scale:
+ outputs = _opCreator->convertScale(layer, inputs);
+ break;
+ case CaffeOpType::batchNorm:
+ outputs = _opCreator->convertBatchNorm(layer, inputs);
+ break;
+ case CaffeOpType::dropout:
+ outputs = _opCreator->convertDropout(layer, inputs);
+ break;
+ case CaffeOpType::tanh:
+ outputs = _opCreator->convertTanH(layer, inputs);
+ break;
+ case CaffeOpType::ELU:
+ outputs = _opCreator->convertELU(layer, inputs);
+ break;
+ case CaffeOpType::eltwise:
+ outputs = _opCreator->convertEltwise(layer, inputs);
+ break;
+ case CaffeOpType::embed:
+ outputs = _opCreator->convertEmbed(layer, inputs);
+ break;
+ case CaffeOpType::deconvolution:
+ outputs = _opCreator->convertDeconvolution(layer, inputs);
+ break;
+ case CaffeOpType::split:
+ outputs = _opCreator->convertSplit(layer, inputs);
+ break;
+ case CaffeOpType::sigmoid:
+ outputs = _opCreator->convertSigmoid(layer, inputs);
+ break;
+ case CaffeOpType::LSTM:
+ outputs = _opCreator->convertLSTM(layer, inputs);
+ break;
+ default:
+ assert(false && "All unsupported types should have been found before this pass.");
+ }
+
+ assert(static_cast<int>(outputs.size()) == layer.top_size() && "Number of outputs differs.");
+ for (int i = 0; i < layer.top_size(); ++i)
+ setOutputForBlob(layer.top(i), outputs[i]);
+}
+
+void CaffeImporter::collectUnsupportedOp(const caffe::LayerParameter &layer,
+ std::set<std::string> &problems)
+{
+ auto it = _operatorTypes.find(layer.type());
+ if (it == _operatorTypes.end())
+ {
+ problems.insert(layer.type() + ": unknown layer");
+ return;
+ }
+
+ CaffeOpType op_type = it->second;
+
+ switch (op_type)
+ {
+ case CaffeOpType::concat:
+ case CaffeOpType::input:
+ case CaffeOpType::softmax:
+ case CaffeOpType::scale:
+ case CaffeOpType::dropout:
+ case CaffeOpType::split:
+ case CaffeOpType::eltwise:
+ case CaffeOpType::ELU:
+ case CaffeOpType::ReLU:
+ case CaffeOpType::embed:
+ case CaffeOpType::sigmoid:
+ case CaffeOpType::tanh:
+ case CaffeOpType::innerProduct:
+ // No checks
+ break;
+ case CaffeOpType::deconvolution:
+ case CaffeOpType::convolution:
+ _opCreator->checkConvolution(layer, problems);
+ break;
+ case CaffeOpType::pooling:
+ _opCreator->checkPooling(layer, problems);
+ break;
+ case CaffeOpType::reshape:
+ _opCreator->checkReshape(layer, problems);
+ break;
+ case CaffeOpType::batchNorm:
+ _opCreator->checkBatchNorm(layer, problems);
+ break;
+ case CaffeOpType::LSTM:
+ _opCreator->checkLSTM(layer, problems);
+ break;
+ default:
+ problems.insert(layer.type() + ": unsupported layer");
+ break;
+ }
+}
+
+void CaffeImporter::processDeprecatedInput()
+{
+ if (_net->input_dim_size() != 0 || _net->input_shape_size() != 0)
+ throw std::runtime_error("Deprecated Caffe input types are not supported");
+}
+
+std::vector<mir::Operation::Output *>
+CaffeImporter::getMIRInputsForLayer(const caffe::LayerParameter &layer)
+{
+ std::vector<mir::Operation::Output *> inputs;
+
+ for (const auto &input_name : layer.bottom())
+ inputs.push_back(getOutputForBlob(input_name));
+
+ return inputs;
+}
+
+mir::Operation::Output *CaffeImporter::getOutputForBlob(const std::string &blob_name) const
+{
+ return _blobNameToOpOutput.at(blob_name);
+}
+
+void CaffeImporter::setOutputForBlob(const std::string &blob_name, mir::Operation::Output *output)
+{
+ const auto it = _blobNameToOpOutput.find(blob_name);
+ if (it != _blobNameToOpOutput.cend())
+ {
+ // caffe input blob name could be same as output blob name, and next line will overwrite
+ // '_blobNameToOpOutput' element, but in all networks that I saw it was not a problem
+ it->second->setName("");
+ }
+
+ // Do not overwrite the name in case of fall-through layers (ex. Dropout, Split).
+ // TODO Find a way to handle it properly.
+ if (output->getName().empty())
+ output->setName(blob_name);
+
+ _blobNameToOpOutput[blob_name] = output;
+}
+
+void CaffeImporter::setGraphOutputs(mir::Graph *graph)
+{
+ // TODO For now, we assume that:
+ // - there is exactly one output;
+ // - the output is from the last layer.
+ const auto &last_layer = *_net->layer().rbegin();
+ auto output = getOutputForBlob(last_layer.top(0));
+ graph->create<mir::ops::OutputOp>(output);
+}
+
+const std::map<std::string, CaffeOpType> CaffeImporter::_operatorTypes = {
+ {"AbsVal", CaffeOpType::absVal},
+ {"Accuracy", CaffeOpType::accuracy},
+ {"ArgMax", CaffeOpType::argMax},
+ {"BatchNorm", CaffeOpType::batchNorm},
+ {"BatchReindex", CaffeOpType::batchReindex},
+ {"Bias", CaffeOpType::bias},
+ {"BNLL", CaffeOpType::BNLL},
+ {"Clip", CaffeOpType::clip},
+ {"Concat", CaffeOpType::concat},
+ {"ContrastiveLoss", CaffeOpType::contrastiveLoss},
+ {"Convolution", CaffeOpType::convolution},
+ {"Crop", CaffeOpType::crop},
+ {"Data", CaffeOpType::data},
+ {"Deconvolution", CaffeOpType::deconvolution},
+ {"Dropout", CaffeOpType::dropout},
+ {"DummyData", CaffeOpType::dummyData},
+ {"Eltwise", CaffeOpType::eltwise},
+ {"ELU", CaffeOpType::ELU},
+ {"Embed", CaffeOpType::embed},
+ {"EuclidianLoss", CaffeOpType::euclidianLoss},
+ {"Exp", CaffeOpType::exp},
+ {"Filter", CaffeOpType::filter},
+ {"Flatten", CaffeOpType::flatten},
+ {"HDF5Data", CaffeOpType::HDF5Data},
+ {"HDF5Output", CaffeOpType::HDF5Output},
+ {"HingeLoss", CaffeOpType::hingeLoss},
+ {"Im2Col", CaffeOpType::im2Col},
+ {"ImageData", CaffeOpType::imageData},
+ {"InfogainLoss", CaffeOpType::infogainLoss},
+ {"InnerProduct", CaffeOpType::innerProduct},
+ {"Input", CaffeOpType::input},
+ {"Log", CaffeOpType::log},
+ {"LRN", CaffeOpType::LRN},
+ {"LSTM", CaffeOpType::LSTM},
+ {"MemoryData", CaffeOpType::memoryData},
+ {"MultinomialLogisticLoss", CaffeOpType::multinomialLogisticLoss},
+ {"MVN", CaffeOpType::MVN},
+ {"Parameter", CaffeOpType::parameter},
+ {"Pooling", CaffeOpType::pooling},
+ {"Power", CaffeOpType::power},
+ {"PReLU", CaffeOpType::PReLU},
+ {"Python", CaffeOpType::python},
+ {"Recurrent", CaffeOpType::recurrent},
+ {"Reduction", CaffeOpType::reduction},
+ {"ReLU", CaffeOpType::ReLU},
+ {"Reshape", CaffeOpType::reshape},
+ {"RNN", CaffeOpType::RNN},
+ {"Scale", CaffeOpType::scale},
+ {"SigmoidCrossEntropyLoss", CaffeOpType::sigmoidCrossEntropyLoss},
+ {"Sigmoid", CaffeOpType::sigmoid},
+ {"Silence", CaffeOpType::silence},
+ {"Softmax", CaffeOpType::softmax},
+ {"SoftmaxWithLoss", CaffeOpType::softmaxWithLoss},
+ {"SPP", CaffeOpType::SPP},
+ {"Split", CaffeOpType::split},
+ {"Slice", CaffeOpType::slice},
+ {"TanH", CaffeOpType::tanh},
+ {"Threshold", CaffeOpType::threshold},
+ {"Tile", CaffeOpType::tile},
+ {"WindowData", CaffeOpType::windowData}};
+} // namespace
+
+std::unique_ptr<mir::Graph> importModelFromBinaryFile(const std::string &filename)
+{
+ CaffeImporter importer;
+ return importer.importModelFromBinaryFile(filename);
+}
+
+std::unique_ptr<mir::Graph> importModelFromTextFile(const std::string &filename)
+{
+ CaffeImporter importer;
+ return importer.importModelFromTextFile(filename);
+}
+
+std::unique_ptr<mir::Graph> loadModel(const std::string &filename)
+{
+ return importModelFromBinaryFile(filename);
+}
+
+} // namespace mir_caffe
diff --git a/compiler/mir/src/mir_caffe_importer/caffe_op_creator.cpp b/compiler/mir/src/mir_caffe_importer/caffe_op_creator.cpp
new file mode 100644
index 000000000..37edc69c4
--- /dev/null
+++ b/compiler/mir/src/mir_caffe_importer/caffe_op_creator.cpp
@@ -0,0 +1,835 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "caffe_op_creator.h"
+
+#include "mir/ops/AddOp.h"
+#include "mir/ops/AvgPool2DOp.h"
+#include "mir/ops/ConcatOp.h"
+#include "mir/ops/ConstantOp.h"
+#include "mir/ops/Conv2DOp.h"
+#include "mir/ops/Deconv2DOp.h"
+#include "mir/ops/EluOp.h"
+#include "mir/ops/FullyConnectedOp.h"
+#include "mir/ops/GatherOp.h"
+#include "mir/ops/LeakyReluOp.h"
+#include "mir/ops/MaxOp.h"
+#include "mir/ops/MaxPool2DOp.h"
+#include "mir/ops/MulOp.h"
+#include "mir/ops/ReluOp.h"
+#include "mir/ops/ReshapeOp.h"
+#include "mir/ops/SigmoidOp.h"
+#include "mir/ops/SliceOp.h"
+#include "mir/ops/SoftmaxOp.h"
+#include "mir/ops/TanhOp.h"
+#include "mir/ops/TransposeOp.h"
+#include "mir/Index.h"
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+#include <cmath>
+#include <iostream>
+#include <set>
+#include <stdexcept>
+
+namespace mir_caffe
+{
+
+static mir::Shape convertBlobShape(const caffe::BlobShape &shape)
+{
+ mir::Shape mir_shape(shape.dim_size());
+
+ for (int i = 0; i < shape.dim_size(); ++i)
+ {
+ mir_shape.dim(i) = shape.dim(i);
+ }
+
+ return mir_shape;
+}
+
+using namespace mir;
+
+/// @brief Split arg into @p num_parts equal parts along @p axis axis.
+std::vector<mir::Operation::Output *> CaffeOpCreator::createSplit(mir::Operation::Output *arg,
+ int32_t num_parts, int32_t axis)
+{
+ const auto &arg_shape = arg->getShape();
+
+ assert(axis >= 0 && axis < arg_shape.rank());
+ int32_t part_size = arg_shape.dim(axis) / num_parts;
+ assert(part_size * num_parts == arg_shape.dim(axis));
+
+ Shape starts(arg_shape.rank());
+ Shape sizes(arg_shape);
+ sizes.dim(axis) = part_size;
+
+ std::vector<mir::Operation::Output *> outputs(num_parts);
+ for (int32_t i = 0; i < num_parts; ++i)
+ {
+ outputs[i] = createOp<ops::SliceOp>(arg, starts, sizes)->getOutput(0);
+ starts.dim(axis) += part_size;
+ }
+
+ return outputs;
+}
+
+/// @brief Helper function for creating FullyConnected operation with non-square input.
+mir::Operation::Output *CaffeOpCreator::createFullyConnected(mir::Operation::Output *input,
+ mir::Operation::Output *weights,
+ int32_t axis)
+{
+ const auto &input_shape = input->getShape();
+ const auto &weights_shape = weights->getShape();
+
+ assert(axis >= 0 && axis < input_shape.rank());
+ assert(weights_shape.rank() == 2);
+
+ // Result shape is: input.shape[0:axis] + weights.shape[1].
+ Shape result_shape = input_shape;
+ result_shape.resize(axis + 1);
+ result_shape.dim(axis) = weights_shape.dim(1);
+
+ // Flatten input to 2-D shape.
+ int32_t outer_size = 1;
+ for (int32_t i = 0; i < axis; ++i)
+ outer_size *= input_shape.dim(i);
+ int32_t inner_size = 1;
+ for (int32_t i = axis; i < input_shape.rank(); ++i)
+ inner_size *= input_shape.dim(i);
+
+ auto flatten = createOp<ops::ReshapeOp>(input, Shape{outer_size, inner_size})->getOutput(0);
+ auto fc = createOp<ops::FullyConnectedOp>(flatten, weights)->getOutput(0);
+ return createOp<ops::ReshapeOp>(fc, result_shape)->getOutput(0);
+}
+
+TensorVariant CaffeOpCreator::convertBlob(const caffe::BlobProto &blob)
+{
+ const void *src_data;
+
+ mir::DataType dtype;
+ if (blob.data_size() != 0)
+ {
+ assert(blob.double_data_size() == 0);
+ dtype = mir::DataType::FLOAT32;
+ src_data = blob.data().data();
+ }
+ else if (blob.double_data_size() != 0)
+ {
+ dtype = mir::DataType::FLOAT64;
+ src_data = blob.double_data().data();
+ }
+ else
+ {
+ throw std::runtime_error("No data in Caffe BlobProto, investigate");
+ }
+
+ const mir::Shape shape = convertBlobShape(blob.shape());
+ return TensorVariant({dtype, shape}, src_data);
+}
+
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertInput(const caffe::LayerParameter &layer)
+{
+ const auto &params = layer.input_param();
+ const auto num_inputs = layer.top_size();
+ const auto num_shapes = params.shape_size();
+ std::vector<mir::Operation::Output *> outputs;
+
+ assert((num_shapes == 1 || num_shapes == num_inputs) && "Unsupported number of shapes.");
+
+ for (int i = 0; i < num_inputs; ++i)
+ {
+ const auto &blob_shape = params.shape(num_shapes == 1 ? 0 : i);
+ mir::TensorType input_type(DataType::FLOAT32, convertBlobShape(blob_shape));
+ auto input = createOp<ops::InputOp>(input_type)->getOutput(0);
+ outputs.push_back(input);
+ }
+
+ return outputs;
+}
+
+template <class OperationAttributes>
+static void convertConvolutionParam(const caffe::ConvolutionParameter &conv_param,
+ OperationAttributes &attributes)
+{
+ std::int32_t stride_h, stride_w;
+ if (conv_param.has_stride_h() || conv_param.has_stride_w())
+ {
+ // If stride_h or stride_w are set, they take precedence.
+ stride_h = conv_param.stride_h();
+ stride_w = conv_param.stride_w();
+ }
+ else if (conv_param.stride_size() == 0)
+ {
+ // If no strides specified, they defaults to 1.
+ stride_h = stride_w = 1;
+ }
+ else if (conv_param.stride_size() == 1)
+ {
+ // If only one stride specified, all strides take the same value.
+ stride_h = stride_w = conv_param.stride(0);
+ }
+ else
+ {
+ // Otherwise, there must be a stride for each dimension.
+ assert(conv_param.stride_size() == 2);
+ stride_h = conv_param.stride(0);
+ stride_w = conv_param.stride(1);
+ }
+ attributes.strides = {stride_h, stride_w};
+
+ std::int32_t pad_h, pad_w;
+ if (conv_param.has_pad_h() || conv_param.has_pad_w())
+ {
+ // If pad_h or pad_w are set, they take precedence.
+ pad_h = conv_param.pad_h();
+ pad_w = conv_param.pad_w();
+ }
+ else if (conv_param.pad_size() == 0)
+ {
+ // If no pads specified, they defaults to 0.
+ pad_h = pad_w = 0;
+ }
+ else if (conv_param.pad_size() == 1)
+ {
+ // If only one pad specified, all pads take the same value.
+ pad_h = pad_w = conv_param.pad(0);
+ }
+ else
+ {
+ // Otherwise, there must be a pad for each dimension.
+ assert(conv_param.pad_size() == 2);
+ pad_h = conv_param.pad(0);
+ pad_w = conv_param.pad(1);
+ }
+ attributes.padding_after = attributes.padding_before = {pad_h, pad_w};
+}
+
+void CaffeOpCreator::checkConvolution(const caffe::LayerParameter &layer,
+ std::set<std::string> &problems_ops_set)
+{
+ const caffe::ConvolutionParameter &params = layer.convolution_param();
+
+ assert(params.stride_size() <= 2);
+
+ if (params.axis() != 1)
+ problems_ops_set.insert("Conv2D: Unsupported axis");
+
+ if (params.pad_size() != 0 && (params.has_pad_h() || params.has_pad_w()))
+ problems_ops_set.insert("Conv2D: Conflicting padding properties");
+
+ if (params.pad_size() > 2)
+ problems_ops_set.insert("Conv2D: Unsupported number of pads");
+}
+
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertConvolution(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ const auto &params = layer.convolution_param();
+ Conv2DOpAttributes attributes;
+
+ convertConvolutionParam(params, attributes);
+ attributes.num_groups = params.group();
+ attributes.data_format = DataFormat::NCHW;
+
+ assert(layer.blobs(0).shape().dim_size() == 4);
+ auto kernel = createOp<ops::ConstantOp>(convertBlob(layer.blobs(0)))->getOutput(0);
+ std::vector<std::size_t> perm{0, 2, 3, 1}; // OIHW -> OHWI
+ kernel = createOp<ops::TransposeOp>(kernel, perm)->getOutput(0);
+ auto result = createOp<ops::Conv2DOp>(inputs[0], kernel, attributes)->getOutput(0);
+
+ // Add the bias, if any.
+ if (params.bias_term())
+ {
+ auto bias = createOp<ops::ConstantOp>(convertBlob(layer.blobs(1)))->getOutput(0);
+ bias = createOp<ops::ReshapeOp>(bias, Shape{1, bias->getShape().dim(0), 1, 1})->getOutput(0);
+ result = createOp<ops::AddOp>(result, bias)->getOutput(0);
+ }
+
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertDeconvolution(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ const caffe::ConvolutionParameter &params = layer.convolution_param();
+ Deconv2DOpAttributes attributes;
+
+ convertConvolutionParam(params, attributes);
+ attributes.data_format = DataFormat::NCHW;
+
+ if (params.group() != 1)
+ {
+ throw std::runtime_error("Deconvolution: 'group' != 1 is not supported.");
+ }
+
+ auto kernel = createOp<ops::ConstantOp>(convertBlob(layer.blobs(0)))->getOutput(0);
+ std::vector<std::size_t> perm{2, 3, 1, 0}; // IOHW -> HWOI
+ kernel = createOp<ops::TransposeOp>(kernel, perm)->getOutput(0);
+ auto result = createOp<ops::DeConv2DOp>(inputs[0], kernel, attributes)->getOutput(0);
+
+ // bias_term is optional (so might not be present) and defaults to true
+ if (params.bias_term())
+ {
+ auto bias = createOp<ops::ConstantOp>(convertBlob(layer.blobs(1)))->getOutput(0);
+ bias = createOp<ops::ReshapeOp>(bias, Shape{1, bias->getShape().dim(0), 1, 1})->getOutput(0);
+ result = createOp<ops::AddOp>(result, bias)->getOutput(0);
+ }
+
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertInnerProduct(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ const auto &params = layer.inner_product_param();
+ auto weights = createOp<ops::ConstantOp>(convertBlob(layer.blobs(0)))->getOutput(0);
+
+ if (!params.transpose())
+ weights = createOp<ops::TransposeOp>(weights, std::vector<std::size_t>{1, 0})->getOutput(0);
+
+ auto result = createFullyConnected(inputs[0], weights, params.axis());
+
+ // Add the bias, if any.
+ if (params.bias_term())
+ {
+ auto bias = createOp<ops::ConstantOp>(convertBlob(layer.blobs(1)))->getOutput(0);
+ result = createOp<ops::AddOp>(result, bias)->getOutput(0);
+ }
+
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertConcat(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ const auto &params = layer.concat_param();
+ auto concat = createOp<ops::ConcatOp>(inputs, params.axis());
+ return {concat->getOutput(0)};
+}
+
+template <class PoolingAttributes>
+static void convertPoolingParam(const caffe::PoolingParameter &params,
+ const mir::Shape &input_shape, PoolingAttributes &attributes)
+{
+ std::int32_t kernel_h, kernel_w;
+ assert(!params.global_pooling());
+ if (params.has_kernel_size())
+ {
+ kernel_h = kernel_w = params.kernel_size();
+ }
+ else
+ {
+ kernel_h = params.kernel_h();
+ kernel_w = params.kernel_w();
+ }
+ attributes.window = {kernel_h, kernel_w};
+
+ std::int32_t stride_h, stride_w;
+ if (params.has_stride_h() || params.has_stride_w())
+ {
+ stride_h = params.stride_h();
+ stride_w = params.stride_w();
+ }
+ else
+ {
+ stride_h = stride_w = params.stride();
+ }
+ attributes.strides = {stride_h, stride_w};
+
+ std::int32_t pad_h, pad_w;
+ if (params.has_pad_h() || params.has_pad_w())
+ {
+ pad_h = params.pad_h();
+ pad_w = params.pad_w();
+ }
+ else
+ {
+ pad_h = pad_w = params.pad();
+ }
+
+ attributes.padding_before = attributes.padding_after = {pad_h, pad_w};
+
+ // Caffe uses different formula for computing output shape than MIR. Adjust padding so that
+ // the output shape stays the same.
+ constexpr int num_spatial_dims = 2;
+ for (int i = 0; i < num_spatial_dims; ++i)
+ {
+ // Assuming NCHW format.
+ const std::int32_t padded_input =
+ input_shape.dim(2 + i) + attributes.padding_before[i] + attributes.padding_after[i];
+ if ((padded_input - attributes.window[i]) % attributes.strides[i] != 0)
+ ++attributes.padding_after[i];
+ }
+}
+
+void CaffeOpCreator::checkPooling(const caffe::LayerParameter &layer,
+ std::set<std::string> &problems_ops_set)
+{
+ const caffe::PoolingParameter &params = layer.pooling_param();
+
+ if (params.has_global_pooling() && params.global_pooling())
+ problems_ops_set.insert("Pooling: pooling layer global_pooling param is not supported yet");
+
+ if (params.pool() != caffe::PoolingParameter::AVE &&
+ params.pool() != caffe::PoolingParameter::MAX)
+ problems_ops_set.insert("Pooling: unsupported pooling type");
+
+ if (params.has_pad() && (params.has_pad_h() || params.has_pad_w()))
+ problems_ops_set.insert("Pooling: conflicting padding properties in pooling");
+}
+
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertPooling(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ const auto &params = layer.pooling_param();
+
+ assert(inputs.size() == 1);
+ auto input = inputs[0];
+
+ mir::Operation::Output *result;
+
+ switch (params.pool())
+ {
+ case caffe::PoolingParameter::AVE:
+ {
+ AvgPool2DOpAttributes attributes_avg;
+ attributes_avg.data_format = DataFormat::NCHW;
+ convertPoolingParam(params, input->getShape(), attributes_avg);
+ result = createOp<ops::AvgPool2DOp>(input, attributes_avg)->getOutput(0);
+ break;
+ }
+ case caffe::PoolingParameter::MAX:
+ {
+ MaxPool2DOpAttributes attributes_max;
+ attributes_max.data_format = DataFormat::NCHW;
+ convertPoolingParam(params, input->getShape(), attributes_max);
+ result = createOp<ops::MaxPool2DOp>(input, attributes_max)->getOutput(0);
+ break;
+ }
+ default:
+ throw std::runtime_error("Unsupported PoolMethod: " + std::to_string(params.pool()));
+ }
+
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertSoftmax(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ const auto &params = layer.softmax_param();
+
+ // CPP and ACL backends are able to perform Softmax only along the last axis.
+ // FIXME Do it in backends.
+ if (inputs[0]->getShape().rank() == 4)
+ {
+ // For now, we only account for the most common case.
+ if (params.axis() != 1)
+ throw std::runtime_error("Softmax: unsupported axis");
+ int32_t axis = 3;
+ auto input = createOp<ops::TransposeOp>(inputs[0], std::vector<std::size_t>{0, 2, 3, 1});
+ auto softmax = createOp<ops::SoftmaxOp>(input->getOutput(0), axis);
+ auto result =
+ createOp<ops::TransposeOp>(softmax->getOutput(0), std::vector<std::size_t>{0, 3, 1, 2});
+ return {result->getOutput(0)};
+ }
+
+ auto softmax = createOp<ops::SoftmaxOp>(inputs[0], params.axis());
+ return {softmax->getOutput(0)};
+}
+
+void CaffeOpCreator::checkReshape(const caffe::LayerParameter &layer,
+ std::set<std::string> &problems_ops_set)
+{
+ const caffe::ReshapeParameter &params = layer.reshape_param();
+
+ if (params.has_axis() || params.has_num_axes())
+ problems_ops_set.insert("Reshape layer axis and num_axes params are not supported yet");
+
+ if (!params.has_shape())
+ problems_ops_set.insert("Reshape layer doesn't have shape parameter");
+
+ const mir::Shape newShape = convertBlobShape(params.shape());
+
+ for (int32_t i = 0; i < newShape.rank(); ++i)
+ if (newShape.dim(i) == 0)
+ problems_ops_set.insert("Reshape layer zero shape values are not supported yet");
+}
+
+/**
+ * @brief Converts Caffe Reshape layer to Model IR Reshape operation.
+ * @todo Support "axis" and "num_axes" parameters as needed.
+ * @todo Decide how to react to the absence of "shape" parameter.
+ * @todo Support zero values in "shape" parameter.
+ */
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertReshape(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ const caffe::ReshapeParameter &params = layer.reshape_param();
+
+ const mir::Shape new_shape = convertBlobShape(params.shape());
+ auto reshape = createOp<ops::ReshapeOp>(inputs[0], new_shape);
+ return {reshape->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertReLU(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ mir::Operation *relu;
+ if (layer.relu_param().has_negative_slope())
+ {
+ float alpha = layer.relu_param().negative_slope();
+ relu = createOp<ops::LeakyReluOp>(inputs[0], alpha);
+ }
+ else
+ {
+ relu = createOp<ops::ReluOp>(inputs[0]);
+ }
+
+ return {relu->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertScale(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ const auto &params = layer.scale_param();
+ auto scale = createOp<ops::ConstantOp>(convertBlob(layer.blobs(0)))->getOutput(0);
+ scale = createOp<ops::ReshapeOp>(scale, Shape{1, scale->getShape().dim(0), 1, 1})->getOutput(0);
+ auto result = createOp<ops::MulOp>(inputs[0], scale)->getOutput(0);
+
+ // Add the bias, if any.
+ if (params.bias_term())
+ {
+ auto bias = createOp<ops::ConstantOp>(convertBlob(layer.blobs(1)))->getOutput(0);
+ bias = createOp<ops::ReshapeOp>(bias, Shape{1, bias->getShape().dim(0), 1, 1})->getOutput(0);
+ result = createOp<ops::AddOp>(result, bias)->getOutput(0);
+ }
+
+ return {result};
+}
+
+void CaffeOpCreator::checkBatchNorm(const caffe::LayerParameter &layer,
+ std::set<std::string> &problems_ops_set)
+{
+ const auto &scale_shape = layer.blobs(2).shape();
+
+ // Check that last blob(with scaleFactor) containing only one number
+ if (scale_shape.dim_size() != 1 || scale_shape.dim(0) != 1)
+ problems_ops_set.insert("Unexpected shape of scale parameter in batch norm");
+}
+
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertBatchNorm(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ const caffe::BatchNormParameter &params = layer.batch_norm_param();
+
+ auto input = inputs[0];
+ auto mean_tensor = convertBlob(layer.blobs(0));
+ auto var_tensor = convertBlob(layer.blobs(1));
+ auto scale_tensor = convertBlob(layer.blobs(2));
+ const float eps = params.eps();
+
+ float scale_factor = *reinterpret_cast<float *>(scale_tensor.at(mir::Index{0}));
+
+ // See https://github.com/BVLC/caffe/blob/master/src/caffe/layers/batch_norm_layer.cpp#L100
+ // Y = (X - mean / scale_factor) / sqrt(var / scale_factor + epsilon) =
+ // = (X + C1) * C2
+ if (scale_factor != 0.0f)
+ scale_factor = 1.0f / scale_factor;
+
+ // C1 = -mean / scale_factor
+ Tensor<float> mean_accessor(mean_tensor);
+ for (const auto &idx : ShapeRange(mean_accessor.getShape()))
+ mean_accessor.at(idx) *= -scale_factor;
+ auto c1 = createOp<ops::ConstantOp>(mean_tensor)->getOutput(0);
+
+ // C2 = 1 / sqrt(var / scale_factor + epsilon)
+ Tensor<float> var_accessor(var_tensor);
+ for (const auto &idx : ShapeRange(var_accessor.getShape()))
+ var_accessor.at(idx) = 1.0f / std::sqrt(var_accessor.at(idx) * scale_factor + eps);
+ auto c2 = createOp<ops::ConstantOp>(var_tensor)->getOutput(0);
+
+ c1 = createOp<ops::ReshapeOp>(c1, Shape{1, c1->getShape().dim(0), 1, 1})->getOutput(0);
+ c2 = createOp<ops::ReshapeOp>(c2, Shape{1, c2->getShape().dim(0), 1, 1})->getOutput(0);
+
+ // Y = (X + C1) * C2
+ auto result = createOp<ops::AddOp>(input, c1)->getOutput(0);
+ result = createOp<ops::MulOp>(result, c2)->getOutput(0);
+
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertDropout(const caffe::LayerParameter &,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ // This is a no-op in inference mode.
+ return {inputs[0]};
+}
+
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertELU(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ const caffe::ELUParameter &params = layer.elu_param();
+
+ auto elu = createOp<ops::EluOp>(inputs[0], params.alpha());
+ return {elu->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertEmbed(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ const auto &params = layer.embed_param();
+ auto data = createOp<ops::ConstantOp>(convertBlob(layer.blobs(0)));
+ auto result = createOp<ops::GatherOp>(data->getOutput(0), inputs[0], 0)->getOutput(0);
+
+ // Add the bias, if any.
+ if (params.bias_term())
+ {
+ auto bias = createOp<ops::ConstantOp>(convertBlob(layer.blobs(1)))->getOutput(0);
+ result = createOp<ops::AddOp>(result, bias)->getOutput(0);
+ }
+
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertSigmoid(const caffe::LayerParameter &,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto result = createOp<ops::SigmoidOp>(inputs[0]);
+ return {result->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertTanH(const caffe::LayerParameter &,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto tanh = createOp<ops::TanhOp>(inputs[0]);
+ return {tanh->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertEltwise(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto &params = layer.eltwise_param();
+
+ mir::Operation::Output *result;
+ switch (params.operation())
+ {
+ case caffe::EltwiseParameter::PROD:
+ {
+ result = createOp<ops::MulOp>(inputs[0], inputs[1])->getOutput(0);
+ for (int i = 2; i < layer.bottom_size(); ++i)
+ {
+ result = createOp<ops::MulOp>(result, inputs[i])->getOutput(0);
+ }
+ break;
+ }
+ case caffe::EltwiseParameter::SUM:
+ {
+ std::vector<mir::Operation::Output *> scaled_inputs = inputs;
+ if (params.coeff_size() > 0)
+ {
+ assert(params.coeff_size() == layer.bottom_size());
+ for (int i = 0; i < layer.bottom_size(); i++)
+ {
+ if (params.coeff(i) != 1.0f)
+ {
+ const float coeff_val = params.coeff(i);
+ TensorVariant coeff_tensor({DataType::FLOAT32, {}}, &coeff_val);
+ auto coeff_const = createOp<ops::ConstantOp>(coeff_tensor)->getOutput(0);
+ scaled_inputs[i] = createOp<ops::MulOp>(coeff_const, inputs[i])->getOutput(0);
+ }
+ }
+ }
+ result = createOp<ops::AddOp>(scaled_inputs[0], scaled_inputs[1])->getOutput(0);
+ for (int i = 2; i < layer.bottom_size(); ++i)
+ {
+ result = createOp<ops::AddOp>(result, scaled_inputs[i])->getOutput(0);
+ }
+ break;
+ }
+ case caffe::EltwiseParameter::MAX:
+ {
+ result = createOp<ops::MaxOp>(inputs[0], inputs[1])->getOutput(0);
+ for (int i = 2; i < layer.bottom_size(); ++i)
+ {
+ result = createOp<ops::MaxOp>(result, inputs[i])->getOutput(0);
+ }
+ break;
+ }
+ default:
+ throw std::runtime_error("Unknown element-wise operation.");
+ }
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertSplit(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ std::vector<mir::Operation::Output *> outputs(layer.top_size(), inputs.at(0));
+ return outputs;
+}
+
+void CaffeOpCreator::checkLSTM(const caffe::LayerParameter &layer,
+ std::set<std::string> &problems_ops_set)
+{
+ const auto &params = layer.recurrent_param();
+ if (params.expose_hidden())
+ problems_ops_set.insert("LSTM: parameter 'expose_hidden' has unsupported value: " +
+ std::to_string(params.expose_hidden()));
+}
+
+static TensorVariant createZeroedTensor(const mir::Shape &shape)
+{
+ // TODO For now it is hardcoded float32.
+ auto elem_type = mir::DataType::FLOAT32;
+ std::vector<float> zeros(static_cast<std::size_t>(shape.numElements()), 0.0f);
+ return TensorVariant({elem_type, shape}, zeros.data());
+}
+
+/* See the following links for details on implementation:
+ * https://github.com/BVLC/caffe/blob/master/src/caffe/layers/recurrent_layer.cpp
+ * https://github.com/BVLC/caffe/blob/master/src/caffe/layers/lstm_layer.cpp
+ * https://github.com/BVLC/caffe/blob/master/src/caffe/layers/lstm_unit_layer.cpp
+ *
+ * Inputs:
+ * x -- The time-varying input. Shape: [T, N, d0, d1, ..., dn].
+ * cont -- The sequence continuation indicators. Shape: [T, N].
+ * x_static -- The static (non-time-varying) input. Shape: [N, ...].
+ * This parameter is optional and not currently supported.
+ *
+ * Additional inputs when parameter "expose_hidden" is true (not currently supported):
+ * h_0 -- The initial value of the hidden state. Shape: [1, N, D].
+ * c_0 -- The initial value of the cell state. Shape: [1, N, D].
+ *
+ * Learned parameters:
+ * xw -- x weights for input, output, forget and cell gates concatenated.
+ * Shape: [4 * D, d0 * d1 * ... * dn].
+ * xb -- x biases for input, output, forget and cell gates concatenated. Shape: [4 * D].
+ * hw -- h weights for input, output, forget and cell gates concatenated. Shape: [4 * D, D].
+ *
+ * Outputs:
+ * h -- The time-varying output. Shape: [T, N, D].
+ *
+ * Additional outputs when parameter "expose_hidden" is true (not currently supported):
+ * h_T -- The value of the hidden state at the last timestep. Shape: [1, N, D].
+ * c_T -- The value of the cell state at the last timestep. Shape: [1, N, D].
+ *
+ * Here:
+ * T - the number of timesteps,
+ * N - the number of independent streams.
+ * D - the number of hidden parameters.
+ *
+ * Formulas:
+ * c_cont = c[t-1] * cont[t]
+ * h_cont = h[t-1] * cont[t]
+ * i[t] = Sigmoid(x[t] . xw_i + xb_i + h_cont . hw_i)
+ * f[t] = Sigmoid(x[t] . xw_f + xb_f + h_cont . hw_f)
+ * o[t] = Sigmoid(x[t] . xw_o + xb_o + h_cont . hw_o)
+ * g[t] = Tanh(x[t] . xw_g + xb_g + h_cont . hw_g)
+ * c[t] = c_cont * f[t] + i[t] * g[t]
+ * h[t] = o[t] * Tanh(c[t])
+ *
+ * Here:
+ * t -- the timestep (ranges from 1 to T),
+ * * -- the inner product,
+ * . -- the Hadamard product (elementwise product).
+ *
+ * In this implementation the inner products for all gates are performed as single inner product for
+ * efficiency.
+ */
+std::vector<mir::Operation::Output *>
+CaffeOpCreator::convertLSTM(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ const auto &params = layer.recurrent_param();
+
+ // Inputs to the layer.
+ auto x = inputs[0];
+ auto cont = inputs[1];
+ assert(inputs.size() == 2);
+
+ const auto &x_shape = x->getShape();
+ const int32_t seq_length = x_shape.dim(0);
+ const int32_t batch_size = x_shape.dim(1);
+ const int32_t hidden_size = params.num_output();
+
+ // Learned parameters of the layer. Tensors are transposed to match the ModelIR.
+ auto xw = createOp<ops::ConstantOp>(convertBlob(layer.blobs(0)))->getOutput(0);
+ auto xb = createOp<ops::ConstantOp>(convertBlob(layer.blobs(1)))->getOutput(0);
+ auto hw = createOp<ops::ConstantOp>(convertBlob(layer.blobs(2)))->getOutput(0);
+ xw = createOp<ops::TransposeOp>(xw, std::vector<std::size_t>{1, 0})->getOutput(0);
+ hw = createOp<ops::TransposeOp>(hw, std::vector<std::size_t>{1, 0})->getOutput(0);
+
+ // Add a dummy dimension so that element-wise operations perform properly.
+ cont = createOp<ops::ReshapeOp>(cont, Shape{seq_length, batch_size, 1})->getOutput(0);
+
+ // Initialize cell and hidden states with zeros.
+ auto zero_tensor = createZeroedTensor(Shape{1, batch_size, hidden_size});
+ auto c_t = createOp<ops::ConstantOp>(zero_tensor)->getOutput(0);
+ auto h_t = createOp<ops::ConstantOp>(zero_tensor)->getOutput(0);
+
+ auto x_xw = createFullyConnected(x, xw, 2);
+ auto x_xw_b = createOp<ops::AddOp>(x_xw, xb)->getOutput(0);
+
+ // Split input and continuation tensors into seq_length slices.
+ std::vector<mir::Operation::Output *> x_xw_b_slices = createSplit(x_xw_b, seq_length, 0);
+ std::vector<mir::Operation::Output *> cont_slices = createSplit(cont, seq_length, 0);
+ std::vector<mir::Operation::Output *> h_slices(seq_length);
+
+ for (int32_t t = 0; t < seq_length; t++)
+ {
+ auto c_cont_t = createOp<ops::MulOp>(c_t, cont_slices[t])->getOutput(0);
+ auto h_cont_t = createOp<ops::MulOp>(h_t, cont_slices[t])->getOutput(0);
+
+ auto x_xw_b_t = x_xw_b_slices[t];
+ auto h_hw_t = createFullyConnected(h_cont_t, hw, 2);
+ auto activation_inputs_concat = createOp<ops::AddOp>(x_xw_b_t, h_hw_t)->getOutput(0);
+ auto activation_inputs = createSplit(activation_inputs_concat, 4, 2);
+
+ auto i_t = createOp<ops::SigmoidOp>(activation_inputs[0])->getOutput(0);
+ auto f_t = createOp<ops::SigmoidOp>(activation_inputs[1])->getOutput(0);
+ auto o_t = createOp<ops::SigmoidOp>(activation_inputs[2])->getOutput(0);
+ auto g_t = createOp<ops::TanhOp>(activation_inputs[3])->getOutput(0);
+
+ c_t = createOp<ops::AddOp>(createOp<ops::MulOp>(c_cont_t, f_t)->getOutput(0),
+ createOp<ops::MulOp>(i_t, g_t)->getOutput(0))
+ ->getOutput(0);
+ h_t = createOp<ops::MulOp>(createOp<ops::TanhOp>(c_t)->getOutput(0), o_t)->getOutput(0);
+
+ h_slices[t] = h_t;
+ }
+
+ return {createOp<ops::ConcatOp>(h_slices, 0)->getOutput(0)};
+}
+
+} // namespace mir_caffe
diff --git a/compiler/mir/src/mir_caffe_importer/caffe_op_creator.h b/compiler/mir/src/mir_caffe_importer/caffe_op_creator.h
new file mode 100644
index 000000000..721bb90b8
--- /dev/null
+++ b/compiler/mir/src/mir_caffe_importer/caffe_op_creator.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_CAFFE_OP_CREATOR_H
+#define MIR_CAFFE_OP_CREATOR_H
+
+#include <set>
+#include <map>
+#include <vector>
+#include <memory>
+
+#include "mir/Graph.h"
+#include "mir/TensorVariant.h"
+#include "mir/Shape.h"
+
+#include "caffe/proto/caffe.pb.h"
+
+namespace mir_caffe
+{
+
+class CaffeOpCreator
+{
+public:
+ explicit CaffeOpCreator(mir::Graph *g) : _graph(g){};
+
+ std::vector<mir::Operation::Output *> convertInput(const caffe::LayerParameter &layer);
+
+ std::vector<mir::Operation::Output *>
+ convertConvolution(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertInnerProduct(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertConcat(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertPooling(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertSoftmax(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertReshape(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertReLU(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertScale(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertBatchNorm(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertDropout(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertDeconvolution(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertELU(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertEmbed(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertSigmoid(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertTanH(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertEltwise(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertSplit(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertLSTM(const caffe::LayerParameter &layer,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ void checkConvolution(const caffe::LayerParameter &layer,
+ std::set<std::string> &problems_ops_set);
+
+ void checkPooling(const caffe::LayerParameter &layer, std::set<std::string> &problems_ops_set);
+
+ void checkReshape(const caffe::LayerParameter &layer, std::set<std::string> &problems_ops_set);
+
+ void checkBatchNorm(const caffe::LayerParameter &layer, std::set<std::string> &problems_ops_set);
+
+ void checkLSTM(const caffe::LayerParameter &layer, std::set<std::string> &problems_ops_set);
+
+private:
+ mir::Graph *_graph = nullptr;
+
+ std::vector<mir::Operation::Output *> createSplit(mir::Operation::Output *arg, int32_t num_parts,
+ int32_t axis);
+
+ mir::Operation::Output *createFullyConnected(mir::Operation::Output *input,
+ mir::Operation::Output *weights, int32_t axis);
+
+ mir::TensorVariant convertBlob(const caffe::BlobProto &blob);
+
+ template <typename OpType, typename... Types> mir::Operation *createOp(Types &&... args);
+};
+
+template <typename OpType, typename... Types>
+mir::Operation *CaffeOpCreator::createOp(Types &&... args)
+{
+ return _graph->create<OpType>(std::forward<Types>(args)...);
+}
+
+} // namespace mir_caffe
+
+#endif // MIR_CAFFE_OP_CREATOR_H
diff --git a/compiler/mir-caffe-importer/caffe_op_types.h b/compiler/mir/src/mir_caffe_importer/caffe_op_types.h
index 30fce7d5f..30fce7d5f 100644
--- a/compiler/mir-caffe-importer/caffe_op_types.h
+++ b/compiler/mir/src/mir_caffe_importer/caffe_op_types.h
diff --git a/compiler/mir/src/mir_onnx_importer/AttributeHelpers.h b/compiler/mir/src/mir_onnx_importer/AttributeHelpers.h
new file mode 100644
index 000000000..9a93b5b7d
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/AttributeHelpers.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_ATTRIBUTE_HELPERS_H
+#define MIR_ONNX_ATTRIBUTE_HELPERS_H
+
+#include "onnx/onnx.pb.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace mir_onnx
+{
+
+template <typename T> T getAttributeValue(const onnx::AttributeProto &attribute) = delete;
+
+template <> inline float getAttributeValue(const onnx::AttributeProto &attribute)
+{
+ assert(attribute.type() == onnx::AttributeProto::FLOAT);
+ return attribute.f();
+}
+
+template <> inline std::int64_t getAttributeValue(const onnx::AttributeProto &attribute)
+{
+ assert(attribute.type() == onnx::AttributeProto::INT);
+ return attribute.i();
+}
+
+template <> inline std::string getAttributeValue(const onnx::AttributeProto &attribute)
+{
+ assert(attribute.type() == onnx::AttributeProto::STRING);
+ return attribute.s();
+}
+
+template <> inline onnx::TensorProto getAttributeValue(const onnx::AttributeProto &attribute)
+{
+ assert(attribute.type() == onnx::AttributeProto::TENSOR);
+ return attribute.t();
+}
+
+template <>
+inline std::vector<std::int32_t> getAttributeValue(const onnx::AttributeProto &attribute)
+{
+ assert(attribute.type() == onnx::AttributeProto::INTS);
+ // TODO Check that values fit.
+ return {attribute.ints().cbegin(), attribute.ints().cend()};
+}
+
+template <>
+inline std::vector<std::int64_t> getAttributeValue(const onnx::AttributeProto &attribute)
+{
+ assert(attribute.type() == onnx::AttributeProto::INTS);
+ return {attribute.ints().cbegin(), attribute.ints().cend()};
+}
+
+inline const onnx::AttributeProto *findAttribute(const onnx::NodeProto &node,
+ const std::string &name)
+{
+ const auto &attributes = node.attribute();
+ const auto it = std::find_if(
+ attributes.cbegin(), attributes.cend(),
+ [&name](const onnx::AttributeProto &attribute) { return attribute.name() == name; });
+ if (it == attributes.cend())
+ return nullptr;
+ return &*it;
+}
+
+template <typename T> T getAttributeValue(const onnx::NodeProto &node, const std::string &name)
+{
+ const auto *attribute = findAttribute(node, name);
+ if (attribute == nullptr)
+ throw std::runtime_error("Cannot find attribute '" + name + "' in node '" + node.name() + "'.");
+ return getAttributeValue<T>(*attribute);
+}
+
+template <typename T>
+T getAttributeValue(const onnx::NodeProto &node, const std::string &name, T default_value)
+{
+ const auto *attribute = findAttribute(node, name);
+ if (attribute == nullptr)
+ return default_value;
+ return getAttributeValue<T>(*attribute);
+}
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_ATTRIBUTE_HELPERS_H
diff --git a/compiler/mir/src/mir_onnx_importer/CMakeLists.txt b/compiler/mir/src/mir_onnx_importer/CMakeLists.txt
new file mode 100644
index 000000000..e6eb13b93
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/CMakeLists.txt
@@ -0,0 +1,119 @@
+nnas_find_package(ONNXSource EXACT 1.6.0 QUIET)
+nnas_find_package(Protobuf QUIET)
+
+if (NOT ONNXSource_FOUND)
+ return()
+endif ()
+
+if (NOT Protobuf_FOUND)
+ return()
+endif ()
+
+Protobuf_Generate(MIR_ONNX_PROTO
+ ${CMAKE_CURRENT_BINARY_DIR}/generated
+ ${ONNXSource_DIR}
+ onnx/onnx.proto)
+
+add_library(mir_onnx_proto STATIC ${MIR_ONNX_PROTO_SOURCES})
+set_target_properties(mir_onnx_proto PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(mir_onnx_proto PUBLIC ${MIR_ONNX_PROTO_INCLUDE_DIRS})
+target_link_libraries(mir_onnx_proto PUBLIC libprotobuf)
+
+set(MIR_ONNX_IMPORTER_SOURCES
+ AttributeHelpers.h
+ ConvPoolHelpers.cpp
+ ConvPoolHelpers.h
+ ONNXHelpers.cpp
+ ONNXHelpers.h
+ ONNXImporterImpl.cpp
+ ONNXNodeConverterRegistry.h
+ ONNXNodeConverterRegistry.cpp
+ ONNXOpRegistration.h
+ Op/Abs.cpp
+ Op/Abs.h
+ Op/Add.cpp
+ Op/Add.h
+ Op/AveragePool.cpp
+ Op/AveragePool.h
+ Op/BatchNormalization.cpp
+ Op/BatchNormalization.h
+ Op/Concat.cpp
+ Op/Concat.h
+ Op/Constant.cpp
+ Op/Constant.h
+ Op/Conv.cpp
+ Op/Conv.h
+ Op/ConvTranspose.cpp
+ Op/ConvTranspose.h
+ Op/Div.cpp
+ Op/Div.h
+ Op/Dropout.cpp
+ Op/Dropout.h
+ Op/Equal.cpp
+ Op/Equal.h
+ Op/Expand.cpp
+ Op/Expand.h
+ Op/Flatten.cpp
+ Op/Flatten.h
+ Op/Gather.cpp
+ Op/Gather.h
+ Op/Greater.cpp
+ Op/Greater.h
+ Op/Gemm.cpp
+ Op/Gemm.h
+ Op/Identity.cpp
+ Op/Identity.h
+ Op/Less.cpp
+ Op/Less.h
+ Op/MatMul.cpp
+ Op/MatMul.h
+ Op/GlobalAveragePool.cpp
+ Op/GlobalAveragePool.h
+ Op/Max.cpp
+ Op/Max.h
+ Op/MaxPool.cpp
+ Op/MaxPool.h
+ Op/Mul.cpp
+ Op/Mul.h
+ Op/Pad.cpp
+ Op/Pad.h
+ Op/Reciprocal.cpp
+ Op/Reciprocal.h
+ Op/ReduceMean.cpp
+ Op/ReduceMean.h
+ Op/Relu.cpp
+ Op/Relu.h
+ Op/Reshape.cpp
+ Op/Reshape.h
+ Op/Shape.cpp
+ Op/Shape.h
+ Op/Sigmoid.cpp
+ Op/Sigmoid.h
+ Op/Softmax.cpp
+ Op/Softmax.h
+ Op/Sqrt.cpp
+ Op/Sqrt.h
+ Op/Sub.cpp
+ Op/Sub.h
+ Op/Sum.cpp
+ Op/Sum.h
+ Op/Tanh.cpp
+ Op/Tanh.h
+ Op/Transpose.cpp
+ Op/Transpose.h
+ Op/Unsqueeze.cpp
+ Op/Unsqueeze.h
+ Op/Upsample.cpp
+ Op/Upsample.h)
+
+add_library(mir_onnx_importer STATIC ${MIR_ONNX_IMPORTER_SOURCES})
+set_target_properties(mir_onnx_importer PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(mir_onnx_importer PUBLIC ../../include/mir_onnx_importer)
+target_include_directories(mir_onnx_importer PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+target_link_libraries(mir_onnx_importer PUBLIC mir mir_onnx_proto PRIVATE mir_interpreter nncc_common)
+
+nnas_find_package(GTest REQUIRED)
+
+file(GLOB_RECURSE TEST_SOURCES "*.test.cpp")
+GTest_AddTest(mir_onnx_importer_test ${TEST_SOURCES})
+target_link_libraries(mir_onnx_importer_test mir_onnx_importer)
diff --git a/compiler/mir/src/mir_onnx_importer/ConvPoolHelpers.cpp b/compiler/mir/src/mir_onnx_importer/ConvPoolHelpers.cpp
new file mode 100644
index 000000000..d98e6deae
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/ConvPoolHelpers.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ConvPoolHelpers.h"
+
+#include <algorithm>
+#include <cassert>
+
+namespace mir_onnx
+{
+
+void inferAutoPadding(const std::string &pad_type, const mir::Shape &input_shape,
+ const std::vector<std::int32_t> &dilations,
+ const std::vector<std::int32_t> &strides,
+ const std::vector<std::int32_t> &window_size,
+ std::vector<std::int32_t> &padding_before,
+ std::vector<std::int32_t> &padding_after)
+{
+ constexpr int num_spatial_dims = 2;
+
+ if (pad_type == "NOTSET")
+ {
+ // Do nothing.
+ }
+ else if (pad_type == "VALID")
+ {
+ padding_before.assign(num_spatial_dims, 0);
+ padding_after.assign(num_spatial_dims, 0);
+ }
+ else
+ {
+ padding_before.resize(num_spatial_dims);
+ padding_after.resize(num_spatial_dims);
+
+ assert(dilations.size() == num_spatial_dims);
+ assert(strides.size() == num_spatial_dims);
+ assert(window_size.size() == num_spatial_dims);
+
+ for (int i = 0; i < num_spatial_dims; ++i)
+ {
+ const std::int32_t eff_window_size = (window_size[i] - 1) * dilations[i] + 1;
+ // Assuming input has NCHW format.
+ const std::int32_t residual = input_shape.dim(2 + i) % strides[i];
+ const std::int32_t total_pad = std::max(
+ INT32_C(0), residual == 0 ? eff_window_size - strides[i] : eff_window_size - residual);
+ if (pad_type == "SAME_UPPER")
+ {
+ padding_before[i] = total_pad / 2;
+ padding_after[i] = (total_pad + 1) / 2;
+ }
+ else
+ {
+ assert(pad_type == "SAME_LOWER");
+ padding_before[i] = (total_pad + 1) / 2;
+ padding_after[i] = total_pad / 2;
+ }
+ }
+ }
+}
+
+std::vector<std::int32_t> fixPads(const mir::Shape &input_shape,
+ const std::vector<std::int32_t> &pads,
+ const std::vector<std::int32_t> &strides,
+ const std::vector<std::int32_t> &dilation,
+ const std::vector<std::int32_t> &kernel_shape)
+{
+ assert(pads.size() % 2 == 0);
+ int spatial_dimensions = pads.size() / 2;
+ std::vector<std::int32_t> fixed_pads(pads);
+ for (int i = 0; i < spatial_dimensions; ++i)
+ {
+ auto effective_window_dim = (kernel_shape[i] - 1) * dilation[i] + 1;
+ auto effective_input_dim = input_shape.dim(i + 2) + pads[i] + pads[i + spatial_dimensions];
+ // Computing number of "redundant" elements at the end of input dimension
+ // for example we have effective_input_dim == 8, effective_window)dim == 3 and stride == 2:
+ // [1][2][3][4][5][6][7][8] - input
+ // * * * . . . . - first kernel application
+ // . . * * * . . - second kernel application
+ // . . . . * * * - third kernel application
+ // element 8 is unused (remainder should be 1)
+ //
+ // glossary:
+ // i - effective input size
+ // w - effective window size
+ // s - stride
+ // n - number of kernel applications (3 in example)
+ //
+ // i = s * (n-1) + w + r
+ // r = i - w - s * (n-1)
+ // n - is the maximum number of windows we can fit into input, so this formula is equal to
+ // r = (i - w) % s
+ auto remainder = (effective_input_dim - effective_window_dim) % strides[i];
+
+ // remove redundant pad, but no more than there are padding
+ fixed_pads[i + spatial_dimensions] -= std::min(remainder, pads[i + spatial_dimensions]);
+ }
+ return fixed_pads;
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/ConvPoolHelpers.h b/compiler/mir/src/mir_onnx_importer/ConvPoolHelpers.h
new file mode 100644
index 000000000..099392f4f
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/ConvPoolHelpers.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_CONV_POOL_HELPERS_H
+#define MIR_ONNX_CONV_POOL_HELPERS_H
+
+#include "mir/Shape.h"
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace mir_onnx
+{
+
+void inferAutoPadding(const std::string &pad_type, const mir::Shape &input_shape,
+ const std::vector<std::int32_t> &dilations,
+ const std::vector<std::int32_t> &strides,
+ const std::vector<std::int32_t> &window_size,
+ std::vector<std::int32_t> &padding_before,
+ std::vector<std::int32_t> &padding_after);
+
+std::vector<std::int32_t> fixPads(const mir::Shape &input_shape,
+ const std::vector<std::int32_t> &pads,
+ const std::vector<std::int32_t> &strides,
+ const std::vector<std::int32_t> &dilation,
+ const std::vector<std::int32_t> &kernel_shape);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_CONV_POOL_HELPERS_H
diff --git a/compiler/mir/src/mir_onnx_importer/ONNXHelpers.cpp b/compiler/mir/src/mir_onnx_importer/ONNXHelpers.cpp
new file mode 100644
index 000000000..f3a9d182d
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/ONNXHelpers.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "MirInterpreter.h"
+#include "mir/ops/ConstantOp.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+#include "mir/TensorVariant.h"
+#include "mir/Index.h"
+
+namespace mir_onnx
+{
+
+const int64_t firstUnknownOpset = 13;
+
+template <typename T> static mir::Shape constantToShapeT(const mir::TensorVariant &t)
+{
+ const mir::Shape &t_shape = t.getShape();
+ mir::Tensor<T> input(t);
+ if (t_shape.rank() != 1)
+ throw std::runtime_error("only 1-d tensors supported as a shape input");
+
+ mir::Shape target_shape;
+ std::int32_t rank = t_shape.dim(0);
+ target_shape.resize(rank);
+ for (int i = 0; i < rank; ++i)
+ target_shape.dim(i) = static_cast<std::int32_t>(input.at(mir::Index{i}));
+ return target_shape;
+}
+
+mir::Shape constantToShape(const mir::ops::ConstantOp *op)
+{
+ const auto &t = op->getValue();
+ mir::DataType d_type = t.getElementType();
+
+ if (t.getType().isQuantized())
+ throw std::runtime_error("unsupported data type of shape operator");
+
+ switch (d_type)
+ {
+ case mir::DataType::FLOAT32:
+ return constantToShapeT<float>(t);
+ break;
+ case mir::DataType::FLOAT64:
+ return constantToShapeT<double>(t);
+ break;
+ case mir::DataType::INT32:
+ return constantToShapeT<int32_t>(t);
+ break;
+ case mir::DataType::INT64:
+ return constantToShapeT<int64_t>(t);
+ break;
+ case mir::DataType::UINT8:
+ return constantToShapeT<uint8_t>(t);
+ break;
+ default:
+ throw std::runtime_error{"Unknown datatype in constant"};
+ break;
+ }
+}
+
+mir::DataType onnxDataTypeToMirDataType(onnx::TensorProto::DataType type)
+{
+ switch (type)
+ {
+ case onnx::TensorProto_DataType_UINT8:
+ return mir::DataType::UINT8;
+ break;
+ case onnx::TensorProto_DataType_INT32:
+ return mir::DataType::INT32;
+ break;
+ case onnx::TensorProto_DataType_INT64:
+ return mir::DataType::INT64;
+ break;
+ case onnx::TensorProto_DataType_DOUBLE:
+ return mir::DataType::FLOAT64;
+ break;
+ case onnx::TensorProto_DataType_FLOAT:
+ return mir::DataType::FLOAT32;
+ break;
+ case onnx::TensorProto_DataType_UNDEFINED:
+ throw std::runtime_error{"Undefined input data type not supported"};
+ break;
+ default:
+ throw std::runtime_error{"Unsupported tensor element data type"};
+ }
+}
+
+mir::TensorVariant createTensor(const onnx::TensorProto *tensor)
+{
+ mir::DataType type;
+ const void *src_data;
+ mir::Shape shape(tensor->dims_size());
+ for (int i = 0; i < tensor->dims_size(); ++i)
+ {
+ shape.dim(i) = tensor->dims(i);
+ }
+
+ if (tensor->float_data_size() != 0)
+ {
+ assert(tensor->data_type() == onnx::TensorProto::FLOAT);
+ type = mir::DataType::FLOAT32;
+ src_data = tensor->float_data().data();
+ }
+ else if (tensor->double_data_size() != 0)
+ {
+ assert(tensor->data_type() == onnx::TensorProto::DOUBLE);
+ type = mir::DataType::FLOAT64;
+ src_data = tensor->double_data().data();
+ }
+ else if (tensor->int32_data_size() != 0)
+ {
+ assert(tensor->data_type() == onnx::TensorProto::INT32);
+ type = mir::DataType::INT32;
+ src_data = tensor->int32_data().data();
+ }
+ else if (tensor->int64_data_size() != 0)
+ {
+ assert(tensor->data_type() == onnx::TensorProto::INT64);
+ type = mir::DataType::INT64;
+ src_data = tensor->int64_data().data();
+ }
+ else if (tensor->has_raw_data())
+ {
+ type = onnxDataTypeToMirDataType((onnx::TensorProto_DataType)tensor->data_type());
+ src_data = tensor->raw_data().data();
+ }
+ else
+ {
+ throw std::runtime_error("Invalid data in Proto file, investigate");
+ }
+
+ return mir::TensorVariant({type, shape}, src_data);
+}
+
+mir::Operation *foldConstants(mir::Graph *graph, mir::Operation *op)
+{
+ if (op->getType() == mir::Operation::Type::constant ||
+ op->getType() == mir::Operation::Type::input || op->getType() == mir::Operation::Type::output)
+ {
+ // don't fold input, output and constant nodes
+ return op;
+ }
+
+ if (op->getNumOutputs() != 1)
+ {
+ // this operation either have more than 1 output or none at all
+ return op;
+ }
+
+ bool is_foldable =
+ std::all_of(op->getInputs().begin(), op->getInputs().end(), [](mir::Operation::Output *out) {
+ return out->getNode()->getType() == mir::Operation::Type::constant;
+ });
+
+ if (!is_foldable)
+ return op;
+
+ mir_interpreter::MIRInterpreter interpreter;
+ for (mir::Operation::Output *out : op->getInputs())
+ {
+ auto *constant = static_cast<mir::ops::ConstantOp *>(out->getNode());
+ interpreter.setTensor(out, constant->getValue());
+ }
+ op->accept(&interpreter);
+ const mir::TensorVariant &output = interpreter.getTensor(op->getOutput(0));
+
+ return graph->create<mir::ops::ConstantOp>(output);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/ONNXHelpers.h b/compiler/mir/src/mir_onnx_importer/ONNXHelpers.h
new file mode 100644
index 000000000..1367ab82a
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/ONNXHelpers.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MIR_ONNX_HELPERS_H__
+#define __MIR_ONNX_HELPERS_H__
+
+#include "mir/Graph.h"
+#include "mir/ops/ConstantOp.h"
+#include "mir/TensorVariant.h"
+#include "mir/ops/TransposeOp.h"
+
+#include "onnx/onnx.pb.h"
+
+namespace mir_onnx
+{
+
+extern const int64_t firstUnknownOpset;
+
+mir::DataType onnxDataTypeToMirDataType(onnx::TensorProto::DataType type);
+
+mir::Shape constantToShape(const mir::ops::ConstantOp *op);
+
+mir::TensorVariant createTensor(const onnx::TensorProto *tensor);
+
+mir::Operation *foldConstants(mir::Graph *graph, mir::Operation *op);
+
+template <typename OpType, typename... Types>
+mir::Operation *createOp(mir::Graph *graph, Types &&... args)
+{
+ auto op = graph->create<OpType>(std::forward<Types>(args)...);
+ op = foldConstants(graph, op);
+ return op;
+}
+
+} // namespace mir_onnx
+
+#endif // __MIR_ONNX_HELPERS_H__
diff --git a/compiler/mir/src/mir_onnx_importer/ONNXImporterImpl.cpp b/compiler/mir/src/mir_onnx_importer/ONNXImporterImpl.cpp
new file mode 100644
index 000000000..8b996244f
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/ONNXImporterImpl.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ONNXImporterImpl.h"
+#include "ONNXHelpers.h"
+#include "ONNXOpRegistration.h"
+#include "onnx/onnx.pb.h"
+
+#include "mir/Shape.h"
+#include "mir/TensorUtil.h"
+
+#include "mir/ops/ConstantOp.h"
+
+#include <fcntl.h>
+
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/text_format.h>
+#include <functional>
+#include <iostream>
+#include <memory>
+#include <utility>
+
+namespace mir_onnx
+{
+
+namespace
+{
+
+class ONNXImporterImpl final
+{
+public:
+ ONNXImporterImpl();
+ ~ONNXImporterImpl();
+ /// @brief Load the model and convert it into a MIR Graph.
+ std::unique_ptr<mir::Graph> importModelFromBinaryFile(const std::string &filename);
+ std::unique_ptr<mir::Graph> importModelFromTextFile(const std::string &filename);
+
+private:
+ std::unique_ptr<mir::Graph> createIR();
+ void createGraphInputs();
+ void collectUnsupportedOps();
+ std::unique_ptr<onnx::ModelProto> _model;
+ std::unique_ptr<ConverterContext> _converterCtx;
+ std::unique_ptr<ModelContext> _modelCtx;
+ std::unique_ptr<mir::Graph> _graph;
+};
+
+ONNXImporterImpl::ONNXImporterImpl() { registerSupportedOps(); }
+
+ONNXImporterImpl::~ONNXImporterImpl() = default;
+
+void loadModelFromBinaryFile(const std::string &filename, onnx::ModelProto *model)
+{
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+ int file_handle = open(filename.c_str(), O_RDONLY);
+
+ if (file_handle == -1)
+ throw std::runtime_error("Couldn't open file \"" + filename + "\": " + std::strerror(errno) +
+ ".");
+
+ google::protobuf::io::FileInputStream file_stream(file_handle);
+ file_stream.SetCloseOnDelete(true);
+
+ google::protobuf::io::CodedInputStream coded_stream(&file_stream);
+ coded_stream.SetTotalBytesLimit(INT_MAX, INT_MAX);
+
+ if (!model->ParseFromCodedStream(&coded_stream))
+ throw std::runtime_error("Couldn't parse file \"" + filename + "\".");
+
+ // If the file has not been consumed entirely, assume that the file is in the wrong format.
+ if (!coded_stream.ConsumedEntireMessage())
+ throw std::runtime_error("File \"" + filename + "\" has not been consumed entirely.");
+}
+
+void loadModelFromTextFile(const std::string &filename, onnx::ModelProto *model)
+{
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+ int file_handle = open(filename.c_str(), O_RDONLY);
+
+ if (file_handle == -1)
+ throw std::runtime_error("Couldn't open file \"" + filename + "\": " + std::strerror(errno) +
+ ".");
+
+ google::protobuf::io::FileInputStream file_stream(file_handle);
+ file_stream.SetCloseOnDelete(true);
+
+ if (!google::protobuf::TextFormat::Parse(&file_stream, model))
+ throw std::runtime_error("Couldn't parse file \"" + filename + "\".");
+}
+
+std::unique_ptr<mir::Graph> ONNXImporterImpl::importModelFromBinaryFile(const std::string &filename)
+{
+ _model = std::make_unique<onnx::ModelProto>();
+ loadModelFromBinaryFile(filename, _model.get());
+ _modelCtx = std::make_unique<ModelContext>(_model.get());
+ collectUnsupportedOps();
+ return createIR();
+}
+
+std::unique_ptr<mir::Graph> ONNXImporterImpl::importModelFromTextFile(const std::string &filename)
+{
+ _model = std::make_unique<onnx::ModelProto>();
+ loadModelFromTextFile(filename, _model.get());
+ _modelCtx = std::make_unique<ModelContext>(_model.get());
+ collectUnsupportedOps();
+ return createIR();
+}
+
+void ONNXImporterImpl::collectUnsupportedOps()
+{
+ std::set<std::pair<std::string, int64_t>> problems_op_set;
+
+ for (int i = 0; i < _model->graph().node_size(); i++)
+ {
+ const auto &onnx_node = _model->graph().node(i);
+ assert(onnx_node.has_op_type());
+ const auto &op_type = onnx_node.op_type();
+ auto opset = _modelCtx->getDomainOpsetVersion(onnx_node.domain());
+
+ NodeConverterRegistry::ConverterFunc converter =
+ NodeConverterRegistry::getInstance().lookup(op_type, opset);
+
+ if (converter == nullptr)
+ problems_op_set.emplace(op_type, opset);
+ }
+ if (!problems_op_set.empty())
+ {
+ std::cerr << "The following operators are not supported:\n";
+ for (const auto &op : problems_op_set)
+ std::cerr << op.first << " opset " << op.second << std::endl;
+ throw std::runtime_error("Unsupported operators found");
+ }
+}
+
+void ONNXImporterImpl::createGraphInputs()
+{
+ const auto &graph = _model->graph();
+ const auto &initializer = graph.initializer();
+
+ // Create all initializer Tensors
+ for (const auto &tensor : initializer)
+ {
+ const auto mir_tensor = createTensor(&tensor);
+ auto *op = _graph->create<mir::ops::ConstantOp>(mir_tensor);
+ _converterCtx->setOutput(tensor.name(), op->getOutput(0));
+ }
+
+ for (const auto &input : graph.input())
+ {
+ assert(input.has_name());
+
+ if (_converterCtx->getOutput(input.name()) == nullptr)
+ {
+ const auto &onnx_input_shape = input.type().tensor_type().shape();
+ mir::Shape shape(onnx_input_shape.dim_size());
+ for (int i = 0; i < onnx_input_shape.dim_size(); i++)
+ {
+ assert(onnx_input_shape.dim(i).has_dim_value());
+ shape.dim(i) = static_cast<int32_t>(onnx_input_shape.dim(i).dim_value());
+ }
+
+ auto elem_type = onnxDataTypeToMirDataType(
+ (onnx::TensorProto_DataType)input.type().tensor_type().elem_type());
+ mir::TensorType type{elem_type, shape};
+ auto *op = _graph->create<mir::ops::InputOp>(type);
+ _converterCtx->setOutput(input.name(), op->getOutput(0));
+ }
+ }
+}
+
+std::unique_ptr<mir::Graph> ONNXImporterImpl::createIR()
+{
+ _graph = std::make_unique<mir::Graph>();
+ _converterCtx = std::make_unique<ConverterContext>(_graph.get());
+
+ createGraphInputs();
+
+ // Forming partially ordered computation graph
+ for (const auto &onnx_node : _model->graph().node())
+ {
+ assert(onnx_node.has_op_type());
+ auto &op_type = onnx_node.op_type();
+ auto opset = _modelCtx->getDomainOpsetVersion(onnx_node.domain());
+ // Get converter
+ NodeConverterRegistry::ConverterFunc converter =
+ NodeConverterRegistry::getInstance().lookup(op_type, opset);
+ assert(converter != nullptr);
+ converter(onnx_node, _converterCtx.get());
+ }
+ // Set graph outputs
+ const auto &outputs = _model->graph().output();
+ for (const auto &output : outputs)
+ {
+ assert(output.has_name());
+ auto mir_output = _converterCtx->getOutput(output.name());
+ if (mir_output == nullptr)
+ throw std::runtime_error("Bad output name!");
+
+ _graph->create<mir::ops::OutputOp>(mir_output);
+ }
+
+ return std::move(_graph);
+}
+
+} // namespace
+
+std::unique_ptr<mir::Graph> importModelFromBinaryFile(const std::string &filename)
+{
+ ONNXImporterImpl importer;
+ return importer.importModelFromBinaryFile(filename);
+}
+
+std::unique_ptr<mir::Graph> importModelFromTextFile(const std::string &filename)
+{
+ ONNXImporterImpl importer;
+ return importer.importModelFromTextFile(filename);
+}
+
+std::unique_ptr<mir::Graph> loadModel(const std::string &filename)
+{
+ return importModelFromBinaryFile(filename);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/ONNXNodeConverterRegistry.cpp b/compiler/mir/src/mir_onnx_importer/ONNXNodeConverterRegistry.cpp
new file mode 100644
index 000000000..a11b18e89
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/ONNXNodeConverterRegistry.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ONNXNodeConverterRegistry.h"
+
+#include <memory>
+
+namespace mir_onnx
+{
+
+void ModelContext::setDomainOpsetVersion(const std::string &domain, const int64_t opset_version)
+{
+ _domainToOpsetVersion.emplace(domain, opset_version);
+}
+
+int64_t ModelContext::getDomainOpsetVersion(const std::string &domain) const
+{
+ auto iter = _domainToOpsetVersion.find(domain);
+ if (iter == _domainToOpsetVersion.end())
+ throw std::runtime_error("Didn't have domain " + domain + "!");
+ return iter->second;
+}
+
+ModelContext::ModelContext(const onnx::ModelProto *model)
+{
+ if (model == nullptr)
+ {
+ throw std::runtime_error{"Model should be imported before importer prepare"};
+ }
+
+ if (model->ir_version() > onnx::IR_VERSION)
+ {
+ throw std::runtime_error("IR version " + std::to_string(model->ir_version()) +
+ " is not supported yet.");
+ }
+
+ // Set Opset Version for each domain
+ for (const auto &op_set : model->opset_import())
+ {
+ setDomainOpsetVersion(op_set.domain(), op_set.version());
+ }
+}
+
+// ConverterContext
+
+ConverterContext::ConverterContext(mir::Graph *graph) : _graph(graph) {}
+
+void ConverterContext::setOutput(const std::string &name, mir::Operation::Output *output)
+{
+ output->setName(name);
+ auto result = _tensorNameToOutput.emplace(name, output);
+ if (!result.second)
+ throw std::runtime_error("Name duplication: " + name);
+}
+
+mir::Operation::Output *ConverterContext::getOutput(const std::string &name) const
+{
+ auto iter = _tensorNameToOutput.find(name);
+ if (iter == _tensorNameToOutput.end())
+ return nullptr;
+ else
+ return iter->second;
+}
+
+std::vector<mir::Operation::Output *>
+ConverterContext::getNodeInputs(const onnx::NodeProto &onnx_node) const
+{
+ const auto &input_names = onnx_node.input();
+ std::vector<mir::Operation::Output *> outputs;
+
+ for (const auto &input_name : input_names)
+ {
+ if (!input_name.empty())
+ {
+ auto *mir_output = getOutput(input_name);
+ assert(mir_output != nullptr);
+ outputs.emplace_back(mir_output);
+ }
+ }
+ return outputs;
+}
+
+void ConverterContext::setNodeOutputs(const onnx::NodeProto &onnx_node,
+ const std::vector<mir::Operation::Output *> &outputs)
+{
+ assert(!outputs.empty());
+ for (std::size_t i = 0; i < outputs.size(); ++i)
+ {
+ setOutput(onnx_node.output(i), outputs[i]);
+ }
+}
+
+// NodeConverterRegistry
+
+NodeConverterRegistry::ConverterFunc NodeConverterRegistry::lookup(const std::string &optype,
+ int64_t opset) const
+{
+ auto it = _converter_map.find(optype);
+ if (it == _converter_map.end())
+ {
+ return nullptr;
+ }
+
+ const VersionMap &conv_map = it->second;
+
+ auto res = std::lower_bound(
+ conv_map.crbegin(), conv_map.crend(), opset,
+ [](const VersionMap::value_type &pair, int64_t opset) { return pair.first > opset; });
+
+ if (res == conv_map.crend())
+ {
+ return nullptr;
+ }
+ return res->second;
+}
+
+NodeConverterRegistry &NodeConverterRegistry::getInstance()
+{
+ static NodeConverterRegistry instance;
+ return instance;
+}
+
+void NodeConverterRegistry::registerConverter(const std::string &op_type, int64_t opset,
+ NodeConverterRegistry::ConverterFunc conv)
+{
+ _converter_map[op_type].emplace(opset, conv);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/ONNXNodeConverterRegistry.h b/compiler/mir/src/mir_onnx_importer/ONNXNodeConverterRegistry.h
new file mode 100644
index 000000000..ea712ad23
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/ONNXNodeConverterRegistry.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ONNX_NODE_CONVERTER_REGISTRY_H__
+#define __ONNX_NODE_CONVERTER_REGISTRY_H__
+
+#include "onnx/onnx.pb.h"
+#include "mir/Graph.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace mir_onnx
+{
+
+class ModelContext
+{
+public:
+ explicit ModelContext(const onnx::ModelProto *model);
+
+ void setDomainOpsetVersion(const std::string &domain, const int64_t opset_version);
+ int64_t getDomainOpsetVersion(const std::string &domain) const;
+
+private:
+ std::map<std::string, int64_t> _domainToOpsetVersion;
+};
+
+class ConverterContext
+{
+public:
+ explicit ConverterContext(mir::Graph *graph);
+ ~ConverterContext() = default;
+
+ void setOutput(const std::string &name, mir::Operation::Output *output);
+ mir::Operation::Output *getOutput(const std::string &name) const;
+ std::vector<mir::Operation::Output *> getNodeInputs(const onnx::NodeProto &onnx_node) const;
+ void setNodeOutputs(const onnx::NodeProto &onnx_node,
+ const std::vector<mir::Operation::Output *> &outputs);
+ mir::Graph *getGraph() const { return _graph; }
+
+private:
+ std::map<std::string, mir::Operation::Output *> _tensorNameToOutput;
+ mir::Graph *_graph;
+};
+
+class NodeConverterRegistry
+{
+public:
+ using ConverterFunc = void (*)(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+ NodeConverterRegistry() = default;
+
+ ConverterFunc lookup(const std::string &optype, int64_t opset) const;
+ void registerConverter(const std::string &op_type, int64_t opset, ConverterFunc conv);
+
+ static NodeConverterRegistry &getInstance();
+
+private:
+ using VersionMap = std::map<int64_t, ConverterFunc>;
+
+ std::unordered_map<std::string, VersionMap> _converter_map;
+};
+
+} // namespace mir_onnx
+
+#endif // __ONNX_NODE_CONVERTER_REGISTRY_H__
diff --git a/compiler/mir/src/mir_onnx_importer/ONNXNodeConverterRegistry.test.cpp b/compiler/mir/src/mir_onnx_importer/ONNXNodeConverterRegistry.test.cpp
new file mode 100644
index 000000000..dfc3e4216
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/ONNXNodeConverterRegistry.test.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 "ONNXNodeConverterRegistry.h"
+#include "ONNXHelpers.h"
+
+#include "gtest/gtest.h"
+
+using namespace mir_onnx;
+
+void converterV1(const onnx::NodeProto &node, ConverterContext *ctx) {}
+void converterV3(const onnx::NodeProto &node, ConverterContext *ctx) {}
+void converterV7(const onnx::NodeProto &node, ConverterContext *ctx) {}
+
+class NodeConverterRegsitryTest : public ::testing::Test
+{
+protected:
+ void SetUp() override
+ {
+ registry.registerConverter("dummy", 1, converterV1);
+ registry.registerConverter("dummy", 3, converterV3);
+ registry.registerConverter("dummy", 7, converterV7);
+ registry.registerConverter("dummy", firstUnknownOpset, nullptr);
+ }
+
+ NodeConverterRegistry registry;
+};
+
+TEST_F(NodeConverterRegsitryTest, existing_lookup_works)
+{
+ auto res = registry.lookup("dummy", 1);
+ ASSERT_EQ(res, &converterV1);
+}
+
+TEST_F(NodeConverterRegsitryTest, skipped_lookup_works)
+{
+ auto res = registry.lookup("dummy", 2);
+ ASSERT_EQ(res, &converterV1);
+}
+
+TEST_F(NodeConverterRegsitryTest, first_unknown_version_works)
+{
+ auto res = registry.lookup("dummy", 14);
+ ASSERT_EQ(res, nullptr);
+}
+
+TEST_F(NodeConverterRegsitryTest, lower_than_first_version)
+{
+ auto res = registry.lookup("dummy", 0);
+ ASSERT_EQ(res, nullptr);
+}
diff --git a/compiler/mir/src/mir_onnx_importer/ONNXOpRegistration.h b/compiler/mir/src/mir_onnx_importer/ONNXOpRegistration.h
new file mode 100644
index 000000000..e3001b000
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/ONNXOpRegistration.h
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ONNX_OP_REGISTRATION_H__
+#define __ONNX_OP_REGISTRATION_H__
+
+#include "ONNXNodeConverterRegistry.h"
+
+#include "Op/Abs.h"
+#include "Op/Add.h"
+#include "Op/AveragePool.h"
+#include "Op/BatchNormalization.h"
+#include "Op/Concat.h"
+#include "Op/Constant.h"
+#include "Op/Conv.h"
+#include "Op/ConvTranspose.h"
+#include "Op/Div.h"
+#include "Op/Dropout.h"
+#include "Op/Equal.h"
+#include "Op/Expand.h"
+#include "Op/Flatten.h"
+#include "Op/Gather.h"
+#include "Op/Greater.h"
+#include "Op/Gemm.h"
+#include "Op/GlobalAveragePool.h"
+#include "Op/Identity.h"
+#include "Op/Less.h"
+#include "Op/MatMul.h"
+#include "Op/Max.h"
+#include "Op/MaxPool.h"
+#include "Op/Mul.h"
+#include "Op/Pad.h"
+#include "Op/Reciprocal.h"
+#include "Op/ReduceMean.h"
+#include "Op/Relu.h"
+#include "Op/Reshape.h"
+#include "Op/Shape.h"
+#include "Op/Sigmoid.h"
+#include "Op/Softmax.h"
+#include "Op/Sqrt.h"
+#include "Op/Sub.h"
+#include "Op/Sum.h"
+#include "Op/Tanh.h"
+#include "Op/Transpose.h"
+#include "Op/Unsqueeze.h"
+#include "Op/Upsample.h"
+
+namespace mir_onnx
+{
+
+inline void registerSupportedOps()
+{
+ auto &registry = NodeConverterRegistry::getInstance();
+
+#define REG_CONVERTER(name, version, function) registry.registerConverter(name, version, function)
+#define REG(name, version) REG_CONVERTER(#name, version, convert##name##V##version)
+#define UNSUPPORTED(name, version) REG_CONVERTER(#name, version, nullptr)
+
+ REG(Abs, 1);
+ REG(Abs, 6);
+ UNSUPPORTED(Abs, firstUnknownOpset);
+
+ REG(Add, 1);
+ REG(Add, 6);
+ REG(Add, 7);
+ UNSUPPORTED(Add, firstUnknownOpset);
+
+ REG(AveragePool, 1);
+ REG(AveragePool, 7);
+ REG(AveragePool, 10);
+ UNSUPPORTED(AveragePool, 11);
+ UNSUPPORTED(AveragePool, firstUnknownOpset);
+
+ REG(BatchNormalization, 1);
+ REG(BatchNormalization, 6);
+ REG(BatchNormalization, 7);
+ REG(BatchNormalization, 9);
+ UNSUPPORTED(BatchNormalization, firstUnknownOpset);
+
+ REG(Concat, 1);
+ REG(Concat, 4);
+ UNSUPPORTED(Concat, 11);
+ UNSUPPORTED(Concat, firstUnknownOpset);
+
+ REG(Constant, 1);
+ REG(Constant, 9);
+ REG(Constant, 11);
+ UNSUPPORTED(Constant, 12);
+ UNSUPPORTED(Constant, firstUnknownOpset);
+
+ REG(Conv, 1);
+ UNSUPPORTED(Conv, 11);
+ UNSUPPORTED(Conv, firstUnknownOpset);
+
+ REG(ConvTranspose, 1);
+ UNSUPPORTED(ConvTranspose, 11);
+ UNSUPPORTED(ConvTranspose, firstUnknownOpset);
+
+ UNSUPPORTED(Div, 1);
+ UNSUPPORTED(Div, 6);
+ REG(Div, 7);
+ UNSUPPORTED(Div, firstUnknownOpset);
+
+ REG(Dropout, 1);
+ REG(Dropout, 6);
+ REG(Dropout, 7);
+ REG(Dropout, 10);
+ UNSUPPORTED(Dropout, 12);
+ UNSUPPORTED(Dropout, firstUnknownOpset);
+
+ UNSUPPORTED(Equal, 1);
+ REG(Equal, 7);
+ REG(Equal, 11);
+ UNSUPPORTED(Equal, firstUnknownOpset);
+
+ REG(Expand, 8);
+ UNSUPPORTED(Expand, firstUnknownOpset);
+
+ REG(Flatten, 1);
+ REG(Flatten, 9);
+ UNSUPPORTED(Flatten, 11);
+ UNSUPPORTED(Flatten, firstUnknownOpset);
+
+ REG(Gather, 1);
+ UNSUPPORTED(Gather, 11);
+ UNSUPPORTED(Gather, firstUnknownOpset);
+
+ REG(Gemm, 1);
+ REG(Gemm, 6);
+ REG(Gemm, 7);
+ REG(Gemm, 9);
+ REG(Gemm, 11);
+ UNSUPPORTED(Gemm, firstUnknownOpset);
+
+ UNSUPPORTED(GlobalAveragePool, 1);
+ REG(GlobalAveragePool, 2);
+ UNSUPPORTED(GlobalAveragePool, firstUnknownOpset);
+
+ UNSUPPORTED(Greater, 1);
+ REG(Greater, 7);
+ REG(Greater, 9);
+ UNSUPPORTED(Greater, firstUnknownOpset);
+
+ REG(Identity, 1);
+ UNSUPPORTED(Identity, firstUnknownOpset);
+
+ UNSUPPORTED(Less, 1);
+ REG(Less, 7);
+ REG(Less, 9);
+ UNSUPPORTED(Less, firstUnknownOpset);
+
+ REG(MatMul, 1);
+ REG(MatMul, 9);
+ UNSUPPORTED(MatMul, firstUnknownOpset);
+
+ REG(Max, 1);
+ REG(Max, 6);
+ REG(Max, 8);
+ UNSUPPORTED(Max, firstUnknownOpset);
+
+ REG(MaxPool, 1);
+ REG(MaxPool, 8);
+ REG(MaxPool, 10);
+ UNSUPPORTED(MaxPool, 11);
+ UNSUPPORTED(MaxPool, 12);
+ UNSUPPORTED(MaxPool, firstUnknownOpset);
+
+ UNSUPPORTED(Mul, 1);
+ UNSUPPORTED(Mul, 6);
+ REG(Mul, 7);
+ UNSUPPORTED(Mul, firstUnknownOpset);
+
+ REG(Pad, 1);
+ REG(Pad, 2);
+ UNSUPPORTED(Pad, 11);
+ UNSUPPORTED(Pad, firstUnknownOpset);
+
+ REG(Reciprocal, 1);
+ REG(Reciprocal, 6);
+ UNSUPPORTED(Reciprocal, firstUnknownOpset);
+
+ REG(ReduceMean, 1);
+ UNSUPPORTED(ReduceMean, 11);
+ UNSUPPORTED(ReduceMean, firstUnknownOpset);
+
+ REG(Relu, 1);
+ REG(Relu, 6);
+ UNSUPPORTED(Relu, firstUnknownOpset);
+
+ REG(Reshape, 1);
+ REG(Reshape, 5);
+ UNSUPPORTED(Reshape, firstUnknownOpset);
+
+ REG(Shape, 1);
+ UNSUPPORTED(Shape, firstUnknownOpset);
+
+ REG(Sigmoid, 1);
+ REG(Sigmoid, 6);
+ UNSUPPORTED(Sigmoid, firstUnknownOpset);
+
+ REG(Softmax, 1);
+ // TODO SoftmaxV11 is mostly the same, needs a check though
+ UNSUPPORTED(Softmax, firstUnknownOpset);
+
+ REG(Sqrt, 1);
+ REG(Sqrt, 6);
+ UNSUPPORTED(Sqrt, firstUnknownOpset);
+
+ REG(Sub, 1);
+ REG(Sub, 6);
+ REG(Sub, 7);
+ UNSUPPORTED(Sub, firstUnknownOpset);
+
+ UNSUPPORTED(Sum, 1);
+ UNSUPPORTED(Sum, 6);
+ REG(Sum, 8);
+ UNSUPPORTED(Sum, firstUnknownOpset);
+
+ REG(Tanh, 1);
+ REG(Tanh, 6);
+ UNSUPPORTED(Tanh, firstUnknownOpset);
+
+ REG(Transpose, 1);
+ UNSUPPORTED(Transpose, firstUnknownOpset);
+
+ REG(Unsqueeze, 1);
+ UNSUPPORTED(Unsqueeze, 11);
+ UNSUPPORTED(Unsqueeze, firstUnknownOpset);
+
+ // Upsample-1 is not mentioned in onnx master and was considered experimental at the time
+ REG(Upsample, 1);
+ REG(Upsample, 7);
+ REG(Upsample, 9);
+ UNSUPPORTED(Upsample, firstUnknownOpset);
+
+#undef REG
+#undef REG_CONVERTER
+#undef UNSUPPORTED
+}
+
+} // namespace mir_onnx
+
+#endif // __ONNX_OP_REGISTRATION_H__
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Abs.cpp b/compiler/mir/src/mir_onnx_importer/Op/Abs.cpp
new file mode 100644
index 000000000..350270cfd
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Abs.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Abs.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/ops/AbsOp.h"
+
+namespace mir_onnx
+{
+
+static void convertAbsGeneric(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ auto result = createOp<mir::ops::AbsOp>(graph, inputs[0])->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertAbsV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertAbsGeneric(onnx_node, context);
+}
+
+void convertAbsV6(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertAbsGeneric(onnx_node, context);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Abs.h b/compiler/mir/src/mir_onnx_importer/Op/Abs.h
new file mode 100644
index 000000000..06fcd5f3c
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Abs.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_ABS_H
+#define MIR_ONNX_OP_ABS_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertAbsV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertAbsV6(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_ABS_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Add.cpp b/compiler/mir/src/mir_onnx_importer/Op/Add.cpp
new file mode 100644
index 000000000..8944b4e66
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Add.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Add.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/ops/AddOp.h"
+
+namespace mir_onnx
+{
+
+void convertAddV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ // consumed_inputs attribute not used
+ convertAddV6(onnx_node, context);
+}
+
+void convertAddV6(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ // broadcast attribute not used
+ const auto *axis = findAttribute(onnx_node, "axis");
+ if (axis != nullptr)
+ throw std::runtime_error("Not supported axis attribute in Add operation!");
+
+ convertAddV7(onnx_node, context);
+}
+
+void convertAddV7(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ auto result = createOp<mir::ops::AddOp>(graph, inputs[0], inputs[1])->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Add.h b/compiler/mir/src/mir_onnx_importer/Op/Add.h
new file mode 100644
index 000000000..a11aa6bb7
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Add.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_ADD_H
+#define MIR_ONNX_OP_ADD_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertAddV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertAddV6(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertAddV7(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_ADD_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/AveragePool.cpp b/compiler/mir/src/mir_onnx_importer/Op/AveragePool.cpp
new file mode 100644
index 000000000..503feffc8
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/AveragePool.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AveragePool.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+#include "ConvPoolHelpers.h"
+
+#include "mir/ops/AvgPool2DOp.h"
+
+namespace mir_onnx
+{
+
+void convertAveragePoolV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ assert(inputs.size() == 1);
+ auto input = inputs[0];
+
+ const auto &input_shape = input->getShape();
+ if (input_shape.rank() != 4)
+ throw std::runtime_error("AveragePool: only 2-D input is supported.");
+
+ constexpr int num_spatial_dims = 2;
+
+ const auto strides =
+ getAttributeValue(onnx_node, "strides", std::vector<std::int32_t>(num_spatial_dims, 1));
+ if (strides.size() != num_spatial_dims)
+ throw std::runtime_error("AveragePool: attribute 'strides' has incorrect size.");
+
+ const auto kernel_shape = getAttributeValue<std::vector<std::int32_t>>(onnx_node, "kernel_shape");
+ if (kernel_shape.size() != num_spatial_dims)
+ throw std::runtime_error("AveragePool: attribute 'kernel_shape' has incorrect size.");
+
+ std::vector<std::int32_t> padding_before(num_spatial_dims, 0);
+ std::vector<std::int32_t> padding_after(num_spatial_dims, 0);
+ if (const auto *pads_attr = findAttribute(onnx_node, "pads"))
+ {
+ const auto pads = getAttributeValue<std::vector<std::int32_t>>(*pads_attr);
+ if (pads.size() != num_spatial_dims * 2)
+ throw std::runtime_error("AveragePool: attribute 'pads' has incorrect size.");
+ padding_before.assign(pads.cbegin(), std::next(pads.cbegin(), num_spatial_dims));
+ padding_after.assign(std::next(pads.cbegin(), num_spatial_dims), pads.cend());
+ }
+ else
+ {
+ const auto auto_pad = getAttributeValue<std::string>(onnx_node, "auto_pad", "NOTSET");
+ const std::vector<std::int32_t> dilations(num_spatial_dims, 1);
+ inferAutoPadding(auto_pad, input_shape, dilations, strides, kernel_shape, padding_before,
+ padding_after);
+ }
+
+ mir::AvgPool2DOpAttributes attributes;
+ attributes.window = kernel_shape;
+ attributes.strides = strides;
+ attributes.padding_before = padding_before;
+ attributes.padding_after = padding_after;
+ attributes.include_pad = false;
+ attributes.data_format = mir::DataFormat::NCHW;
+ auto result = createOp<mir::ops::AvgPool2DOp>(graph, input, attributes)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertAveragePoolV7(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ const auto count_include_pad = getAttributeValue<int64_t>(onnx_node, "count_include_pad", 0);
+ if (count_include_pad != 0)
+ throw std::runtime_error("Not supported count_include_pad attribute!");
+
+ convertAveragePoolV1(onnx_node, context);
+}
+
+void convertAveragePoolV10(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ const auto ceil_mode = getAttributeValue<int64_t>(onnx_node, "ceil_mode", 0);
+ if (ceil_mode != 0)
+ throw std::runtime_error("Not supported ceil_mode attribute!");
+
+ convertAveragePoolV7(onnx_node, context);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/AveragePool.h b/compiler/mir/src/mir_onnx_importer/Op/AveragePool.h
new file mode 100644
index 000000000..54e406daf
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/AveragePool.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_AVERAGE_POOL_H
+#define MIR_ONNX_OP_AVERAGE_POOL_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertAveragePoolV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertAveragePoolV7(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertAveragePoolV10(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_AVERAGE_POOL_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/BatchNormalization.cpp b/compiler/mir/src/mir_onnx_importer/Op/BatchNormalization.cpp
new file mode 100644
index 000000000..8a6d8cc51
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/BatchNormalization.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BatchNormalization.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+#include "mir/ops/AddOp.h"
+#include "mir/ops/ConstantOp.h"
+#include "mir/ops/MulOp.h"
+#include "mir/ops/ReshapeOp.h"
+
+#include <cmath>
+
+namespace mir_onnx
+{
+
+void convertBatchNormalizationV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ // consumed_inputs attribute not used
+ convertBatchNormalizationV6(onnx_node, context);
+}
+
+void convertBatchNormalizationV6(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ const auto is_test = getAttributeValue<std::int64_t>(onnx_node, "is_test", 0);
+ if (is_test == 0)
+ throw std::runtime_error("Not supported is_test attribute!");
+
+ convertBatchNormalizationV7(onnx_node, context);
+}
+
+void convertBatchNormalizationV7(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ // spatial attribute used only for learning
+
+ convertBatchNormalizationV9(onnx_node, context);
+}
+
+void convertBatchNormalizationV9(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ // momentum attrribute used only for learning
+
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ assert(inputs.size() == 5);
+ auto input = inputs[0];
+ auto scale = inputs[1];
+ auto bias = inputs[2];
+ auto mean = inputs[3];
+ auto var = inputs[4];
+
+ // 1e-05f is the default epsilon.
+ const auto epsilon = getAttributeValue<float>(onnx_node, "epsilon", 1e-05f);
+
+ // Y = (X - mean) * scale / sqrt(var + epsilon) + bias =
+ // = (X + C1) * C2 + bias
+ // We need these to be constants since we are going to change them.
+ // TODO Implement the formula using ops and let the optimizer constant-fold them.
+ auto scale_op = dynamic_cast<mir::ops::ConstantOp *>(scale->getNode());
+ auto mean_op = dynamic_cast<mir::ops::ConstantOp *>(mean->getNode());
+ auto var_op = dynamic_cast<mir::ops::ConstantOp *>(var->getNode());
+
+ if (scale_op == nullptr || mean_op == nullptr || var_op == nullptr)
+ throw std::runtime_error(
+ "BatchNormalization: only constant 'scale', 'mean' and 'variance' inputs are supported.");
+
+ mir::Tensor<float> scale_accessor(scale_op->getValue());
+ mir::Tensor<float> mean_accessor(mean_op->getValue());
+ mir::Tensor<float> var_accessor(var_op->getValue());
+
+ // C1 = -mean
+ for (const auto &idx : mir::ShapeRange(mean_accessor.getShape()))
+ mean_accessor.at(idx) *= -1;
+
+ // C2 = scale / sqrt(var + epsilon)
+ for (const auto &idx : mir::ShapeRange(scale_accessor.getShape()))
+ scale_accessor.at(idx) /= std::sqrt(var_accessor.at(idx) + epsilon);
+
+ assert(mean_accessor.getShape().rank() == 1);
+ auto input_rank = input->getShape().rank();
+ if (input_rank < 2)
+ throw std::runtime_error("Inputs with shape rank < 2 are not supported for batchnorm");
+
+ mir::Shape new_shape(std::vector<std::int32_t>(input_rank, 1));
+
+ new_shape.dim(1) = mean_accessor.getShape().dim(0); // set channel dim
+
+ auto reshaped_mean = createOp<mir::ops::ReshapeOp>(graph, mean, new_shape)->getOutput(0);
+ auto reshaped_scale = createOp<mir::ops::ReshapeOp>(graph, scale, new_shape)->getOutput(0);
+ auto reshaped_bias = createOp<mir::ops::ReshapeOp>(graph, bias, new_shape)->getOutput(0);
+
+ // Y = (X + C1) * C2 + bias
+ auto result = createOp<mir::ops::AddOp>(graph, input, reshaped_mean)->getOutput(0);
+ result = createOp<mir::ops::MulOp>(graph, result, reshaped_scale)->getOutput(0);
+ result = createOp<mir::ops::AddOp>(graph, result, reshaped_bias)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/BatchNormalization.h b/compiler/mir/src/mir_onnx_importer/Op/BatchNormalization.h
new file mode 100644
index 000000000..7c2e37a9c
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/BatchNormalization.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_BATCH_NORMALIZATION_H
+#define MIR_ONNX_OP_BATCH_NORMALIZATION_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertBatchNormalizationV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertBatchNormalizationV6(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertBatchNormalizationV7(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertBatchNormalizationV9(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_BATCH_NORMALIZATION_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Concat.cpp b/compiler/mir/src/mir_onnx_importer/Op/Concat.cpp
new file mode 100644
index 000000000..dbe752647
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Concat.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Concat.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/ops/ConcatOp.h"
+
+namespace mir_onnx
+{
+
+void convertConcatV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ const auto axis = getAttributeValue<int64_t>(onnx_node, "axis", 1);
+
+ auto result = createOp<mir::ops::ConcatOp>(graph, inputs, axis)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertConcatV4(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+ // From version 4 axis attribute is required
+ auto attr = findAttribute(onnx_node, "axis");
+ if (!attr)
+ throw std::runtime_error("Attribute axis is required!");
+ int32_t axis = attr->i();
+
+ auto result = createOp<mir::ops::ConcatOp>(graph, inputs, axis)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Concat.h b/compiler/mir/src/mir_onnx_importer/Op/Concat.h
new file mode 100644
index 000000000..430a2d9e4
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Concat.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_CONCAT_H
+#define MIR_ONNX_OP_CONCAT_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertConcatV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertConcatV4(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_CONCAT_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Constant.cpp b/compiler/mir/src/mir_onnx_importer/Op/Constant.cpp
new file mode 100644
index 000000000..710760ed3
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Constant.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Constant.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/TensorVariant.h"
+#include "mir/ops/ConstantOp.h"
+
+namespace mir_onnx
+{
+
+void convertConstantV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ const auto onnx_tensor = getAttributeValue<onnx::TensorProto>(onnx_node, "value");
+ auto mir_tensor = createTensor(&onnx_tensor);
+
+ auto result = graph->create<mir::ops::ConstantOp>(mir_tensor)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertConstantV9(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ // Since version 9 Constant operation support other types contained in tensor
+ convertConstantV1(onnx_node, context);
+}
+
+void convertConstantV11(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ const auto *value_attr = findAttribute(onnx_node, "value");
+ const auto *sparse_value_attr = findAttribute(onnx_node, "sparse_value");
+ if (value_attr == nullptr && sparse_value_attr == nullptr)
+ throw std::runtime_error("Not enough attributes in Constant operation!");
+
+ if (value_attr != nullptr)
+ return convertConstantV9(onnx_node, context);
+
+ if (sparse_value_attr != nullptr)
+ throw std::runtime_error("Not supported sparse_tensor in Constant operation!");
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Constant.h b/compiler/mir/src/mir_onnx_importer/Op/Constant.h
new file mode 100644
index 000000000..2a4db0fb7
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Constant.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_CONSTANT_H
+#define MIR_ONNX_OP_CONSTANT_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertConstantV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertConstantV9(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertConstantV11(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_CONSTANT_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Conv.cpp b/compiler/mir/src/mir_onnx_importer/Op/Conv.cpp
new file mode 100644
index 000000000..7dc6ce818
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Conv.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Conv.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+#include "ConvPoolHelpers.h"
+
+#include "mir/ops/AddOp.h"
+#include "mir/ops/Conv2DOp.h"
+#include "mir/ops/ReshapeOp.h"
+
+namespace mir_onnx
+{
+
+void convertConvV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ assert(inputs.size() >= 2);
+ auto input = inputs[0];
+ auto kernel = inputs[1];
+
+ auto input_shape = input->getShape();
+ bool conv1d = false;
+ if (input_shape.rank() == 3)
+ {
+ input_shape = {input_shape.dim(0), input_shape.dim(1), input_shape.dim(2), 1};
+ auto reshaped_input = createOp<mir::ops::ReshapeOp>(graph, input, input_shape);
+ input = reshaped_input->getOutput(0);
+ conv1d = true;
+ }
+ else
+ {
+ if (input_shape.rank() != 4)
+ throw std::runtime_error{"Conv is unsupported for tensors with more than 4 dimentions"};
+ }
+
+ constexpr int num_spatial_dims = 2;
+
+ std::vector<int32_t> dilations(num_spatial_dims, 1);
+ if (const auto *dilations_attr = findAttribute(onnx_node, "dilations"))
+ {
+ dilations = getAttributeValue<std::vector<int32_t>>(*dilations_attr);
+ if (conv1d)
+ dilations.emplace_back(1);
+ }
+
+ if (dilations.size() != num_spatial_dims)
+ throw std::runtime_error("Conv: attribute 'dilations' has incorrect size.");
+ if (!std::all_of(dilations.cbegin(), dilations.cend(), [](std::int32_t x) { return x == 1; }))
+ throw std::runtime_error("Conv: attribute 'dilations' has unsupported value.");
+
+ std::vector<int32_t> strides(num_spatial_dims, 1);
+ if (const auto *strides_attr = findAttribute(onnx_node, "strides"))
+ {
+ strides = getAttributeValue<std::vector<int32_t>>(*strides_attr);
+ if (conv1d)
+ strides.emplace_back(1);
+ }
+
+ if (strides.size() != num_spatial_dims)
+ throw std::runtime_error("Conv: attribute 'strides' has incorrect size.");
+
+ // Assuming kernel has OIHW format.
+ if (conv1d)
+ {
+ auto kernel_shape = kernel->getShape();
+ assert(kernel_shape.rank() == 3);
+ kernel_shape = {kernel_shape.dim(0), kernel_shape.dim(1), kernel_shape.dim(2), 1};
+ auto reshaped_kernel = createOp<mir::ops::ReshapeOp>(graph, kernel, kernel_shape);
+ kernel = reshaped_kernel->getOutput(0);
+ }
+
+ std::vector<std::int32_t> kernel_shape{kernel->getShape().dim(2), kernel->getShape().dim(3)};
+ if (const auto *k_shape_attr = findAttribute(onnx_node, "kernel_shape"))
+ {
+ kernel_shape = getAttributeValue<std::vector<std::int32_t>>(*k_shape_attr);
+ if (conv1d)
+ kernel_shape.emplace_back(1);
+ }
+
+ if (kernel_shape.size() != num_spatial_dims)
+ throw std::runtime_error("Conv: attribute 'kernel_shape' has incorrect size.");
+
+ std::vector<std::int32_t> padding_before(num_spatial_dims, 0);
+ std::vector<std::int32_t> padding_after(num_spatial_dims, 0);
+ if (const auto *pads_attr = findAttribute(onnx_node, "pads"))
+ {
+ auto pads = getAttributeValue<std::vector<std::int32_t>>(*pads_attr);
+ if (conv1d)
+ {
+ pads.emplace_back(0);
+ pads.emplace_back(0);
+ }
+
+ if (pads.size() != num_spatial_dims * 2)
+ throw std::runtime_error("Conv: attribute 'pads' has incorrect size.");
+ const auto fixed_pads = fixPads(input_shape, pads, strides, dilations, kernel_shape);
+ padding_before.assign(fixed_pads.cbegin(), std::next(fixed_pads.cbegin(), num_spatial_dims));
+ padding_after.assign(std::next(fixed_pads.cbegin(), num_spatial_dims), fixed_pads.cend());
+ }
+ else
+ {
+ const auto auto_pad = getAttributeValue<std::string>(onnx_node, "auto_pad", "NOTSET");
+ inferAutoPadding(auto_pad, input_shape, dilations, strides, kernel_shape, padding_before,
+ padding_after);
+ }
+
+ const auto group = getAttributeValue<std::int64_t>(onnx_node, "group", 1);
+
+ mir::Conv2DOpAttributes attributes;
+ attributes.strides = strides;
+ attributes.padding_before = padding_before;
+ attributes.padding_after = padding_after;
+ attributes.num_groups = group;
+ attributes.data_format = mir::DataFormat::NCHW;
+
+ std::vector<std::size_t> perm{0, 2, 3, 1}; // OIHW -> OHWI
+ kernel = createOp<mir::ops::TransposeOp>(graph, kernel, perm)->getOutput(0);
+ auto result = createOp<mir::ops::Conv2DOp>(graph, input, kernel, attributes)->getOutput(0);
+
+ if (inputs.size() > 2)
+ {
+ auto bias = inputs[2];
+ bias = createOp<mir::ops::ReshapeOp>(graph, bias, mir::Shape{1, bias->getShape().dim(0), 1, 1})
+ ->getOutput(0);
+ result = createOp<mir::ops::AddOp>(graph, result, bias)->getOutput(0);
+ }
+
+ if (conv1d)
+ {
+ auto output_shape = result->getShape();
+ output_shape.resize(output_shape.rank() - 1);
+ result = createOp<mir::ops::ReshapeOp>(graph, result, output_shape)->getOutput(0);
+ }
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Conv.h b/compiler/mir/src/mir_onnx_importer/Op/Conv.h
new file mode 100644
index 000000000..2af2b8959
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Conv.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_CONV_H
+#define MIR_ONNX_OP_CONV_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertConvV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_CONV_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/ConvTranspose.cpp b/compiler/mir/src/mir_onnx_importer/Op/ConvTranspose.cpp
new file mode 100644
index 000000000..3078a1959
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/ConvTranspose.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ConvTranspose.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+#include "ConvPoolHelpers.h"
+
+#include "mir/TensorUtil.h"
+#include "mir/ops/AddOp.h"
+#include "mir/ops/ConstantOp.h"
+#include "mir/ops/Deconv2DOp.h"
+#include "mir/ops/ReshapeOp.h"
+
+namespace mir_onnx
+{
+
+void convertConvTransposeV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ assert(inputs.size() >= 2);
+ auto input = inputs[0];
+ auto kernel = inputs[1];
+
+ const auto group = getAttributeValue<std::int64_t>(onnx_node, "group", 1);
+ if (group != 1)
+ throw std::runtime_error("ConvTranspose: attribute 'group' has unsupported value.");
+
+ const auto &input_shape = input->getShape();
+ if (input_shape.rank() != 4)
+ throw std::runtime_error("ConvTranspose: only 2-D input is supported.");
+
+ constexpr int num_spatial_dims = 2;
+
+ const auto dilations =
+ getAttributeValue(onnx_node, "dilations", std::vector<std::int32_t>(num_spatial_dims, 1));
+ if (dilations.size() != num_spatial_dims)
+ throw std::runtime_error("ConvTranspose: attribute 'dilations' has incorrect size.");
+ if (!std::all_of(dilations.cbegin(), dilations.cend(), [](std::int32_t x) { return x == 1; }))
+ throw std::runtime_error("ConvTranspose: attribute 'dilations' has unsupported value.");
+
+ const auto strides =
+ getAttributeValue(onnx_node, "strides", std::vector<std::int32_t>(num_spatial_dims, 1));
+ if (strides.size() != num_spatial_dims)
+ throw std::runtime_error("ConvTranspose: attribute 'strides' has incorrect size.");
+
+ const auto output_padding = getAttributeValue(onnx_node, "output_padding",
+ std::vector<std::int32_t>(num_spatial_dims, 0));
+ if (output_padding.size() != num_spatial_dims)
+ throw std::runtime_error("ConvTranspose: attribute 'output_padding' has incorrect size.");
+ if (!std::all_of(output_padding.cbegin(), output_padding.cend(),
+ [](std::int32_t x) { return x == 0; }))
+ throw std::runtime_error("ConvTranspose: attribute 'output_padding' has unsupported value.");
+
+ // Assuming kernel has IOHW format.
+ assert(kernel->getShape().rank() == 4);
+ const auto kernel_size = getAttributeValue(
+ onnx_node, "kernel_shape",
+ std::vector<std::int32_t>{kernel->getShape().dim(2), kernel->getShape().dim(3)});
+ if (kernel_size.size() != num_spatial_dims)
+ throw std::runtime_error("ConvTranspose: attribute 'kernel_shape' has incorrect size.");
+
+ // ONNX IOHW -> MIR HWOI
+ std::vector<std::size_t> perm{2, 3, 1, 0}; // OIHW -> OHWI
+ kernel = createOp<mir::ops::TransposeOp>(graph, kernel, perm)->getOutput(0);
+
+ mir::Operation::Output *result;
+ if (const auto *output_shape_attr = findAttribute(onnx_node, "output_shape"))
+ {
+ const auto output_size = getAttributeValue<std::vector<std::int32_t>>(*output_shape_attr);
+ if (output_size.size() != num_spatial_dims)
+ throw std::runtime_error("ConvTranspose: attribute 'output_shape' has incorrect size.");
+ const mir::Shape output_shape{input_shape.dim(0), kernel->getShape().dim(2), output_size[0],
+ output_size[1]};
+ mir::Deconv2DOpAttributes attributes;
+ attributes.strides = strides;
+ attributes.data_format = mir::DataFormat::NCHW;
+ attributes.padding_type = mir::ops::PaddingType::SameUpper;
+ result = createOp<mir::ops::DeConv2DOp>(graph, input, kernel, attributes, output_shape)
+ ->getOutput(0);
+ }
+ else
+ {
+ // TODO This code was not tested.
+ throw std::runtime_error(
+ "ConvTranspose: absence of attribute 'output_shape' is not supported.");
+ std::vector<std::int32_t> padding_before(num_spatial_dims, 0);
+ std::vector<std::int32_t> padding_after(num_spatial_dims, 0);
+ if (const auto *pads_attr = findAttribute(onnx_node, "pads"))
+ {
+ const auto pads = getAttributeValue<std::vector<std::int32_t>>(*pads_attr);
+ if (pads.size() != num_spatial_dims * 2)
+ throw std::runtime_error("ConvTranspose: attribute 'pads' has incorrect size.");
+ padding_before.assign(pads.cbegin(), std::next(pads.cbegin(), num_spatial_dims));
+ padding_after.assign(std::next(pads.cbegin(), num_spatial_dims), pads.cend());
+ }
+ else
+ {
+ const auto auto_pad = getAttributeValue<std::string>(onnx_node, "auto_pad", "NOTSET");
+ inferAutoPadding(auto_pad, input_shape, dilations, strides, kernel_size, padding_before,
+ padding_after);
+ }
+ mir::Deconv2DOpAttributes attributes;
+ attributes.strides = strides;
+ attributes.padding_before = padding_before;
+ attributes.padding_after = padding_after;
+ attributes.data_format = mir::DataFormat::NCHW;
+ result = createOp<mir::ops::DeConv2DOp>(graph, input, kernel, attributes)->getOutput(0);
+ }
+
+ if (inputs.size() > 2)
+ {
+ auto bias = inputs[2];
+ bias = createOp<mir::ops::ReshapeOp>(graph, bias, mir::Shape{1, bias->getShape().dim(0), 1, 1})
+ ->getOutput(0);
+ result = createOp<mir::ops::AddOp>(graph, result, bias)->getOutput(0);
+ }
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/ConvTranspose.h b/compiler/mir/src/mir_onnx_importer/Op/ConvTranspose.h
new file mode 100644
index 000000000..d203dc6c1
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/ConvTranspose.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_CONV_TRANSPOSE_H
+#define MIR_ONNX_OP_CONV_TRANSPOSE_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertConvTransposeV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_CONV_TRANSPOSE_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Div.cpp b/compiler/mir/src/mir_onnx_importer/Op/Div.cpp
new file mode 100644
index 000000000..40620169a
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Div.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Div.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/ops/DivOp.h"
+
+namespace mir_onnx
+{
+
+void convertDivV7(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+
+ mir::Graph *graph = context->getGraph();
+
+ auto result = createOp<mir::ops::DivOp>(graph, inputs[0], inputs[1])->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Div.h b/compiler/mir/src/mir_onnx_importer/Op/Div.h
new file mode 100644
index 000000000..cdc254fb8
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Div.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_DIV_H
+#define MIR_ONNX_OP_DIV_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertDivV7(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_DIV_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Dropout.cpp b/compiler/mir/src/mir_onnx_importer/Op/Dropout.cpp
new file mode 100644
index 000000000..ef6972784
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Dropout.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dropout.h"
+
+#include "AttributeHelpers.h"
+
+namespace mir_onnx
+{
+
+void convertDropoutV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ // consumed_inputs attribute not used
+ convertDropoutV6(onnx_node, context);
+}
+
+void convertDropoutV6(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ const auto is_test = getAttributeValue<std::int64_t>(onnx_node, "is_test", 0);
+ if (is_test == 0)
+ throw std::runtime_error("Not supported is_test attribute!");
+
+ convertDropoutV10(onnx_node, context);
+}
+
+void convertDropoutV7(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertDropoutV10(onnx_node, context);
+}
+
+void convertDropoutV10(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+
+ // ratio attribute not used
+
+ // This is a no-op in inference mode.
+ context->setNodeOutputs(onnx_node, {inputs[0]});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Dropout.h b/compiler/mir/src/mir_onnx_importer/Op/Dropout.h
new file mode 100644
index 000000000..9a90ac79b
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Dropout.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_DROPOUT_H
+#define MIR_ONNX_OP_DROPOUT_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertDropoutV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertDropoutV6(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertDropoutV7(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertDropoutV10(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_DROPOUT_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Equal.cpp b/compiler/mir/src/mir_onnx_importer/Op/Equal.cpp
new file mode 100644
index 000000000..242389eb5
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Equal.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Equal.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/ops/EqualOp.h"
+
+namespace mir_onnx
+{
+
+void convertEqualV11(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ auto result = createOp<mir::ops::EqualOp>(graph, inputs[0], inputs[1])->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertEqualV7(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ // Other type constraints
+ convertEqualV11(onnx_node, context);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Equal.h b/compiler/mir/src/mir_onnx_importer/Op/Equal.h
new file mode 100644
index 000000000..0672cd661
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Equal.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_EQUAL_H
+#define MIR_ONNX_OP_EQUAL_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertEqualV11(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertEqualV7(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_EQUAL_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Expand.cpp b/compiler/mir/src/mir_onnx_importer/Op/Expand.cpp
new file mode 100644
index 000000000..40002dfa9
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Expand.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Expand.h"
+
+#include "ONNXHelpers.h"
+
+#include "mir/ops/BroadcastOp.h"
+
+namespace mir_onnx
+{
+
+void convertExpandV8(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ if (inputs[1]->getNode()->getType() != mir::Operation::Type::constant)
+ {
+ throw std::runtime_error{"Expand with non-constant input shape is not supported"};
+ }
+
+ auto target_shape = constantToShape(static_cast<mir::ops::ConstantOp *>(inputs[1]->getNode()));
+
+ auto *result = createOp<mir::ops::BroadcastOp>(graph, inputs[0], target_shape)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Expand.h b/compiler/mir/src/mir_onnx_importer/Op/Expand.h
new file mode 100644
index 000000000..35f7af407
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Expand.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_EXPAND_H
+#define MIR_ONNX_OP_EXPAND_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertExpandV8(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_EXPAND_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Flatten.cpp b/compiler/mir/src/mir_onnx_importer/Op/Flatten.cpp
new file mode 100644
index 000000000..dfad6ddbf
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Flatten.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Flatten.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/ops/ReshapeOp.h"
+
+namespace mir_onnx
+{
+
+void convertFlattenV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ const auto axis = getAttributeValue<int64_t>(onnx_node, "axis", 1);
+ assert(inputs.size() == 1);
+ const auto &in_shape = inputs[0]->getShape();
+ assert(axis <= in_shape.rank()); // A tensor of rank >= axis
+ int32_t first_dim = 1, second_dim = 1;
+ int32_t dim = 0;
+
+ for (; dim < axis; dim++)
+ first_dim *= in_shape.dim(dim);
+
+ for (; dim < in_shape.rank(); dim++)
+ second_dim *= in_shape.dim(dim);
+
+ mir::Shape out_shape({first_dim, second_dim}); // Output 2D tensor
+
+ auto result = createOp<mir::ops::ReshapeOp>(graph, inputs[0], out_shape)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertFlattenV9(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ // Other type constraints
+ convertFlattenV1(onnx_node, context);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Flatten.h b/compiler/mir/src/mir_onnx_importer/Op/Flatten.h
new file mode 100644
index 000000000..174a8d906
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Flatten.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_FLATTEN_H
+#define MIR_ONNX_OP_FLATTEN_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertFlattenV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertFlattenV9(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_FLATTEN_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Gather.cpp b/compiler/mir/src/mir_onnx_importer/Op/Gather.cpp
new file mode 100644
index 000000000..fa3746c67
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Gather.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Gather.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/ops/GatherOp.h"
+
+namespace mir_onnx
+{
+
+void convertGatherV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ // 0 is the default axis number.
+ const auto axis = getAttributeValue<std::int64_t>(onnx_node, "axis", 0);
+
+ auto result = createOp<mir::ops::GatherOp>(graph, inputs[0], inputs[1], axis)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Gather.h b/compiler/mir/src/mir_onnx_importer/Op/Gather.h
new file mode 100644
index 000000000..c4308d2be
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Gather.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_GATHER_H
+#define MIR_ONNX_OP_GATHER_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertGatherV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_GATHER_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Gemm.cpp b/compiler/mir/src/mir_onnx_importer/Op/Gemm.cpp
new file mode 100644
index 000000000..1e0759dda
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Gemm.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Gemm.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/TensorUtil.h"
+
+#include "mir/ops/AddOp.h"
+#include "mir/ops/ConstantOp.h"
+#include "mir/ops/FullyConnectedOp.h"
+#include "mir/ops/MulOp.h"
+#include "mir/ops/ReshapeOp.h"
+#include "mir/ops/TransposeOp.h"
+
+namespace mir_onnx
+{
+
+static void convertGemm(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ assert(inputs.size() == 2 || inputs.size() == 3);
+
+ auto a = inputs[0];
+ auto b = inputs[1];
+ auto c = inputs.size() > 2 ? inputs[2] : nullptr;
+
+ // 1.0f is the default factor.
+ const auto alpha_val = getAttributeValue<float>(onnx_node, "alpha", 1.0f);
+ const auto beta_val = getAttributeValue<float>(onnx_node, "beta", 1.0f);
+
+ // 0 means that no transpose is needed. It is the default value.
+ const auto trans_a = getAttributeValue<std::int64_t>(onnx_node, "transA", 0);
+ const auto trans_b = getAttributeValue<std::int64_t>(onnx_node, "transB", 0);
+
+ // Transpose the A and B matrices as needed.
+ if (trans_a)
+ a = createOp<mir::ops::TransposeOp>(graph, a, std::vector<std::size_t>{1, 0})->getOutput(0);
+ if (trans_b)
+ b = createOp<mir::ops::TransposeOp>(graph, b, std::vector<std::size_t>{1, 0})->getOutput(0);
+
+ // Calculate A * B.
+ auto ab = createOp<mir::ops::FullyConnectedOp>(graph, a, b)->getOutput(0);
+
+ // Multiply A * B by the constant factor.
+ if (alpha_val != 1.0f)
+ {
+ mir::TensorVariant alpha_tensor({mir::DataType::FLOAT32, {}}, &alpha_val);
+ auto alpha = createOp<mir::ops::ConstantOp>(graph, alpha_tensor)->getOutput(0);
+ ab = createOp<mir::ops::MulOp>(graph, alpha, ab)->getOutput(0);
+ }
+
+ // If there are no third input, node is simple A*B multiplication
+ if (!c)
+ {
+ context->setNodeOutputs(onnx_node, {ab});
+ return;
+ }
+
+ // Multiply C by the constant factor.
+ if (beta_val != 1.0f)
+ {
+ mir::TensorVariant beta_tensor({mir::DataType::FLOAT32, {}}, &beta_val);
+ auto beta = createOp<mir::ops::ConstantOp>(graph, beta_tensor)->getOutput(0);
+ c = createOp<mir::ops::MulOp>(graph, beta, c)->getOutput(0);
+ }
+
+ // Calculate the result: alpha * A * B + beta * C.
+ auto result = createOp<mir::ops::AddOp>(graph, ab, c)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertGemmV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ return convertGemm(onnx_node, context);
+}
+
+void convertGemmV6(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ // This version differs from V1: in description of C input (redundant text "can be inplace.")
+ return convertGemm(onnx_node, context);
+}
+
+void convertGemmV7(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ // This version differs from V6: removed "broadcast" atribute
+ return convertGemm(onnx_node, context);
+}
+
+void convertGemmV9(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ // This version differs from V7: added more supported types
+ return convertGemm(onnx_node, context);
+}
+
+void convertGemmV11(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ // This operation differs from V11: input C is optional
+ return convertGemm(onnx_node, context);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Gemm.h b/compiler/mir/src/mir_onnx_importer/Op/Gemm.h
new file mode 100644
index 000000000..d87a36e7b
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Gemm.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_GEMM_H
+#define MIR_ONNX_OP_GEMM_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertGemmV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertGemmV6(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertGemmV7(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertGemmV9(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertGemmV11(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_GEMM_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/GlobalAveragePool.cpp b/compiler/mir/src/mir_onnx_importer/Op/GlobalAveragePool.cpp
new file mode 100644
index 000000000..379c8b596
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/GlobalAveragePool.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GlobalAveragePool.h"
+
+#include "ONNXHelpers.h"
+
+#include "mir/ops/AvgPool2DOp.h"
+
+namespace mir_onnx
+{
+
+void convertGlobalAveragePoolV2(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ assert(inputs.size() == 1);
+ auto input = inputs[0];
+
+ const auto &input_shape = input->getShape();
+ if (input_shape.rank() != 4)
+ throw std::runtime_error("GlobalAveragePool: only 2-D input is supported.");
+
+ // GlobalAveragePool is equivalent to AveragePool with kernel size equal
+ // to the spatial dimension of input tensor.
+ const std::vector<std::int32_t> window_size{input->getShape().dim(2), input->getShape().dim(3)};
+ mir::AvgPool2DOpAttributes attributes;
+ attributes.window = window_size;
+ attributes.data_format = mir::DataFormat::NCHW;
+
+ auto result = createOp<mir::ops::AvgPool2DOp>(graph, input, attributes)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/GlobalAveragePool.h b/compiler/mir/src/mir_onnx_importer/Op/GlobalAveragePool.h
new file mode 100644
index 000000000..b2fb9b8c9
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/GlobalAveragePool.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_GLOBAL_AVERAGE_POOL_H
+#define MIR_ONNX_OP_GLOBAL_AVERAGE_POOL_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertGlobalAveragePoolV2(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_GLOBAL_AVERAGE_POOL_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Greater.cpp b/compiler/mir/src/mir_onnx_importer/Op/Greater.cpp
new file mode 100644
index 000000000..deaf96d4b
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Greater.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Greater.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/ops/GreaterOp.h"
+
+namespace mir_onnx
+{
+
+static void convertGreaterVGeneric(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ auto result = createOp<mir::ops::GreaterOp>(graph, inputs[0], inputs[1])->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertGreaterV7(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertGreaterVGeneric(onnx_node, context);
+}
+
+void convertGreaterV9(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertGreaterVGeneric(onnx_node, context);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Greater.h b/compiler/mir/src/mir_onnx_importer/Op/Greater.h
new file mode 100644
index 000000000..3b6a44f33
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Greater.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_GREATER_H
+#define MIR_ONNX_OP_GREATER_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertGreaterV7(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertGreaterV9(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_GREATER_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Identity.cpp b/compiler/mir/src/mir_onnx_importer/Op/Identity.cpp
new file mode 100644
index 000000000..6db70ffcd
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Identity.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Identity.h"
+
+namespace mir_onnx
+{
+
+void convertIdentityV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ const auto inputs = context->getNodeInputs(onnx_node);
+ assert(inputs.size() == 1);
+
+ context->setNodeOutputs(onnx_node, {inputs[0]});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Identity.h b/compiler/mir/src/mir_onnx_importer/Op/Identity.h
new file mode 100644
index 000000000..ea63bab4a
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Identity.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_IDENTITY_H
+#define MIR_ONNX_OP_IDENTITY_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertIdentityV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_IDENTITY_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Less.cpp b/compiler/mir/src/mir_onnx_importer/Op/Less.cpp
new file mode 100644
index 000000000..44f5d8cf4
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Less.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Less.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/ops/LessOp.h"
+
+namespace mir_onnx
+{
+
+static void convertLessGeneric(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ auto result = createOp<mir::ops::LessOp>(graph, inputs[0], inputs[1])->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertLessV7(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertLessGeneric(onnx_node, context);
+}
+
+void convertLessV9(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertLessGeneric(onnx_node, context);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Less.h b/compiler/mir/src/mir_onnx_importer/Op/Less.h
new file mode 100644
index 000000000..682c08725
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Less.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_LESS_H
+#define MIR_ONNX_OP_LESS_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertLessV7(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertLessV9(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_LESS_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/MatMul.cpp b/compiler/mir/src/mir_onnx_importer/Op/MatMul.cpp
new file mode 100644
index 000000000..6d8ea6b83
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/MatMul.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MatMul.h"
+
+#include "ONNXHelpers.h"
+
+#include "mir/ops/FullyConnectedOp.h"
+
+namespace mir_onnx
+{
+
+void convertMatMulV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ assert(inputs.size() == 2);
+ auto A = inputs[0];
+ auto B = inputs[1];
+ // MatMul multiply N-dimentional matrix
+ // FullyConnected layer multiply only 2-dimentional matrix
+ if (A->getShape().rank() != 2 || B->getShape().rank() != 2)
+ throw std::runtime_error("Supported only 2D matrix multiplying!");
+ // Calculate A * B.
+ auto result = createOp<mir::ops::FullyConnectedOp>(graph, A, B)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertMatMulV9(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ // Other type constraints
+ convertMatMulV1(onnx_node, context);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/MatMul.h b/compiler/mir/src/mir_onnx_importer/Op/MatMul.h
new file mode 100644
index 000000000..97e641ebb
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/MatMul.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_MATMUL_H
+#define MIR_ONNX_OP_MATMUL_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertMatMulV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertMatMulV9(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_MATMUL_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Max.cpp b/compiler/mir/src/mir_onnx_importer/Op/Max.cpp
new file mode 100644
index 000000000..d4c7d1775
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Max.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Max.h"
+
+#include "ONNXHelpers.h"
+
+#include "mir/ops/MaxOp.h"
+
+namespace mir_onnx
+{
+
+static void convertMaxGeneric(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ if (inputs.size() != 2)
+ {
+ throw std::runtime_error{"Unsupported number of inputs for Max operator"};
+ }
+ mir::Graph *graph = context->getGraph();
+ auto result = createOp<mir::ops::MaxOp>(graph, inputs[0], inputs[1])->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertMaxV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertMaxGeneric(onnx_node, context);
+}
+
+void convertMaxV6(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertMaxGeneric(onnx_node, context);
+}
+
+void convertMaxV8(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertMaxGeneric(onnx_node, context);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Max.h b/compiler/mir/src/mir_onnx_importer/Op/Max.h
new file mode 100644
index 000000000..1f2754b62
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Max.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_MAX_H
+#define MIR_ONNX_OP_MAX_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertMaxV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertMaxV6(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertMaxV8(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_MAX_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/MaxPool.cpp b/compiler/mir/src/mir_onnx_importer/Op/MaxPool.cpp
new file mode 100644
index 000000000..53e6e1556
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/MaxPool.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MaxPool.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+#include "ConvPoolHelpers.h"
+
+#include "mir/ops/MaxPool2DOp.h"
+
+namespace mir_onnx
+{
+
+void convertMaxPoolV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ assert(inputs.size() == 1);
+ auto input = inputs[0];
+
+ const auto &input_shape = input->getShape();
+ if (input_shape.rank() != 4)
+ throw std::runtime_error("MaxPool: only 2-D input is supported.");
+
+ constexpr int num_spatial_dims = 2;
+
+ const auto strides =
+ getAttributeValue(onnx_node, "strides", std::vector<std::int32_t>(num_spatial_dims, 1));
+ if (strides.size() != num_spatial_dims)
+ throw std::runtime_error("MaxPool: attribute 'strides' has incorrect size.");
+
+ const auto kernel_shape = getAttributeValue<std::vector<std::int32_t>>(onnx_node, "kernel_shape");
+ if (kernel_shape.size() != num_spatial_dims)
+ throw std::runtime_error("MaxPool: attribute 'kernel_shape' has incorrect size.");
+
+ std::vector<std::int32_t> padding_before;
+ std::vector<std::int32_t> padding_after;
+ if (const auto *pads_attr = findAttribute(onnx_node, "pads"))
+ {
+ const auto pads = getAttributeValue<std::vector<std::int32_t>>(*pads_attr);
+ if (pads.size() != num_spatial_dims * 2)
+ throw std::runtime_error("MaxPool: attribute 'pads' has incorrect size.");
+ padding_before.assign(pads.cbegin(), std::next(pads.cbegin(), num_spatial_dims));
+ padding_after.assign(std::next(pads.cbegin(), num_spatial_dims), pads.cend());
+ }
+ else
+ {
+ const auto auto_pad = getAttributeValue<std::string>(onnx_node, "auto_pad", "NOTSET");
+ const std::vector<std::int32_t> dilations(num_spatial_dims, 1);
+ inferAutoPadding(auto_pad, input_shape, dilations, strides, kernel_shape, padding_before,
+ padding_after);
+ }
+
+ mir::MaxPool2DOpAttributes attributes;
+ attributes.window = kernel_shape;
+ attributes.strides = strides;
+ attributes.padding_before = padding_before;
+ attributes.padding_after = padding_after;
+ attributes.data_format = mir::DataFormat::NCHW;
+ auto result = createOp<mir::ops::MaxPool2DOp>(graph, input, attributes)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertMaxPoolV8(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ const auto storage_order = getAttributeValue<int64_t>(onnx_node, "storage_order", 0);
+ if (storage_order != 0)
+ throw std::runtime_error("Not supported storage order attribute!");
+
+ convertMaxPoolV1(onnx_node, context);
+}
+
+void convertMaxPoolV10(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ const auto ceil_mode = getAttributeValue<int64_t>(onnx_node, "ceil_mode", 0);
+ if (ceil_mode != 0)
+ throw std::runtime_error("Not supported ceil_mode attribute!");
+
+ const auto *dilations = findAttribute(onnx_node, "dilations");
+ if (dilations != nullptr)
+ {
+ // check default (=1) dilations on each spatial axis
+ for (auto index = 0; index < dilations->ints_size(); index++)
+ if (dilations->ints(index) != 1)
+ throw std::runtime_error("Not supported dilations in MaxPool operation!");
+ }
+
+ convertMaxPoolV8(onnx_node, context);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/MaxPool.h b/compiler/mir/src/mir_onnx_importer/Op/MaxPool.h
new file mode 100644
index 000000000..85bd9cf1a
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/MaxPool.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_MAX_POOL_H
+#define MIR_ONNX_OP_MAX_POOL_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertMaxPoolV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertMaxPoolV8(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertMaxPoolV10(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_MAX_POOL_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Mul.cpp b/compiler/mir/src/mir_onnx_importer/Op/Mul.cpp
new file mode 100644
index 000000000..dbfdd4950
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Mul.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Mul.h"
+
+#include "ONNXHelpers.h"
+
+#include "mir/ops/MulOp.h"
+
+namespace mir_onnx
+{
+
+void convertMulV7(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+ auto result = createOp<mir::ops::MulOp>(graph, inputs[0], inputs[1])->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Mul.h b/compiler/mir/src/mir_onnx_importer/Op/Mul.h
new file mode 100644
index 000000000..58738c81d
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Mul.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_MUL_H
+#define MIR_ONNX_OP_MUL_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertMulV7(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_MUL_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Pad.cpp b/compiler/mir/src/mir_onnx_importer/Op/Pad.cpp
new file mode 100644
index 000000000..08d7c4ab5
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Pad.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Pad.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/ops/PadOp.h"
+
+namespace mir_onnx
+{
+
+void convertPadAttrName(const std::string &pad_attr_name, const onnx::NodeProto &onnx_node,
+ ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ assert(inputs.size() == 1);
+ auto input = inputs[0];
+
+ // 0.0f is the default value to be filled into padded cells.
+ const auto value = getAttributeValue<float>(onnx_node, "value", 0.0f);
+ const auto pads = getAttributeValue<std::vector<std::int64_t>>(onnx_node, pad_attr_name);
+ // "constant" is the default mode.
+ const auto mode = getAttributeValue<std::string>(onnx_node, "mode", "constant");
+ if (mode != "constant")
+ throw std::runtime_error("Not supported Pad mode attribute!");
+
+ const int num_dims = input->getShape().rank();
+ assert(static_cast<int>(pads.size()) == num_dims * 2);
+ mir::PadOpAttributes attributes(num_dims);
+ for (int i = 0; i < num_dims; i++)
+ {
+ attributes.padding_before[i] = pads[i];
+ attributes.padding_after[i] = pads[num_dims + i];
+ }
+
+ attributes.padding_value = value;
+
+ auto result = createOp<mir::ops::PadOp>(graph, input, attributes)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertPadV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertPadAttrName("paddings", onnx_node, context);
+}
+
+void convertPadV2(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertPadAttrName("pads", onnx_node, context);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Pad.h b/compiler/mir/src/mir_onnx_importer/Op/Pad.h
new file mode 100644
index 000000000..a0731ae4c
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Pad.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_PAD_H
+#define MIR_ONNX_OP_PAD_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertPadV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertPadV2(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_PAD_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Reciprocal.cpp b/compiler/mir/src/mir_onnx_importer/Op/Reciprocal.cpp
new file mode 100644
index 000000000..b063d4b8c
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Reciprocal.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Reciprocal.h"
+
+#include "ONNXHelpers.h"
+
+#include "mir/ops/ConstantOp.h"
+#include "mir/ops/DivOp.h"
+
+namespace mir_onnx
+{
+
+static void convertReciprocal(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ assert(inputs.size() == 1);
+ auto input = inputs[0];
+
+ const float one_value = 1.0f;
+ mir::TensorVariant one_tensor({mir::DataType::FLOAT32, {}}, &one_value);
+ auto one = createOp<mir::ops::ConstantOp>(graph, one_tensor)->getOutput(0);
+ auto result = createOp<mir::ops::DivOp>(graph, input, one)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertReciprocalV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertReciprocal(onnx_node, context);
+}
+
+void convertReciprocalV6(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertReciprocal(onnx_node, context);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Reciprocal.h b/compiler/mir/src/mir_onnx_importer/Op/Reciprocal.h
new file mode 100644
index 000000000..747623ab5
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Reciprocal.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_RECIPROCAL_H
+#define MIR_ONNX_OP_RECIPROCAL_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertReciprocalV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertReciprocalV6(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_RECIPROCAL_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/ReduceMean.cpp b/compiler/mir/src/mir_onnx_importer/Op/ReduceMean.cpp
new file mode 100644
index 000000000..ec43bffb4
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/ReduceMean.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReduceMean.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/ops/ReduceMeanOp.h"
+
+#include <numeric>
+
+namespace mir_onnx
+{
+
+void convertReduceMeanV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ const auto inputs = context->getNodeInputs(onnx_node);
+ assert(inputs.size() == 1);
+
+ const auto axes = getAttributeValue<std::vector<std::int64_t>>(onnx_node, "axes");
+ const auto keepdims = getAttributeValue<int64_t>(onnx_node, "keepdims", 1);
+
+ std::vector<int32_t> reduce_dims;
+ if (axes.empty())
+ { // reduce over all dimensions
+ reduce_dims.resize(inputs[0]->getShape().rank());
+ std::iota(reduce_dims.begin(), reduce_dims.end(), 0);
+ }
+ else
+ {
+ auto rank = inputs[0]->getShape().rank();
+
+ std::transform(axes.begin(), axes.end(), std::back_inserter(reduce_dims),
+ [rank](int64_t axis) { return axis < 0 ? axis + rank : axis; });
+ }
+ // Keep the reduced dimension or not, default 1 mean keep reduced dimension.
+ bool keep_dims = static_cast<bool>(keepdims);
+
+ mir::Graph *graph = context->getGraph();
+ auto result =
+ createOp<mir::ops::ReduceMeanOp>(graph, inputs[0], reduce_dims, keep_dims)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/ReduceMean.h b/compiler/mir/src/mir_onnx_importer/Op/ReduceMean.h
new file mode 100644
index 000000000..3553c96b5
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/ReduceMean.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_REDUCEMEAN_H
+#define MIR_ONNX_OP_REDUCEMEAN_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertReduceMeanV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_REDUCEMEAN_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Relu.cpp b/compiler/mir/src/mir_onnx_importer/Op/Relu.cpp
new file mode 100644
index 000000000..72424e847
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Relu.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Relu.h"
+
+#include "ONNXHelpers.h"
+
+#include "mir/ops/ReluOp.h"
+
+namespace mir_onnx
+{
+
+static void convertRelu(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+ assert(inputs.size() == 1);
+ auto result = createOp<mir::ops::ReluOp>(graph, inputs[0])->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertReluV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertRelu(onnx_node, context);
+}
+
+void convertReluV6(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertRelu(onnx_node, context);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Relu.h b/compiler/mir/src/mir_onnx_importer/Op/Relu.h
new file mode 100644
index 000000000..7159f0add
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Relu.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_RELU_H
+#define MIR_ONNX_OP_RELU_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertReluV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertReluV6(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_RELU_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Reshape.cpp b/compiler/mir/src/mir_onnx_importer/Op/Reshape.cpp
new file mode 100644
index 000000000..5cd4985e2
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Reshape.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Reshape.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/Tensor.h"
+#include "mir/ShapeRange.h"
+
+#include "mir/ops/ConstantOp.h"
+#include "mir/ops/ReshapeOp.h"
+
+namespace mir_onnx
+{
+
+void convertReshapeV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+ // consumed_inputs attribute not used
+ const auto *shape_attr = findAttribute(onnx_node, "shape");
+ if (shape_attr && shape_attr->ints_size() > 0)
+ {
+ mir::Shape in_shape = inputs[0]->getShape();
+ mir::Shape out_shape(shape_attr->ints_size());
+ for (int32_t index = 0; index < out_shape.rank(); index++)
+ {
+ const auto dim_value = shape_attr->ints(index);
+ if (dim_value == 0)
+ out_shape.dim(index) = in_shape.dim(index);
+ else
+ out_shape.dim(index) = dim_value;
+ }
+
+ auto result = createOp<mir::ops::ReshapeOp>(graph, inputs[0], out_shape)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+ }
+ else // dimension value is unchanged
+ {
+ context->setNodeOutputs(onnx_node, {inputs[0]});
+ }
+}
+
+void convertReshapeV5(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+ // The original shape
+ const auto &in_shape = inputs[0]->getShape();
+
+ // Input tensor describing the new shape
+ auto *op = dynamic_cast<mir::ops::ConstantOp *>(inputs[1]->getNode());
+ assert(op && "We support only constant shape input");
+ auto shape_tensor = op->getValue();
+ mir::Shape shape_tensor_shape = (shape_tensor).getShape();
+ assert(shape_tensor_shape.rank() == 1);
+ // The rank of the new shape
+ auto cnt = shape_tensor_shape.numElements();
+ // The vector to build the new shape from
+ std::vector<int32_t> shape_vector(cnt);
+ mir::ShapeRange out_range(shape_tensor_shape);
+ mir::Tensor<int64_t> tensor_accessor(shape_tensor);
+
+ int i = 0;
+ for (auto idx : out_range)
+ {
+ if (tensor_accessor.at(idx) == 0)
+ shape_vector[i] = in_shape.dim(i);
+ else if (tensor_accessor.at(idx) == -1)
+ shape_vector[i] = mir::Shape::autoDim;
+ else
+ shape_vector[i] = tensor_accessor.at(idx);
+ i++;
+ }
+ auto out_shape = mir::Shape(shape_vector);
+ auto result = createOp<mir::ops::ReshapeOp>(graph, inputs[0], out_shape)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Reshape.h b/compiler/mir/src/mir_onnx_importer/Op/Reshape.h
new file mode 100644
index 000000000..4ebbcb7a7
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Reshape.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_RESHAPE_H
+#define MIR_ONNX_OP_RESHAPE_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertReshapeV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertReshapeV5(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_RESHAPE_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Shape.cpp b/compiler/mir/src/mir_onnx_importer/Op/Shape.cpp
new file mode 100644
index 000000000..8cc250b6e
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Shape.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Shape.h"
+
+#include "ONNXHelpers.h"
+
+#include "mir/TensorVariant.h"
+
+#include "mir/ops/ConstantOp.h"
+
+namespace mir_onnx
+{
+
+void convertShapeV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+ const auto &input_shape = inputs[0]->getShape();
+ int size = input_shape.rank();
+ mir::Shape output_shape{size};
+ std::vector<int64_t> data(static_cast<std::size_t>(size));
+ for (int i = 0; i < size; i++)
+ {
+ data[i] = input_shape.dim(i);
+ }
+ mir::TensorVariant tensor({mir::DataType::INT64, output_shape}, data.data());
+ auto result = createOp<mir::ops::ConstantOp>(graph, tensor)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Shape.h b/compiler/mir/src/mir_onnx_importer/Op/Shape.h
new file mode 100644
index 000000000..e427d0330
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Shape.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_SHAPE_H
+#define MIR_ONNX_OP_SHAPE_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertShapeV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_SHAPE_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Sigmoid.cpp b/compiler/mir/src/mir_onnx_importer/Op/Sigmoid.cpp
new file mode 100644
index 000000000..3db547186
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Sigmoid.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Sigmoid.h"
+
+#include "ONNXHelpers.h"
+
+#include "mir/ops/SigmoidOp.h"
+
+namespace mir_onnx
+{
+
+static void convertSigmoid(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+ assert(inputs.size() == 1);
+ auto result = createOp<mir::ops::SigmoidOp>(graph, inputs[0])->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertSigmoidV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertSigmoid(onnx_node, context);
+}
+
+void convertSigmoidV6(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertSigmoid(onnx_node, context);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Sigmoid.h b/compiler/mir/src/mir_onnx_importer/Op/Sigmoid.h
new file mode 100644
index 000000000..e2d85298f
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Sigmoid.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_SIGMOID_H
+#define MIR_ONNX_OP_SIGMOID_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertSigmoidV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertSigmoidV6(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_SIGMOID_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Softmax.cpp b/compiler/mir/src/mir_onnx_importer/Op/Softmax.cpp
new file mode 100644
index 000000000..1a2ca04ae
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Softmax.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Softmax.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/ops/SoftmaxOp.h"
+
+namespace mir_onnx
+{
+
+void convertSoftmaxV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ // 1 is the default axis number.
+ const auto axis = getAttributeValue<std::int64_t>(onnx_node, "axis", 1);
+
+ auto result = createOp<mir::ops::SoftmaxOp>(graph, inputs[0], axis)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Softmax.h b/compiler/mir/src/mir_onnx_importer/Op/Softmax.h
new file mode 100644
index 000000000..23d14c123
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Softmax.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_SOFTMAX_H
+#define MIR_ONNX_OP_SOFTMAX_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertSoftmaxV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_SOFTMAX_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Sqrt.cpp b/compiler/mir/src/mir_onnx_importer/Op/Sqrt.cpp
new file mode 100644
index 000000000..70ef252fe
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Sqrt.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Sqrt.h"
+
+#include "ONNXHelpers.h"
+
+#include "mir/ops/SqrtOp.h"
+
+namespace mir_onnx
+{
+
+static void convertSqrt(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+ assert(inputs.size() == 1);
+ auto result = createOp<mir::ops::SqrtOp>(graph, inputs[0])->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertSqrtV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertSqrt(onnx_node, context);
+}
+
+void convertSqrtV6(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertSqrt(onnx_node, context);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Sqrt.h b/compiler/mir/src/mir_onnx_importer/Op/Sqrt.h
new file mode 100644
index 000000000..51815c93c
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Sqrt.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_SQRT_H
+#define MIR_ONNX_OP_SQRT_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertSqrtV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertSqrtV6(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_SQRT_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Sub.cpp b/compiler/mir/src/mir_onnx_importer/Op/Sub.cpp
new file mode 100644
index 000000000..0c3251909
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Sub.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Sub.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/ops/SubOp.h"
+
+namespace mir_onnx
+{
+
+void convertSubV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ // consumed_inputs attribute not used
+ convertSubV6(onnx_node, context);
+}
+
+void convertSubV6(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ // broadcast attribute not used
+ const auto *axis = findAttribute(onnx_node, "axis");
+ if (axis != nullptr)
+ throw std::runtime_error("Not supported axis attribute in Sub operation!");
+
+ convertSubV7(onnx_node, context);
+}
+
+void convertSubV7(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ auto result = createOp<mir::ops::SubOp>(graph, inputs[0], inputs[1])->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Sub.h b/compiler/mir/src/mir_onnx_importer/Op/Sub.h
new file mode 100644
index 000000000..b521e71ae
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Sub.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_SUB_H
+#define MIR_ONNX_OP_SUB_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertSubV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertSubV6(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertSubV7(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_SUB_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Sum.cpp b/compiler/mir/src/mir_onnx_importer/Op/Sum.cpp
new file mode 100644
index 000000000..c3a8dacca
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Sum.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Sum.h"
+
+#include "ONNXHelpers.h"
+
+#include "mir/ops/AddOp.h"
+
+namespace mir_onnx
+{
+
+void convertSumV8(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+ assert(inputs.size() >= 1);
+
+ auto result = inputs[0];
+ for (int i = 1; i < static_cast<int>(inputs.size()); ++i)
+ {
+ result = createOp<mir::ops::AddOp>(graph, result, inputs[i])->getOutput(0);
+ }
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Sum.h b/compiler/mir/src/mir_onnx_importer/Op/Sum.h
new file mode 100644
index 000000000..74ceb6dd7
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Sum.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_SUM_H
+#define MIR_ONNX_OP_SUM_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertSumV8(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_SUM_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Tanh.cpp b/compiler/mir/src/mir_onnx_importer/Op/Tanh.cpp
new file mode 100644
index 000000000..c7faf157c
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Tanh.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Tanh.h"
+
+#include "ONNXHelpers.h"
+
+#include "mir/ops/TanhOp.h"
+
+namespace mir_onnx
+{
+
+static void convertTanh(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+ assert(inputs.size() == 1);
+ auto result = createOp<mir::ops::TanhOp>(graph, inputs[0])->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertTanhV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertTanh(onnx_node, context);
+}
+
+void convertTanhV6(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ convertTanh(onnx_node, context);
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Tanh.h b/compiler/mir/src/mir_onnx_importer/Op/Tanh.h
new file mode 100644
index 000000000..5d3199541
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Tanh.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_TANH_H
+#define MIR_ONNX_OP_TANH_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertTanhV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertTanhV6(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_TANH_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Transpose.cpp b/compiler/mir/src/mir_onnx_importer/Op/Transpose.cpp
new file mode 100644
index 000000000..ffe0e8471
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Transpose.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Transpose.h"
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/ops/TransposeOp.h"
+
+#include <numeric>
+
+namespace mir_onnx
+{
+
+void convertTransposeV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ const auto inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ assert(inputs.size() == 1);
+ auto input = inputs[0];
+
+ const int num_axes = input->getShape().rank();
+ std::vector<std::size_t> axis_order(num_axes);
+ const auto *perm_attr = findAttribute(onnx_node, "perm");
+
+ if (perm_attr == nullptr)
+ {
+ // Reverse the dimensions.
+ std::iota(axis_order.rbegin(), axis_order.rend(), 0);
+ }
+ else
+ {
+ const auto perm = getAttributeValue<std::vector<std::int64_t>>(*perm_attr);
+ assert(static_cast<int>(perm.size()) == num_axes);
+ std::copy(perm.cbegin(), perm.cend(), axis_order.begin());
+ }
+
+ auto result = createOp<mir::ops::TransposeOp>(graph, input, axis_order)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Transpose.h b/compiler/mir/src/mir_onnx_importer/Op/Transpose.h
new file mode 100644
index 000000000..1f8c4369a
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Transpose.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_TRANSPOSE_H
+#define MIR_ONNX_OP_TRANSPOSE_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertTransposeV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_TRANSPOSE_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Unsqueeze.cpp b/compiler/mir/src/mir_onnx_importer/Op/Unsqueeze.cpp
new file mode 100644
index 000000000..1b5995532
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Unsqueeze.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Unsqueeze.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/ops/ReshapeOp.h"
+
+namespace mir_onnx
+{
+
+void convertUnsqueezeV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+ const auto axes = getAttributeValue<std::vector<std::int64_t>>(onnx_node, "axes");
+ assert(!axes.empty());
+ const mir::Shape &input_shape = inputs[0]->getShape();
+ const int out_rank = input_shape.rank() + static_cast<int>(axes.size());
+ mir::Shape out_shape(out_rank);
+ auto ints_iterator = axes.cbegin();
+ int j = 0;
+ for (int i = 0; i < out_rank; i++)
+ {
+ if (ints_iterator < axes.cend() && i == *ints_iterator)
+ {
+ out_shape.dim(i) = 1;
+ ints_iterator++;
+ }
+ else
+ {
+ out_shape.dim(i) = input_shape.dim(j);
+ j++;
+ }
+ }
+ auto result = createOp<mir::ops::ReshapeOp>(graph, inputs[0], out_shape)->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Unsqueeze.h b/compiler/mir/src/mir_onnx_importer/Op/Unsqueeze.h
new file mode 100644
index 000000000..46fea97ee
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Unsqueeze.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_UNSQUEEZE_H
+#define MIR_ONNX_OP_UNSQUEEZE_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertUnsqueezeV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_UNSQUEEZE_H
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Upsample.cpp b/compiler/mir/src/mir_onnx_importer/Op/Upsample.cpp
new file mode 100644
index 000000000..346e22cc2
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Upsample.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Upsample.h"
+
+#include "ONNXHelpers.h"
+#include "AttributeHelpers.h"
+
+#include "mir/Tensor.h"
+
+#include "mir/ops/ConstantOp.h"
+#include "mir/ops/ResizeOp.h"
+
+#include <stdexcept>
+
+namespace mir_onnx
+{
+
+void convertUpsampleV1(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ // "nearest" is the default mode.
+ std::string mode = getAttributeValue<std::string>(onnx_node, "mode", "nearest");
+ assert(mode == "nearest" && "Unsupported upscale mode!");
+
+ const float h_scale = getAttributeValue<float>(onnx_node, "height_scale", 0.0f); // required
+ const float w_scale = getAttributeValue<float>(onnx_node, "width_scale", 0.0f); // required
+ if (h_scale < 1.0f || w_scale < 1.0f)
+ throw std::runtime_error("Wrong scale attributes!");
+
+ assert(inputs[0]->getShape().rank() == 4 && "Only rank 4 is supported");
+ std::vector<float> scales_vector(4);
+ // NCHW
+ scales_vector.at(0) = 1.0f;
+ scales_vector.at(1) = 1.0f;
+ scales_vector.at(2) = h_scale;
+ scales_vector.at(3) = w_scale;
+
+ auto result =
+ createOp<mir::ops::ResizeOp>(graph, inputs[0],
+ mir::ops::ResizeOp::ResizeMethod::nearestNeighbor, scales_vector)
+ ->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertUpsampleV7(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ // "nearest" is the default mode.
+ std::string mode = getAttributeValue<std::string>(onnx_node, "mode", "nearest");
+ assert(mode == "nearest" && "Unsupported upscale mode!");
+
+ const auto *scales_attr = findAttribute(onnx_node, "scales");
+ if (!scales_attr)
+ throw std::runtime_error("Not enough required scales attribute!");
+
+ if (scales_attr->floats_size() != inputs[0]->getShape().rank())
+ throw std::runtime_error(
+ "Number of elements of scales should be the same as the rank of input");
+
+ assert(inputs[0]->getShape().rank() == 4 && "Only rank 4 is supported");
+ std::vector<float> scales_vector(4);
+ // NCHW
+ scales_vector.at(0) = scales_attr->floats(0);
+ scales_vector.at(1) = scales_attr->floats(1);
+ scales_vector.at(2) = scales_attr->floats(2);
+ scales_vector.at(3) = scales_attr->floats(3);
+
+ auto result =
+ createOp<mir::ops::ResizeOp>(graph, inputs[0],
+ mir::ops::ResizeOp::ResizeMethod::nearestNeighbor, scales_vector)
+ ->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+void convertUpsampleV9(const onnx::NodeProto &onnx_node, ConverterContext *context)
+{
+ std::vector<mir::Operation::Output *> inputs = context->getNodeInputs(onnx_node);
+ mir::Graph *graph = context->getGraph();
+
+ // "nearest" is the default mode.
+ const auto mode = getAttributeValue<std::string>(onnx_node, "mode", "nearest");
+ if (mode != "nearest")
+ throw std::runtime_error("Upsample: only 'nearest' mode is supported.");
+
+ // relies on attributes being lifted to constants (ONNX optimization pass)
+ assert(inputs.size() > 1);
+ auto *scales = dynamic_cast<mir::ops::ConstantOp *>(inputs[1]->getNode());
+ assert(scales && "Weights could be a constant tensor only");
+ auto scales_tensor = mir::Tensor<float>(scales->getValue());
+ int rank = inputs[0]->getShape().rank();
+ if (rank != 4)
+ throw std::runtime_error("Upsample: only 4-D input is supported.");
+ assert(scales_tensor.getShape().numElements() == rank &&
+ "The number of elements of 'scales' should be the same as the rank of input 'X'");
+ std::vector<float> scales_vector(rank);
+ for (int i = 0; i < scales_tensor.getShape().numElements(); i++)
+ scales_vector[i] = scales_tensor.atOffset(i);
+
+ auto result =
+ createOp<mir::ops::ResizeOp>(graph, inputs[0],
+ mir::ops::ResizeOp::ResizeMethod::nearestNeighbor, scales_vector)
+ ->getOutput(0);
+
+ context->setNodeOutputs(onnx_node, {result});
+}
+
+} // namespace mir_onnx
diff --git a/compiler/mir/src/mir_onnx_importer/Op/Upsample.h b/compiler/mir/src/mir_onnx_importer/Op/Upsample.h
new file mode 100644
index 000000000..99600eede
--- /dev/null
+++ b/compiler/mir/src/mir_onnx_importer/Op/Upsample.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_ONNX_OP_UPSAMPLE_H
+#define MIR_ONNX_OP_UPSAMPLE_H
+
+#include "ONNXNodeConverterRegistry.h"
+
+namespace mir_onnx
+{
+
+void convertUpsampleV1(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertUpsampleV7(const onnx::NodeProto &onnx_node, ConverterContext *context);
+void convertUpsampleV9(const onnx::NodeProto &onnx_node, ConverterContext *context);
+
+} // namespace mir_onnx
+
+#endif // MIR_ONNX_OP_UPSAMPLE_H
diff --git a/compiler/mir/src/mir_tflite_importer/CMakeLists.txt b/compiler/mir/src/mir_tflite_importer/CMakeLists.txt
new file mode 100644
index 000000000..952857c86
--- /dev/null
+++ b/compiler/mir/src/mir_tflite_importer/CMakeLists.txt
@@ -0,0 +1,21 @@
+nnas_find_package(FlatBuffers REQUIRED)
+
+if (NOT FlatBuffers_FOUND)
+ return()
+endif ()
+
+FlatBuffers_Target(mir_tflite_schema
+ OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated/schema"
+ SCHEMA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/schema"
+ SCHEMA_FILES schema.fbs)
+
+
+set(MIR_TFLITE_IMPORTER_SOURCES
+ tflite_importer.cpp
+ tflite_op_creator.cpp
+ tflite_op_creator.h)
+
+add_library(mir_tflite_importer STATIC ${MIR_TFLITE_IMPORTER_SOURCES})
+set_target_properties(mir_tflite_importer PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(mir_tflite_importer PUBLIC ../../include/mir_tflite_importer)
+target_link_libraries(mir_tflite_importer PUBLIC mir PRIVATE mir_tflite_schema nncc_common)
diff --git a/compiler/mir/src/mir_tflite_importer/schema/schema.fbs b/compiler/mir/src/mir_tflite_importer/schema/schema.fbs
new file mode 100644
index 000000000..dc7aab128
--- /dev/null
+++ b/compiler/mir/src/mir_tflite_importer/schema/schema.fbs
@@ -0,0 +1,937 @@
+// 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.
+
+// Revision History
+// Version 0: Initial version.
+// Version 1: Add subgraphs to schema.
+// Version 2: Rename operators to conform to NN API.
+// Version 3: Move buffer data from Model.Subgraph.Tensors to Model.Buffers.
+
+namespace tflite;
+
+// This corresponds to the version.
+file_identifier "TFL3";
+// File extension of any written files.
+file_extension "tflite";
+
+// IMPORTANT: All new members of tables, enums and unions must be added at the
+// end to ensure backwards compatibility.
+
+// The type of data stored in a tensor.
+enum TensorType : byte {
+ FLOAT32 = 0,
+ FLOAT16 = 1,
+ INT32 = 2,
+ UINT8 = 3,
+ INT64 = 4,
+ STRING = 5,
+ BOOL = 6,
+ INT16 = 7,
+ COMPLEX64 = 8,
+ INT8 = 9,
+}
+
+// Custom quantization parameters for experimenting with new quantization
+// techniques.
+table CustomQuantization {
+ custom:[ubyte] (force_align: 16);
+}
+
+// Represents a specific quantization technique's parameters.
+union QuantizationDetails {
+ CustomQuantization,
+}
+
+// Parameters for converting a quantized tensor back to float.
+table QuantizationParameters {
+ // These four parameters are the asymmetric linear quantization parameters.
+ // Given a quantized value q, the corresponding float value f should be:
+ // f = scale * (q - zero_point)
+ // For other quantization types, the QuantizationDetails below is used.
+ min:[float]; // For importing back into tensorflow.
+ max:[float]; // For importing back into tensorflow.
+ scale:[float]; // For dequantizing the tensor's values.
+ zero_point:[long];
+
+ // If this is not none, the other quantization parameters (i.e. min, max,
+ // scale, zero_point fields above) are ignored and the value of the
+ // QuantizationDetails union should be used.
+ details:QuantizationDetails;
+
+ // Specifies the dimension of the Tensor's shape that the scales and
+ // zero_points correspond to. For example, a tensor t, with dims=[4, 3, 2, 1]
+ // with quantization params:
+ // scale=[1.0, 2.0, 3.0], zero_point=[1, 2, 3], quantization_dimension=1
+ // will be quantized across the second dimension of t.
+ // t[:, 0, :, :] will have scale[0]=1.0, zero_point[0]=1
+ // t[:, 1, :, :] will have scale[1]=2.0, zero_point[0]=2
+ // t[:, 2, :, :] will have scale[2]=3.0, zero_point[0]=3
+ quantized_dimension:int;
+}
+
+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
+ // Tensorflow's NHWC).
+ shape:[int];
+ type:TensorType;
+ // An index that refers to the buffers table at the root of the model. Or,
+ // if there is no data buffer associated (i.e. intermediate results), then
+ // this is 0 (which refers to an always existent empty buffer).
+ //
+ // The data_buffer itself is an opaque container, with the assumption that the
+ // target device is little-endian. In addition, all builtin operators assume
+ // the memory is ordered such that if `shape` is [4, 3, 2], then index
+ // [i, j, k] maps to data_buffer[i*3*2 + j*2 + k].
+ buffer:uint;
+ name:string; // For debugging and importing back into tensorflow.
+ quantization:QuantizationParameters; // Optional.
+
+ is_variable:bool = false;
+}
+
+// A list of builtin operators. Builtin operators are slightly faster than custom
+// ones, but not by much. Moreover, while custom operators accept an opaque
+// object containing configuration parameters, builtins have a predetermined
+// set of acceptable options.
+enum BuiltinOperator : byte {
+ ADD = 0,
+ AVERAGE_POOL_2D = 1,
+ CONCATENATION = 2,
+ CONV_2D = 3,
+ DEPTHWISE_CONV_2D = 4,
+ DEPTH_TO_SPACE = 5,
+ DEQUANTIZE = 6,
+ EMBEDDING_LOOKUP = 7,
+ FLOOR = 8,
+ FULLY_CONNECTED = 9,
+ HASHTABLE_LOOKUP = 10,
+ L2_NORMALIZATION = 11,
+ L2_POOL_2D = 12,
+ LOCAL_RESPONSE_NORMALIZATION = 13,
+ LOGISTIC = 14,
+ LSH_PROJECTION = 15,
+ LSTM = 16,
+ MAX_POOL_2D = 17,
+ MUL = 18,
+ RELU = 19,
+ // NOTE(aselle): RELU_N1_TO_1 used to be called RELU1, but it was renamed
+ // since different model developers use RELU1 in different ways. Never
+ // create another op called RELU1.
+ RELU_N1_TO_1 = 20,
+ RELU6 = 21,
+ RESHAPE = 22,
+ RESIZE_BILINEAR = 23,
+ RNN = 24,
+ SOFTMAX = 25,
+ SPACE_TO_DEPTH = 26,
+ SVDF = 27,
+ TANH = 28,
+ // TODO(aselle): Consider rename to CONCATENATE_EMBEDDINGS
+ CONCAT_EMBEDDINGS = 29,
+ SKIP_GRAM = 30,
+ CALL = 31,
+ CUSTOM = 32,
+ EMBEDDING_LOOKUP_SPARSE = 33,
+ PAD = 34,
+ UNIDIRECTIONAL_SEQUENCE_RNN = 35,
+ GATHER = 36,
+ BATCH_TO_SPACE_ND = 37,
+ SPACE_TO_BATCH_ND = 38,
+ TRANSPOSE = 39,
+ MEAN = 40,
+ SUB = 41,
+ DIV = 42,
+ SQUEEZE = 43,
+ UNIDIRECTIONAL_SEQUENCE_LSTM = 44,
+ STRIDED_SLICE = 45,
+ BIDIRECTIONAL_SEQUENCE_RNN = 46,
+ EXP = 47,
+ TOPK_V2 = 48,
+ SPLIT = 49,
+ LOG_SOFTMAX = 50,
+ // DELEGATE is a special op type for the operations which are delegated to
+ // other backends.
+ // WARNING: Experimental interface, subject to change
+ DELEGATE = 51,
+ BIDIRECTIONAL_SEQUENCE_LSTM = 52,
+ CAST = 53,
+ PRELU = 54,
+ MAXIMUM = 55,
+ ARG_MAX = 56,
+ MINIMUM = 57,
+ LESS = 58,
+ NEG = 59,
+ PADV2 = 60,
+ GREATER = 61,
+ GREATER_EQUAL = 62,
+ LESS_EQUAL = 63,
+ SELECT = 64,
+ SLICE = 65,
+ SIN = 66,
+ TRANSPOSE_CONV = 67,
+ SPARSE_TO_DENSE = 68,
+ TILE = 69,
+ EXPAND_DIMS = 70,
+ EQUAL = 71,
+ NOT_EQUAL = 72,
+ LOG = 73,
+ SUM = 74,
+ SQRT = 75,
+ RSQRT = 76,
+ SHAPE = 77,
+ POW = 78,
+ ARG_MIN = 79,
+ FAKE_QUANT = 80,
+ REDUCE_PROD = 81,
+ REDUCE_MAX = 82,
+ PACK = 83,
+ LOGICAL_OR = 84,
+ ONE_HOT = 85,
+ LOGICAL_AND = 86,
+ LOGICAL_NOT = 87,
+ UNPACK = 88,
+ REDUCE_MIN = 89,
+ FLOOR_DIV = 90,
+ REDUCE_ANY = 91,
+ SQUARE = 92,
+ ZEROS_LIKE = 93,
+ FILL = 94,
+ FLOOR_MOD = 95,
+ RANGE = 96,
+ RESIZE_NEAREST_NEIGHBOR = 97,
+ LEAKY_RELU = 98,
+ SQUARED_DIFFERENCE = 99,
+ MIRROR_PAD = 100,
+ ABS = 101,
+ SPLIT_V = 102,
+ UNIQUE = 103,
+ CEIL = 104,
+ REVERSE_V2 = 105,
+ ADD_N = 106,
+ GATHER_ND = 107,
+ COS = 108,
+ WHERE = 109,
+ RANK = 110,
+ ELU = 111,
+ REVERSE_SEQUENCE = 112,
+ MATRIX_DIAG = 113,
+ QUANTIZE = 114,
+ MATRIX_SET_DIAG = 115,
+ ROUND = 116,
+ HARD_SWISH = 117,
+ IF = 118,
+ WHILE = 119,
+ NON_MAX_SUPPRESSION_V4 = 120,
+ NON_MAX_SUPPRESSION_V5 = 121,
+ SCATTER_ND = 122
+}
+
+// Options for the builtin operators.
+union BuiltinOptions {
+ 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
+}
+
+enum Padding : byte { SAME, VALID }
+
+enum ActivationFunctionType : byte {
+ NONE = 0,
+ RELU = 1,
+ RELU_N1_TO_1 = 2,
+ RELU6 = 3,
+ TANH = 4,
+ SIGN_BIT = 5,
+}
+
+table Conv2DOptions {
+ padding:Padding;
+ stride_w:int;
+ stride_h:int;
+ fused_activation_function:ActivationFunctionType;
+ dilation_w_factor:int = 1;
+ dilation_h_factor:int = 1;
+}
+
+table Pool2DOptions {
+ padding:Padding;
+ stride_w:int;
+ stride_h:int;
+ filter_width:int;
+ filter_height:int;
+ fused_activation_function:ActivationFunctionType;
+}
+
+table DepthwiseConv2DOptions {
+ // Parameters for DepthwiseConv version 1 or above.
+ padding:Padding;
+ stride_w:int;
+ stride_h:int;
+ depth_multiplier:int;
+ fused_activation_function:ActivationFunctionType;
+ // Parameters for DepthwiseConv version 2 or above.
+ dilation_w_factor:int = 1;
+ dilation_h_factor:int = 1;
+}
+
+table ConcatEmbeddingsOptions {
+ num_channels:int;
+ num_columns_per_channel:[int];
+ embedding_dim_per_channel:[int]; // This could be inferred from parameters.
+}
+
+enum LSHProjectionType: byte {
+ UNKNOWN = 0,
+ SPARSE = 1,
+ DENSE = 2,
+}
+
+table LSHProjectionOptions {
+ type: LSHProjectionType;
+}
+
+table SVDFOptions {
+ rank:int;
+ fused_activation_function:ActivationFunctionType;
+}
+
+// An implementation of TensorFlow RNNCell.
+table RNNOptions {
+ fused_activation_function:ActivationFunctionType;
+}
+
+// An implementation of TensorFlow dynamic_rnn with RNNCell.
+table SequenceRNNOptions {
+ time_major:bool;
+ fused_activation_function:ActivationFunctionType;
+}
+
+// An implementation of TensorFlow bidrectional_dynamic_rnn with RNNCell.
+table BidirectionalSequenceRNNOptions {
+ time_major:bool;
+ fused_activation_function:ActivationFunctionType;
+ merge_outputs: bool;
+}
+
+enum FullyConnectedOptionsWeightsFormat: byte {
+ DEFAULT = 0,
+ SHUFFLED4x16INT8 = 1,
+}
+
+// An implementation of TensorFlow fully_connected (a.k.a Dense) layer.
+table FullyConnectedOptions {
+ // Parameters for FullyConnected version 1 or above.
+ fused_activation_function:ActivationFunctionType;
+
+ // Parameters for FullyConnected version 2 or above.
+ weights_format:FullyConnectedOptionsWeightsFormat = DEFAULT;
+
+ // Parameters for FullyConnected version 5 or above.
+ // If set to true, then the number of dimension is preserved. Furthermore,
+ // all but the last dimension of the input and output shapes will be equal.
+ keep_num_dims: bool;
+}
+
+table SoftmaxOptions {
+ beta: float;
+}
+
+// An implementation of TensorFlow concat.
+table ConcatenationOptions {
+ axis:int;
+ fused_activation_function:ActivationFunctionType;
+}
+
+table AddOptions {
+ fused_activation_function:ActivationFunctionType;
+}
+
+table MulOptions {
+ fused_activation_function:ActivationFunctionType;
+}
+
+table L2NormOptions {
+ fused_activation_function:ActivationFunctionType;
+}
+
+table LocalResponseNormalizationOptions {
+ radius:int;
+ bias:float;
+ alpha:float;
+ beta:float;
+}
+
+enum LSTMKernelType : byte {
+ // Full LSTM kernel which supports peephole and projection.
+ FULL = 0,
+ // Basic LSTM kernels. Equivalent to TensorFlow BasicLSTMCell.
+ BASIC = 1,
+}
+
+// An implementation of TensorFlow LSTMCell and CoupledInputForgetGateLSTMCell
+table LSTMOptions {
+ // Parameters for LSTM version 1 or above.
+ fused_activation_function:ActivationFunctionType;
+ cell_clip: float; // Optional, 0.0 means no clipping
+ proj_clip: float; // Optional, 0.0 means no clipping
+
+ // Parameters for LSTM version 2 or above.
+ // Basic kernel is only supported in version 2 or above.
+ kernel_type: LSTMKernelType = FULL;
+}
+
+// An implementation of TensorFlow dynamic_rnn with LSTMCell.
+table UnidirectionalSequenceLSTMOptions {
+ fused_activation_function:ActivationFunctionType;
+ cell_clip: float; // Optional, 0.0 means no clipping
+ proj_clip: float; // Optional, 0.0 means no clipping
+
+ // If true then first dimension is sequence, otherwise batch.
+ time_major:bool;
+}
+
+table BidirectionalSequenceLSTMOptions {
+ // Parameters supported by version 1:
+ fused_activation_function:ActivationFunctionType;
+ cell_clip: float; // Optional, 0.0 means no clipping
+ proj_clip: float; // Optional, 0.0 means no clipping
+
+ // If true, store the outputs of both directions into the first output.
+ merge_outputs: bool;
+
+ // Parameters supported by version 2:
+ // If true then first dimension is sequence, otherwise batch.
+ // Version 1 implementations assumed time_major to be true, so this default
+ // value should never change.
+ time_major: bool = true;
+}
+
+table ResizeBilinearOptions {
+ new_height: int (deprecated);
+ new_width: int (deprecated);
+ align_corners: bool;
+}
+
+table ResizeNearestNeighborOptions {
+ align_corners: bool;
+}
+
+// A call operation options
+table CallOptions {
+ // The subgraph index that needs to be called.
+ subgraph:uint;
+}
+
+table PadOptions {
+}
+
+table PadV2Options {
+}
+
+table ReshapeOptions {
+ new_shape:[int];
+}
+
+table SpaceToBatchNDOptions {
+}
+
+table BatchToSpaceNDOptions {
+}
+
+table SkipGramOptions {
+ ngram_size: int;
+ max_skip_size: int;
+ include_all_ngrams: bool;
+}
+
+table SpaceToDepthOptions {
+ block_size: int;
+}
+
+table DepthToSpaceOptions {
+ block_size: int;
+}
+
+table SubOptions {
+ fused_activation_function:ActivationFunctionType;
+}
+
+table DivOptions {
+ fused_activation_function:ActivationFunctionType;
+}
+
+table TopKV2Options {
+}
+
+enum CombinerType : byte {
+ SUM = 0,
+ MEAN = 1,
+ SQRTN = 2,
+}
+
+table EmbeddingLookupSparseOptions {
+ combiner:CombinerType;
+}
+
+table GatherOptions {
+ axis: int;
+}
+
+table TransposeOptions {
+}
+
+table ExpOptions {
+}
+
+table CosOptions {
+}
+
+table ReducerOptions {
+ keep_dims: bool;
+}
+
+table SqueezeOptions {
+ squeeze_dims:[int];
+}
+
+table SplitOptions {
+ num_splits: int;
+}
+
+table SplitVOptions {
+ num_splits: int;
+}
+
+table StridedSliceOptions {
+ begin_mask: int;
+ end_mask: int;
+ ellipsis_mask: int;
+ new_axis_mask: int;
+ shrink_axis_mask: int;
+}
+
+table LogSoftmaxOptions {
+}
+
+table CastOptions {
+ in_data_type: TensorType;
+ out_data_type: TensorType;
+}
+
+table DequantizeOptions {
+}
+
+table MaximumMinimumOptions {
+}
+
+table TileOptions {
+}
+
+table ArgMaxOptions {
+ output_type : TensorType;
+}
+
+table ArgMinOptions {
+ output_type : TensorType;
+}
+
+table GreaterOptions {
+}
+
+table GreaterEqualOptions {
+}
+
+table LessOptions {
+}
+
+table LessEqualOptions {
+}
+
+table NegOptions {
+}
+
+table SelectOptions {
+}
+
+table SliceOptions {
+}
+
+table TransposeConvOptions {
+ padding:Padding;
+ stride_w:int;
+ stride_h:int;
+}
+
+table ExpandDimsOptions {
+}
+
+table SparseToDenseOptions {
+ validate_indices:bool;
+}
+
+table EqualOptions {
+}
+
+table NotEqualOptions {
+}
+
+table ShapeOptions {
+ // Optional output type of the operation (int32 or int64). Defaults to int32.
+ out_type : TensorType;
+}
+
+table RankOptions {
+}
+
+table PowOptions {
+}
+
+table FakeQuantOptions {
+ // Parameters supported by version 1:
+ min:float;
+ max:float;
+ num_bits:int;
+
+ // Parameters supported by version 2:
+ narrow_range:bool;
+}
+
+table PackOptions {
+ values_count:int;
+ axis:int;
+}
+
+table LogicalOrOptions {
+}
+
+table OneHotOptions {
+ axis:int;
+}
+
+table AbsOptions {
+}
+
+
+table HardSwishOptions {
+}
+
+table LogicalAndOptions {
+}
+
+table LogicalNotOptions {
+}
+
+table UnpackOptions {
+ num:int;
+ axis:int;
+}
+
+table FloorDivOptions {
+}
+
+table SquareOptions {
+}
+
+table ZerosLikeOptions {
+}
+
+table FillOptions {
+}
+
+table FloorModOptions {
+}
+
+table RangeOptions {
+}
+
+table LeakyReluOptions {
+ alpha:float;
+}
+
+table SquaredDifferenceOptions {
+}
+
+enum MirrorPadMode : byte {
+ // Doesn't include borders.
+ REFLECT = 0,
+ // Includes borders.
+ SYMMETRIC = 1,
+}
+
+table MirrorPadOptions {
+ mode:MirrorPadMode;
+}
+
+table UniqueOptions {
+ idx_out_type:TensorType = INT32;
+}
+
+table ReverseV2Options {
+}
+
+table AddNOptions {
+}
+
+table GatherNdOptions {
+}
+
+table WhereOptions {
+}
+
+table ReverseSequenceOptions {
+ seq_dim:int;
+ batch_dim:int = 0;
+}
+
+table MatrixDiagOptions {
+}
+
+table QuantizeOptions {
+}
+
+table MatrixSetDiagOptions {
+}
+
+table IfOptions {
+ then_subgraph_index:int;
+ else_subgraph_index:int;
+}
+
+table WhileOptions {
+ cond_subgraph_index:int;
+ body_subgraph_index:int;
+}
+
+table NonMaxSuppressionV4Options {
+}
+
+table NonMaxSuppressionV5Options {
+}
+
+table ScatterNdOptions {
+}
+
+// An OperatorCode can be an enum value (BuiltinOperator) if the operator is a
+// builtin, or a string if the operator is custom.
+table OperatorCode {
+ builtin_code:BuiltinOperator;
+ custom_code:string;
+
+ // The version of the operator. The version need to be bumped whenever new
+ // parameters are introduced into an op.
+ version:int = 1;
+}
+
+enum CustomOptionsFormat : byte {
+ FLEXBUFFERS = 0,
+}
+
+// An operator takes tensors as inputs and outputs. The type of operation being
+// performed is determined by an index into the list of valid OperatorCodes,
+// while the specifics of each operations is configured using builtin_options
+// or custom_options.
+table Operator {
+ // Index into the operator_codes array. Using an integer here avoids
+ // complicate map lookups.
+ opcode_index:uint;
+
+ // Optional input and output tensors are indicated by -1.
+ inputs:[int];
+ outputs:[int];
+
+ builtin_options:BuiltinOptions;
+ custom_options:[ubyte];
+ custom_options_format:CustomOptionsFormat;
+
+ // A list of booleans indicating the input tensors which are being mutated by
+ // this operator.(e.g. used by RNN and LSTM).
+ // For example, if the "inputs" array refers to 5 tensors and the second and
+ // fifth are mutable variables, then this list will contain
+ // [false, true, false, false, true].
+ //
+ // If the list is empty, no variable is mutated in this operator.
+ // The list either has the same length as `inputs`, or is empty.
+ mutating_variable_inputs:[bool];
+
+ // A list of indices to the subgraph's "tensors" that are internal to an Op.
+ // Internal tensors are those that do not flow in or out of the operation,
+ // but instead are part of internal computation. As such, the operation's
+ // implementation may manage its memory more efficiently. They are needed
+ // however (i.e. not just an implementation detail) since they are part of the
+ // computation, which may require relevant metadata such as quantization
+ // parameters.
+ intermediates:[int];
+}
+
+// The root type, defining a subgraph, which typically represents an entire
+// model.
+table SubGraph {
+ // A list of all tensors used in this subgraph.
+ tensors:[Tensor];
+
+ // Indices of the tensors that are inputs into this subgraph. Note this is
+ // the list of non-static tensors that feed into the subgraph for inference.
+ inputs:[int];
+
+ // Indices of the tensors that are outputs out of this subgraph. Note this is
+ // the list of output tensors that are considered the product of the
+ // subgraph's inference.
+ outputs:[int];
+
+ // All operators, in execution order.
+ operators:[Operator];
+
+ // Name of this subgraph (used for debugging).
+ name:string;
+}
+
+// Table of raw data buffers (used for constant tensors). Referenced by tensors
+// by index. The generous alignment accommodates mmap-friendly data structures.
+table Buffer {
+ data:[ubyte] (force_align: 16);
+}
+
+table Metadata {
+ // A human readable string to uniquely identify a Metadata.
+ name:string;
+ // An index to the buffers table.
+ buffer:uint;
+}
+
+table Model {
+ // Version of the schema.
+ version:uint;
+
+ // A list of all operator codes used in this model. This is
+ // kept in order because operators carry an index into this
+ // vector.
+ operator_codes:[OperatorCode];
+
+ // All the subgraphs of the model. The 0th is assumed to be the main
+ // model.
+ subgraphs:[SubGraph];
+
+ // A description of the model.
+ description:string;
+
+ // Buffers of the model.
+ // Note the 0th entry of this array must be an empty buffer (sentinel).
+ // This is a convention so that tensors without a buffer can provide 0 as
+ // their buffer.
+ buffers:[Buffer];
+
+ // Metadata about the model. Indirects into the existings buffers list.
+ // Deprecated, prefer to use metadata field.
+ metadata_buffer:[int];
+
+ // Metadata about the model.
+ metadata:[Metadata];
+}
+
+root_type Model;
diff --git a/compiler/mir/src/mir_tflite_importer/schema/schema.meta b/compiler/mir/src/mir_tflite_importer/schema/schema.meta
new file mode 100644
index 000000000..c86134c5a
--- /dev/null
+++ b/compiler/mir/src/mir_tflite_importer/schema/schema.meta
@@ -0,0 +1,2 @@
+REPO=https://github.com/tensorflow/tensorflow.git
+COMMIT=998eadd
diff --git a/compiler/mir-tflite-importer/schema/schema_v0.fbs b/compiler/mir/src/mir_tflite_importer/schema/schema_v0.fbs
index 852ea988f..852ea988f 100644
--- a/compiler/mir-tflite-importer/schema/schema_v0.fbs
+++ b/compiler/mir/src/mir_tflite_importer/schema/schema_v0.fbs
diff --git a/compiler/mir-tflite-importer/schema/schema_v0.meta b/compiler/mir/src/mir_tflite_importer/schema/schema_v0.meta
index 74668ab7a..74668ab7a 100644
--- a/compiler/mir-tflite-importer/schema/schema_v0.meta
+++ b/compiler/mir/src/mir_tflite_importer/schema/schema_v0.meta
diff --git a/compiler/mir-tflite-importer/schema/schema_v1.fbs b/compiler/mir/src/mir_tflite_importer/schema/schema_v1.fbs
index 06cd9408e..06cd9408e 100644
--- a/compiler/mir-tflite-importer/schema/schema_v1.fbs
+++ b/compiler/mir/src/mir_tflite_importer/schema/schema_v1.fbs
diff --git a/compiler/mir-tflite-importer/schema/schema_v1.meta b/compiler/mir/src/mir_tflite_importer/schema/schema_v1.meta
index 74668ab7a..74668ab7a 100644
--- a/compiler/mir-tflite-importer/schema/schema_v1.meta
+++ b/compiler/mir/src/mir_tflite_importer/schema/schema_v1.meta
diff --git a/compiler/mir-tflite-importer/schema/schema_v2.fbs b/compiler/mir/src/mir_tflite_importer/schema/schema_v2.fbs
index 96731c8aa..96731c8aa 100644
--- a/compiler/mir-tflite-importer/schema/schema_v2.fbs
+++ b/compiler/mir/src/mir_tflite_importer/schema/schema_v2.fbs
diff --git a/compiler/mir-tflite-importer/schema/schema_v2.meta b/compiler/mir/src/mir_tflite_importer/schema/schema_v2.meta
index 74668ab7a..74668ab7a 100644
--- a/compiler/mir-tflite-importer/schema/schema_v2.meta
+++ b/compiler/mir/src/mir_tflite_importer/schema/schema_v2.meta
diff --git a/compiler/mir-tflite-importer/schema/schema_v3.fbs b/compiler/mir/src/mir_tflite_importer/schema/schema_v3.fbs
index cedefe08f..cedefe08f 100644
--- a/compiler/mir-tflite-importer/schema/schema_v3.fbs
+++ b/compiler/mir/src/mir_tflite_importer/schema/schema_v3.fbs
diff --git a/compiler/mir-tflite-importer/schema/schema_v3.meta b/compiler/mir/src/mir_tflite_importer/schema/schema_v3.meta
index 74668ab7a..74668ab7a 100644
--- a/compiler/mir-tflite-importer/schema/schema_v3.meta
+++ b/compiler/mir/src/mir_tflite_importer/schema/schema_v3.meta
diff --git a/compiler/mir/src/mir_tflite_importer/tflite_importer.cpp b/compiler/mir/src/mir_tflite_importer/tflite_importer.cpp
new file mode 100644
index 000000000..3f245d2d4
--- /dev/null
+++ b/compiler/mir/src/mir_tflite_importer/tflite_importer.cpp
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "tflite_importer.h"
+#include "tflite_op_creator.h"
+#include "schema_generated.h"
+
+#include "mir/TensorVariant.h"
+#include "mir/ops/ConstantOp.h"
+#include "mir/ops/OutputOp.h"
+
+#include <fstream>
+#include <memory>
+#include <utility>
+#include <vector>
+#include <set>
+
+namespace mir_tflite
+{
+
+namespace
+{
+
+class TfliteImporter
+{
+public:
+ explicit TfliteImporter(std::string filename);
+
+ /// @brief Load the model and convert it into a MIR Graph.
+ std::unique_ptr<mir::Graph> importModel();
+
+ ~TfliteImporter();
+
+private:
+ std::string _filename;
+ std::unique_ptr<tflite::ModelT> _model;
+
+ std::unique_ptr<mir::Graph> _graph;
+ std::unique_ptr<TFLiteOpCreator> _opCreator;
+
+ // Maps TFLite tensors indices to corresponding MIR operation outputs.
+ std::vector<mir::Operation::Output *> _tensorMap;
+
+ void import();
+
+ void walkModel(const tflite::ModelT *model);
+
+ void walkSubgraph(const tflite::SubGraphT *subgraph);
+
+ void walkOperator(const tflite::SubGraphT *subgraph, const tflite::OperatorT *op);
+
+ /**
+ * @brief Pass through tflite graph and collect operators unsupported by NNC
+ * @throw PassException with message, containing detected problems
+ */
+ void collectUnsupportedOps();
+
+ /**
+ * @brief Returns MIR operation outputs corresponding to the inputs of the given operator.
+ */
+ std::vector<mir::Operation::Output *> getMIRInputsForOperator(const tflite::SubGraphT *subgraph,
+ const tflite::OperatorT *op);
+};
+
+TfliteImporter::TfliteImporter(std::string filename) : _filename(std::move(filename))
+{
+ _graph = std::make_unique<mir::Graph>();
+ _opCreator = std::make_unique<TFLiteOpCreator>(_graph.get());
+}
+
+TfliteImporter::~TfliteImporter() = default;
+
+void TfliteImporter::import()
+{
+ std::ifstream stream(_filename, std::ios::in | std::ios::binary);
+ if (stream.fail())
+ throw std::runtime_error("Couldn't open file \"" + _filename + "\".");
+
+ std::vector<char> model_buffer((std::istreambuf_iterator<char>(stream)),
+ std::istreambuf_iterator<char>());
+
+ if (stream.fail())
+ throw std::runtime_error("Couldn't read file \"" + _filename + "\".");
+
+ flatbuffers::Verifier verifier(reinterpret_cast<const std::uint8_t *>(model_buffer.data()),
+ model_buffer.size());
+
+ if (!tflite::VerifyModelBuffer(verifier))
+ throw std::runtime_error("Could not load model: " + _filename + "\n");
+
+ _model = tflite::UnPackModel(model_buffer.data());
+}
+
+static const std::set<tflite::BuiltinOperator> supportedOperators = {
+ tflite::BuiltinOperator_ADD,
+ tflite::BuiltinOperator_AVERAGE_POOL_2D,
+ tflite::BuiltinOperator_CONCATENATION,
+ tflite::BuiltinOperator_CONV_2D,
+ tflite::BuiltinOperator_DEPTHWISE_CONV_2D,
+ tflite::BuiltinOperator_DIV,
+ tflite::BuiltinOperator_FULLY_CONNECTED,
+ tflite::BuiltinOperator_HARD_SWISH,
+ tflite::BuiltinOperator_LEAKY_RELU,
+ tflite::BuiltinOperator_LOGISTIC,
+ tflite::BuiltinOperator_MAX_POOL_2D,
+ tflite::BuiltinOperator_MAXIMUM,
+ tflite::BuiltinOperator_MEAN,
+ tflite::BuiltinOperator_MUL,
+ tflite::BuiltinOperator_PAD,
+ tflite::BuiltinOperator_RELU,
+ tflite::BuiltinOperator_RELU6,
+ tflite::BuiltinOperator_RESHAPE,
+ tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR,
+ tflite::BuiltinOperator_RSQRT,
+ tflite::BuiltinOperator_SHAPE,
+ tflite::BuiltinOperator_SLICE,
+ tflite::BuiltinOperator_SOFTMAX,
+ tflite::BuiltinOperator_SQRT,
+ tflite::BuiltinOperator_SQUARED_DIFFERENCE,
+ tflite::BuiltinOperator_SQUEEZE,
+ tflite::BuiltinOperator_STRIDED_SLICE,
+ tflite::BuiltinOperator_SUB,
+ tflite::BuiltinOperator_TANH,
+ tflite::BuiltinOperator_TRANSPOSE,
+ tflite::BuiltinOperator_TRANSPOSE_CONV,
+};
+
+void TfliteImporter::collectUnsupportedOps()
+{
+ std::set<std::string> errors;
+ for (const auto &subgraph : _model->subgraphs)
+ for (const auto &op : subgraph->operators)
+ {
+ tflite::BuiltinOperator opcode = _model->operator_codes[op->opcode_index]->builtin_code;
+ if (supportedOperators.find(opcode) == supportedOperators.end())
+ {
+ if (opcode <= tflite::BuiltinOperator_MAX)
+ errors.insert(std::string(EnumNameBuiltinOperator(opcode)) + ": unsupported operator");
+ else
+ errors.insert(std::to_string(opcode) + ": unsuppored in tflite custom opcode");
+ }
+ }
+
+ if (!errors.empty())
+ {
+ std::string msg("NNC can't load model. Detected problems:");
+ for (const auto &e : errors)
+ msg.append("\n * " + e);
+ throw std::runtime_error(msg);
+ }
+}
+
+std::unique_ptr<mir::Graph> TfliteImporter::importModel()
+{
+ import();
+ collectUnsupportedOps();
+ walkModel(_model.get());
+ return std::move(_graph);
+}
+
+void TfliteImporter::walkModel(const tflite::ModelT *model)
+{
+ for (const auto &subgraph : model->subgraphs)
+ walkSubgraph(subgraph.get());
+}
+
+mir::DataType convertElementType(tflite::TensorType type)
+{
+ switch (type)
+ {
+ case tflite::TensorType_INT32:
+ return mir::DataType::INT32;
+ case tflite::TensorType_FLOAT32:
+ return mir::DataType::FLOAT32;
+ case tflite::TensorType_INT64:
+ return mir::DataType::INT64;
+ case tflite::TensorType_UINT8:
+ return mir::DataType::UINT8;
+ default:
+ throw std::runtime_error(std::string("Unsupported tensor type: ") + EnumNameTensorType(type));
+ }
+}
+
+mir::TensorType getMirTensorType(const tflite::TensorT &tensor)
+{
+ mir::DataType element_type = convertElementType(tensor.type);
+
+ mir::Shape shape(tensor.shape.size());
+ for (std::size_t i = 0; i < tensor.shape.size(); ++i)
+ {
+ shape.dim(i) = tensor.shape[i];
+ }
+
+ if (tensor.quantization != nullptr)
+ {
+ const tflite::QuantizationParametersT &params = *tensor.quantization;
+
+ if (params.details.type != tflite::QuantizationDetails_NONE)
+ throw std::runtime_error("Custom quantization is not supported.");
+
+ // Empty parameters mean no quantization at all.
+ if (params.scale.empty() && params.zero_point.empty())
+ return mir::TensorType{element_type, shape};
+
+ if (params.scale.size() != 1 || params.zero_point.size() != 1)
+ throw std::runtime_error("Non-scalar quantization is not supported.");
+
+ mir::AffineQuantization quantization{params.scale[0], static_cast<int>(params.zero_point[0])};
+
+ return mir::TensorType{element_type, shape, quantization};
+ }
+ else
+ {
+ return mir::TensorType{element_type, shape};
+ }
+}
+
+void TfliteImporter::walkSubgraph(const tflite::SubGraphT *subgraph)
+{
+ _tensorMap.assign(subgraph->tensors.size(), nullptr);
+
+ for (const auto input_tensor_index : subgraph->inputs)
+ {
+ const tflite::TensorT &tensor = *subgraph->tensors[input_tensor_index];
+
+ mir::TensorType input_type = getMirTensorType(tensor);
+ auto input = _graph->create<mir::ops::InputOp>(input_type)->getOutput(0);
+ input->setName(tensor.name);
+
+ assert(_tensorMap[input_tensor_index] == nullptr);
+ _tensorMap[input_tensor_index] = input;
+ }
+
+ for (const auto &op : subgraph->operators)
+ {
+ walkOperator(subgraph, op.get());
+ }
+
+ for (const auto output_tensor_index : subgraph->outputs)
+ {
+ auto output = _tensorMap[output_tensor_index];
+ _graph->create<mir::ops::OutputOp>(output);
+ }
+}
+
+void TfliteImporter::walkOperator(const tflite::SubGraphT *subgraph, const tflite::OperatorT *op)
+{
+ std::vector<mir::Operation::Output *> inputs = getMIRInputsForOperator(subgraph, op);
+ std::vector<mir::Operation::Output *> outputs;
+
+ tflite::BuiltinOperator opcode = _model->operator_codes[op->opcode_index]->builtin_code;
+ switch (opcode)
+ {
+ case tflite::BuiltinOperator_CONV_2D:
+ outputs = _opCreator->convertConv2D(op->builtin_options.AsConv2DOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_DEPTHWISE_CONV_2D:
+ outputs = _opCreator->convertDepthwiseConv2D(op->builtin_options.AsDepthwiseConv2DOptions(),
+ inputs);
+ break;
+ case tflite::BuiltinOperator_MAX_POOL_2D:
+ outputs = _opCreator->convertMaxPool2D(op->builtin_options.AsPool2DOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_AVERAGE_POOL_2D:
+ outputs = _opCreator->convertAveragePool2D(op->builtin_options.AsPool2DOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_CONCATENATION:
+ outputs =
+ _opCreator->convertConcatenation(op->builtin_options.AsConcatenationOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_RESHAPE:
+ outputs = _opCreator->convertReshape(op->builtin_options.AsReshapeOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR:
+ outputs = _opCreator->convertResizeNearestNeighbor(
+ op->builtin_options.AsResizeNearestNeighborOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_MEAN:
+ outputs = _opCreator->convertMean(op->builtin_options.AsReducerOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_FULLY_CONNECTED:
+ outputs =
+ _opCreator->convertFullyConnected(op->builtin_options.AsFullyConnectedOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_SOFTMAX:
+ outputs = _opCreator->convertSoftmax(op->builtin_options.AsSoftmaxOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_SLICE:
+ outputs = _opCreator->convertSlice(op->builtin_options.AsSliceOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_SQUEEZE:
+ outputs = _opCreator->convertSqueeze(op->builtin_options.AsSqueezeOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_LOGISTIC:
+ outputs = _opCreator->convertLogistic(inputs);
+ break;
+ case tflite::BuiltinOperator_RSQRT:
+ outputs = _opCreator->convertRsqrt(inputs);
+ break;
+ case tflite::BuiltinOperator_SQRT:
+ outputs = _opCreator->convertSqrt(inputs);
+ break;
+ case tflite::BuiltinOperator_ADD:
+ outputs = _opCreator->convertAdd(op->builtin_options.AsAddOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_SUB:
+ outputs = _opCreator->convertSub(op->builtin_options.AsSubOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_MUL:
+ outputs = _opCreator->convertMul(op->builtin_options.AsMulOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_DIV:
+ outputs = _opCreator->convertDiv(op->builtin_options.AsDivOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_MAXIMUM:
+ outputs = _opCreator->convertMax(inputs);
+ break;
+ case tflite::BuiltinOperator_SQUARED_DIFFERENCE:
+ outputs = _opCreator->convertSquaredDifference(inputs);
+ break;
+ case tflite::BuiltinOperator_TRANSPOSE_CONV:
+ outputs =
+ _opCreator->convertTransposeConv(op->builtin_options.AsTransposeConvOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_PAD:
+ outputs = _opCreator->convertPad(op->builtin_options.AsPadOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_TANH:
+ outputs = _opCreator->convertTanh(inputs);
+ break;
+ case tflite::BuiltinOperator_RELU:
+ outputs = _opCreator->convertReLU(inputs);
+ break;
+ case tflite::BuiltinOperator_RELU6:
+ outputs = _opCreator->convertReLU6(inputs);
+ break;
+ case tflite::BuiltinOperator_TRANSPOSE:
+ outputs = _opCreator->convertTranspose(op->builtin_options.AsTransposeOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_STRIDED_SLICE:
+ outputs =
+ _opCreator->convertStridedSlice(op->builtin_options.AsStridedSliceOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_LEAKY_RELU:
+ outputs = _opCreator->convertLeakyReLU(op->builtin_options.AsLeakyReluOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_SHAPE:
+ outputs = _opCreator->convertShape(op->builtin_options.AsShapeOptions(), inputs);
+ break;
+ case tflite::BuiltinOperator_HARD_SWISH:
+ outputs = _opCreator->convertHardSwish(op->builtin_options.AsHardSwishOptions(), inputs);
+ break;
+ default:
+ assert(false && "All unsupported types should have been found before this pass.");
+ }
+
+ assert(outputs.size() == op->outputs.size());
+ for (std::size_t i = 0; i < op->outputs.size(); ++i)
+ {
+ const auto tensor_index = op->outputs[i];
+ const tflite::TensorT &tensor = *subgraph->tensors[tensor_index];
+
+ mir::TensorType output_type = getMirTensorType(tensor);
+
+ // The type should have been inferred correctly, except for quantization information.
+ assert(outputs[i]->getType().getElementType() == output_type.getElementType() &&
+ outputs[i]->getType().getShape() == output_type.getShape());
+
+ outputs[i]->setName(tensor.name);
+ outputs[i]->setType(output_type);
+
+ assert(_tensorMap[tensor_index] == nullptr);
+ _tensorMap[tensor_index] = outputs[i];
+ }
+}
+
+std::vector<mir::Operation::Output *>
+TfliteImporter::getMIRInputsForOperator(const tflite::SubGraphT *subgraph,
+ const tflite::OperatorT *op)
+{
+ std::vector<mir::Operation::Output *> inputs;
+
+ for (const auto tensor_index : op->inputs)
+ {
+ const tflite::TensorT &tensor = *subgraph->tensors[tensor_index];
+ const tflite::BufferT &buffer = *_model->buffers[tensor.buffer];
+ if (!buffer.data.empty())
+ {
+ assert(_tensorMap[tensor_index] == nullptr);
+ mir::TensorType type = getMirTensorType(tensor);
+ mir::TensorVariant mir_tensor{type, buffer.data.data()};
+ inputs.emplace_back(_graph->create<mir::ops::ConstantOp>(mir_tensor)->getOutput(0));
+ }
+ else
+ {
+ assert(_tensorMap[tensor_index] != nullptr);
+ // By this point every input for the operation "op" should have corresponding
+ // Model IR operations that output its inputs. This assumption is provided by the fact
+ // that TFLite format specifies all operations in the execution order.
+ inputs.emplace_back(_tensorMap[tensor_index]);
+ }
+ }
+
+ return inputs;
+}
+
+} // namespace
+
+std::unique_ptr<mir::Graph> loadModel(std::string filename)
+{
+ TfliteImporter importer(std::move(filename));
+ return importer.importModel();
+}
+
+} // namespace mir_tflite
diff --git a/compiler/mir/src/mir_tflite_importer/tflite_op_creator.cpp b/compiler/mir/src/mir_tflite_importer/tflite_op_creator.cpp
new file mode 100644
index 000000000..d9f98da55
--- /dev/null
+++ b/compiler/mir/src/mir_tflite_importer/tflite_op_creator.cpp
@@ -0,0 +1,652 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "tflite_op_creator.h"
+#include "schema_generated.h"
+
+#include "mir/ops/AddOp.h"
+#include "mir/ops/AvgPool2DOp.h"
+#include "mir/ops/CappedReluOp.h"
+#include "mir/ops/ConcatOp.h"
+#include "mir/ops/ConstantOp.h"
+#include "mir/ops/Conv2DOp.h"
+#include "mir/ops/Deconv2DOp.h"
+#include "mir/ops/DepthwiseConv2DOp.h"
+#include "mir/ops/DivOp.h"
+#include "mir/ops/FullyConnectedOp.h"
+#include "mir/ops/HardSwishOp.h"
+#include "mir/ops/LeakyReluOp.h"
+#include "mir/ops/MaxOp.h"
+#include "mir/ops/MaxPool2DOp.h"
+#include "mir/ops/MulOp.h"
+#include "mir/ops/PadOp.h"
+#include "mir/ops/ReduceMeanOp.h"
+#include "mir/ops/ReluOp.h"
+#include "mir/ops/ReshapeOp.h"
+#include "mir/ops/ResizeOp.h"
+#include "mir/ops/SigmoidOp.h"
+#include "mir/ops/SliceOp.h"
+#include "mir/ops/SoftmaxOp.h"
+#include "mir/ops/SqrtOp.h"
+#include "mir/ops/SqueezeOp.h"
+#include "mir/ops/SubOp.h"
+#include "mir/ops/TanhOp.h"
+#include "mir/ops/TransposeOp.h"
+
+#include "mir/Shape.h"
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+#include <stdexcept>
+
+namespace mir_tflite
+{
+
+namespace ops = mir::ops;
+using mir::Shape;
+
+static mir::ops::PaddingType convertPadding(tflite::Padding padding)
+{
+ switch (padding)
+ {
+ case tflite::Padding_VALID:
+ return mir::ops::PaddingType::Valid;
+ case tflite::Padding_SAME:
+ return mir::ops::PaddingType::SameUpper;
+ default:
+ throw std::runtime_error(std::string("Unsupported Padding: ") +
+ tflite::EnumNamePadding(padding));
+ }
+}
+
+// TODO Move this to MIR?
+static void calculatePadding(mir::ops::PaddingType padding_type, const mir::Shape &input_shape,
+ const std::vector<std::int32_t> &window_size,
+ const std::vector<std::int32_t> &strides,
+ std::vector<std::int32_t> &padding_before,
+ std::vector<std::int32_t> &padding_after)
+{
+ constexpr int num_spatial_dims = 2;
+ assert(window_size.size() == num_spatial_dims);
+ assert(strides.size() == num_spatial_dims);
+ assert(padding_before.size() == num_spatial_dims);
+ assert(padding_after.size() == num_spatial_dims);
+
+ switch (padding_type)
+ {
+ case mir::ops::PaddingType::SameUpper:
+ for (int i = 0; i < num_spatial_dims; ++i)
+ {
+ // Assuming NHWC format.
+ const std::int32_t total_padding =
+ (input_shape.dim(1 + i) % strides[i] == 0)
+ ? std::max(0, window_size[i] - strides[i])
+ : std::max(0, window_size[i] - input_shape.dim(1 + i) % strides[i]);
+ padding_before[i] = total_padding / 2;
+ padding_after[i] = total_padding - padding_before[i];
+ }
+ break;
+ case mir::ops::PaddingType::Valid:
+ for (int i = 0; i < num_spatial_dims; ++i)
+ {
+ padding_before[i] = 0;
+ padding_after[i] = 0;
+ }
+ break;
+ default:
+ assert(false);
+ }
+}
+
+template <typename VectorT>
+static std::vector<VectorT> convertIntTensorToVector(const mir::Tensor<int32_t> &tensor)
+{
+ std::vector<VectorT> v;
+ for (const auto &i : mir::ShapeRange(tensor.getShape()))
+ v.emplace_back(static_cast<VectorT>(tensor.at(i)));
+ return v;
+}
+
+static const mir::TensorVariant &extractTensor(const mir::Operation::Output *output)
+{
+ auto constant_op = dynamic_cast<const ops::ConstantOp *>(output->getNode());
+ if (constant_op == nullptr)
+ throw std::runtime_error("Non-constant input is not supported.");
+ return constant_op->getValue();
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertConv2D(const tflite::Conv2DOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+ auto kernel = inputs.at(1);
+ auto bias = inputs.at(2);
+
+ mir::Conv2DOpAttributes attributes;
+ attributes.strides = {opts->stride_h, opts->stride_w};
+
+ const auto padding_type = convertPadding(opts->padding);
+ const auto &input_shape = input->getShape();
+ const auto &kernel_shape = kernel->getShape();
+ const auto &strides = attributes.strides;
+ auto &pad_before = attributes.padding_before;
+ auto &pad_after = attributes.padding_after;
+ std::vector<std::int32_t> kernel_size{kernel_shape.dim(1), kernel_shape.dim(2)};
+ calculatePadding(padding_type, input_shape, kernel_size, strides, pad_before, pad_after);
+
+ mir::Operation::Output *result;
+ if (input->getType().isQuantized())
+ {
+ result = createOp<ops::Conv2DOp>(input, kernel, bias, attributes)->getOutput(0);
+ }
+ else // TODO Fuse bias to other backends
+ {
+ result = createOp<ops::Conv2DOp>(input, kernel, attributes)->getOutput(0);
+ result = createOp<ops::AddOp>(result, bias)->getOutput(0);
+ }
+ return {addFusedActivation(result, opts->fused_activation_function)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertDepthwiseConv2D(const tflite::DepthwiseConv2DOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+ auto kernel = inputs.at(1);
+ auto bias = inputs.at(2);
+
+ // OHWI -> HWIO
+ const std::vector<std::size_t> axis_order{1, 2, 3, 0};
+ kernel = createOp<ops::TransposeOp>(kernel, axis_order)->getOutput(0);
+
+ mir::Conv2DOpAttributes attributes;
+ attributes.strides = {opts->stride_h, opts->stride_w};
+
+ const auto padding_type = convertPadding(opts->padding);
+ const auto &input_shape = input->getShape();
+ const auto &kernel_shape = kernel->getShape();
+ std::vector<std::int32_t> kernel_size{kernel_shape.dim(0), kernel_shape.dim(1)};
+ const auto &strides = attributes.strides;
+ auto &pad_before = attributes.padding_before;
+ auto &pad_after = attributes.padding_after;
+ calculatePadding(padding_type, input_shape, kernel_size, strides, pad_before, pad_after);
+
+ mir::Operation::Output *result;
+ if (input->getType().isQuantized())
+ {
+ result = createOp<ops::DepthwiseConv2DOp>(input, kernel, bias, attributes)->getOutput(0);
+ }
+ else // TODO Fuse bias to other backends
+ {
+ result = createOp<ops::DepthwiseConv2DOp>(input, kernel, attributes)->getOutput(0);
+ result = createOp<ops::AddOp>(result, bias)->getOutput(0);
+ }
+ return {addFusedActivation(result, opts->fused_activation_function)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertConcatenation(const tflite::ConcatenationOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto result = createOp<ops::ConcatOp>(inputs, opts->axis);
+ return {addFusedActivation(result->getOutput(0), opts->fused_activation_function)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertMaxPool2D(const tflite::Pool2DOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+
+ const auto &input_shape = input->getShape();
+
+ mir::MaxPool2DOpAttributes attributes;
+ attributes.window = {opts->filter_height, opts->filter_width};
+ attributes.strides = {opts->stride_h, opts->stride_w};
+
+ const auto padding_type = convertPadding(opts->padding);
+ const auto &window_size = attributes.window;
+ const auto &strides = attributes.strides;
+ auto &pad_before = attributes.padding_before;
+ auto &pad_after = attributes.padding_after;
+ calculatePadding(padding_type, input_shape, window_size, strides, pad_before, pad_after);
+
+ auto result = createOp<ops::MaxPool2DOp>(input, attributes);
+ return {addFusedActivation(result->getOutput(0), opts->fused_activation_function)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertAveragePool2D(const tflite::Pool2DOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+
+ const auto &input_shape = input->getShape();
+
+ mir::AvgPool2DOpAttributes attributes;
+ attributes.window = {opts->filter_height, opts->filter_width};
+ attributes.strides = {opts->stride_h, opts->stride_w};
+ attributes.include_pad = false;
+
+ const auto padding_type = convertPadding(opts->padding);
+ const auto &window_size = attributes.window;
+ const auto &strides = attributes.strides;
+ auto &pad_before = attributes.padding_before;
+ auto &pad_after = attributes.padding_after;
+ calculatePadding(padding_type, input_shape, window_size, strides, pad_before, pad_after);
+
+ auto result = createOp<ops::AvgPool2DOp>(input, attributes);
+ return {addFusedActivation(result->getOutput(0), opts->fused_activation_function)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertSoftmax(const tflite::SoftmaxOptionsT * /*opts*/,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+
+ // Softmax in TFLite is always 2-D.
+ assert(input->getShape().rank() == 2);
+ const int32_t axis = 1;
+ auto result = createOp<ops::SoftmaxOp>(input, axis);
+ return {result->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertSlice(const tflite::SliceOptionsT * /*opts*/,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+ mir::Tensor<int32_t> begin_tensor(extractTensor(inputs.at(1)));
+ mir::Tensor<int32_t> size_tensor(extractTensor(inputs.at(2)));
+
+ Shape starts(convertIntTensorToVector<int32_t>(begin_tensor));
+ Shape sizes(convertIntTensorToVector<int32_t>(size_tensor));
+ auto result = createOp<ops::SliceOp>(input, starts, sizes);
+ return {result->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertReshape(const tflite::ReshapeOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+
+ // TODO: we should also support "-1" values in new_shape, which means that correct
+ // shape values must be calculated. Better do it in the shape inference module.
+ Shape new_shape(opts->new_shape.size());
+ for (int i = 0; i < static_cast<int>(opts->new_shape.size()); ++i)
+ {
+ new_shape.dim(i) = opts->new_shape[i];
+ }
+ auto result = createOp<ops::ReshapeOp>(input, new_shape);
+ return {result->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertTransposeConv(const tflite::TransposeConvOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ mir::Tensor<int32_t> output_shape_tensor(extractTensor(inputs.at(0)));
+ auto kernel = inputs.at(1);
+ auto input = inputs.at(2);
+
+ mir::Deconv2DOpAttributes attributes;
+ attributes.strides = {opts->stride_h, opts->stride_w};
+ Shape output_shape(convertIntTensorToVector<int32_t>(output_shape_tensor));
+
+ // OHWI -> HWOI
+ const std::vector<std::size_t> axis_order{1, 2, 0, 3};
+ kernel = createOp<ops::TransposeOp>(kernel, axis_order)->getOutput(0);
+
+ attributes.padding_type = convertPadding(opts->padding);
+ auto result = createOp<ops::DeConv2DOp>(input, kernel, attributes, output_shape)->getOutput(0);
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertResizeNearestNeighbor(const tflite::ResizeNearestNeighborOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ if (opts->align_corners)
+ throw std::runtime_error("'align_corners' is not currently supported");
+
+ auto input = inputs.at(0);
+ mir::Tensor<int32_t> size_tensor(extractTensor(inputs.at(1)));
+
+ const auto &input_shape = input->getShape();
+ Shape res_shape{input_shape.dim(0), size_tensor.at(mir::Index{0}), size_tensor.at(mir::Index{1}),
+ input_shape.dim(3)};
+ auto result =
+ createOp<ops::ResizeOp>(input, ops::ResizeOp::ResizeMethod::nearestNeighbor, res_shape);
+ return {result->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertAdd(const tflite::AddOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ assert(inputs.size() == 2);
+ auto result = createOp<ops::AddOp>(inputs[0], inputs[1])->getOutput(0);
+ return {addFusedActivation(result, opts->fused_activation_function)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertSub(const tflite::SubOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ assert(inputs.size() == 2);
+ auto result = createOp<ops::SubOp>(inputs[0], inputs[1])->getOutput(0);
+ return {addFusedActivation(result, opts->fused_activation_function)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertMul(const tflite::MulOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ assert(inputs.size() == 2);
+ auto result = createOp<ops::MulOp>(inputs[0], inputs[1])->getOutput(0);
+ return {addFusedActivation(result, opts->fused_activation_function)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertDiv(const tflite::DivOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ assert(inputs.size() == 2);
+ auto result = createOp<ops::DivOp>(inputs[0], inputs[1])->getOutput(0);
+ return {addFusedActivation(result, opts->fused_activation_function)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertMax(const std::vector<mir::Operation::Output *> &inputs)
+{
+ assert(inputs.size() == 2);
+ auto result = createOp<ops::MaxOp>(inputs[0], inputs[1])->getOutput(0);
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertSquaredDifference(const std::vector<mir::Operation::Output *> &inputs)
+{
+ assert(inputs.size() == 2);
+ auto result = createOp<ops::SubOp>(inputs[0], inputs[1])->getOutput(0);
+ result = createOp<ops::MulOp>(result, result)->getOutput(0);
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertMean(const tflite::ReducerOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+ mir::Tensor<int32_t> axes_tensor(extractTensor(inputs.at(1)));
+
+ std::vector<int32_t> axes = convertIntTensorToVector<int32_t>(axes_tensor);
+ auto result = createOp<ops::ReduceMeanOp>(input, axes, opts->keep_dims);
+ return {result->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertFullyConnected(const tflite::FullyConnectedOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+ auto weights = inputs.at(1);
+ auto bias = inputs.at(2);
+
+ // Flatten input to 2-D shape.
+ const auto &input_shape = input->getShape();
+ int32_t outer_size = input_shape.dim(0);
+ int32_t inner_size = input_shape.numElements() / outer_size;
+ auto flatten = createOp<ops::ReshapeOp>(input, Shape{outer_size, inner_size})->getOutput(0);
+
+ // Transpose the weights.
+ const std::vector<std::size_t> axis_order{1, 0};
+ weights = createOp<ops::TransposeOp>(weights, axis_order)->getOutput(0);
+
+ mir::Operation::Output *result;
+ if (input->getType().isQuantized())
+ {
+ result = createOp<ops::FullyConnectedOp>(flatten, weights, bias)->getOutput(0);
+ }
+ else // TODO Fuse bias to other backends
+ {
+ result = createOp<ops::FullyConnectedOp>(flatten, weights)->getOutput(0);
+ result = createOp<ops::AddOp>(result, bias)->getOutput(0);
+ }
+ return {addFusedActivation(result, opts->fused_activation_function)};
+}
+
+mir::Operation::Output *
+TFLiteOpCreator::addFusedActivation(mir::Operation::Output *input,
+ tflite::ActivationFunctionType activation_type)
+{
+ switch (activation_type)
+ {
+ case tflite::ActivationFunctionType_NONE:
+ return input;
+ case tflite::ActivationFunctionType_RELU:
+ return createOp<ops::ReluOp>(input)->getOutput(0);
+ case tflite::ActivationFunctionType_RELU6:
+ return createOp<ops::CappedReluOp>(input, 6)->getOutput(0);
+ case tflite::ActivationFunctionType_TANH:
+ return createOp<ops::TanhOp>(input)->getOutput(0);
+ default:
+ throw std::runtime_error(std::string("Unsupported activation type: ") +
+ tflite::EnumNameActivationFunctionType(activation_type));
+ }
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertSqueeze(const tflite::SqueezeOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+
+ std::vector<int32_t> squeeze_dims(opts->squeeze_dims.begin(), opts->squeeze_dims.end());
+ auto result = createOp<ops::SqueezeOp>(input, squeeze_dims);
+ return {result->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertPad(const tflite::PadOptionsT * /*opts*/,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+ mir::Tensor<int32_t> paddings_tensor(extractTensor(inputs.at(1)));
+
+ const auto &input_shape = input->getShape();
+ const int num_dims = input_shape.rank();
+
+ mir::PadOpAttributes attributes(num_dims);
+ for (int i = 0; i < num_dims; i++)
+ {
+ attributes.padding_before[i] = paddings_tensor.at(mir::Index({i, 0}));
+ attributes.padding_after[i] = paddings_tensor.at(mir::Index({i, 1}));
+ }
+
+ auto result = createOp<ops::PadOp>(input, attributes)->getOutput(0);
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertTanh(const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+
+ auto result = createOp<ops::TanhOp>(input);
+ return {result->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertReLU(const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+
+ auto result = createOp<ops::ReluOp>(input);
+ return {result->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertReLU6(const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+
+ auto result = createOp<ops::CappedReluOp>(input, 6);
+ return {result->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertRsqrt(const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+
+ const float one_value = 1.0f;
+ mir::TensorVariant one_tensor({mir::DataType::FLOAT32, {}}, &one_value);
+ auto one = createOp<ops::ConstantOp>(one_tensor)->getOutput(0);
+ auto sqrt = createOp<ops::SqrtOp>(input)->getOutput(0);
+ auto result = createOp<ops::DivOp>(one, sqrt)->getOutput(0);
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertSqrt(const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+
+ auto result = createOp<ops::SqrtOp>(input)->getOutput(0);
+ return {result};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertLogistic(const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+
+ auto result = createOp<ops::SigmoidOp>(input);
+ return {result->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertTranspose(const tflite::TransposeOptionsT * /*opts*/,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+ mir::Tensor<int32_t> perm_tensor(extractTensor(inputs.at(1)));
+
+ std::vector<std::size_t> axis_order = convertIntTensorToVector<std::size_t>(perm_tensor);
+ auto result = createOp<ops::TransposeOp>(input, axis_order);
+ return {result->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertStridedSlice(const tflite::StridedSliceOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ if (opts->ellipsis_mask != 0)
+ throw std::runtime_error("StridedSlice: parameter 'ellipsis_mask' is not supported.");
+
+ if (opts->new_axis_mask != 0)
+ throw std::runtime_error("StridedSlice: parameter 'new_axis_mask' is not supported.");
+
+ auto input = inputs.at(0);
+ mir::Tensor<int32_t> begin_tensor(extractTensor(inputs.at(1)));
+ mir::Tensor<int32_t> end_tensor(extractTensor(inputs.at(2)));
+ mir::Tensor<int32_t> strides_tensor(extractTensor(inputs.at(3)));
+
+ std::vector<int32_t> begin = convertIntTensorToVector<int32_t>(begin_tensor);
+ std::vector<int32_t> end = convertIntTensorToVector<int32_t>(end_tensor);
+ std::vector<int32_t> strides = convertIntTensorToVector<int32_t>(strides_tensor);
+
+ int32_t begin_mask = opts->begin_mask;
+ int32_t end_mask = opts->end_mask;
+ int32_t shrink_axis_mask = opts->shrink_axis_mask;
+
+ const auto &input_shape = input->getShape();
+ int32_t num_dims = input_shape.rank();
+
+ for (int32_t stride : strides)
+ {
+ if (stride != 1)
+ throw std::runtime_error("StridedSlice: parameter 'strides' is not supported");
+ }
+
+ Shape start(num_dims);
+ Shape size(num_dims);
+ std::vector<int32_t> squeeze_dims;
+ for (int axis = 0; axis < num_dims; axis++)
+ {
+ if (static_cast<uint32_t>(begin_mask) & (1u << static_cast<uint32_t>(axis)))
+ start.dim(axis) = 0;
+ else
+ start.dim(axis) = begin.at(static_cast<uint64_t>(axis));
+
+ if (static_cast<uint32_t>(end_mask) & (1u << static_cast<uint32_t>(axis)))
+ size.dim(axis) = input_shape.dim(axis) - start.dim(axis);
+ else
+ size.dim(axis) = end.at(static_cast<uint64_t>(axis)) - start.dim(axis);
+
+ if (static_cast<uint32_t>(shrink_axis_mask) & (1u << static_cast<uint32_t>(axis)))
+ squeeze_dims.push_back(axis);
+ }
+
+ auto result = createOp<ops::SliceOp>(input, start, size);
+ result = createOp<ops::SqueezeOp>(result->getOutput(0), squeeze_dims);
+ return {result->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertLeakyReLU(const tflite::LeakyReluOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto input = inputs.at(0);
+
+ auto result = createOp<ops::LeakyReluOp>(input, opts->alpha);
+ return {result->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertShape(const tflite::ShapeOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ if (opts->out_type != tflite::TensorType_INT32)
+ {
+ throw std::runtime_error(std::string("SHAPE: Unsupported tensor type: ") +
+ EnumNameTensorType(opts->out_type));
+ }
+
+ const auto &input_shape = inputs[0]->getShape();
+ int32_t rank = input_shape.rank();
+ std::vector<int32_t> data;
+ data.reserve(static_cast<uint64_t>(rank));
+ for (int32_t i = 0; i < rank; i++)
+ data.emplace_back(input_shape.dim(i));
+ mir::TensorVariant tensor({mir::DataType::INT32, {rank}}, data.data());
+ auto result = createOp<ops::ConstantOp>(tensor);
+ return {result->getOutput(0)};
+}
+
+std::vector<mir::Operation::Output *>
+TFLiteOpCreator::convertHardSwish(const tflite::HardSwishOptionsT *,
+ const std::vector<mir::Operation::Output *> &inputs)
+{
+ auto result = createOp<ops::HardSwishOp>(inputs[0])->getOutput(0);
+ return {result};
+}
+
+} // namespace mir_tflite
diff --git a/compiler/mir/src/mir_tflite_importer/tflite_op_creator.h b/compiler/mir/src/mir_tflite_importer/tflite_op_creator.h
new file mode 100644
index 000000000..820436f33
--- /dev/null
+++ b/compiler/mir/src/mir_tflite_importer/tflite_op_creator.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MIR_TFLITE_OP_CREATOR_H
+#define MIR_TFLITE_OP_CREATOR_H
+
+#include "schema_generated.h"
+
+#include "mir/Graph.h"
+
+#include <utility>
+#include <vector>
+
+namespace mir_tflite
+{
+
+class TFLiteOpCreator
+{
+public:
+ explicit TFLiteOpCreator(mir::Graph *g) : _graph(g) {}
+
+ std::vector<mir::Operation::Output *>
+ convertConv2D(const tflite::Conv2DOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertDepthwiseConv2D(const tflite::DepthwiseConv2DOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertConcatenation(const tflite::ConcatenationOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertMaxPool2D(const tflite::Pool2DOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertAveragePool2D(const tflite::Pool2DOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertMean(const tflite::ReducerOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertSoftmax(const tflite::SoftmaxOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertSlice(const tflite::SliceOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertReshape(const tflite::ReshapeOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertFullyConnected(const tflite::FullyConnectedOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertResizeNearestNeighbor(const tflite::ResizeNearestNeighborOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertLogistic(const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertRsqrt(const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertSqrt(const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertSqueeze(const tflite::SqueezeOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertAdd(const tflite::AddOptionsT *opts, const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertSub(const tflite::SubOptionsT *opts, const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertMul(const tflite::MulOptionsT *opts, const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertDiv(const tflite::DivOptionsT *opts, const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertMax(const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertSquaredDifference(const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertTanh(const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertReLU(const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertReLU6(const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertTransposeConv(const tflite::TransposeConvOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertPad(const tflite::PadOptionsT *opts, const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertTranspose(const tflite::TransposeOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertStridedSlice(const tflite::StridedSliceOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertLeakyReLU(const tflite::LeakyReluOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertShape(const tflite::ShapeOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+ std::vector<mir::Operation::Output *>
+ convertHardSwish(const tflite::HardSwishOptionsT *opts,
+ const std::vector<mir::Operation::Output *> &inputs);
+
+private:
+ mir::Graph *_graph;
+
+ mir::Operation::Output *addFusedActivation(mir::Operation::Output *input,
+ tflite::ActivationFunctionType activation_type);
+
+ template <typename OpType, typename... Types> mir::Operation *createOp(Types &&... args);
+};
+
+template <typename OpType, typename... Types>
+mir::Operation *TFLiteOpCreator::createOp(Types &&... args)
+{
+ return _graph->create<OpType>(std::forward<Types>(args)...);
+}
+
+} // namespace mir_tflite
+
+#endif // MIR_TFLITE_OP_CREATOR_H
diff --git a/compiler/mir/src/ops/AvgPool2DOp.cpp b/compiler/mir/src/ops/AvgPool2DOp.cpp
index 5a16e3a95..52b67303f 100644
--- a/compiler/mir/src/ops/AvgPool2DOp.cpp
+++ b/compiler/mir/src/ops/AvgPool2DOp.cpp
@@ -21,19 +21,19 @@ namespace mir
namespace ops
{
-void AvgPool2DOp::inferOutputShapes()
+void AvgPool2DOp::inferOutputTypes()
{
const auto &input_shape = getInputShape(0);
- const int batch_dim_index = getDataBatchDimIndex(_data_format);
- const int channel_dim_index = getDataChannelDimIndex(_data_format);
+ const int batch_dim_index = getDataBatchDimIndex(_attributes.data_format);
+ const int channel_dim_index = getDataChannelDimIndex(_attributes.data_format);
constexpr int num_spatial_dims = 2;
assert(input_shape.rank() == 4);
- assert(_window_size.size() == num_spatial_dims);
- assert(_strides.size() == num_spatial_dims);
- assert(_padding_before.size() == num_spatial_dims);
- assert(_padding_after.size() == num_spatial_dims);
+ assert(_attributes.window.size() == num_spatial_dims);
+ assert(_attributes.strides.size() == num_spatial_dims);
+ assert(_attributes.padding_before.size() == num_spatial_dims);
+ assert(_attributes.padding_after.size() == num_spatial_dims);
Shape output_shape(4);
@@ -42,16 +42,18 @@ void AvgPool2DOp::inferOutputShapes()
for (int i = 0; i < num_spatial_dims; i++)
{
- const int spatial_dim_index = getDataSpatialDimIndex(_data_format, i);
- const std::int32_t padded_input =
- input_shape.dim(spatial_dim_index) + _padding_before.at(i) + _padding_after.at(i);
+ const int spatial_dim_index = getDataSpatialDimIndex(_attributes.data_format, i);
+ const std::int32_t padded_input = input_shape.dim(spatial_dim_index) +
+ _attributes.padding_before.at(i) +
+ _attributes.padding_after.at(i);
// out_size = ceil((in_size - window_size + 1) / stride) =
// (in_size - window_size + 1 + stride - 1) / stride =
// (in_size - window_size) / stride + 1
- output_shape.dim(spatial_dim_index) = (padded_input - _window_size[i]) / _strides[i] + 1;
+ output_shape.dim(spatial_dim_index) =
+ (padded_input - _attributes.window[i]) / _attributes.strides[i] + 1;
}
- setOutputShape(0, output_shape);
+ setOutputType(0, {getInput(0)->getElementType(), output_shape});
}
} // namespace ops
diff --git a/compiler/mir/src/ops/BinaryElementwiseOp.cpp b/compiler/mir/src/ops/BinaryElementwiseOp.cpp
index 06df8cd05..982df3193 100644
--- a/compiler/mir/src/ops/BinaryElementwiseOp.cpp
+++ b/compiler/mir/src/ops/BinaryElementwiseOp.cpp
@@ -16,48 +16,24 @@
#include "mir/ops/BinaryElementwiseOp.h"
-#include <cassert>
-
namespace mir
{
namespace ops
{
-void BinaryElementwiseOp::inferOutputShapes()
+void BinaryElementwiseOp::inferOutputTypes()
{
- int max_rank = getInputShape(0).rank();
- size_t max_ind = 0;
- for (size_t i = 0; i < getNumInputs(); i++)
- {
- if (max_rank < getInputShape(i).rank())
- {
- max_rank = getInputShape(i).rank();
- max_ind = i;
- }
- }
- Shape max_shape = getInputShape(max_ind);
- for (size_t i = 0; i < getNumInputs(); i++)
- {
- const auto &current_shape = getInputShape(i);
- _needs_broadcast = _needs_broadcast || max_shape != current_shape; // check not equal
- const int rank = current_shape.rank();
- for (int axis = 0; axis < rank; axis++)
- {
- auto current_dim = current_shape.dim(rank - axis - 1);
- // get max for all axes
- if (max_shape.dim(max_rank - axis - 1) == 1 && current_dim != 1)
- {
- max_shape.dim(max_rank - axis - 1) = current_dim;
- }
- else
- {
- assert((current_dim == 1 || current_dim == max_shape.dim(max_rank - axis - 1)) &&
- "Incompatible shapes in broadcast!");
- }
- }
- }
- setOutputShape(0, max_shape);
+ const auto &lhs_shape = getInputShape(0);
+ const auto &rhs_shape = getInputShape(1);
+
+ auto dt = getInput(0)->getElementType();
+
+ // lhs and rhs have equal element types
+ assert(dt == getInput(1)->getElementType());
+ auto out_shape = broadcastShapes(lhs_shape, rhs_shape);
+
+ setOutputType(0, {dt, out_shape});
}
} // namespace ops
-} // namespace mir \ No newline at end of file
+} // namespace mir
diff --git a/compiler/mir/src/ops/BroadcastOp.cpp b/compiler/mir/src/ops/BroadcastOp.cpp
new file mode 100644
index 000000000..b9f915a83
--- /dev/null
+++ b/compiler/mir/src/ops/BroadcastOp.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "mir/ops/BroadcastOp.h"
+#include "mir/ops/ConstantOp.h"
+#include "mir/Tensor.h"
+
+namespace mir
+{
+namespace ops
+{
+
+void BroadcastOp::inferOutputTypes(const Shape &target_shape)
+{
+ const Shape &input_shape = getInputShape(0);
+ Shape output_shape = broadcastShapes(input_shape, target_shape);
+
+ setOutputType(0, {getInput(0)->getElementType(), output_shape});
+}
+
+} // namespace ops
+} // namespace mir
diff --git a/compiler/mir/src/ops/ConcatOp.cpp b/compiler/mir/src/ops/ConcatOp.cpp
index 34a29ed99..c8c4764ad 100644
--- a/compiler/mir/src/ops/ConcatOp.cpp
+++ b/compiler/mir/src/ops/ConcatOp.cpp
@@ -21,13 +21,19 @@ namespace mir
namespace ops
{
-void ConcatOp::inferOutputShapes()
+void ConcatOp::inferOutputTypes()
{
Shape output_shape(getInputShape(0));
output_shape.dim(_axis) = 0;
+ auto element_type = getInput(0)->getElementType();
+
for (std::size_t i = 0; i < getNumInputs(); ++i)
+ {
output_shape.dim(_axis) += getInputShape(i).dim(_axis);
- setOutputShape(0, output_shape);
+ assert(getInput(i)->getElementType() == element_type);
+ }
+
+ setOutputType(0, {element_type, output_shape});
}
} // namespace ops
diff --git a/compiler/mir/src/ops/Conv2DOp.cpp b/compiler/mir/src/ops/Conv2DOp.cpp
index 9967aa51e..1addc5734 100644
--- a/compiler/mir/src/ops/Conv2DOp.cpp
+++ b/compiler/mir/src/ops/Conv2DOp.cpp
@@ -21,39 +21,46 @@ namespace mir
namespace ops
{
-void Conv2DOp::inferOutputShapes()
+void Conv2DOp::inferOutputTypes()
{
- // Kernel shape: [Co, Hk, Wk, Ci].
+ // Kernel shape: [O, H, W, I / M].
const auto &input_shape = getInputShape(0);
const auto &kernel_shape = getInputShape(1);
- const int batch_dim_index = getDataBatchDimIndex(_data_format);
- const int channel_dim_index = getDataChannelDimIndex(_data_format);
+ const int batch_dim_index = getDataBatchDimIndex(_attributes.data_format);
+ const int channel_dim_index = getDataChannelDimIndex(_attributes.data_format);
- assert(input_shape.rank() == 4);
- assert(kernel_shape.rank() == 4);
- assert(kernel_shape.dim(3) == input_shape.dim(channel_dim_index));
- assert(_strides.size() == 2);
- assert(_padding_before.size() == 2);
- assert(_padding_after.size() == 2);
+ constexpr int num_spatial_dims = 2;
- Shape output_shape(4);
+ assert(input_shape.rank() == 2 + num_spatial_dims);
+ assert(kernel_shape.rank() == 2 + num_spatial_dims);
+ assert(kernel_shape.dim(3) * _attributes.num_groups == input_shape.dim(channel_dim_index));
+ assert(kernel_shape.dim(0) % _attributes.num_groups == 0);
+
+ assert(_attributes.strides.size() == num_spatial_dims);
+ assert(_attributes.padding_before.size() == num_spatial_dims);
+ assert(_attributes.padding_after.size() == num_spatial_dims);
+
+ Shape output_shape(2 + num_spatial_dims);
output_shape.dim(batch_dim_index) = input_shape.dim(batch_dim_index);
output_shape.dim(channel_dim_index) = kernel_shape.dim(0);
- for (int i = 0; i < 2; i++)
+ for (int i = 0; i < num_spatial_dims; i++)
{
- const int spatial_dim_index = getDataSpatialDimIndex(_data_format, i);
- const std::int32_t padded_input =
- input_shape.dim(spatial_dim_index) + _padding_before[i] + _padding_after[i];
+ const int spatial_dim_index = getDataSpatialDimIndex(_attributes.data_format, i);
+ const std::int32_t padded_input = input_shape.dim(spatial_dim_index) +
+ _attributes.padding_before[i] + _attributes.padding_after[i];
// out_size = ceil((in_size - kernel_size + 1) / stride) =
// (in_size - kernel_size + 1 + stride - 1) / stride =
// (in_size - kernel_size) / stride + 1
output_shape.dim(spatial_dim_index) =
- (padded_input - kernel_shape.dim(1 + i)) / _strides[i] + 1;
+ (padded_input - kernel_shape.dim(1 + i)) / _attributes.strides[i] + 1;
}
- setOutputShape(0, output_shape);
+ auto dt = getInput(0)->getElementType();
+ assert(dt == getInput(1)->getElementType() && "kernel should have same data type as input");
+
+ setOutputType(0, {dt, output_shape});
}
} // namespace ops
diff --git a/compiler/mir/src/ops/DeConv2DOp.cpp b/compiler/mir/src/ops/DeConv2DOp.cpp
index 75a4a24d9..35b111bc0 100644
--- a/compiler/mir/src/ops/DeConv2DOp.cpp
+++ b/compiler/mir/src/ops/DeConv2DOp.cpp
@@ -24,7 +24,7 @@ namespace ops
// See the formulas at https://github.com/onnx/onnx/blob/master/docs/Operators.md#convtranspose.
void DeConv2DOp::inferPaddings()
{
- assert(_padding_type != PaddingType::Explicit);
+ assert(_attributes.padding_type != PaddingType::Explicit);
const auto &input_shape = getInputShape(0);
const auto &kernel_shape = getInputShape(1);
@@ -34,23 +34,24 @@ void DeConv2DOp::inferPaddings()
for (int i = 0; i < num_spatial_dims; ++i)
{
- const int spatial_dim_index = getDataSpatialDimIndex(_data_format, i);
- const std::int32_t total_padding = (input_shape.dim(spatial_dim_index) - 1) * _strides[i] +
- kernel_shape.dim(i) - output_shape.dim(spatial_dim_index);
+ const int spatial_dim_index = getDataSpatialDimIndex(_attributes.data_format, i);
+ const std::int32_t total_padding =
+ (input_shape.dim(spatial_dim_index) - 1) * _attributes.strides[i] + kernel_shape.dim(i) -
+ output_shape.dim(spatial_dim_index);
- switch (_padding_type)
+ switch (_attributes.padding_type)
{
case PaddingType::Valid:
// TODO Figure out what to do.
assert(false);
break;
case PaddingType::SameLower:
- _padding_after[i] = total_padding / 2;
- _padding_before[i] = total_padding - _padding_after[i];
+ _attributes.padding_after[i] = total_padding / 2;
+ _attributes.padding_before[i] = total_padding - _attributes.padding_after[i];
break;
case PaddingType::SameUpper:
- _padding_before[i] = total_padding / 2;
- _padding_after[i] = total_padding - _padding_before[i];
+ _attributes.padding_before[i] = total_padding / 2;
+ _attributes.padding_after[i] = total_padding - _attributes.padding_before[i];
break;
default:
assert(false);
@@ -59,15 +60,15 @@ void DeConv2DOp::inferPaddings()
}
// See the formulas at https://github.com/onnx/onnx/blob/master/docs/Operators.md#convtranspose.
-void DeConv2DOp::inferOutputShapes()
+void DeConv2DOp::inferOutputTypes()
{
- assert(_padding_type == PaddingType::Explicit);
+ assert(_attributes.padding_type == PaddingType::Explicit);
// Kernel shape: [Hk, Wk, Co, Ci]
const auto &input_shape = getInputShape(0);
const auto &kernel_shape = getInputShape(1);
- const int batch_dim_index = getDataBatchDimIndex(_data_format);
- const int channel_dim_index = getDataChannelDimIndex(_data_format);
+ const int batch_dim_index = getDataBatchDimIndex(_attributes.data_format);
+ const int channel_dim_index = getDataChannelDimIndex(_attributes.data_format);
assert(input_shape.rank() == 4);
assert(kernel_shape.rank() == 4);
@@ -82,13 +83,13 @@ void DeConv2DOp::inferOutputShapes()
for (int i = 0; i < num_spatial_dims; i++)
{
- const int spatial_dim_index = getDataSpatialDimIndex(_data_format, i);
- output_shape.dim(spatial_dim_index) = (input_shape.dim(spatial_dim_index) - 1) * _strides[i] +
- kernel_shape.dim(i) -
- (_padding_before.at(i) + _padding_after.at(i));
+ const int spatial_dim_index = getDataSpatialDimIndex(_attributes.data_format, i);
+ output_shape.dim(spatial_dim_index) =
+ (input_shape.dim(spatial_dim_index) - 1) * _attributes.strides[i] + kernel_shape.dim(i) -
+ (_attributes.padding_before.at(i) + _attributes.padding_after.at(i));
}
- setOutputShape(0, output_shape);
+ setOutputType(0, {getInput(0)->getElementType(), output_shape});
}
} // namespace ops
diff --git a/compiler/mir/src/ops/DepthwiseConv2DOp.cpp b/compiler/mir/src/ops/DepthwiseConv2DOp.cpp
index ff128b379..0154bcd09 100644
--- a/compiler/mir/src/ops/DepthwiseConv2DOp.cpp
+++ b/compiler/mir/src/ops/DepthwiseConv2DOp.cpp
@@ -21,20 +21,20 @@ namespace mir
namespace ops
{
-void DepthwiseConv2DOp::inferOutputShapes()
+void DepthwiseConv2DOp::inferOutputTypes()
{
// Kernel shape: [Hk, Wk, Ci, M].
const auto &input_shape = getInputShape(0);
const auto &kernel_shape = getInputShape(1);
- const int batch_dim_index = getDataBatchDimIndex(_data_format);
- const int channel_dim_index = getDataChannelDimIndex(_data_format);
+ const int batch_dim_index = getDataBatchDimIndex(_attributes.data_format);
+ const int channel_dim_index = getDataChannelDimIndex(_attributes.data_format);
assert(input_shape.rank() == 4);
assert(kernel_shape.rank() == 4);
assert(input_shape.dim(channel_dim_index) == kernel_shape.dim(2));
- assert(_strides.size() == 2);
- assert(_padding_before.size() == 2);
- assert(_padding_after.size() == 2);
+ assert(_attributes.strides.size() == 2);
+ assert(_attributes.padding_before.size() == 2);
+ assert(_attributes.padding_after.size() == 2);
Shape output_shape(4);
@@ -43,16 +43,17 @@ void DepthwiseConv2DOp::inferOutputShapes()
for (int i = 0; i < 2; i++)
{
- const int spatial_dim_index = getDataSpatialDimIndex(_data_format, i);
- const std::int32_t padded_input =
- input_shape.dim(spatial_dim_index) + _padding_before[i] + _padding_after[i];
+ const int spatial_dim_index = getDataSpatialDimIndex(_attributes.data_format, i);
+ const std::int32_t padded_input = input_shape.dim(spatial_dim_index) +
+ _attributes.padding_before[i] + _attributes.padding_after[i];
// out_size = ceil((in_size - kernel_size + 1) / stride) =
// (in_size - kernel_size + 1 + stride - 1) / stride =
// (in_size - kernel_size) / stride + 1
- output_shape.dim(spatial_dim_index) = (padded_input - kernel_shape.dim(i)) / _strides[i] + 1;
+ output_shape.dim(spatial_dim_index) =
+ (padded_input - kernel_shape.dim(i)) / _attributes.strides[i] + 1;
}
- setOutputShape(0, output_shape);
+ setOutputType(0, {getInput(0)->getElementType(), output_shape});
}
} // namespace ops
diff --git a/compiler/mir/src/ops/FullyConnectedOp.cpp b/compiler/mir/src/ops/FullyConnectedOp.cpp
index c362757f6..2865b6c87 100644
--- a/compiler/mir/src/ops/FullyConnectedOp.cpp
+++ b/compiler/mir/src/ops/FullyConnectedOp.cpp
@@ -21,7 +21,7 @@ namespace mir
namespace ops
{
-void FullyConnectedOp::inferOutputShapes()
+void FullyConnectedOp::inferOutputTypes()
{
auto &input_shape = getInputShape(0);
auto &weights_shape = getInputShape(1);
@@ -38,7 +38,8 @@ void FullyConnectedOp::inferOutputShapes()
Shape output_shape = weights_shape;
output_shape.dim(weights_rank - 1) = weights_shape.dim(weights_rank - 1);
output_shape.dim(weights_rank - 2) = input_shape.dim(weights_rank - 2);
- setOutputShape(0, output_shape);
+
+ setOutputType(0, {getInput(0)->getElementType(), output_shape});
}
} // namespace ops
diff --git a/compiler/mir/src/ops/GatherOp.cpp b/compiler/mir/src/ops/GatherOp.cpp
index 259642bfb..264ac2618 100644
--- a/compiler/mir/src/ops/GatherOp.cpp
+++ b/compiler/mir/src/ops/GatherOp.cpp
@@ -21,7 +21,7 @@ namespace mir
namespace ops
{
-void GatherOp::inferOutputShapes()
+void GatherOp::inferOutputTypes()
{
const auto &data_shape = getInputShape(0);
const auto &indices_shape = getInputShape(1);
@@ -45,7 +45,7 @@ void GatherOp::inferOutputShapes()
for (int32_t i = axis + 1; i < data_rank; ++i)
output_shape.dim(output_index++) = data_shape.dim(i);
- setOutputShape(0, output_shape);
+ setOutputType(0, {getInput(0)->getElementType(), output_shape});
}
} // namespace ops
diff --git a/compiler/mir/src/ops/MaxPool2DOp.cpp b/compiler/mir/src/ops/MaxPool2DOp.cpp
index df7e5383f..38e72424e 100644
--- a/compiler/mir/src/ops/MaxPool2DOp.cpp
+++ b/compiler/mir/src/ops/MaxPool2DOp.cpp
@@ -21,19 +21,19 @@ namespace mir
namespace ops
{
-void MaxPool2DOp::inferOutputShapes()
+void MaxPool2DOp::inferOutputTypes()
{
const auto &input_shape = getInputShape(0);
- const int batch_dim_index = getDataBatchDimIndex(_data_format);
- const int channel_dim_index = getDataChannelDimIndex(_data_format);
+ const int batch_dim_index = getDataBatchDimIndex(_attributes.data_format);
+ const int channel_dim_index = getDataChannelDimIndex(_attributes.data_format);
constexpr int num_spatial_dims = 2;
assert(input_shape.rank() == 4);
- assert(_window_size.size() == num_spatial_dims);
- assert(_strides.size() == num_spatial_dims);
- assert(_padding_before.size() == num_spatial_dims);
- assert(_padding_after.size() == num_spatial_dims);
+ assert(_attributes.window.size() == num_spatial_dims);
+ assert(_attributes.strides.size() == num_spatial_dims);
+ assert(_attributes.padding_before.size() == num_spatial_dims);
+ assert(_attributes.padding_after.size() == num_spatial_dims);
Shape output_shape(4);
@@ -42,16 +42,18 @@ void MaxPool2DOp::inferOutputShapes()
for (int i = 0; i < num_spatial_dims; i++)
{
- const int spatial_dim_index = getDataSpatialDimIndex(_data_format, i);
- const std::int32_t padded_input =
- input_shape.dim(spatial_dim_index) + _padding_before.at(i) + _padding_after.at(i);
+ const int spatial_dim_index = getDataSpatialDimIndex(_attributes.data_format, i);
+ const std::int32_t padded_input = input_shape.dim(spatial_dim_index) +
+ _attributes.padding_before.at(i) +
+ _attributes.padding_after.at(i);
// out_size = ceil((in_size - window_size + 1) / stride) =
// (in_size - window_size + 1 + stride - 1) / stride =
// (in_size - window_size) / stride + 1
- output_shape.dim(spatial_dim_index) = (padded_input - _window_size[i]) / _strides[i] + 1;
+ output_shape.dim(spatial_dim_index) =
+ (padded_input - _attributes.window[i]) / _attributes.strides[i] + 1;
}
- setOutputShape(0, output_shape);
+ setOutputType(0, {getInput(0)->getElementType(), output_shape});
}
} // namespace ops
diff --git a/compiler/mir/src/ops/PadOp.cpp b/compiler/mir/src/ops/PadOp.cpp
index 75d8662b4..465856d92 100644
--- a/compiler/mir/src/ops/PadOp.cpp
+++ b/compiler/mir/src/ops/PadOp.cpp
@@ -21,7 +21,7 @@ namespace mir
namespace ops
{
-void PadOp::inferOutputShapes()
+void PadOp::inferOutputTypes()
{
const Shape &input_shape = getInputShape(0);
int32_t num_dims = input_shape.rank();
@@ -29,10 +29,11 @@ void PadOp::inferOutputShapes()
Shape out_shape(num_dims);
for (int32_t dim = 0; dim < num_dims; ++dim)
{
- out_shape.dim(dim) = _padding_before[dim] + input_shape.dim(dim) + _padding_after[dim];
+ out_shape.dim(dim) =
+ _attributes.padding_before[dim] + input_shape.dim(dim) + _attributes.padding_after[dim];
}
- setOutputShape(0, out_shape);
+ setOutputType(0, {getInput(0)->getElementType(), out_shape});
}
} // namespace ops
diff --git a/compiler/mir/src/ops/ReduceOp.cpp b/compiler/mir/src/ops/ReduceOp.cpp
index c03f1097d..ac4c322e1 100644
--- a/compiler/mir/src/ops/ReduceOp.cpp
+++ b/compiler/mir/src/ops/ReduceOp.cpp
@@ -21,7 +21,7 @@ namespace mir
namespace ops
{
-void ReduceOp::inferOutputShapes()
+void ReduceOp::inferOutputTypes()
{
const auto &input_shape = getInputShape(0);
const auto &reduction_dims = getReductionDims();
@@ -54,7 +54,7 @@ void ReduceOp::inferOutputShapes()
output_shape = Shape(out_dims);
}
- setOutputShape(0, output_shape);
+ setOutputType(0, {getInput(0)->getElementType(), output_shape});
}
} // namespace ops
diff --git a/compiler/mir/src/ops/SliceOp.cpp b/compiler/mir/src/ops/SliceOp.cpp
index 9012f2cde..6e65be70a 100644
--- a/compiler/mir/src/ops/SliceOp.cpp
+++ b/compiler/mir/src/ops/SliceOp.cpp
@@ -22,7 +22,7 @@ namespace ops
{
// Only supports 4d inputs
-void SliceOp::inferOutputShapes()
+void SliceOp::inferOutputTypes()
{
const Shape &input_shape = getInputShape(0);
assert(input_shape.rank() <= 4 && "Support only 4D tensors or smaller");
@@ -38,8 +38,8 @@ void SliceOp::inferOutputShapes()
output_shape.dim(i) = _sizes.dim(i);
}
}
- setOutputShape(0, output_shape);
+ setOutputType(0, {getInput(0)->getElementType(), output_shape});
}
} // namespace ops
-} // namespace mir \ No newline at end of file
+} // namespace mir
diff --git a/compiler/mir/src/ops/SqueezeOp.cpp b/compiler/mir/src/ops/SqueezeOp.cpp
index 521e5fefa..63f7d43f3 100644
--- a/compiler/mir/src/ops/SqueezeOp.cpp
+++ b/compiler/mir/src/ops/SqueezeOp.cpp
@@ -21,11 +21,12 @@ namespace mir
namespace ops
{
-void SqueezeOp::inferOutputShapes()
+void SqueezeOp::inferOutputTypes()
{
assert(getNumInputs() == 1);
const auto &input_shape = getInputShape(0);
+ auto dt = getInput(0)->getElementType();
int32_t input_rank = input_shape.rank();
std::vector<int32_t> dims_to_squeeze;
@@ -51,7 +52,7 @@ void SqueezeOp::inferOutputShapes()
if (dims_to_squeeze.size() == static_cast<size_t>(input_rank))
{
// Input shape have 1s in all dimensions, output shape is (1,)
- setOutputShape(0, Shape{1});
+ setOutputType(0, {dt, Shape{1}});
return;
}
@@ -73,7 +74,7 @@ void SqueezeOp::inferOutputShapes()
}
}
- setOutputShape(0, output_shape);
+ setOutputType(0, {dt, output_shape});
}
} // namespace ops
diff --git a/compiler/mir/src/ops/TransposeOp.cpp b/compiler/mir/src/ops/TransposeOp.cpp
index d62650646..92282e17d 100644
--- a/compiler/mir/src/ops/TransposeOp.cpp
+++ b/compiler/mir/src/ops/TransposeOp.cpp
@@ -25,17 +25,18 @@ TransposeOp::TransposeOp(Output *arg, const std::vector<std::size_t> &axis_order
: Operation(Type::transpose, {arg}), _axis_order(axis_order)
{
assert(_axis_order.size() == static_cast<std::size_t>(getInputShape(0).rank()));
- inferOutputShapes();
+ inferOutputTypes();
}
-void TransposeOp::inferOutputShapes()
+void TransposeOp::inferOutputTypes()
{
auto &input_shape = getInputShape(0);
Shape output_shape(input_shape.rank());
for (std::size_t i = 0; i < _axis_order.size(); ++i)
output_shape.dim(static_cast<std::int64_t>(i)) =
input_shape.dim(static_cast<int32_t>(_axis_order.at(i)));
- setOutputShape(0, output_shape);
+
+ setOutputType(0, {getInput(0)->getElementType(), output_shape});
}
} // namespace ops
diff --git a/compiler/mir/unittests/CMakeLists.txt b/compiler/mir/unittests/CMakeLists.txt
index 98f5fe6fe..4844eba3a 100644
--- a/compiler/mir/unittests/CMakeLists.txt
+++ b/compiler/mir/unittests/CMakeLists.txt
@@ -4,14 +4,13 @@ set(MIR_TEST_SOURCES
ShapeInference.cpp
ShapeRange.cpp
TensorVariant.cpp
- NodeReplacer.cpp
- Graph.cpp)
+ NodeReplacer.cpp)
if(NOT ENABLE_TEST)
return()
endif(NOT ENABLE_TEST)
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
GTest_AddTest(mir_test ${MIR_TEST_SOURCES})
target_link_libraries(mir_test mir)
diff --git a/compiler/mir/unittests/Graph.cpp b/compiler/mir/unittests/Graph.cpp
deleted file mode 100644
index 66df881ca..000000000
--- a/compiler/mir/unittests/Graph.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include "mir/Graph.h"
-#include "mir/Visitor.h"
-#include "mir/ops/ConcatOp.h"
-#include "mir/ops/InputOp.h"
-#include "mir/ops/OutputOp.h"
-#include "mir/ops/ReluOp.h"
-
-namespace
-{
-
-using namespace mir;
-
-class DumpVisitor : public Visitor
-{
-public:
- DumpVisitor(std::ostream &s) : _s(s) {}
-
- void visit(ops::InputOp &op) override { _s << "i" << std::to_string(op.getId()); };
-
- void visit(ops::ReluOp &op) override { _s << "r" << std::to_string(op.getId()); }
-
- void visit(ops::ConcatOp &op) override { _s << "c" << std::to_string(op.getId()); }
-
- std::ostream &_s;
-};
-
-TEST(Graph, ReplaceInputs)
-{
- auto g = new Graph;
-
- auto n1 = g->create<ops::InputOp>(Shape{1});
- auto n2 = g->create<ops::ReluOp>(n1->getOutput(0));
- auto n3 = g->create<ops::ReluOp>(n2->getOutput(0));
- auto n4 = g->create<ops::ReluOp>(n2->getOutput(0));
- std::vector<Operation::Output *> concat_inputs{n3->getOutput(0), n4->getOutput(0)};
- auto n5 = g->create<ops::ConcatOp>(concat_inputs, 0);
- n1->getOutput(0)->setName("op1");
- n4->getOutput(0)->setName("op4");
-
- g->replaceInputNodes({"op1", "op4"});
-
- std::stringstream ss;
- DumpVisitor d(ss);
- g->accept(&d);
-
- auto str = ss.str();
- ASSERT_TRUE(str == "i5i6r1r2c4" || str == "i6i5r1r2c4") << "str = " << str;
- delete g;
-};
-
-TEST(Graph, ReplaceOutputNodeWithInput)
-{
- auto g = new Graph;
-
- auto n1 = g->create<ops::InputOp>(Shape{});
- auto n2 = g->create<ops::ReluOp>(n1->getOutput(0));
- auto n3 = g->create<ops::OutputOp>(n2->getOutput(0));
-
- auto in2 = g->replaceWithInputNode(n2);
-
- std::vector<ops::InputOp *> expected_inputs{dynamic_cast<ops::InputOp *>(n1), in2};
- ASSERT_EQ(g->getInputs(), expected_inputs);
- delete g;
-}
-
-} // namespace
diff --git a/compiler/mir/unittests/NodeReplacer.cpp b/compiler/mir/unittests/NodeReplacer.cpp
index 41bb10419..2aa0481c6 100644
--- a/compiler/mir/unittests/NodeReplacer.cpp
+++ b/compiler/mir/unittests/NodeReplacer.cpp
@@ -44,7 +44,8 @@ public:
TEST(NodeMutatorTest, SimpleChainTest)
{
auto g = new Graph;
- auto n1 = g->create<ops::InputOp>(Shape{});
+ mir::TensorType input_type{mir::DataType::FLOAT32, Shape{}};
+ auto n1 = g->create<ops::InputOp>(input_type);
auto n2 = g->create<ops::ReluOp>(n1->getOutput(0));
auto n3 = g->create<ops::ReluOp>(n2->getOutput(0));
auto n4 = g->create<ops::ReluOp>(n2->getOutput(0));
diff --git a/compiler/mir/unittests/Operation.cpp b/compiler/mir/unittests/Operation.cpp
index e8b769e43..132f84696 100644
--- a/compiler/mir/unittests/Operation.cpp
+++ b/compiler/mir/unittests/Operation.cpp
@@ -27,12 +27,13 @@ using namespace mir;
TEST(Operation, ConnectionTest)
{
- auto op1 = new ops::InputOp(Shape{});
+ mir::TensorType input_type{mir::DataType::FLOAT32, Shape{}};
+ auto op1 = new ops::InputOp(input_type);
op1->setId(0);
auto op2 = new ops::ReshapeOp(op1->getOutput(0), Shape{});
op2->setId(1);
- ASSERT_EQ(op1, op2->getInput(0)->getProducer()->getNode());
+ ASSERT_EQ(op1, op2->getInput(0)->getNode());
delete op1;
delete op2;
@@ -42,7 +43,8 @@ TEST(Operation, InputOutputShapeTest)
{
Shape input_shape{1, 2, 3};
- ops::InputOp input(input_shape);
+ mir::TensorType input_type{mir::DataType::FLOAT32, input_shape};
+ ops::InputOp input(input_type);
ops::SoftmaxOp op(input.getOutput(0), 0);
ASSERT_EQ(input_shape, input.getOutputShape(0));
@@ -53,7 +55,8 @@ TEST(Operation, SoftmaxAxisTest)
{
Shape input_shape{1, 2, 3};
- ops::InputOp input(input_shape);
+ mir::TensorType input_type{mir::DataType::FLOAT32, input_shape};
+ ops::InputOp input(input_type);
ops::SoftmaxOp op_1(input.getOutput(0), 1);
ASSERT_EQ(op_1.getAxis(), 1);
@@ -69,7 +72,8 @@ TEST(Operation, ConcatAxisTest)
{
Shape in_shape{1, 2, 3};
- ops::InputOp input1(in_shape), input2(in_shape);
+ mir::TensorType in_type{mir::DataType::FLOAT32, in_shape};
+ ops::InputOp input1(in_type), input2(in_type);
ops::ConcatOp op_1({input1.getOutput(0), input2.getOutput(0)}, 1);
ASSERT_EQ(op_1.getAxis(), 1);
diff --git a/compiler/mir/unittests/ShapeInference.cpp b/compiler/mir/unittests/ShapeInference.cpp
index 4d7017f77..bae4ec5e2 100644
--- a/compiler/mir/unittests/ShapeInference.cpp
+++ b/compiler/mir/unittests/ShapeInference.cpp
@@ -28,6 +28,19 @@
using namespace mir;
+TEST(ShapeInferenceTest, BidirectionalBroadcast)
+{
+ const Shape shape1{2, 1, 2};
+ const Shape shape2{3, 1};
+ const Shape reference{2, 3, 2};
+
+ const Shape result1 = broadcastShapes(shape1, shape2);
+ const Shape result2 = broadcastShapes(shape2, shape1);
+
+ ASSERT_EQ(result1, reference);
+ ASSERT_EQ(result2, reference);
+}
+
TEST(ShapeInferenceTest, ReshapeAutoDimension)
{
Graph g;
@@ -35,7 +48,8 @@ TEST(ShapeInferenceTest, ReshapeAutoDimension)
Shape input_shape{10, 2, 5};
Shape expected_shape{10, 1, 10};
- auto input = g.create<ops::InputOp>(input_shape);
+ mir::TensorType input_type{mir::DataType::FLOAT32, input_shape};
+ auto input = g.create<ops::InputOp>(input_type);
auto op = g.create<ops::ReshapeOp>(input->getOutput(0), Shape{10, 1, Shape::autoDim});
ASSERT_EQ(expected_shape, op->getOutputShape(0));
@@ -47,7 +61,8 @@ TEST(ShapeInferenceTest, ResizeWithShape)
Shape result_shape{2, 10, 10, 3};
- auto input = g.create<ops::InputOp>(Shape{1, 5, 5, 3});
+ mir::TensorType input_type{mir::DataType::FLOAT32, Shape{1, 5, 5, 3}};
+ auto input = g.create<ops::InputOp>(input_type);
auto op = g.create<ops::ResizeOp>(input->getOutput(0),
ops::ResizeOp::ResizeMethod::nearestNeighbor, result_shape);
@@ -61,7 +76,8 @@ TEST(ShapeInferenceTest, ResizeWithScale)
Shape result_shape{1, 30, 10, 3};
- auto input = g.create<ops::InputOp>(Shape{1, 5, 5, 3});
+ mir::TensorType input_type{mir::DataType::FLOAT32, Shape{1, 5, 5, 3}};
+ auto input = g.create<ops::InputOp>(input_type);
auto op =
g.create<ops::ResizeOp>(input->getOutput(0), ops::ResizeOp::ResizeMethod::nearestNeighbor,
@@ -76,7 +92,8 @@ TEST(ShapeInferenceTest, ReduceChangeRank)
Shape resultShape{10, 10};
- auto input = g.create<ops::InputOp>(Shape{10, 2, 10, 9});
+ mir::TensorType input_type{mir::DataType::FLOAT32, Shape{10, 2, 10, 9}};
+ auto input = g.create<ops::InputOp>(input_type);
auto n = g.create<ops::ReduceMeanOp>(input->getOutput(0), std::vector<int32_t>{1, 3}, false);
@@ -90,7 +107,8 @@ TEST(ShapeInferenceTest, ReshapeAutoDimensionShrink)
Shape input_shape{10, 2, 10};
Shape result_shape_shrink{10, 20};
- auto input = g.create<ops::InputOp>(input_shape);
+ mir::TensorType input_type{mir::DataType::FLOAT32, input_shape};
+ auto input = g.create<ops::InputOp>(input_type);
auto op = g.create<ops::ReshapeOp>(input->getOutput(0), Shape{10, Shape::autoDim});
ASSERT_EQ(result_shape_shrink, op->getOutputShape(0));
@@ -103,7 +121,8 @@ TEST(ShapeInferenceTest, ReshapeAutoDimensionExpand)
Shape input_shape{10, 2, 10};
Shape result_shape_expand{5, 10, 2, 2};
- auto input = g.create<ops::InputOp>(input_shape);
+ mir::TensorType input_type{mir::DataType::FLOAT32, input_shape};
+ auto input = g.create<ops::InputOp>(input_type);
auto op = g.create<ops::ReshapeOp>(input->getOutput(0), Shape{5, Shape::autoDim, 2, 2});
ASSERT_EQ(result_shape_expand, op->getOutputShape(0));
@@ -116,7 +135,8 @@ TEST(ShapeInferenceTest, ReshapeAutoDimensionUnsqueeze)
Shape input_shape{10, 2, 10};
Shape result_shape_expand{1, 10, 2, 1, 10, 1};
- auto input = g.create<ops::InputOp>(input_shape);
+ mir::TensorType input_type{mir::DataType::FLOAT32, input_shape};
+ auto input = g.create<ops::InputOp>(input_type);
auto op = g.create<ops::ReshapeOp>(input->getOutput(0), Shape{1, Shape::autoDim, 2, 1, 10, 1});
ASSERT_EQ(result_shape_expand, op->getOutputShape(0));
@@ -129,7 +149,8 @@ TEST(ShapeInferenceTest, SqueezeTestAllDims)
Shape input_shape{1, 2, 1, 4};
Shape expected_shape{2, 4};
- auto input = g.create<ops::InputOp>(input_shape);
+ mir::TensorType input_type{mir::DataType::FLOAT32, input_shape};
+ auto input = g.create<ops::InputOp>(input_type);
auto sq1 = g.create<ops::SqueezeOp>(input->getOutput(0), std::vector<int32_t>{});
ASSERT_EQ(sq1->getOutputShape(0), expected_shape);
@@ -141,8 +162,12 @@ TEST(ShapeInferenceTest, ElementwiseBC)
Shape input_shape{1, 10, 10, 1};
Shape input2_shape{1, 1, 10, 10};
- auto input = g.create<ops::InputOp>(input_shape);
- auto input2 = g.create<ops::InputOp>(input2_shape);
+
+ mir::TensorType input_type{mir::DataType::FLOAT32, input_shape};
+ mir::TensorType input2_type{mir::DataType::FLOAT32, input2_shape};
+
+ auto input = g.create<ops::InputOp>(input_type);
+ auto input2 = g.create<ops::InputOp>(input2_type);
auto add = g.create<ops::AddOp>(input->getOutput(0), input2->getOutput(0));
@@ -156,7 +181,8 @@ TEST(ShapeInferenceTest, SqueezeTestSpecificDims)
Shape input_shape{1, 2, 1, 4};
Shape expected_shape{1, 2, 4};
- auto input = g.create<ops::InputOp>(input_shape);
+ mir::TensorType input_type{mir::DataType::FLOAT32, input_shape};
+ auto input = g.create<ops::InputOp>(input_type);
auto sq1 = g.create<ops::SqueezeOp>(input->getOutput(0), std::vector<int32_t>{2});
ASSERT_EQ(sq1->getOutputShape(0), expected_shape);
@@ -169,7 +195,8 @@ TEST(ShapeInferenceTest, SqueezeTestScalarResult)
Shape input_shape{1, 1, 1, 1};
Shape expected_shape{1};
- auto input = g.create<ops::InputOp>(input_shape);
+ mir::TensorType input_type{mir::DataType::FLOAT32, input_shape};
+ auto input = g.create<ops::InputOp>(input_type);
auto sq1 = g.create<ops::SqueezeOp>(input->getOutput(0), std::vector<int32_t>{});
ASSERT_EQ(sq1->getOutputShape(0), expected_shape);
diff --git a/compiler/mir2loco/CMakeLists.txt b/compiler/mir2loco/CMakeLists.txt
index b506559fd..a8a096ef4 100644
--- a/compiler/mir2loco/CMakeLists.txt
+++ b/compiler/mir2loco/CMakeLists.txt
@@ -7,9 +7,8 @@ target_include_directories(mir2loco PRIVATE src)
target_include_directories(mir2loco PUBLIC include)
target_link_libraries(mir2loco PUBLIC mir)
target_link_libraries(mir2loco PUBLIC loco)
-target_link_libraries(mir2loco PRIVATE stdex)
-nncc_find_package(GTest QUIET)
+nnas_find_package(GTest QUIET)
if(NOT GTest_FOUND)
return()
diff --git a/compiler/mir2loco/README.md b/compiler/mir2loco/README.md
new file mode 100644
index 000000000..a11e10464
--- /dev/null
+++ b/compiler/mir2loco/README.md
@@ -0,0 +1 @@
+# mir2loco
diff --git a/compiler/mir2loco/include/mir2loco.h b/compiler/mir2loco/include/mir2loco.h
index 21b0f802a..54dcbf8a6 100644
--- a/compiler/mir2loco/include/mir2loco.h
+++ b/compiler/mir2loco/include/mir2loco.h
@@ -32,7 +32,10 @@ public:
void visit(mir::ops::ConcatOp &op) override;
void visit(mir::ops::ConstantOp &op) override;
void visit(mir::ops::Conv2DOp &op) override;
+ void visit(mir::ops::DeConv2DOp &op) override;
void visit(mir::ops::DepthwiseConv2DOp &op) override;
+ void visit(mir::ops::DivOp &op) override;
+ void visit(mir::ops::FullyConnectedOp &op) override;
void visit(mir::ops::InputOp &op) override;
void visit(mir::ops::MaxPool2DOp &op) override;
void visit(mir::ops::MulOp &op) override;
@@ -40,6 +43,8 @@ public:
void visit(mir::ops::ReluOp &op) override;
void visit(mir::ops::ReshapeOp &op) override;
void visit(mir::ops::SoftmaxOp &op) override;
+ void visit(mir::ops::SubOp &op) override;
+ void visit(mir::ops::TransposeOp &op) override;
void visit_fallback(mir::Operation &op) override;
@@ -47,7 +52,7 @@ public:
private:
std::unique_ptr<loco::Graph> _loco_graph;
- std::unordered_map<mir::Operation *, loco::Node *> _mir2loco_map;
+ std::unordered_map<mir::Operation::Output *, loco::Node *> _mir2loco_map;
};
} // namespace mir2loco
diff --git a/compiler/mir2loco/src/mir2loco.cpp b/compiler/mir2loco/src/mir2loco.cpp
index b54e2a2c6..e1370fe1e 100644
--- a/compiler/mir2loco/src/mir2loco.cpp
+++ b/compiler/mir2loco/src/mir2loco.cpp
@@ -21,18 +21,23 @@
#include "mir/ops/ConcatOp.h"
#include "mir/ops/ConstantOp.h"
#include "mir/ops/Conv2DOp.h"
+#include "mir/ops/Deconv2DOp.h"
#include "mir/ops/DepthwiseConv2DOp.h"
+#include "mir/ops/DivOp.h"
+#include "mir/ops/FullyConnectedOp.h"
#include "mir/ops/MaxPool2DOp.h"
#include "mir/ops/MulOp.h"
#include "mir/ops/ReluOp.h"
#include "mir/ops/ReshapeOp.h"
#include "mir/ops/SoftmaxOp.h"
+#include "mir/ops/SubOp.h"
+#include "mir/ops/TransposeOp.h"
#include "mir/ShapeRange.h"
#include <cassert>
#include <cstring>
-#include <stdex/Memory.h>
+#include <memory>
namespace mir2loco
{
@@ -49,7 +54,7 @@ template <class NodeType> void setupShape(const mir::Shape &shape, NodeType *nod
std::unique_ptr<loco::TensorShape> make_tensor_shape(const mir::Shape &shape)
{
- auto res = stdex::make_unique<loco::TensorShape>();
+ auto res = std::make_unique<loco::TensorShape>();
setupShape(shape, res.get());
return std::move(res);
}
@@ -58,122 +63,113 @@ void setupPad(const std::vector<std::int32_t> &padding_before,
const std::vector<std::int32_t> &padding_after, loco::Padding2D *pad)
{
assert(padding_before.size() == 2 && padding_after.size() == 2);
- pad->left(padding_before.at(0));
- pad->top(padding_before.at(1));
- pad->right(padding_after.at(0));
- pad->bottom(padding_after.at(1));
-}
-
-void setupWindow(const mir::Shape &window_shape, loco::Window<2> *window)
-{
- assert(window_shape.rank() == 2);
- window->horizontal(window_shape.dim(0));
- window->vertical(window_shape.dim(1));
+ pad->top(padding_before[0]);
+ pad->left(padding_before[1]);
+ pad->bottom(padding_after[0]);
+ pad->right(padding_after[1]);
}
void setupWindow(const std::vector<std::int32_t> &window_size, loco::Window<2> *window)
{
assert(window_size.size() == 2);
- window->horizontal(window_size[0]);
- window->vertical(window_size[1]);
-}
-
-void setupStride(const mir::Shape &stride_shape, loco::Stride<2> *stride)
-{
- assert(stride_shape.rank() == 2);
- stride->horizontal(stride_shape.dim(0));
- stride->vertical(stride_shape.dim(1));
+ window->vertical(window_size[0]);
+ window->horizontal(window_size[1]);
}
void setupStride(const std::vector<std::int32_t> &strides, loco::Stride<2> *stride)
{
assert(strides.size() == 2);
- stride->horizontal(strides[0]);
- stride->vertical(strides[1]);
+ stride->vertical(strides[0]);
+ stride->horizontal(strides[1]);
}
-loco::FeatureEncode *createFeatureEncode(loco::Graph *graph, mir::DataFormat format)
+loco::Permutation<loco::Domain::Feature> createFeaturePermutation(mir::DataFormat format)
{
- auto encode_node = graph->nodes()->create<loco::FeatureEncode>();
-
- auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>();
-
+ loco::Permutation<loco::Domain::Feature> perm;
if (format == mir::DataFormat::NHWC)
{
- enc->perm()->axis(loco::FeatureAxis::Count) = 0;
- enc->perm()->axis(loco::FeatureAxis::Height) = 1;
- enc->perm()->axis(loco::FeatureAxis::Width) = 2;
- enc->perm()->axis(loco::FeatureAxis::Depth) = 3;
+ perm.axis(loco::FeatureAxis::Count) = 0;
+ perm.axis(loco::FeatureAxis::Height) = 1;
+ perm.axis(loco::FeatureAxis::Width) = 2;
+ perm.axis(loco::FeatureAxis::Depth) = 3;
}
else
{
assert(format == mir::DataFormat::NCHW);
- enc->perm()->axis(loco::FeatureAxis::Count) = 0;
- enc->perm()->axis(loco::FeatureAxis::Depth) = 1;
- enc->perm()->axis(loco::FeatureAxis::Height) = 2;
- enc->perm()->axis(loco::FeatureAxis::Width) = 3;
+ perm.axis(loco::FeatureAxis::Count) = 0;
+ perm.axis(loco::FeatureAxis::Depth) = 1;
+ perm.axis(loco::FeatureAxis::Height) = 2;
+ perm.axis(loco::FeatureAxis::Width) = 3;
}
-
- encode_node->encoder(std::move(enc));
- return encode_node;
+ return perm;
}
-loco::FeatureDecode *createFeatureDecode(loco::Graph *graph, mir::DataFormat format)
+std::unique_ptr<loco::FeatureEncoder> createFeatureEncoder(mir::DataFormat data_format)
{
- auto decode_node = graph->nodes()->create<loco::FeatureDecode>();
+ auto perm = createFeaturePermutation(data_format);
+ return std::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>(perm);
+}
- auto dec = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>();
+std::unique_ptr<loco::FeatureDecoder> createFeatureDecoder(mir::DataFormat data_format)
+{
+ auto perm = createFeaturePermutation(data_format);
+ return std::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>(perm);
+}
- if (format == mir::DataFormat::NHWC)
- {
- dec->perm()->axis(loco::FeatureAxis::Count) = 0;
- dec->perm()->axis(loco::FeatureAxis::Height) = 1;
- dec->perm()->axis(loco::FeatureAxis::Width) = 2;
- dec->perm()->axis(loco::FeatureAxis::Depth) = 3;
- }
- else
- {
- assert(format == mir::DataFormat::NCHW);
- dec->perm()->axis(loco::FeatureAxis::Count) = 0;
- dec->perm()->axis(loco::FeatureAxis::Depth) = 1;
- dec->perm()->axis(loco::FeatureAxis::Height) = 2;
- dec->perm()->axis(loco::FeatureAxis::Width) = 3;
- }
+std::unique_ptr<loco::FilterEncoder> createOHWIFilterEncoder()
+{
+ loco::Permutation<loco::Domain::Filter> perm;
+ perm.axis(loco::FilterAxis::Count) = 0;
+ perm.axis(loco::FilterAxis::Height) = 1;
+ perm.axis(loco::FilterAxis::Width) = 2;
+ perm.axis(loco::FilterAxis::Depth) = 3;
+ return std::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>(perm);
+}
- decode_node->decoder(std::move(dec));
- return decode_node;
+std::unique_ptr<loco::FilterEncoder> createHWOIFilterEncoder()
+{
+ loco::Permutation<loco::Domain::Filter> perm;
+ perm.axis(loco::FilterAxis::Height) = 0;
+ perm.axis(loco::FilterAxis::Width) = 1;
+ perm.axis(loco::FilterAxis::Count) = 2;
+ perm.axis(loco::FilterAxis::Depth) = 3;
+ return std::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>(perm);
}
-loco::FilterEncode *createHWIOFilterEncode(loco::Graph *graph)
+std::unique_ptr<loco::DepthwiseFilterEncoder> createHWIMDepthwiseFilterEncoder()
{
- auto filter_enc = graph->nodes()->create<loco::FilterEncode>();
- auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>();
- // mir using filter convention as TF
- // In TensorFlow, conv2d filter is a 4-D tensor of following shape:
- // [filter_height, filter_width, in_channels, out_channels] -> HWIO (HWCN)
- enc->perm()->axis(loco::FilterAxis::Height) = 0;
- enc->perm()->axis(loco::FilterAxis::Width) = 1;
- enc->perm()->axis(loco::FilterAxis::Depth) = 2;
- enc->perm()->axis(loco::FilterAxis::Count) = 3;
+ loco::Permutation<loco::Domain::DepthwiseFilter> perm;
+ perm.axis(loco::DepthwiseFilterAxis::Height) = 0;
+ perm.axis(loco::DepthwiseFilterAxis::Width) = 1;
+ perm.axis(loco::DepthwiseFilterAxis::Depth) = 2;
+ perm.axis(loco::DepthwiseFilterAxis::Multiplier) = 3;
+ return std::make_unique<loco::PermutingEncoder<loco::Domain::DepthwiseFilter>>(perm);
+}
- filter_enc->encoder(std::move(enc));
- return filter_enc;
+std::unique_ptr<loco::DepthwiseFilterEncoder> createIHWMDepthwiseFilterEncoder()
+{
+ loco::Permutation<loco::Domain::DepthwiseFilter> perm;
+ perm.axis(loco::DepthwiseFilterAxis::Depth) = 0;
+ perm.axis(loco::DepthwiseFilterAxis::Height) = 1;
+ perm.axis(loco::DepthwiseFilterAxis::Width) = 2;
+ perm.axis(loco::DepthwiseFilterAxis::Multiplier) = 3;
+ return std::make_unique<loco::PermutingEncoder<loco::Domain::DepthwiseFilter>>(perm);
}
-loco::DepthwiseFilterEncode *createHWCMDepthwiseFilterEncode(loco::Graph *graph)
+std::unique_ptr<loco::MatrixEncoder> createHWMatrixEncoder()
{
- auto filter_enc = graph->nodes()->create<loco::DepthwiseFilterEncode>();
- auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::DepthwiseFilter>>();
- // mir using filter convention as TF
- // In TensorFlow, dwconv2d filter is a 4-D tensor of following shape:
- // [filter_height, filter_width, in_channels, channel_multiplier]
- enc->perm()->axis(loco::DepthwiseFilterAxis::Height) = 0;
- enc->perm()->axis(loco::DepthwiseFilterAxis::Width) = 1;
- enc->perm()->axis(loco::DepthwiseFilterAxis::Depth) = 2;
- enc->perm()->axis(loco::DepthwiseFilterAxis::Multiplier) = 3;
+ loco::Permutation<loco::Domain::Matrix> perm;
+ perm.axis(loco::MatrixAxis::Height) = 0;
+ perm.axis(loco::MatrixAxis::Width) = 1;
+ return std::make_unique<loco::PermutingEncoder<loco::Domain::Matrix>>(perm);
+}
- filter_enc->encoder(std::move(enc));
- return filter_enc;
+std::unique_ptr<loco::MatrixDecoder> createHWMatrixDecoder()
+{
+ loco::Permutation<loco::Domain::Matrix> perm;
+ perm.axis(loco::MatrixAxis::Height) = 0;
+ perm.axis(loco::MatrixAxis::Width) = 1;
+ return std::make_unique<loco::PermutingDecoder<loco::Domain::Matrix>>(perm);
}
loco::DataType convertDataType(mir::DataType data_type)
@@ -195,50 +191,110 @@ loco::DataType convertDataType(mir::DataType data_type)
}
throw std::runtime_error("Unsupported data type");
}
+
+loco::Node *createBroadcastIfNeeded(loco::Node *node, const mir::Shape &shape,
+ const mir::Shape &out_shape)
+{
+ auto graph = node->graph();
+
+ if (shape == out_shape)
+ return node; // not needed
+
+ int32_t out_rank = out_shape.rank();
+ int32_t rank_diff = out_rank - shape.rank();
+ // Create Broadcast
+ auto *broadcast = graph->nodes()->create<loco::TensorBroadcast>();
+ // Create Reshape for equal ranks
+ if (shape.rank() != out_rank)
+ {
+ auto *reshape = graph->nodes()->create<loco::FixedReshape>();
+ reshape->input(node);
+ reshape->rank(out_rank);
+ broadcast->input(reshape);
+ // Set reshape dims
+ for (int32_t dim = 0; dim < out_rank; dim++)
+ {
+ if (dim < rank_diff)
+ reshape->dim(dim) = 1;
+ else
+ reshape->dim(dim) = shape.dim(dim - rank_diff);
+ }
+ }
+ else
+ {
+ broadcast->input(node);
+ }
+ // Flag if no one dim isn't equal
+ bool compatible_shapes = true;
+ for (int32_t dim = 0; dim < out_rank; dim++)
+ {
+ // Set broadcast mapping
+ if (dim < rank_diff || (shape.dim(dim - rank_diff) == 1 && out_shape.dim(dim) != 1))
+ broadcast->mapping()->dim(dim) = out_shape.dim(dim);
+ // Check compatibility
+ if (dim >= rank_diff && shape.dim(dim - rank_diff) != 1 &&
+ shape.dim(dim - rank_diff) != out_shape.dim(dim))
+ compatible_shapes = false;
+ }
+ // Check compatibility
+ if (!compatible_shapes)
+ throw std::runtime_error("Not compatible shapes for broadcasting!");
+
+ return broadcast;
+}
+
+template <typename NodeType>
+NodeType *createEltwiseBinary(const mir::ops::BinaryElementwiseOp &op, loco::Node *lhs,
+ loco::Node *rhs)
+{
+ auto graph = lhs->graph();
+
+ const auto &lhs_shape = op.getInput(0)->getShape();
+ const auto &rhs_shape = op.getInput(1)->getShape();
+ const auto &out_shape = op.getOutputShape(0);
+ // Create Broadcast if it's needed
+ auto lhs_node = createBroadcastIfNeeded(lhs, lhs_shape, out_shape);
+ auto rhs_node = createBroadcastIfNeeded(rhs, rhs_shape, out_shape);
+ // Create Node
+ auto result = graph->nodes()->create<NodeType>();
+ result->lhs(lhs_node);
+ result->rhs(rhs_node);
+ return result;
+}
} // namespace
void Transformer::visit(mir::ops::AddOp &op)
{
// Get Input
- auto lhs = _mir2loco_map.at(op.getInput(0)->getProducer()->getNode());
- auto rhs = _mir2loco_map.at(op.getInput(1)->getProducer()->getNode());
-
- auto result = _loco_graph->nodes()->create<loco::EltwiseAdd>();
- result->lhs(lhs);
- result->rhs(rhs);
-
+ auto lhs = _mir2loco_map.at(op.getInput(0));
+ auto rhs = _mir2loco_map.at(op.getInput(1));
+ auto result = createEltwiseBinary<loco::EltwiseAdd>(op, lhs, rhs);
// Not set Shape
// Add to map
- _mir2loco_map.emplace(&op, result);
+ _mir2loco_map.emplace(op.getOutput(0), result);
}
void Transformer::visit(mir::ops::AvgPool2DOp &op)
{
- // Get Input
- auto loco_it = _mir2loco_map.find(op.getInput(0)->getProducer()->getNode());
- assert(loco_it != _mir2loco_map.end()); // can't find the input
- // FeatureEncode
- auto encode_node = createFeatureEncode(_loco_graph.get(), op.getDataFormat());
- encode_node->input(loco_it->second);
-
- auto avg_pool_node = _loco_graph->nodes()->create<loco::AvgPool2D>();
- // Set Input
- avg_pool_node->ifm(encode_node);
- // Set convention (like tensorflow)
+ loco::Node *input = _mir2loco_map.at(op.getInput(0));
+
+ auto *encoded_input = _loco_graph->nodes()->create<loco::FeatureEncode>();
+ encoded_input->input(input);
+ encoded_input->encoder(createFeatureEncoder(op.getDataFormat()));
+
+ auto *avg_pool_node = _loco_graph->nodes()->create<loco::AvgPool2D>();
+ avg_pool_node->ifm(encoded_input);
avg_pool_node->convention(op.getIncludePad() ? loco::AvgPool2D::Convention::Full
: loco::AvgPool2D::Convention::Valid);
-
setupWindow(op.getWindowSize(), avg_pool_node->window());
setupStride(op.getStrides(), avg_pool_node->stride());
setupPad(op.getPaddingBefore(), op.getPaddingAfter(), avg_pool_node->pad());
- // FeatureDecode
- auto decode_node = createFeatureDecode(_loco_graph.get(), op.getDataFormat());
- // Set Input
- decode_node->input(avg_pool_node);
- // Not set Shape
- // Add to map
- _mir2loco_map.emplace(&op, decode_node);
+ auto *output = _loco_graph->nodes()->create<loco::FeatureDecode>();
+ output->input(avg_pool_node);
+ output->decoder(createFeatureDecoder(op.getDataFormat()));
+
+ _mir2loco_map.emplace(op.getOutput(0), output);
}
void Transformer::visit(mir::ops::ConcatOp &op)
@@ -250,19 +306,19 @@ void Transformer::visit(mir::ops::ConcatOp &op)
for (std::size_t i = 1; i < op.getNumInputs(); i++)
{
- loco::Node *loco_lhs = last_concat;
- if (loco_lhs == nullptr)
+ loco::Node *lhs = last_concat;
+ if (lhs == nullptr)
{
- auto lhs = op.getInput(i - 1)->getProducer()->getNode();
- loco_lhs = _mir2loco_map.at(lhs);
+ mir::Operation::Output *mir_lhs = op.getInput(i - 1);
+ lhs = _mir2loco_map.at(mir_lhs);
}
- auto rhs = op.getInput(i)->getProducer()->getNode();
- auto loco_rhs = _mir2loco_map.at(rhs);
+ mir::Operation::Output *mir_rhs = op.getInput(i);
+ loco::Node *rhs = _mir2loco_map.at(mir_rhs);
// Create TensorConcat
auto concat_node = _loco_graph->nodes()->create<loco::TensorConcat>();
// Set inputs
- concat_node->lhs(loco_lhs);
- concat_node->rhs(loco_rhs);
+ concat_node->lhs(lhs);
+ concat_node->rhs(rhs);
// Set axis
concat_node->axis(op.getAxis());
// Set last concat
@@ -270,7 +326,7 @@ void Transformer::visit(mir::ops::ConcatOp &op)
}
// Not set Shape
// Add to map
- _mir2loco_map.emplace(&op, last_concat);
+ _mir2loco_map.emplace(op.getOutput(0), last_concat);
}
void Transformer::visit(mir::ops::ConstantOp &op)
@@ -282,7 +338,7 @@ void Transformer::visit(mir::ops::ConstantOp &op)
setupShape(out_shape, const_node);
// Copy value
const auto &value = op.getValue();
- const_node->dtype(convertDataType(value.getDataType()));
+ const_node->dtype(convertDataType(value.getElementType()));
// TODO Support other data types
switch (const_node->dtype())
{
@@ -342,196 +398,311 @@ void Transformer::visit(mir::ops::ConstantOp &op)
std::runtime_error("Unsupported data type");
}
// Add to map
- _mir2loco_map.emplace(&op, const_node);
+ _mir2loco_map.emplace(op.getOutput(0), const_node);
}
void Transformer::visit(mir::ops::Conv2DOp &op)
{
- auto input = op.getInput(0)->getProducer()->getNode();
- auto kernel = op.getInput(1)->getProducer()->getNode();
- // Get ConstantOp
- auto const_node = _mir2loco_map.at(kernel);
-
- auto filter_enc = createHWIOFilterEncode(_loco_graph.get());
- // Set filter input
- filter_enc->input(const_node);
-
- // Setting up conv2d
- // FeatureEncode
- auto encode_node = createFeatureEncode(_loco_graph.get(), op.getDataFormat());
- // Set Input
- auto loco_it = _mir2loco_map.find(input);
- assert(loco_it != _mir2loco_map.end()); // can't find the input
- encode_node->input(loco_it->second);
- // Conv2D
- auto conv2d_node = _loco_graph->nodes()->create<loco::Conv2D>();
- setupStride(op.getStrides(), conv2d_node->stride());
- setupPad(op.getPaddingBefore(), op.getPaddingAfter(), conv2d_node->pad());
- // Set Input
- conv2d_node->ifm(encode_node);
- conv2d_node->ker(filter_enc);
- // FeatureDecode
- auto decode_node = createFeatureDecode(_loco_graph.get(), op.getDataFormat());
- // Set Input
- decode_node->input(conv2d_node);
- // Not set Shape
- // Add to map
- _mir2loco_map.emplace(&op, decode_node);
+ mir::Operation::Output *mir_input = op.getInput(0);
+ mir::Operation::Output *mir_filter = op.getInput(1);
+
+ loco::Node *input = _mir2loco_map.at(mir_input);
+ loco::Node *filter = _mir2loco_map.at(mir_filter);
+
+ // loco does not have grouped Conv2D operation. Try to translate into something else.
+ if (op.getNumGroups() != 1)
+ {
+ const std::int32_t group_size = mir_filter->getShape().dim(3);
+ const std::int32_t num_in_channels = group_size * op.getNumGroups();
+ const std::int32_t num_out_channels = mir_filter->getShape().dim(0);
+
+ // If the size of the group is 1, translate the operation into DepthwiseConv2D. Limit ourselves
+ // with the case of 'multiplier' == 1 for now.
+ if (group_size == 1 && (num_out_channels == num_in_channels))
+ {
+ // [O, H, W, I / group] == [I, H, W, M].
+ auto *encoded_input = _loco_graph->nodes()->create<loco::FeatureEncode>();
+ encoded_input->input(input);
+ encoded_input->encoder(createFeatureEncoder(op.getDataFormat()));
+
+ auto *encoded_filter = _loco_graph->nodes()->create<loco::DepthwiseFilterEncode>();
+ encoded_filter->input(filter);
+ encoded_filter->encoder(createIHWMDepthwiseFilterEncoder());
+
+ auto *dw_conv2d_node = _loco_graph->nodes()->create<loco::DepthwiseConv2D>();
+ dw_conv2d_node->ifm(encoded_input);
+ dw_conv2d_node->ker(encoded_filter);
+ setupStride(op.getStrides(), dw_conv2d_node->stride());
+ setupPad(op.getPaddingBefore(), op.getPaddingAfter(), dw_conv2d_node->pad());
+
+ auto *output = _loco_graph->nodes()->create<loco::FeatureDecode>();
+ output->input(dw_conv2d_node);
+ output->decoder(createFeatureDecoder(op.getDataFormat()));
+
+ _mir2loco_map.emplace(op.getOutput(0), output);
+ }
+ else
+ {
+ // There are few things we can do here:
+ // 1) If group_size == 1, reshape the kernel [O, H, W, I / group] == [I * M, H, W, 1] ->
+ // [I, M, H, W] and use DepthwiseConv2D.
+ // 2) Split the operation into smaller Conv2Ds.
+ // 3) Replicate the filter along 'O' axis 'num_groups' times, zero out some elements, and use
+ // ordinary Conv2D.
+ throw std::runtime_error("Grouped Conv2D operation is not fully supported.");
+ }
+ }
+ else
+ {
+ auto *encoded_input = _loco_graph->nodes()->create<loco::FeatureEncode>();
+ encoded_input->input(input);
+ encoded_input->encoder(createFeatureEncoder(op.getDataFormat()));
+
+ auto *encoded_filter = _loco_graph->nodes()->create<loco::FilterEncode>();
+ encoded_filter->input(filter);
+ encoded_filter->encoder(createOHWIFilterEncoder());
+
+ auto *conv2d_node = _loco_graph->nodes()->create<loco::Conv2D>();
+ conv2d_node->ifm(encoded_input);
+ conv2d_node->ker(encoded_filter);
+ setupStride(op.getStrides(), conv2d_node->stride());
+ setupPad(op.getPaddingBefore(), op.getPaddingAfter(), conv2d_node->pad());
+
+ auto *output = _loco_graph->nodes()->create<loco::FeatureDecode>();
+ output->input(conv2d_node);
+ output->decoder(createFeatureDecoder(op.getDataFormat()));
+
+ _mir2loco_map.emplace(op.getOutput(0), output);
+ }
+}
+
+void Transformer::visit(mir::ops::DeConv2DOp &op)
+{
+ mir::Operation::Output *mir_input = op.getInput(0);
+ mir::Operation::Output *mir_filter = op.getInput(1);
+
+ loco::Node *input = _mir2loco_map.at(mir_input);
+ loco::Node *filter = _mir2loco_map.at(mir_filter);
+
+ auto *encoded_input = _loco_graph->nodes()->create<loco::FeatureEncode>();
+ encoded_input->input(input);
+ encoded_input->encoder(createFeatureEncoder(op.getDataFormat()));
+
+ auto *encoded_filter = _loco_graph->nodes()->create<loco::FilterEncode>();
+ encoded_filter->input(filter);
+ encoded_filter->encoder(createHWOIFilterEncoder());
+
+ auto *tr_conv2d_node = _loco_graph->nodes()->create<loco::TransposedConv2D>();
+ tr_conv2d_node->ifm(encoded_input);
+ tr_conv2d_node->ker(encoded_filter);
+ setupStride(op.getStrides(), tr_conv2d_node->stride());
+ if (op.getPaddingType() == mir::ops::PaddingType::Explicit)
+ setupPad(op.getPaddingBefore(), op.getPaddingAfter(), tr_conv2d_node->pad());
+ else
+ throw std::runtime_error("Not supported non explicit paddings on loco!");
+
+ auto *output = _loco_graph->nodes()->create<loco::FeatureDecode>();
+ output->input(tr_conv2d_node);
+ output->decoder(createFeatureDecoder(op.getDataFormat()));
+
+ _mir2loco_map.emplace(op.getOutput(0), output);
}
void Transformer::visit(mir::ops::DepthwiseConv2DOp &op)
{
- auto input = op.getInput(0)->getProducer()->getNode();
- auto kernel = op.getInput(1)->getProducer()->getNode();
- // Get ConstantOp
- auto const_node = _mir2loco_map.at(kernel);
-
- auto filter_enc = createHWCMDepthwiseFilterEncode(_loco_graph.get());
- // Set filter input
- filter_enc->input(const_node);
- // Setting up DepthwiseConv2D
-
- // FeatureEncode
- auto feature_enc = createFeatureEncode(_loco_graph.get(), op.getDataFormat());
- // Set Input
- auto loco_it = _mir2loco_map.find(input);
- assert(loco_it != _mir2loco_map.end()); // can't find the input
- feature_enc->input(loco_it->second);
- // DepthwiseConv2D
- auto dw_conv2d_node = _loco_graph->nodes()->create<loco::DepthwiseConv2D>();
+ mir::Operation::Output *mir_input = op.getInput(0);
+ mir::Operation::Output *mir_filter = op.getInput(1);
+
+ loco::Node *input = _mir2loco_map.at(mir_input);
+ loco::Node *filter = _mir2loco_map.at(mir_filter);
+
+ auto *encoded_input = _loco_graph->nodes()->create<loco::FeatureEncode>();
+ encoded_input->input(input);
+ encoded_input->encoder(createFeatureEncoder(op.getDataFormat()));
+
+ auto *encoded_filter = _loco_graph->nodes()->create<loco::DepthwiseFilterEncode>();
+ encoded_filter->input(filter);
+ encoded_filter->encoder(createHWIMDepthwiseFilterEncoder());
+
+ auto *dw_conv2d_node = _loco_graph->nodes()->create<loco::DepthwiseConv2D>();
+ dw_conv2d_node->ifm(encoded_input);
+ dw_conv2d_node->ker(encoded_filter);
setupStride(op.getStrides(), dw_conv2d_node->stride());
setupPad(op.getPaddingBefore(), op.getPaddingAfter(), dw_conv2d_node->pad());
- // Set Input
- dw_conv2d_node->ifm(feature_enc);
- dw_conv2d_node->ker(filter_enc);
- // FeatureDecode
- auto feature_dec = createFeatureDecode(_loco_graph.get(), op.getDataFormat());
- // Set Input
- feature_dec->input(dw_conv2d_node);
+
+ auto *output = _loco_graph->nodes()->create<loco::FeatureDecode>();
+ output->input(dw_conv2d_node);
+ output->decoder(createFeatureDecoder(op.getDataFormat()));
+
+ _mir2loco_map.emplace(op.getOutput(0), output);
+}
+
+void Transformer::visit(mir::ops::DivOp &op)
+{
+ // Get Input
+ loco::Node *lhs = _mir2loco_map.at(op.getInput(0));
+ loco::Node *rhs = _mir2loco_map.at(op.getInput(1));
+ auto result = createEltwiseBinary<loco::EltwiseDiv>(op, lhs, rhs);
// Not set Shape
// Add to map
- _mir2loco_map.emplace(&op, feature_dec);
+ _mir2loco_map.emplace(op.getOutput(0), result);
+}
+
+void Transformer::visit(mir::ops::FullyConnectedOp &op)
+{
+ mir::Operation::Output *mir_lhs = op.getInput(0);
+ mir::Operation::Output *mir_rhs = op.getInput(1);
+ // Check 2D shape
+ assert(op.getInput(0)->getShape().rank() == 2);
+ assert(op.getInput(1)->getShape().rank() == 2);
+
+ loco::Node *lhs = _mir2loco_map.at(mir_lhs);
+ loco::Node *rhs = _mir2loco_map.at(mir_rhs);
+
+ auto *encoded_lhs = _loco_graph->nodes()->create<loco::MatrixEncode>();
+ encoded_lhs->input(lhs);
+ encoded_lhs->encoder(createHWMatrixEncoder());
+
+ auto *encoded_rhs = _loco_graph->nodes()->create<loco::MatrixEncode>();
+ encoded_rhs->input(rhs);
+ encoded_rhs->encoder(createHWMatrixEncoder());
+
+ auto *mat_mul = _loco_graph->nodes()->create<loco::MatMul>();
+ mat_mul->lhs(encoded_lhs);
+ mat_mul->rhs(encoded_rhs);
+
+ auto *output = _loco_graph->nodes()->create<loco::MatrixDecode>();
+ output->input(mat_mul);
+ output->decoder(createHWMatrixDecoder());
+
+ _mir2loco_map.emplace(op.getOutput(0), output);
}
void Transformer::visit(mir::ops::InputOp &op)
{
- auto pull_node = _loco_graph->nodes()->create<loco::Pull>();
- // Set Type
- pull_node->dtype(loco::DataType::FLOAT32); // TODO Support other types
- // Set Shape
- auto &out_shape = op.getOutputShape(0);
- setupShape(out_shape, pull_node);
- // Set graph input
- auto graph_input = _loco_graph->inputs()->create();
- const auto &input_name = op.getOutput(0)->getName();
- assert(!input_name.empty());
- graph_input->name(input_name);
- graph_input->dtype(loco::DataType::FLOAT32); // TODO Support other types
+ mir::Operation::Output *mir_output = op.getOutput(0);
+
+ loco::GraphInput *graph_input = _loco_graph->inputs()->create();
+ graph_input->name(mir_output->getName());
+ graph_input->dtype(convertDataType(mir_output->getElementType()));
+
+ auto *pull_node = _loco_graph->nodes()->create<loco::Pull>();
+ setupShape(mir_output->getShape(), pull_node);
+
loco::link(graph_input, pull_node);
- // Not set inputs
- // Add to map
- _mir2loco_map.emplace(&op, pull_node);
+
+ _mir2loco_map.emplace(mir_output, pull_node);
}
void Transformer::visit(mir::ops::MaxPool2DOp &op)
{
- // Get Input
- auto loco_it = _mir2loco_map.find(op.getInput(0)->getProducer()->getNode());
- assert(loco_it != _mir2loco_map.end()); // can't find the input
- // FeatureEncode
- auto encode_node = createFeatureEncode(_loco_graph.get(), op.getDataFormat());
- encode_node->input(loco_it->second);
+ loco::Node *input = _mir2loco_map.at(op.getInput(0));
- auto max_pool_node = _loco_graph->nodes()->create<loco::MaxPool2D>();
- // Set Input
- max_pool_node->ifm(encode_node);
+ auto *encoded_input = _loco_graph->nodes()->create<loco::FeatureEncode>();
+ encoded_input->input(input);
+ encoded_input->encoder(createFeatureEncoder(op.getDataFormat()));
+ auto max_pool_node = _loco_graph->nodes()->create<loco::MaxPool2D>();
+ max_pool_node->ifm(encoded_input);
setupWindow(op.getWindowSize(), max_pool_node->window());
setupStride(op.getStrides(), max_pool_node->stride());
setupPad(op.getPaddingBefore(), op.getPaddingAfter(), max_pool_node->pad());
- // FeatureDecode
- auto decode_node = createFeatureDecode(_loco_graph.get(), op.getDataFormat());
- // Set Input
- decode_node->input(max_pool_node);
- // Not set Shape
- // Add to map
- _mir2loco_map.emplace(&op, decode_node);
+ auto *output = _loco_graph->nodes()->create<loco::FeatureDecode>();
+ output->input(max_pool_node);
+ output->decoder(createFeatureDecoder(op.getDataFormat()));
+
+ _mir2loco_map.emplace(op.getOutput(0), output);
}
void Transformer::visit(mir::ops::MulOp &op)
{
// Get Input
- auto lhs = _mir2loco_map.at(op.getInput(0)->getProducer()->getNode());
- auto rhs = _mir2loco_map.at(op.getInput(1)->getProducer()->getNode());
-
- auto result = _loco_graph->nodes()->create<loco::EltwiseMul>();
- result->lhs(lhs);
- result->rhs(rhs);
-
+ loco::Node *lhs = _mir2loco_map.at(op.getInput(0));
+ loco::Node *rhs = _mir2loco_map.at(op.getInput(1));
+ auto result = createEltwiseBinary<loco::EltwiseMul>(op, lhs, rhs);
// Not set Shape
// Add to map
- _mir2loco_map.emplace(&op, result);
+ _mir2loco_map.emplace(op.getOutput(0), result);
}
void Transformer::visit(mir::ops::OutputOp &op)
{
- auto push_node = _loco_graph->nodes()->create<loco::Push>();
- // Set Input
- auto loco_it = _mir2loco_map.find(op.getInput(0)->getProducer()->getNode());
- assert(loco_it != _mir2loco_map.end()); // can't find the input
- push_node->from(loco_it->second);
- // Set graph output
- auto graph_output = _loco_graph->outputs()->create();
- const auto &output_name = op.getInput(0)->getProducer()->getName();
- assert(!output_name.empty());
- graph_output->name(output_name);
- graph_output->dtype(loco::DataType::FLOAT32); // TODO Support other types
- // Set graph output shape
- const auto &out_shape = op.getInput(0)->getProducer()->getShape();
- graph_output->shape(make_tensor_shape(out_shape));
+ mir::Operation::Output *mir_input = op.getInput(0);
+ loco::Node *input = _mir2loco_map.at(mir_input);
+
+ loco::GraphOutput *graph_output = _loco_graph->outputs()->create();
+ graph_output->name(mir_input->getName());
+ graph_output->dtype(convertDataType(mir_input->getElementType()));
+ graph_output->shape(make_tensor_shape(mir_input->getShape()));
+
+ auto *push_node = _loco_graph->nodes()->create<loco::Push>();
+ push_node->from(input);
+
loco::link(graph_output, push_node);
- // Add to map
- _mir2loco_map.emplace(&op, push_node);
}
void Transformer::visit(mir::ops::ReluOp &op)
{
+ loco::Node *input = _mir2loco_map.at(op.getInput(0));
+
auto relu_node = _loco_graph->nodes()->create<loco::ReLU>();
- // Set Input
- auto loco_it = _mir2loco_map.find(op.getInput(0)->getProducer()->getNode());
- assert(loco_it != _mir2loco_map.end()); // can't find the input
- relu_node->input(loco_it->second);
+ relu_node->input(input);
// Not set shape
// Add to map
- _mir2loco_map.emplace(&op, relu_node);
+ _mir2loco_map.emplace(op.getOutput(0), relu_node);
}
void Transformer::visit(mir::ops::ReshapeOp &op)
{
+ loco::Node *input = _mir2loco_map.at(op.getInput(0));
+
auto reshape_node = _loco_graph->nodes()->create<loco::Reshape<loco::ReshapeType::Fixed>>();
- // Set Input
- auto loco_it = _mir2loco_map.find(op.getInput(0)->getProducer()->getNode());
- assert(loco_it != _mir2loco_map.end()); // can't find the input
- reshape_node->input(loco_it->second);
+ reshape_node->input(input);
// Set Shape
auto &out_shape = op.getOutputShape(0);
setupShape(out_shape, reshape_node);
// Add to map
- _mir2loco_map.emplace(&op, reshape_node);
+ _mir2loco_map.emplace(op.getOutput(0), reshape_node);
}
void Transformer::visit(mir::ops::SoftmaxOp &op)
{
+ loco::Node *input = _mir2loco_map.at(op.getInput(0));
+
auto softmax_node = _loco_graph->nodes()->create<loco::TensorSoftmax>();
- // Set Input
- auto loco_it = _mir2loco_map.find(op.getInput(0)->getProducer()->getNode());
- assert(loco_it != _mir2loco_map.end()); // can't find the input
- softmax_node->input(loco_it->second);
+ softmax_node->input(input);
// Set Axis
softmax_node->axis(op.getAxis());
// Add to map
- _mir2loco_map.emplace(&op, softmax_node);
+ _mir2loco_map.emplace(op.getOutput(0), softmax_node);
+}
+
+void Transformer::visit(mir::ops::SubOp &op)
+{
+ // Get Input
+ loco::Node *lhs = _mir2loco_map.at(op.getInput(0));
+ loco::Node *rhs = _mir2loco_map.at(op.getInput(1));
+ auto result = createEltwiseBinary<loco::EltwiseSub>(op, lhs, rhs);
+ // Not set Shape
+ // Add to map
+ _mir2loco_map.emplace(op.getOutput(0), result);
+}
+
+void Transformer::visit(mir::ops::TransposeOp &op)
+{
+ loco::Node *input = _mir2loco_map.at(op.getInput(0));
+ const auto &axis_order = op.getAxisOrder();
+
+ auto transpose_node = _loco_graph->nodes()->create<loco::TensorTranspose>();
+ transpose_node->input(input);
+ // Set axis order
+ transpose_node->perm()->size(axis_order.size());
+ for (size_t i = 0; i < axis_order.size(); i++)
+ transpose_node->perm()->axis(i) = axis_order[i];
+ // Not set shape
+ // Add to map
+ _mir2loco_map.emplace(op.getOutput(0), transpose_node);
}
void Transformer::visit_fallback(mir::Operation &op) { throw std::runtime_error("NYI operation"); }
@@ -539,7 +710,7 @@ void Transformer::visit_fallback(mir::Operation &op) { throw std::runtime_error(
std::unique_ptr<loco::Graph> Transformer::transform(mir::Graph *mir_graph)
{
_mir2loco_map.clear();
- _loco_graph.release();
+ _loco_graph.reset();
_loco_graph = loco::make_graph();
// Transform Nodes
diff --git a/compiler/mir2loco/src/mir2loco.test.cpp b/compiler/mir2loco/src/mir2loco.test.cpp
index e5f289043..3870caeb5 100644
--- a/compiler/mir2loco/src/mir2loco.test.cpp
+++ b/compiler/mir2loco/src/mir2loco.test.cpp
@@ -21,12 +21,15 @@
#include "mir/ops/ConcatOp.h"
#include "mir/ops/ConstantOp.h"
#include "mir/ops/Conv2DOp.h"
+#include "mir/ops/Deconv2DOp.h"
#include "mir/ops/DepthwiseConv2DOp.h"
+#include "mir/ops/FullyConnectedOp.h"
#include "mir/ops/MaxPool2DOp.h"
#include "mir/ops/MulOp.h"
#include "mir/ops/ReluOp.h"
#include "mir/ops/ReshapeOp.h"
#include "mir/ops/SoftmaxOp.h"
+#include "mir/ops/TransposeOp.h"
#include <gtest/gtest.h>
@@ -38,8 +41,8 @@ TEST_F(TestTransformer_mir2loco, Input_Output_Test)
{
mir::Graph mir_graph;
- mir::Shape input_shape = mir::Shape({5, 6, 7, 8});
- auto *input = mir_graph.create<mir::ops::InputOp>(input_shape)->getOutput(0);
+ mir::TensorType input_type{mir::DataType::FLOAT32, {5, 6, 7, 8}};
+ auto *input = mir_graph.create<mir::ops::InputOp>(input_type)->getOutput(0);
mir_graph.create<mir::ops::OutputOp>(input);
input->setName("x");
@@ -76,8 +79,8 @@ TEST_F(TestTransformer_mir2loco, Relu_Test)
{
mir::Graph mir_graph;
- mir::Shape input_shape = mir::Shape({7, 7, 9, 9});
- auto *input = mir_graph.create<mir::ops::InputOp>(input_shape)->getOutput(0);
+ mir::TensorType input_type{mir::DataType::FLOAT32, {7, 7, 9, 9}};
+ auto *input = mir_graph.create<mir::ops::InputOp>(input_type)->getOutput(0);
auto *relu = mir_graph.create<mir::ops::ReluOp>(input)->getOutput(0);
mir_graph.create<mir::ops::OutputOp>(relu);
input->setName("x");
@@ -119,14 +122,15 @@ TEST_F(TestTransformer_mir2loco, Avg_Pool_Test)
{
mir::Graph mir_graph;
- mir::Shape input_shape = mir::Shape({7, 7, 9, 9});
- auto *input = mir_graph.create<mir::ops::InputOp>(input_shape)->getOutput(0);
- auto *pool = mir_graph
- .create<mir::ops::AvgPool2DOp>(
- input, std::vector<std::int32_t>{2, 3}, std::vector<std::int32_t>{4, 5},
- std::vector<std::int32_t>{5, 9}, std::vector<std::int32_t>{7, 4}, true,
- mir::DataFormat::NHWC)
- ->getOutput(0);
+ mir::TensorType input_type{mir::DataType::FLOAT32, {7, 7, 9, 9}};
+ auto *input = mir_graph.create<mir::ops::InputOp>(input_type)->getOutput(0);
+
+ mir::AvgPool2DOpAttributes attributes;
+ attributes.window = {2, 3};
+ attributes.strides = {4, 5};
+ attributes.padding_before = {5, 9};
+ attributes.padding_after = {7, 4};
+ auto *pool = mir_graph.create<mir::ops::AvgPool2DOp>(input, attributes)->getOutput(0);
mir_graph.create<mir::ops::OutputOp>(pool);
input->setName("x");
pool->setName("y");
@@ -153,28 +157,28 @@ TEST_F(TestTransformer_mir2loco, Avg_Pool_Test)
ASSERT_EQ(push_node->from(), decode_node);
// Check params
ASSERT_EQ(pool_node->convention(), loco::AvgPool2D::Convention::Full);
- ASSERT_EQ(pool_node->pad()->left(), 5);
- ASSERT_EQ(pool_node->pad()->top(), 9);
- ASSERT_EQ(pool_node->pad()->right(), 7);
- ASSERT_EQ(pool_node->pad()->bottom(), 4);
- ASSERT_EQ(pool_node->window()->horizontal(), 2);
- ASSERT_EQ(pool_node->window()->vertical(), 3);
- ASSERT_EQ(pool_node->stride()->horizontal(), 4);
- ASSERT_EQ(pool_node->stride()->vertical(), 5);
+ ASSERT_EQ(pool_node->pad()->top(), 5);
+ ASSERT_EQ(pool_node->pad()->left(), 9);
+ ASSERT_EQ(pool_node->pad()->bottom(), 7);
+ ASSERT_EQ(pool_node->pad()->right(), 4);
+ ASSERT_EQ(pool_node->window()->vertical(), 2);
+ ASSERT_EQ(pool_node->window()->horizontal(), 3);
+ ASSERT_EQ(pool_node->stride()->vertical(), 4);
+ ASSERT_EQ(pool_node->stride()->horizontal(), 5);
}
TEST_F(TestTransformer_mir2loco, Max_Pool_Test)
{
mir::Graph mir_graph;
- mir::Shape input_shape = mir::Shape({7, 7, 9, 9});
- auto *input = mir_graph.create<mir::ops::InputOp>(input_shape)->getOutput(0);
- auto *pool = mir_graph
- .create<mir::ops::MaxPool2DOp>(
- input, std::vector<std::int32_t>{2, 3}, std::vector<std::int32_t>{4, 5},
- std::vector<std::int32_t>{5, 9}, std::vector<std::int32_t>{7, 4},
- mir::DataFormat::NHWC)
- ->getOutput(0);
+ mir::TensorType input_type{mir::DataType::FLOAT32, {7, 7, 9, 9}};
+ auto *input = mir_graph.create<mir::ops::InputOp>(input_type)->getOutput(0);
+ mir::MaxPool2DOpAttributes attributes;
+ attributes.window = {2, 3};
+ attributes.strides = {4, 5};
+ attributes.padding_before = {5, 9};
+ attributes.padding_after = {7, 4};
+ auto *pool = mir_graph.create<mir::ops::MaxPool2DOp>(input, attributes)->getOutput(0);
mir_graph.create<mir::ops::OutputOp>(pool);
input->setName("x");
pool->setName("y");
@@ -200,24 +204,24 @@ TEST_F(TestTransformer_mir2loco, Max_Pool_Test)
ASSERT_EQ(decode_node->input(), pool_node);
ASSERT_EQ(push_node->from(), decode_node);
// Check params
- ASSERT_EQ(pool_node->pad()->left(), 5);
- ASSERT_EQ(pool_node->pad()->top(), 9);
- ASSERT_EQ(pool_node->pad()->right(), 7);
- ASSERT_EQ(pool_node->pad()->bottom(), 4);
- ASSERT_EQ(pool_node->window()->horizontal(), 2);
- ASSERT_EQ(pool_node->window()->vertical(), 3);
- ASSERT_EQ(pool_node->stride()->horizontal(), 4);
- ASSERT_EQ(pool_node->stride()->vertical(), 5);
+ ASSERT_EQ(pool_node->pad()->top(), 5);
+ ASSERT_EQ(pool_node->pad()->left(), 9);
+ ASSERT_EQ(pool_node->pad()->bottom(), 7);
+ ASSERT_EQ(pool_node->pad()->right(), 4);
+ ASSERT_EQ(pool_node->window()->vertical(), 2);
+ ASSERT_EQ(pool_node->window()->horizontal(), 3);
+ ASSERT_EQ(pool_node->stride()->vertical(), 4);
+ ASSERT_EQ(pool_node->stride()->horizontal(), 5);
}
TEST_F(TestTransformer_mir2loco, Concat_Test)
{
mir::Graph mir_graph;
- mir::Shape input_shape{5, 6, 7, 3};
- auto *input1 = mir_graph.create<mir::ops::InputOp>(input_shape)->getOutput(0);
- auto *input2 = mir_graph.create<mir::ops::InputOp>(input_shape)->getOutput(0);
- auto *input3 = mir_graph.create<mir::ops::InputOp>(input_shape)->getOutput(0);
+ mir::TensorType input_type{mir::DataType::FLOAT32, {5, 6, 7, 3}};
+ auto *input1 = mir_graph.create<mir::ops::InputOp>(input_type)->getOutput(0);
+ auto *input2 = mir_graph.create<mir::ops::InputOp>(input_type)->getOutput(0);
+ auto *input3 = mir_graph.create<mir::ops::InputOp>(input_type)->getOutput(0);
std::vector<mir::Operation::Output *> inputs{input1, input2, input3};
auto *concat = mir_graph.create<mir::ops::ConcatOp>(inputs, 2)->getOutput(0);
mir_graph.create<mir::ops::OutputOp>(concat);
@@ -257,7 +261,8 @@ TEST_F(TestTransformer_mir2loco, Reshape_Test)
{
mir::Graph mir_graph;
- auto *input = mir_graph.create<mir::ops::InputOp>(mir::Shape{7, 8, 9, 9})->getOutput(0);
+ mir::TensorType input_type{mir::DataType::FLOAT32, {7, 8, 9, 9}};
+ auto *input = mir_graph.create<mir::ops::InputOp>(input_type)->getOutput(0);
auto *reshape = mir_graph.create<mir::ops::ReshapeOp>(input, mir::Shape{7, 8, 81})->getOutput(0);
mir_graph.create<mir::ops::OutputOp>(reshape);
input->setName("x");
@@ -287,9 +292,9 @@ TEST_F(TestTransformer_mir2loco, Const_Float_Test)
{
mir::Graph mir_graph;
- mir::Shape shape = mir::Shape({2, 3});
+ mir::TensorType type{mir::DataType::FLOAT32, {2, 3}};
const float data[] = {5.9, 6.7, 5.32, 54.11231, 43.2444, 3.409};
- auto mir_tensor = mir::TensorVariant(mir::DataType::FLOAT32, shape, (const void *)data);
+ mir::TensorVariant mir_tensor{type, data};
auto *constant = mir_graph.create<mir::ops::ConstantOp>(mir_tensor)->getOutput(0);
mir_graph.create<mir::ops::OutputOp>(constant);
constant->setName("x");
@@ -316,10 +321,10 @@ TEST_F(TestTransformer_mir2loco, Add_Test)
{
mir::Graph mir_graph;
- mir::Shape input_shape{5, 6, 7, 3};
-
- auto *input1 = mir_graph.create<mir::ops::InputOp>(input_shape)->getOutput(0);
- auto *input2 = mir_graph.create<mir::ops::InputOp>(input_shape)->getOutput(0);
+ mir::TensorType input1_type{mir::DataType::FLOAT32, {5, 6, 7, 3}};
+ mir::TensorType input2_type{mir::DataType::FLOAT32, {5, 1, 7, 3}};
+ auto *input1 = mir_graph.create<mir::ops::InputOp>(input1_type)->getOutput(0);
+ auto *input2 = mir_graph.create<mir::ops::InputOp>(input2_type)->getOutput(0);
auto *add = mir_graph.create<mir::ops::AddOp>(input1, input2)->getOutput(0);
mir_graph.create<mir::ops::OutputOp>(add);
input1->setName("x1");
@@ -329,50 +334,47 @@ TEST_F(TestTransformer_mir2loco, Add_Test)
mir2loco::Transformer transformer;
auto loco_graph = transformer.transform(&mir_graph);
- auto *pull1_node = dynamic_cast<loco::Pull *>(loco_graph->nodes()->at(0));
- auto *pull2_node = dynamic_cast<loco::Pull *>(loco_graph->nodes()->at(1));
- auto *add_node = dynamic_cast<loco::EltwiseAdd *>(loco_graph->nodes()->at(2));
- auto *push_node = dynamic_cast<loco::Push *>(loco_graph->nodes()->at(3));
-
- ASSERT_NE(pull1_node, nullptr);
- ASSERT_NE(pull2_node, nullptr);
+ // Pull
+ auto inputs = loco_graph->inputs();
+ ASSERT_EQ(inputs->size(), 2);
+ loco::Pull *pull_node0 = loco::pull_node(loco_graph.get(), 0);
+ ASSERT_NE(pull_node0, nullptr);
+ loco::Pull *pull_node1 = loco::pull_node(loco_graph.get(), 1);
+ ASSERT_NE(pull_node1, nullptr);
+ // Add
+ auto pull_uses = loco::succs(pull_node0);
+ ASSERT_EQ(pull_uses.size(), 1);
+ loco::EltwiseAdd *add_node = dynamic_cast<loco::EltwiseAdd *>(*pull_uses.begin());
ASSERT_NE(add_node, nullptr);
- ASSERT_NE(push_node, nullptr);
-
- ASSERT_EQ(add_node->lhs(), pull1_node);
- ASSERT_EQ(add_node->rhs(), pull2_node);
- ASSERT_EQ(push_node->from(), add_node);
-
- // Shape check
- ASSERT_EQ(pull1_node->rank(), 4);
- ASSERT_EQ(pull1_node->dim(0), 5);
- ASSERT_EQ(pull1_node->dim(1), 6);
- ASSERT_EQ(pull1_node->dim(2), 7);
- ASSERT_EQ(pull1_node->dim(3), 3);
-
- ASSERT_EQ(pull2_node->rank(), 4);
- ASSERT_EQ(pull2_node->dim(0), 5);
- ASSERT_EQ(pull2_node->dim(1), 6);
- ASSERT_EQ(pull2_node->dim(2), 7);
- ASSERT_EQ(pull2_node->dim(3), 3);
+ ASSERT_EQ(add_node->lhs(), pull_node0);
+ // TensorBroadcast
+ loco::TensorBroadcast *broadcast_node = dynamic_cast<loco::TensorBroadcast *>(add_node->rhs());
+ ASSERT_NE(broadcast_node, nullptr);
+ ASSERT_EQ(broadcast_node->input(), pull_node1);
+ // Check params
+ ASSERT_TRUE(broadcast_node->mapping()->defined(1));
+ ASSERT_EQ(broadcast_node->mapping()->dim(1), 6);
}
TEST_F(TestTransformer_mir2loco, Conv2D_Test)
{
mir::Graph mir_graph;
- mir::Shape input_shape = mir::Shape({7, 7, 9, 1});
- auto *input = mir_graph.create<mir::ops::InputOp>(input_shape)->getOutput(0);
- mir::Shape shape = mir::Shape({2, 3, 1, 1});
+ mir::TensorType input_type{mir::DataType::FLOAT32, {7, 7, 9, 1}};
+ auto *input = mir_graph.create<mir::ops::InputOp>(input_type)->getOutput(0);
+
+ mir::TensorType filter_type{mir::DataType::FLOAT32, {2, 3, 1, 1}};
const float data[] = {5.9, 6.7, 5.32, 54.11231, 43.2444, 3.409};
- auto mir_tensor = mir::TensorVariant(mir::DataType::FLOAT32, shape, (const void *)data);
- auto *constant = mir_graph.create<mir::ops::ConstantOp>(mir_tensor)->getOutput(0);
- auto *conv =
- mir_graph
- .create<mir::ops::Conv2DOp>(input, constant, std::vector<std::int32_t>{2, 3},
- std::vector<std::int32_t>{5, 9},
- std::vector<std::int32_t>{7, 4}, mir::DataFormat::NHWC)
- ->getOutput(0);
+ mir::TensorVariant filter_tensor{filter_type, data};
+ auto *filter = mir_graph.create<mir::ops::ConstantOp>(filter_tensor)->getOutput(0);
+
+ mir::Conv2DOpAttributes attributes;
+ attributes.strides = {2, 3};
+ attributes.padding_before = {5, 9};
+ attributes.padding_after = {7, 4};
+
+ auto *conv = mir_graph.create<mir::ops::Conv2DOp>(input, filter, attributes)->getOutput(0);
+
mir_graph.create<mir::ops::OutputOp>(conv);
input->setName("x");
conv->setName("y");
@@ -382,9 +384,9 @@ TEST_F(TestTransformer_mir2loco, Conv2D_Test)
loco::Pull *pull_node = dynamic_cast<loco::Pull *>(loco_graph->nodes()->at(0));
loco::ConstGen *const_node = dynamic_cast<loco::ConstGen *>(loco_graph->nodes()->at(1));
- loco::FilterEncode *filter_node = dynamic_cast<loco::FilterEncode *>(loco_graph->nodes()->at(2));
loco::FeatureEncode *encode_node =
- dynamic_cast<loco::FeatureEncode *>(loco_graph->nodes()->at(3));
+ dynamic_cast<loco::FeatureEncode *>(loco_graph->nodes()->at(2));
+ loco::FilterEncode *filter_node = dynamic_cast<loco::FilterEncode *>(loco_graph->nodes()->at(3));
loco::Conv2D *conv_node = dynamic_cast<loco::Conv2D *>(loco_graph->nodes()->at(4));
loco::FeatureDecode *decode_node =
dynamic_cast<loco::FeatureDecode *>(loco_graph->nodes()->at(5));
@@ -404,20 +406,20 @@ TEST_F(TestTransformer_mir2loco, Conv2D_Test)
ASSERT_EQ(decode_node->input(), conv_node);
ASSERT_EQ(push_node->from(), decode_node);
// Check params
- ASSERT_EQ(conv_node->pad()->left(), 5);
- ASSERT_EQ(conv_node->pad()->top(), 9);
- ASSERT_EQ(conv_node->pad()->right(), 7);
- ASSERT_EQ(conv_node->pad()->bottom(), 4);
- ASSERT_EQ(conv_node->stride()->horizontal(), 2);
- ASSERT_EQ(conv_node->stride()->vertical(), 3);
+ ASSERT_EQ(conv_node->pad()->top(), 5);
+ ASSERT_EQ(conv_node->pad()->left(), 9);
+ ASSERT_EQ(conv_node->pad()->bottom(), 7);
+ ASSERT_EQ(conv_node->pad()->right(), 4);
+ ASSERT_EQ(conv_node->stride()->vertical(), 2);
+ ASSERT_EQ(conv_node->stride()->horizontal(), 3);
}
TEST_F(TestTransformer_mir2loco, Softmax_Test)
{
mir::Graph mir_graph;
- mir::Shape input_shape = mir::Shape({7, 7, 1, 9});
- auto *input = mir_graph.create<mir::ops::InputOp>(input_shape)->getOutput(0);
+ mir::TensorType input_type{mir::DataType::FLOAT32, {7, 7, 1, 9}};
+ auto *input = mir_graph.create<mir::ops::InputOp>(input_type)->getOutput(0);
auto *softmax = mir_graph.create<mir::ops::SoftmaxOp>(input, 2)->getOutput(0);
mir_graph.create<mir::ops::OutputOp>(softmax);
input->setName("x");
@@ -444,10 +446,10 @@ TEST_F(TestTransformer_mir2loco, Mul_Test)
{
mir::Graph mir_graph;
- mir::Shape input_shape{5, 6, 7, 3};
-
- auto *input1 = mir_graph.create<mir::ops::InputOp>(input_shape)->getOutput(0);
- auto *input2 = mir_graph.create<mir::ops::InputOp>(input_shape)->getOutput(0);
+ mir::TensorType input1_type{mir::DataType::FLOAT32, {5, 6, 7, 13}};
+ mir::TensorType input2_type{mir::DataType::FLOAT32, {13}};
+ auto *input1 = mir_graph.create<mir::ops::InputOp>(input1_type)->getOutput(0);
+ auto *input2 = mir_graph.create<mir::ops::InputOp>(input2_type)->getOutput(0);
auto *add = mir_graph.create<mir::ops::MulOp>(input1, input2)->getOutput(0);
mir_graph.create<mir::ops::OutputOp>(add);
input1->setName("x1");
@@ -457,50 +459,69 @@ TEST_F(TestTransformer_mir2loco, Mul_Test)
mir2loco::Transformer transformer;
auto loco_graph = transformer.transform(&mir_graph);
- auto *pull1_node = dynamic_cast<loco::Pull *>(loco_graph->nodes()->at(0));
- auto *pull2_node = dynamic_cast<loco::Pull *>(loco_graph->nodes()->at(1));
- auto *add_node = dynamic_cast<loco::EltwiseMul *>(loco_graph->nodes()->at(2));
- auto *push_node = dynamic_cast<loco::Push *>(loco_graph->nodes()->at(3));
-
- ASSERT_NE(pull1_node, nullptr);
- ASSERT_NE(pull2_node, nullptr);
- ASSERT_NE(add_node, nullptr);
- ASSERT_NE(push_node, nullptr);
-
- ASSERT_EQ(add_node->lhs(), pull1_node);
- ASSERT_EQ(add_node->rhs(), pull2_node);
- ASSERT_EQ(push_node->from(), add_node);
-
- // Shape check
- ASSERT_EQ(pull1_node->rank(), 4);
- ASSERT_EQ(pull1_node->dim(0), 5);
- ASSERT_EQ(pull1_node->dim(1), 6);
- ASSERT_EQ(pull1_node->dim(2), 7);
- ASSERT_EQ(pull1_node->dim(3), 3);
-
- ASSERT_EQ(pull2_node->rank(), 4);
- ASSERT_EQ(pull2_node->dim(0), 5);
- ASSERT_EQ(pull2_node->dim(1), 6);
- ASSERT_EQ(pull2_node->dim(2), 7);
- ASSERT_EQ(pull2_node->dim(3), 3);
+ // Pulls
+ auto inputs = loco_graph->inputs();
+ ASSERT_EQ(inputs->size(), 2);
+ loco::Pull *pull_node0 = loco::pull_node(loco_graph.get(), 0);
+ ASSERT_NE(pull_node0, nullptr);
+ loco::Pull *pull_node1 = loco::pull_node(loco_graph.get(), 1);
+ ASSERT_NE(pull_node1, nullptr);
+ // Mul
+ auto pull0_uses = loco::succs(pull_node0);
+ ASSERT_EQ(pull0_uses.size(), 1);
+ loco::EltwiseMul *mul_node = dynamic_cast<loco::EltwiseMul *>(*pull0_uses.begin());
+ ASSERT_NE(mul_node, nullptr);
+ // Broadcast
+ loco::TensorBroadcast *broadcast_node = dynamic_cast<loco::TensorBroadcast *>(mul_node->rhs());
+ ASSERT_NE(broadcast_node, nullptr);
+ ASSERT_EQ(mul_node->lhs(), pull_node0);
+ ASSERT_EQ(mul_node->rhs(), broadcast_node);
+ loco::FixedReshape *reshape_node = dynamic_cast<loco::FixedReshape *>(broadcast_node->input());
+ ASSERT_NE(reshape_node, nullptr);
+ ASSERT_EQ(reshape_node->input(), pull_node1);
+ ASSERT_EQ(reshape_node->rank(), 4);
+ ASSERT_EQ(reshape_node->dim(0), 1);
+ ASSERT_EQ(reshape_node->dim(1), 1);
+ ASSERT_EQ(reshape_node->dim(2), 1);
+ ASSERT_EQ(reshape_node->dim(3), 13);
+ // Params checks
+ ASSERT_EQ(pull_node0->rank(), 4);
+ ASSERT_EQ(pull_node0->dim(0), 5);
+ ASSERT_EQ(pull_node0->dim(1), 6);
+ ASSERT_EQ(pull_node0->dim(2), 7);
+ ASSERT_EQ(pull_node0->dim(3), 13);
+
+ ASSERT_EQ(pull_node1->rank(), 1);
+ ASSERT_EQ(pull_node1->dim(0), 13);
+
+ ASSERT_TRUE(broadcast_node->mapping()->defined(0));
+ ASSERT_EQ(broadcast_node->mapping()->dim(0), 5);
+ ASSERT_TRUE(broadcast_node->mapping()->defined(1));
+ ASSERT_EQ(broadcast_node->mapping()->dim(1), 6);
+ ASSERT_TRUE(broadcast_node->mapping()->defined(2));
+ ASSERT_EQ(broadcast_node->mapping()->dim(2), 7);
}
TEST_F(TestTransformer_mir2loco, DepthwiseConv2D_Test)
{
mir::Graph mir_graph;
- mir::Shape input_shape = mir::Shape({7, 7, 9, 1});
- auto *input = mir_graph.create<mir::ops::InputOp>(input_shape)->getOutput(0);
- mir::Shape shape = mir::Shape({2, 3, 1, 1});
+ mir::TensorType input_type{mir::DataType::FLOAT32, {7, 7, 9, 1}};
+ auto *input = mir_graph.create<mir::ops::InputOp>(input_type)->getOutput(0);
+
+ mir::TensorType filter_type{mir::DataType::FLOAT32, {2, 3, 1, 1}};
const float data[] = {5.9, 6.7, 5.32, 54.11231, 43.2444, 3.409};
- auto mir_tensor = mir::TensorVariant(mir::DataType::FLOAT32, shape, (const void *)data);
- auto *constant = mir_graph.create<mir::ops::ConstantOp>(mir_tensor)->getOutput(0);
+ mir::TensorVariant filter_tensor{filter_type, data};
+ auto *filter = mir_graph.create<mir::ops::ConstantOp>(filter_tensor)->getOutput(0);
+
+ mir::Conv2DOpAttributes attributes;
+ attributes.strides = {2, 3};
+ attributes.padding_before = {5, 9};
+ attributes.padding_after = {7, 4};
+
auto *conv =
- mir_graph
- .create<mir::ops::DepthwiseConv2DOp>(
- input, constant, std::vector<std::int32_t>{2, 3}, std::vector<std::int32_t>{5, 9},
- std::vector<std::int32_t>{7, 4}, mir::DataFormat::NHWC)
- ->getOutput(0);
+ mir_graph.create<mir::ops::DepthwiseConv2DOp>(input, filter, attributes)->getOutput(0);
+
mir_graph.create<mir::ops::OutputOp>(conv);
input->setName("x");
conv->setName("y");
@@ -528,12 +549,12 @@ TEST_F(TestTransformer_mir2loco, DepthwiseConv2D_Test)
ASSERT_NE(filter_node, nullptr);
ASSERT_EQ(dw_conv_node->ifm(), encode_node);
// Check params
- ASSERT_EQ(dw_conv_node->pad()->left(), 5);
- ASSERT_EQ(dw_conv_node->pad()->top(), 9);
- ASSERT_EQ(dw_conv_node->pad()->right(), 7);
- ASSERT_EQ(dw_conv_node->pad()->bottom(), 4);
- ASSERT_EQ(dw_conv_node->stride()->horizontal(), 2);
- ASSERT_EQ(dw_conv_node->stride()->vertical(), 3);
+ ASSERT_EQ(dw_conv_node->pad()->top(), 5);
+ ASSERT_EQ(dw_conv_node->pad()->left(), 9);
+ ASSERT_EQ(dw_conv_node->pad()->bottom(), 7);
+ ASSERT_EQ(dw_conv_node->pad()->right(), 4);
+ ASSERT_EQ(dw_conv_node->stride()->vertical(), 2);
+ ASSERT_EQ(dw_conv_node->stride()->horizontal(), 3);
// ConstGen
loco::ConstGen *const_node = dynamic_cast<loco::ConstGen *>(filter_node->input());
ASSERT_NE(const_node, nullptr);
@@ -550,3 +571,166 @@ TEST_F(TestTransformer_mir2loco, DepthwiseConv2D_Test)
ASSERT_NE(push_node, nullptr);
ASSERT_EQ(push_node->from(), decode_node);
}
+
+TEST_F(TestTransformer_mir2loco, DeConv2D_Test)
+{
+ mir::Graph mir_graph;
+
+ mir::TensorType input_type{mir::DataType::FLOAT32, {7, 7, 9, 1}};
+ auto *input = mir_graph.create<mir::ops::InputOp>(input_type)->getOutput(0);
+
+ mir::TensorType filter_type{mir::DataType::FLOAT32, {2, 3, 1, 1}};
+ const float data[] = {5.9, 6.7, 5.32, 54.11231, 43.2444, 3.409};
+ mir::TensorVariant filter_tensor{filter_type, data};
+ auto *filter = mir_graph.create<mir::ops::ConstantOp>(filter_tensor)->getOutput(0);
+
+ mir::Deconv2DOpAttributes attributes;
+ attributes.strides = {1, 2};
+ attributes.padding_before = {3, 4};
+ attributes.padding_after = {5, 6};
+
+ auto *conv = mir_graph.create<mir::ops::DeConv2DOp>(input, filter, attributes)->getOutput(0);
+
+ mir_graph.create<mir::ops::OutputOp>(conv);
+ input->setName("x");
+ conv->setName("y");
+
+ mir2loco::Transformer transformer;
+ auto loco_graph = transformer.transform(&mir_graph);
+
+ // Pull
+ loco::Pull *pull_node = loco::pull_node(loco_graph.get(), 0);
+ ASSERT_NE(pull_node, nullptr);
+ // FeatureEncode
+ auto pull_uses = loco::succs(pull_node);
+ ASSERT_EQ(pull_uses.size(), 1);
+ loco::FeatureEncode *encode_node = dynamic_cast<loco::FeatureEncode *>(*pull_uses.begin());
+ ASSERT_NE(encode_node, nullptr);
+ ASSERT_EQ(encode_node->input(), pull_node);
+ // TransposedConv2D
+ auto encode_uses = loco::succs(encode_node);
+ ASSERT_EQ(encode_uses.size(), 1);
+ loco::TransposedConv2D *tr_conv_node =
+ dynamic_cast<loco::TransposedConv2D *>(*encode_uses.begin());
+ ASSERT_NE(tr_conv_node, nullptr);
+ loco::FilterEncode *filter_node = dynamic_cast<loco::FilterEncode *>(tr_conv_node->ker());
+ ASSERT_NE(filter_node, nullptr);
+ ASSERT_EQ(tr_conv_node->ifm(), encode_node);
+ // Check params
+ ASSERT_EQ(tr_conv_node->pad()->top(), 3);
+ ASSERT_EQ(tr_conv_node->pad()->left(), 4);
+ ASSERT_EQ(tr_conv_node->pad()->bottom(), 5);
+ ASSERT_EQ(tr_conv_node->pad()->right(), 6);
+ ASSERT_EQ(tr_conv_node->stride()->vertical(), 1);
+ ASSERT_EQ(tr_conv_node->stride()->horizontal(), 2);
+ // ConstGen
+ loco::ConstGen *const_node = dynamic_cast<loco::ConstGen *>(filter_node->input());
+ ASSERT_NE(const_node, nullptr);
+ // FeatureDecode
+ auto tr_conv_uses = loco::succs(tr_conv_node);
+ ASSERT_EQ(tr_conv_uses.size(), 1);
+ loco::FeatureDecode *decode_node = dynamic_cast<loco::FeatureDecode *>(*tr_conv_uses.begin());
+ ASSERT_NE(decode_node, nullptr);
+ ASSERT_EQ(decode_node->input(), tr_conv_node);
+ // Push
+ auto decode_uses = loco::succs(decode_node);
+ ASSERT_EQ(decode_uses.size(), 1);
+ loco::Push *push_node = dynamic_cast<loco::Push *>(*decode_uses.begin());
+ ASSERT_NE(push_node, nullptr);
+ ASSERT_EQ(push_node->from(), decode_node);
+}
+
+TEST_F(TestTransformer_mir2loco, FullyConnected_Test)
+{
+ mir::Graph mir_graph;
+
+ mir::TensorType input_type{mir::DataType::FLOAT32, {10, 2}};
+ auto *input = mir_graph.create<mir::ops::InputOp>(input_type)->getOutput(0);
+
+ mir::TensorType weights_type{mir::DataType::FLOAT32, mir::Shape{2, 2}};
+ const float data[] = {5.9, 5.32, 54.11231, 3.409};
+ mir::TensorVariant weights_tensor{weights_type, data};
+ auto *weights = mir_graph.create<mir::ops::ConstantOp>(weights_tensor)->getOutput(0);
+
+ auto *fc = mir_graph.create<mir::ops::FullyConnectedOp>(input, weights)->getOutput(0);
+
+ mir_graph.create<mir::ops::OutputOp>(fc);
+ input->setName("x");
+ fc->setName("y");
+
+ mir2loco::Transformer transformer;
+ auto loco_graph = transformer.transform(&mir_graph);
+
+ // Pull
+ auto inputs = loco_graph->inputs();
+ loco::Pull *pull_node = loco::pull_node(loco_graph.get(), 0);
+ ASSERT_NE(pull_node, nullptr);
+ // MatrixEncode
+ auto pull_uses = loco::succs(pull_node);
+ ASSERT_EQ(pull_uses.size(), 1);
+ loco::MatrixEncode *encode_node = dynamic_cast<loco::MatrixEncode *>(*pull_uses.begin());
+ ASSERT_NE(encode_node, nullptr);
+ ASSERT_EQ(encode_node->input(), pull_node);
+ // MatMul
+ auto encode_uses = loco::succs(encode_node);
+ ASSERT_EQ(encode_uses.size(), 1);
+ loco::MatMul *fc_node = dynamic_cast<loco::MatMul *>(*encode_uses.begin());
+ ASSERT_NE(fc_node, nullptr);
+ loco::MatrixEncode *kernel_encode_node = dynamic_cast<loco::MatrixEncode *>(fc_node->rhs());
+ ASSERT_NE(kernel_encode_node, nullptr);
+ ASSERT_EQ(fc_node->lhs(), encode_node);
+ // ConstGen
+ loco::ConstGen *const_node = dynamic_cast<loco::ConstGen *>(kernel_encode_node->input());
+ ASSERT_NE(const_node, nullptr);
+ // MatrixDecode
+ auto fc_uses = loco::succs(fc_node);
+ ASSERT_EQ(fc_uses.size(), 1);
+ loco::MatrixDecode *decode_node = dynamic_cast<loco::MatrixDecode *>(*fc_uses.begin());
+ ASSERT_NE(decode_node, nullptr);
+ ASSERT_EQ(decode_node->input(), fc_node);
+ // Push
+ auto decode_uses = loco::succs(decode_node);
+ ASSERT_EQ(decode_uses.size(), 1);
+ loco::Push *push_node = dynamic_cast<loco::Push *>(*decode_uses.begin());
+ ASSERT_NE(push_node, nullptr);
+ ASSERT_EQ(push_node->from(), decode_node);
+}
+
+TEST_F(TestTransformer_mir2loco, Transpose_Test)
+{
+ mir::Graph mir_graph;
+
+ mir::TensorType input_type{mir::DataType::FLOAT32, {2, 7, 9, 5}};
+ auto *input = mir_graph.create<mir::ops::InputOp>(input_type)->getOutput(0);
+ auto *transpose =
+ mir_graph.create<mir::ops::TransposeOp>(input, std::vector<std::size_t>{3, 0, 1, 2})
+ ->getOutput(0);
+ mir_graph.create<mir::ops::OutputOp>(transpose);
+ input->setName("x");
+ transpose->setName("y");
+
+ mir2loco::Transformer transformer;
+ auto loco_graph = transformer.transform(&mir_graph);
+ // Pull
+ auto inputs = loco_graph->inputs();
+ loco::Pull *pull_node = loco::pull_node(loco_graph.get(), 0);
+ ASSERT_NE(pull_node, nullptr);
+ // Transpose
+ auto pull_uses = loco::succs(pull_node);
+ ASSERT_EQ(pull_uses.size(), 1);
+ loco::TensorTranspose *transpose_node = dynamic_cast<loco::TensorTranspose *>(*pull_uses.begin());
+ ASSERT_NE(transpose_node, nullptr);
+ ASSERT_EQ(transpose_node->input(), pull_node);
+ // Push
+ auto transpose_uses = loco::succs(transpose_node);
+ ASSERT_EQ(transpose_uses.size(), 1);
+ loco::Push *push_node = dynamic_cast<loco::Push *>(*transpose_uses.begin());
+ ASSERT_NE(push_node, nullptr);
+ ASSERT_EQ(push_node->from(), transpose_node);
+ // Axis check
+ ASSERT_EQ(transpose_node->perm()->size(), 4);
+ ASSERT_EQ(transpose_node->perm()->axis(0), 3);
+ ASSERT_EQ(transpose_node->perm()->axis(1), 0);
+ ASSERT_EQ(transpose_node->perm()->axis(2), 1);
+ ASSERT_EQ(transpose_node->perm()->axis(3), 2);
+}
diff --git a/compiler/moco-log/CMakeLists.txt b/compiler/moco-log/CMakeLists.txt
index bbf3dfe90..036b4e74b 100644
--- a/compiler/moco-log/CMakeLists.txt
+++ b/compiler/moco-log/CMakeLists.txt
@@ -6,3 +6,4 @@ target_include_directories(moco_log PUBLIC include)
target_link_libraries(moco_log PUBLIC hermes)
target_link_libraries(moco_log PRIVATE hermes_std)
target_link_libraries(moco_log PRIVATE stdex)
+install(TARGETS moco_log DESTINATION lib)
diff --git a/compiler/moco-onnx/CMakeLists.txt b/compiler/moco-onnx/CMakeLists.txt
deleted file mode 100644
index 372900f00..000000000
--- a/compiler/moco-onnx/CMakeLists.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-nncc_find_package(Protobuf QUIET)
-nncc_find_package(ONNXSource EXACT 1.4.1 QUIET)
-
-if(NOT Protobuf_FOUND)
- return()
-endif(NOT Protobuf_FOUND)
-
-if(NOT ONNXSource_FOUND)
- return()
-endif(NOT ONNXSource_FOUND)
-
-add_subdirectory(proto)
-
-file(GLOB_RECURSE SOURCES "src/*.cpp")
-file(GLOB_RECURSE TESTS "src/*.test.cpp")
-list(REMOVE_ITEM SOURCES ${TESTS})
-
-add_library(moco_onnx_frontend SHARED ${SOURCES})
-target_include_directories(moco_onnx_frontend PRIVATE src)
-target_include_directories(moco_onnx_frontend PUBLIC include)
-target_link_libraries(moco_onnx_frontend PUBLIC moco_onnx_proto)
-target_link_libraries(moco_onnx_frontend PUBLIC loco)
-target_link_libraries(moco_onnx_frontend PRIVATE stdex)
-target_link_libraries(moco_onnx_frontend PRIVATE cwrap)
-
-nncc_find_package(GTest QUIET)
-
-if(NOT GTest_FOUND)
- return()
-endif(NOT GTest_FOUND)
-
-add_executable(moco_onnx_frontend_test ${TESTS})
-target_include_directories(moco_onnx_frontend_test PRIVATE src)
-target_link_libraries(moco_onnx_frontend_test gtest_main)
-target_link_libraries(moco_onnx_frontend_test moco_onnx_frontend)
-add_test(moco_onnx_frontend_test moco_onnx_frontend_test)
diff --git a/compiler/moco-tf/CMakeLists.txt b/compiler/moco-tf/CMakeLists.txt
index 9ca5777ad..5516388a4 100644
--- a/compiler/moco-tf/CMakeLists.txt
+++ b/compiler/moco-tf/CMakeLists.txt
@@ -1,16 +1,6 @@
-nncc_find_package(Protobuf QUIET)
-# TensorFlowSource package is used to use ~.proto files
-nncc_find_package(TensorFlowSource EXACT 1.12 QUIET)
-
-if(NOT Protobuf_FOUND)
- return()
-endif(NOT Protobuf_FOUND)
-
-if(NOT TensorFlowSource_FOUND)
+if(NOT TARGET mio_tf)
return()
-endif(NOT TensorFlowSource_FOUND)
-
-add_subdirectory(proto)
+endif(NOT TARGET mio_tf)
file(GLOB_RECURSE SOURCES "src/*.cpp")
file(GLOB_RECURSE TESTS "src/*.test.cpp")
@@ -19,13 +9,17 @@ list(REMOVE_ITEM SOURCES ${TESTS})
add_library(moco_tf_frontend SHARED ${SOURCES})
target_include_directories(moco_tf_frontend PRIVATE src)
target_include_directories(moco_tf_frontend PUBLIC include)
-target_link_libraries(moco_tf_frontend PUBLIC moco_tf_proto)
target_link_libraries(moco_tf_frontend PUBLIC loco)
+target_link_libraries(moco_tf_frontend PUBLIC moco_lang)
+target_link_libraries(moco_tf_frontend PUBLIC moco_import)
+target_link_libraries(moco_tf_frontend PUBLIC moco_pass)
+target_link_libraries(moco_tf_frontend PUBLIC mio_tf)
+target_link_libraries(moco_tf_frontend PRIVATE moco_service)
+target_link_libraries(moco_tf_frontend PRIVATE moco_support)
target_link_libraries(moco_tf_frontend PRIVATE bino)
target_link_libraries(moco_tf_frontend PRIVATE fipe)
target_link_libraries(moco_tf_frontend PRIVATE locop)
target_link_libraries(moco_tf_frontend PRIVATE stdex)
-target_link_libraries(moco_tf_frontend PRIVATE cwrap)
target_link_libraries(moco_tf_frontend PRIVATE moco_log)
target_link_libraries(moco_tf_frontend PRIVATE pepper_str)
target_link_libraries(moco_tf_frontend PRIVATE pepper_strcast)
@@ -33,12 +27,14 @@ target_link_libraries(moco_tf_frontend PRIVATE locomotiv)
target_link_libraries(moco_tf_frontend PRIVATE plier_tf)
target_link_libraries(moco_tf_frontend PRIVATE locoex_customop)
target_link_libraries(moco_tf_frontend PRIVATE logo)
+target_link_libraries(moco_tf_frontend PRIVATE oops)
+install(TARGETS moco_tf_frontend DESTINATION lib)
if(NOT ENABLE_TEST)
return()
endif(NOT ENABLE_TEST)
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
add_executable(moco_tf_frontend_test ${TESTS})
target_include_directories(moco_tf_frontend_test PRIVATE src)
diff --git a/compiler/moco-tf/include/moco/tf/Frontend.h b/compiler/moco-tf/include/moco/tf/Frontend.h
index 4507a76d4..6914fdd38 100644
--- a/compiler/moco-tf/include/moco/tf/Frontend.h
+++ b/compiler/moco-tf/include/moco/tf/Frontend.h
@@ -17,72 +17,17 @@
#ifndef __MOCO_TENSORFLOW_FRONTEND_H__
#define __MOCO_TENSORFLOW_FRONTEND_H__
-#include <moco/tf/Names.h>
+#include <moco/Import/ModelSignature.h>
#include <loco.h>
-#include <angkor/TensorShape.h>
#include <tensorflow/core/framework/graph.pb.h>
-#include <istream>
-#include <memory>
-#include <string>
-#include <vector>
-
namespace moco
{
namespace tf
{
-using TensorShape = angkor::TensorShape;
-
-/**
- * @brief Class to store information to run a model. Normally this info comes from users
- * via CLI params or configuration file.
- */
-struct ModelSignature
-{
-public:
- void add_input(const TensorName &input) { _inputs.push_back(input); }
- void add_input(const TensorName &&input) { _inputs.push_back(input); }
- void add_output(const TensorName &output) { _outputs.push_back(output); }
- void add_output(const TensorName &&output) { _outputs.push_back(output); }
-
- const std::vector<TensorName> &inputs() const { return _inputs; }
- const std::vector<TensorName> &outputs() const { return _outputs; }
-
- /**
- * @brief Adds customop op type (not name of node) provided from user
- */
- void add_customop(const std::string &op);
- const std::vector<std::string> &customops() const { return _customops; }
-
- /**
- * @brief Adds node name and its shape provided from user
- */
- void shape(const std::string &node_name, const TensorShape &shape);
- const TensorShape *shape(const std::string &node_name) const;
-
- /**
- * @brief Adds node name and its dtype provided from user
- */
- void dtype(const std::string &node_name, loco::DataType dtype);
- loco::DataType dtype(const std::string &node_name) const;
-
-private:
- std::vector<TensorName> _inputs; // graph inputs
- std::vector<TensorName> _outputs; // graph outputs
-
- // For custom op types passed from user (e.g., via CLI)
- std::vector<std::string> _customops;
-
- // For and node names and shapes passed from user (e.g., via CLI)
- std::map<std::string, TensorShape> _shapes;
-
- // For and node names and dtype passed from user (e.g., via CLI)
- std::map<std::string, loco::DataType> _dtypes;
-};
-
class Frontend
{
public:
@@ -103,15 +48,6 @@ private:
std::unique_ptr<loco::Graph> import(const ModelSignature &, tensorflow::GraphDef &) const;
};
-/**
- * @brief This will do internal memory cleanup of the graph returned from
- * Frontend::load() method.
- *
- * @note Calling this can be omitted as all allocations will be freed at
- * termination but memory usage can be unnecessary higher.
- */
-void cleanup(loco::Graph *graph);
-
} // namespace tf
} // namespace moco
diff --git a/compiler/moco-tf/include/moco/tf/Names.h b/compiler/moco-tf/include/moco/tf/Names.h
deleted file mode 100644
index fc612d27d..000000000
--- a/compiler/moco-tf/include/moco/tf/Names.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_NAMES_H__
-#define __MOCO_TF_NAMES_H__
-
-#include <string>
-#include <stdexcept>
-
-namespace moco
-{
-namespace tf
-{
-
-struct TensorName final
-{
-public:
- /**
- * @brief Constructor
- *
- * @note If tensor_name does not have ":index", this constructor adds ":0" by default
- */
- explicit TensorName(const std::string &tensor_name)
- {
- if (tensor_name.find(":") != std::string::npos) // tensor_name is a form of letter:0
- {
- _name.assign(tensor_name);
- }
- else
- {
- _name.assign(tensor_name + ":0"); // if it does not have ":index", adds ":0" by default
- }
- }
-
- explicit TensorName(const std::string &node_name, const int tensor_index)
- {
- if (node_name.find(":") != std::string::npos) // tensor_name is already a form of name:0
- {
- throw std::runtime_error("Node name has already tensor index:" + node_name);
- }
- else
- {
- _name.assign(node_name + ":" + std::to_string(tensor_index));
- }
- }
-
- const std::string &name() const { return _name; }
-
- /**
- * @brief Returns node name from tensor name by removing, e.g., ":0"
- */
- const std::string nodeName() const
- {
- auto index = _name.find(":");
-
- if (index != std::string::npos)
- return _name.substr(0, index);
- else
- throw std::runtime_error{"Tensor name should be a name:number format: " + _name};
- };
-
-private:
- std::string _name;
-};
-
-/**
- * @brief To use TensorName as a key in std::map, this struct defines how to compare two TensorNames
- */
-struct TensorNameCompare
-{
- bool operator()(const TensorName &lhs, const TensorName &rhs) const
- {
- return lhs.name() < rhs.name();
- }
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_NAMES_H__
diff --git a/compiler/moco-tf/proto/CMakeLists.txt b/compiler/moco-tf/proto/CMakeLists.txt
deleted file mode 100644
index 2053a6893..000000000
--- a/compiler/moco-tf/proto/CMakeLists.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-# Minimal Protocol Buffer specification for GraphDef file (.pb) encoding/decoding
-unset(PROTO_FILES)
-list(APPEND PROTO_FILES tensorflow/core/framework/versions.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/resource_handle.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/types.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/tensor.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/tensor_shape.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/attr_value.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/op_def.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/node_def.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/function.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/graph.proto)
-
-Protobuf_Generate(GRAPHDEF_PROTO
- "${CMAKE_CURRENT_BINARY_DIR}/generated"
- "${TensorFlowSource_DIR}"
- ${PROTO_FILES})
-
-add_library(moco_tf_proto STATIC ${GRAPHDEF_PROTO_SOURCES})
-set_target_properties(moco_tf_proto PROPERTIES POSITION_INDEPENDENT_CODE ON)
-target_include_directories(moco_tf_proto PUBLIC ${GRAPHDEF_PROTO_INCLUDE_DIRS})
-target_link_libraries(moco_tf_proto PUBLIC libprotobuf)
diff --git a/compiler/moco-tf/requires.cmake b/compiler/moco-tf/requires.cmake
index 10e4774e7..3e0fabee9 100644
--- a/compiler/moco-tf/requires.cmake
+++ b/compiler/moco-tf/requires.cmake
@@ -1,11 +1,14 @@
require("fipe")
require("loco")
+require("moco")
require("locop")
-require("cwrap")
require("stdex")
require("moco-log")
require("pepper-strcast")
require("locomotiv")
+require("mio-tf")
require("plier-tf")
require("locoex-customop")
require("logo")
+require("oops")
+require("bino")
diff --git a/compiler/moco-tf/src/Annotations/ConcatData.h b/compiler/moco-tf/src/Annotations/ConcatData.h
deleted file mode 100644
index 4c8e5fa5e..000000000
--- a/compiler/moco-tf/src/Annotations/ConcatData.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_CONCAT_DATA_H__
-#define __MOCO_TF_CONCAT_DATA_H__
-
-#include <loco.h>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief ConcatData holds temporary axis attribute of Concat while building the graph
-*/
-class ConcatData : public loco::NodeAnnotation
-{
-public:
- ConcatData(int32_t axis) : _axis(axis) {}
-
- int32_t axis(void) const { return _axis; }
-
-private:
- int32_t _axis;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_CONCAT_DATA_H__
diff --git a/compiler/moco-tf/src/Annotations/PadData.h b/compiler/moco-tf/src/Annotations/PadData.h
deleted file mode 100644
index 887a1c503..000000000
--- a/compiler/moco-tf/src/Annotations/PadData.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_PAD_DATA_H__
-#define __MOCO_TF_PAD_DATA_H__
-
-#include <loco.h>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief PadData holds temporary 'pad' attribute of TFConv2D
- *
- * @note This holds the same pad attribute that exist in Canonical Conv2D
- * to simplify Canonicalizing step of TFConv2D to Conv2D conversion.
- * Values of 'pad' will be calculated in FixPaddingTransformation.
- * PadData holds Padding2D where PaddingData holds 'padding' as a string.
- */
-class PadData : public loco::NodeAnnotation
-{
-public:
- PadData() = default;
-
-public:
- const loco::Padding2D *pad(void) const { return &_pad; }
- loco::Padding2D *pad(void) { return &_pad; }
-
-private:
- loco::Padding2D _pad;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_PAD_DATA_H__
diff --git a/compiler/moco-tf/src/Annotations/PaddingData.h b/compiler/moco-tf/src/Annotations/PaddingData.h
deleted file mode 100644
index e875cca7d..000000000
--- a/compiler/moco-tf/src/Annotations/PaddingData.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_PADDING_DATA_H__
-#define __MOCO_TF_PADDING_DATA_H__
-
-#include <loco.h>
-
-#include <string>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief PaddingData holds temporary padding attribute
- *
- * @note Related nodes are AvgPool2D, MaxPool2D, Conv2D and maybe others
- * PaddingData holds 'padding' as a string where PadData holds Padding2D
- */
-class PaddingData : public loco::NodeAnnotation
-{
-public:
- PaddingData(const std::string &padding) : _padding(padding) {}
-
- const std::string &padding(void) const { return _padding; }
-
-private:
- std::string _padding;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_PADDING_DATA_H__
diff --git a/compiler/moco-tf/src/Annotations/ShapeInferenceData.cpp b/compiler/moco-tf/src/Annotations/ShapeInferenceData.cpp
deleted file mode 100644
index e6ffa98ae..000000000
--- a/compiler/moco-tf/src/Annotations/ShapeInferenceData.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ShapeInferenceData.h"
-
-#include <stdexcept>
-
-namespace moco
-{
-namespace tf
-{
-
-loco::TensorShape ShapeInferenceData::tensor_shape(void) const
-{
- assert(_domain == loco::Domain::Tensor);
-
- loco::TensorShape shape;
-
- shape.rank(rank());
- for (uint32_t r = 0; r < rank(); ++r)
- {
- if (dim(r).known())
- shape.dim(r) = dim(r).value();
- else
- shape.dim(r).unset();
- }
-
- return shape;
-}
-
-loco::FeatureShape ShapeInferenceData::feature_shape(void) const
-{
- assert(_domain == loco::Domain::Feature);
-
- loco::FeatureShape shape;
-
- if (rank() != 4)
- throw std::runtime_error("Feature should be rank 4");
-
- shape.count() = dim(0);
- shape.height() = dim(1);
- shape.width() = dim(2);
- shape.depth() = dim(3);
-
- return shape;
-}
-
-loco::FilterShape ShapeInferenceData::filter_shape(void) const
-{
- assert(_domain == loco::Domain::Filter);
-
- loco::FilterShape shape;
-
- if (rank() != 4)
- throw std::runtime_error("Filter should be rank 4");
-
- shape.count() = dim(0);
- shape.height() = dim(1);
- shape.width() = dim(2);
- shape.depth() = dim(3);
-
- return shape;
-}
-
-loco::DepthwiseFilterShape ShapeInferenceData::depthwisefilter_shape(void) const
-{
- assert(_domain == loco::Domain::DepthwiseFilter);
-
- loco::DepthwiseFilterShape shape;
-
- if (rank() != 4)
- throw std::runtime_error("DepthwiseFilter should be rank 4");
-
- shape.height() = dim(0);
- shape.width() = dim(1);
- shape.depth() = dim(2);
- shape.multiplier() = dim(3);
-
- return shape;
-}
-
-loco::BiasShape ShapeInferenceData::bias_shape(void) const
-{
- assert(_domain == loco::Domain::Bias);
-
- loco::BiasShape shape;
-
- // Note: this may change when loco::BiasShape becomes available
- shape.length() = dim(0).value();
-
- return shape;
-}
-
-void ShapeInferenceData::tensor_shape(const loco::TensorShape &shape)
-{
- _domain = loco::Domain::Tensor;
-
- rank(shape.rank());
- for (uint32_t r = 0; r < shape.rank(); ++r)
- {
- if (shape.dim(r).known())
- dim(r) = shape.dim(r).value();
- else
- dim(r).unset();
- }
-}
-
-void ShapeInferenceData::feature_shape(const loco::FeatureShape &shape)
-{
- _domain = loco::Domain::Feature;
-
- rank(4);
- dim(0) = shape.count();
- dim(1) = shape.height();
- dim(2) = shape.width();
- dim(3) = shape.depth();
-}
-
-void ShapeInferenceData::filter_shape(const loco::FilterShape &shape)
-{
- _domain = loco::Domain::Filter;
-
- rank(4);
- dim(0) = shape.count();
- dim(1) = shape.height();
- dim(2) = shape.width();
- dim(3) = shape.depth();
-}
-
-void ShapeInferenceData::depthwisefilter_shape(const loco::DepthwiseFilterShape &shape)
-{
- _domain = loco::Domain::DepthwiseFilter;
-
- rank(4);
- dim(0) = shape.height();
- dim(1) = shape.width();
- dim(2) = shape.depth();
- dim(3) = shape.multiplier();
-}
-
-void ShapeInferenceData::bias_shape(const loco::BiasShape &shape)
-{
- _domain = loco::Domain::Bias;
-
- // Note: this may change when loco::BiasShape becomes available
- rank(1);
- dim(0) = shape.length();
-}
-
-void as_tensor_shape(ShapeInferenceData &shapedata, const loco::FeatureShape &feature_shape,
- const TFDataLayout &data_layout)
-{
- loco::TensorShape tensor_shape;
-
- tensor_shape.rank(4);
- if (data_layout == "NHWC")
- {
- tensor_shape.dim(0) = feature_shape.count();
- tensor_shape.dim(1) = feature_shape.height();
- tensor_shape.dim(2) = feature_shape.width();
- tensor_shape.dim(3) = feature_shape.depth();
- }
- else if (data_layout == "NCHW")
- {
- tensor_shape.dim(0) = feature_shape.count();
- tensor_shape.dim(1) = feature_shape.depth();
- tensor_shape.dim(2) = feature_shape.height();
- tensor_shape.dim(3) = feature_shape.width();
- }
- else
- {
- // TODO support for other data_layout if needed
- throw std::runtime_error("as_tensor_shape: only supports NHWC or NCHW");
- }
-
- shapedata.tensor_shape(tensor_shape);
-}
-
-loco::FeatureShape as_feature_shape(const ShapeInferenceData &shapedata,
- const TFDataLayout &data_layout)
-{
- if (shapedata.domain() == loco::Domain::Feature)
- return shapedata.feature_shape();
-
- loco::FeatureShape feature_shape;
-
- // only convert from tensor to feature
- if (shapedata.domain() != loco::Domain::Tensor)
- {
- throw std::runtime_error("as_feature_shape: domain is not tensor");
- }
- if (shapedata.rank() != 4)
- {
- throw std::runtime_error("as_feature_shape: rank is not 4");
- }
-
- // TODO support for other data_layout if needed
- if (data_layout != "NHWC" && data_layout != "NCHW")
- {
- throw std::runtime_error("as_feature_shape: only supports NHWC or NCHW");
- }
-
- if (data_layout == "NHWC")
- {
- feature_shape.count() = shapedata.dim(0);
- feature_shape.height() = shapedata.dim(1);
- feature_shape.width() = shapedata.dim(2);
- feature_shape.depth() = shapedata.dim(3);
- }
- else
- {
- feature_shape.count() = shapedata.dim(0);
- feature_shape.depth() = shapedata.dim(1);
- feature_shape.height() = shapedata.dim(2);
- feature_shape.width() = shapedata.dim(3);
- }
-
- return feature_shape;
-}
-
-bool operator==(const ShapeInferenceData &lhs, const ShapeInferenceData &rhs)
-{
- if (lhs.domain() != rhs.domain())
- return false;
-
- switch (lhs.domain())
- {
- case loco::Domain::Tensor:
- {
- auto lhs_t = lhs.tensor_shape();
- auto rhs_t = rhs.tensor_shape();
- if (lhs_t.rank() != rhs.rank())
- return false;
- for (uint32_t axis = 0; axis < lhs_t.rank(); ++axis)
- {
- if (!(lhs_t.dim(axis) == rhs_t.dim(axis)))
- return false;
- }
- return true;
- }
- // TODO Support other domains
- // case loco::Domain::Feature:
- // case loco::Domain::Filter:
- // case loco::Domain::Bias:
- default:
- throw std::runtime_error("Not supported domain for ShapeInferenceData equality");
- }
-}
-
-} // namespace tf
-} // namespace moco
diff --git a/compiler/moco-tf/src/Annotations/ShapeInferenceData.h b/compiler/moco-tf/src/Annotations/ShapeInferenceData.h
deleted file mode 100644
index d48699356..000000000
--- a/compiler/moco-tf/src/Annotations/ShapeInferenceData.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_SHAPEINFERENCE_DATA_H__
-#define __MOCO_TF_SHAPEINFERENCE_DATA_H__
-
-#include <loco.h>
-#include "loco/IR/BiasShape.h"
-
-#include <cassert>
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note Below alias may be introduced as separate class
-using TFDataLayout = std::string;
-
-/**
- * @brief ShapeInferenceData provides shape inference data tracking from the start(input)
- *
- * @note For Feature and Filter, NHWC is used for shape layout
- */
-class ShapeInferenceData : public loco::NodeAnnotation,
- public loco::NodeMixin<loco::NodeTrait::TensorShape>
-{
-public:
- ~ShapeInferenceData(){};
-
-public:
- loco::Domain domain(void) const { return _domain; }
-
- loco::TensorShape tensor_shape(void) const;
- loco::FeatureShape feature_shape(void) const;
- loco::FilterShape filter_shape(void) const;
- loco::DepthwiseFilterShape depthwisefilter_shape(void) const;
- loco::BiasShape bias_shape(void) const;
-
- void tensor_shape(const loco::TensorShape &shape);
- void feature_shape(const loco::FeatureShape &shape);
- void filter_shape(const loco::FilterShape &shape);
- void depthwisefilter_shape(const loco::DepthwiseFilterShape &shape);
- void bias_shape(const loco::BiasShape &shape);
-
-private:
- // TODO set default as Unknown, setting Tensor is to minimize change
- loco::Domain _domain{loco::Domain::Tensor};
-};
-
-void as_tensor_shape(ShapeInferenceData &shapedata, const loco::FeatureShape &shape,
- const TFDataLayout &data_layout);
-
-loco::FeatureShape as_feature_shape(const ShapeInferenceData &shapedata,
- const TFDataLayout &data_layout);
-
-bool operator==(const ShapeInferenceData &lhs, const ShapeInferenceData &rhs);
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_SHAPEINFERENCE_DATA_H__
diff --git a/compiler/moco-tf/src/Annotations/ShapeInferenceData.test.cpp b/compiler/moco-tf/src/Annotations/ShapeInferenceData.test.cpp
deleted file mode 100644
index 8b8de535e..000000000
--- a/compiler/moco-tf/src/Annotations/ShapeInferenceData.test.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ShapeInferenceData.h"
-
-#include <gtest/gtest.h>
-
-TEST(TensorFlowImport, shapeinferencedata_tensor_get)
-{
- moco::tf::ShapeInferenceData shapedata;
-
- shapedata.rank(4);
- shapedata.dim(0) = 1;
- shapedata.dim(1) = 2;
- shapedata.dim(2) = 3;
- shapedata.dim(3) = 4;
-
- loco::TensorShape tensor = shapedata.tensor_shape();
-
- ASSERT_EQ(tensor.rank(), 4);
- ASSERT_EQ(tensor.dim(0), 1);
- ASSERT_EQ(tensor.dim(1), 2);
- ASSERT_EQ(tensor.dim(2), 3);
- ASSERT_EQ(tensor.dim(3), 4);
-}
-
-TEST(TensorFlowImport, shapeinferencedata_feature)
-{
- loco::FeatureShape feature_s;
-
- feature_s.count() = 1;
- feature_s.height() = 2;
- feature_s.width() = 3;
- feature_s.depth() = 4;
-
- moco::tf::ShapeInferenceData shapedata;
-
- shapedata.feature_shape(feature_s);
-
- loco::FeatureShape feature_g = shapedata.feature_shape();
-
- ASSERT_EQ(feature_g.count(), 1);
- ASSERT_EQ(feature_g.height(), 2);
- ASSERT_EQ(feature_g.width(), 3);
- ASSERT_EQ(feature_g.depth(), 4);
-}
-
-TEST(TensorFlowImport, shapeinferencedata_filter)
-{
- loco::FilterShape filter_s;
-
- filter_s.count() = 1;
- filter_s.height() = 2;
- filter_s.width() = 3;
- filter_s.depth() = 4;
-
- moco::tf::ShapeInferenceData shapedata;
-
- shapedata.filter_shape(filter_s);
-
- ASSERT_EQ(shapedata.domain(), loco::Domain::Filter);
-
- loco::FilterShape filter_g = shapedata.filter_shape();
-
- ASSERT_EQ(filter_g.count(), 1);
- ASSERT_EQ(filter_g.height(), 2);
- ASSERT_EQ(filter_g.width(), 3);
- ASSERT_EQ(filter_g.depth(), 4);
-}
-
-TEST(TensorFlowImport, shapeinferencedata_bias)
-{
- // Note: this may change when loco::BiasShape becomes available
-
- loco::BiasShape bias_s;
-
- bias_s.length() = 3;
-
- moco::tf::ShapeInferenceData shapedata;
-
- shapedata.bias_shape(bias_s);
-
- loco::BiasShape bias_g = shapedata.bias_shape();
-
- ASSERT_EQ(bias_g.length(), 3);
-}
-
-TEST(TensorFlowImport, shapeinferencedata_as_tensor_set)
-{
- loco::FeatureShape feature_s;
-
- feature_s.count() = 1;
- feature_s.height() = 2;
- feature_s.width() = 3;
- feature_s.depth() = 4;
-
- moco::tf::ShapeInferenceData shapedata;
-
- as_tensor_shape(shapedata, feature_s, "NHWC");
-
- loco::TensorShape tensor_g;
-
- tensor_g = shapedata.tensor_shape();
-
- ASSERT_EQ(tensor_g.rank(), 4);
- ASSERT_EQ(tensor_g.dim(0), 1);
- ASSERT_EQ(tensor_g.dim(1), 2);
- ASSERT_EQ(tensor_g.dim(2), 3);
- ASSERT_EQ(tensor_g.dim(3), 4);
-}
-
-TEST(TensorFlowImport, shapeinferencedata_as_feature)
-{
- loco::TensorShape tensor_s;
-
- tensor_s.rank(4);
- tensor_s.dim(0) = 1;
- tensor_s.dim(1) = 2;
- tensor_s.dim(2) = 3;
- tensor_s.dim(3) = 4;
-
- moco::tf::ShapeInferenceData shapedata;
-
- shapedata.tensor_shape(tensor_s);
-
- loco::FeatureShape feature_g = as_feature_shape(shapedata, "NHWC");
-
- ASSERT_EQ(feature_g.count(), 1);
- ASSERT_EQ(feature_g.height(), 2);
- ASSERT_EQ(feature_g.width(), 3);
- ASSERT_EQ(feature_g.depth(), 4);
-}
-
-TEST(TensorFlowImport, shapeinferencedata_equality_tensor)
-{
- moco::tf::ShapeInferenceData left;
- moco::tf::ShapeInferenceData right, wrong1, wrong2;
-
- left.rank(2);
- left.dim(0) = 1;
- left.dim(1) = 2;
- ASSERT_EQ(left.domain(), loco::Domain::Tensor);
-
- right.rank(2);
- right.dim(0) = 1;
- right.dim(1) = 2;
- ASSERT_TRUE(left == right);
-
- wrong1.rank(1);
- wrong1.dim(0) = 1;
- ASSERT_FALSE(left == wrong1);
-
- loco::FeatureShape wrong2_f;
- wrong2_f.count() = 1;
- wrong2_f.depth() = 1;
- wrong2_f.height() = 1;
- wrong2_f.width() = 1;
-
- wrong2.feature_shape(wrong2_f);
- ASSERT_FALSE(left == wrong2);
-}
diff --git a/compiler/moco-tf/src/Annotations/StrideData.h b/compiler/moco-tf/src/Annotations/StrideData.h
deleted file mode 100644
index fb9a4b304..000000000
--- a/compiler/moco-tf/src/Annotations/StrideData.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_STRIDE_DATA_H__
-#define __MOCO_TF_STRIDE_DATA_H__
-
-#include <loco.h>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief StrideData holds temporary 'stride' attribute of TFConv2D, same thing
- * like the one in Conv2D used for Canonicalizing TFConv2D to Conv2D.
- * 'stride' will be calculated in FixShapeTransformation as for now.
- */
-class StrideData : public loco::NodeAnnotation
-{
-public:
- StrideData() = default;
-
-public:
- const loco::Stride<2> *stride(void) const { return &_stride; }
- loco::Stride<2> *stride(void) { return &_stride; }
-
-private:
- loco::Stride<2> _stride;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_STRIDE_DATA_H__
diff --git a/compiler/moco-tf/src/Annotations/WindowData.h b/compiler/moco-tf/src/Annotations/WindowData.h
deleted file mode 100644
index 8bd962578..000000000
--- a/compiler/moco-tf/src/Annotations/WindowData.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_WINDOW_DATA_H__
-#define __MOCO_TF_WINDOW_DATA_H__
-
-#include <loco.h>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief WindowData holds temporary 'window' attribute of AvgPool2D, MaxPool2D
- */
-class WindowData : public loco::NodeAnnotation
-{
-public:
- WindowData() = default;
-
-public:
- const loco::Window<2> *window(void) const { return &_window; }
- loco::Window<2> *window(void) { return &_window; }
-
-private:
- loco::Window<2> _window;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_WINDOW_DATA_H__
diff --git a/compiler/moco-tf/src/CanonicalEltwiseInputConnector.cpp b/compiler/moco-tf/src/CanonicalEltwiseInputConnector.cpp
index 7142336bf..adeae39de 100644
--- a/compiler/moco-tf/src/CanonicalEltwiseInputConnector.cpp
+++ b/compiler/moco-tf/src/CanonicalEltwiseInputConnector.cpp
@@ -37,6 +37,7 @@ template <typename NodeTy> void InputConnector<NodeTy>::operator()(const NodePai
INSTANTIATE(EltwiseAdd);
INSTANTIATE(EltwiseSub);
+INSTANTIATE(EltwiseMax);
INSTANTIATE(EltwiseMul);
INSTANTIATE(EltwiseDiv);
diff --git a/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.cpp
index ef82f3dab..8028a870c 100644
--- a/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.cpp
@@ -16,8 +16,8 @@
#include "AddCanonicalizer.h"
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
+#include <moco/IR/TFDialect.h>
+#include <moco/IR/TFNodes.h>
#include "TFEltwiseBinaryCanonicalzeHelper.h"
@@ -26,25 +26,9 @@ namespace moco
namespace tf
{
-bool AddCanonicalizer::run(loco::Graph *graph)
+bool AddCanonicalizer::transform(TFAdd *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_node = dynamic_cast<moco::tf::TFAdd *>(node);
- if (tf_node != nullptr)
- {
- if (canonicalize_eltwise_binary_node(tf_node))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_eltwise_binary_node(node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.h
index 07b8a72de..53ba9ed58 100644
--- a/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_ADD_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Convert TFAdd to Canonical EltwiseAdd
*/
-class AddCanonicalizer : public Transform
+class AddCanonicalizer : public SimpleNodeTransform<TFAdd>
{
public:
const char *name(void) const final { return "AddCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(TFAdd *node) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.cpp
index 66a71089e..e07a4f64f 100644
--- a/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.cpp
@@ -16,71 +16,19 @@
#include "AvgPoolCanonicalizer.h"
-#include "Annotations/PadData.h"
-#include "Annotations/StrideData.h"
-#include "Annotations/ShapeInferenceData.h"
-#include "Annotations/WindowData.h"
+#include <moco/IR/TFDialect.h>
+#include <moco/Support/TFShapeInferenceHelper.h>
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
-#include "Dialect/TFNodeVisitor.h"
-#include "Dialect/TFNodeImpl.h"
+#include "CodecHelper.h"
-#include <moco/Log.h>
-#include <plier/tf/Convert.h>
+#include <loco/IR/NodeShape.h>
-#include <stdex/Memory.h>
+#include <moco/Log.h>
namespace
{
-using plier::tf::DataLayout;
-
-void set_feature_enc(loco::FeatureEncode *feature_enc, DataLayout data_layout)
-{
- auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>();
-
- if (data_layout == DataLayout::NHWC)
- {
- enc->perm()->axis(loco::FeatureAxis::Count) = 0;
- enc->perm()->axis(loco::FeatureAxis::Height) = 1;
- enc->perm()->axis(loco::FeatureAxis::Width) = 2;
- enc->perm()->axis(loco::FeatureAxis::Depth) = 3;
- }
- else if (data_layout == DataLayout::NCHW)
- {
- enc->perm()->axis(loco::FeatureAxis::Count) = 0;
- enc->perm()->axis(loco::FeatureAxis::Depth) = 1;
- enc->perm()->axis(loco::FeatureAxis::Height) = 2;
- enc->perm()->axis(loco::FeatureAxis::Width) = 3;
- }
-
- feature_enc->encoder(std::move(enc));
-}
-
-void set_feature_dec(loco::FeatureDecode *feature_dec, DataLayout data_layout)
-{
- auto dec = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>();
-
- if (data_layout == DataLayout::NHWC)
- {
- dec->perm()->axis(loco::FeatureAxis::Count) = 0;
- dec->perm()->axis(loco::FeatureAxis::Height) = 1;
- dec->perm()->axis(loco::FeatureAxis::Width) = 2;
- dec->perm()->axis(loco::FeatureAxis::Depth) = 3;
- }
- else if (data_layout == DataLayout::NCHW)
- {
- dec->perm()->axis(loco::FeatureAxis::Count) = 0;
- dec->perm()->axis(loco::FeatureAxis::Depth) = 1;
- dec->perm()->axis(loco::FeatureAxis::Height) = 2;
- dec->perm()->axis(loco::FeatureAxis::Width) = 3;
- }
-
- feature_dec->decoder(std::move(dec));
-}
-
-bool canonicalize_avgpool2d(loco::Graph *graph, moco::tf::TFAvgPool *node)
+bool canonicalize_avgpool2d(loco::Graph *graph, moco::TFAvgPool *node)
{
LOGGER(l);
@@ -113,30 +61,24 @@ bool canonicalize_avgpool2d(loco::Graph *graph, moco::tf::TFAvgPool *node)
avgPool2d_node->convention(loco::AvgPool2D::Convention::Valid);
- // paddata to pad
- auto pad_data = node->annot<moco::tf::PadData>();
- assert(pad_data != nullptr);
+ auto value_shape = moco::node_shape(node->value());
+ assert(value_shape.domain() != loco::Domain::Unknown);
- avgPool2d_node->pad()->top(pad_data->pad()->top());
- avgPool2d_node->pad()->bottom(pad_data->pad()->bottom());
- avgPool2d_node->pad()->left(pad_data->pad()->left());
- avgPool2d_node->pad()->right(pad_data->pad()->right());
+ auto node_stride = moco::stride_of(node->strides(), node->data_layout());
+ auto node_window = moco::window_of(node->ksize(), node->data_layout());
- // windowdata to window (ksize to window)
- auto window_data = node->annot<moco::tf::WindowData>();
- assert(window_data != nullptr);
+ moco::Padding2DInference infer_padding2d;
- auto window = avgPool2d_node->window();
- window->vertical(window_data->window()->vertical());
- window->horizontal(window_data->window()->horizontal());
+ infer_padding2d.padding(node->padding());
+ infer_padding2d.stride(node_stride);
+ infer_padding2d.window(node_window);
- // stridedata to stride (strides to stride)
- auto stride_data = node->annot<moco::tf::StrideData>();
- assert(stride_data != nullptr);
+ auto input_feature_shape = moco::as_feature_shape(value_shape, node->data_layout());
+ auto input_plane_shape = moco::make_plane_shape(input_feature_shape);
- auto stride = avgPool2d_node->stride();
- stride->vertical(stride_data->stride()->vertical());
- stride->horizontal(stride_data->stride()->horizontal());
+ *avgPool2d_node->pad() = infer_padding2d(input_plane_shape);
+ *avgPool2d_node->stride() = node_stride;
+ *avgPool2d_node->window() = node_window;
INFO(l) << "Canonicalize TFAvgPool pad = T " << avgPool2d_node->pad()->top() << ", L "
<< avgPool2d_node->pad()->left() << ", B " << avgPool2d_node->pad()->bottom() << ", R "
@@ -163,25 +105,9 @@ namespace moco
namespace tf
{
-bool AvgPoolCanonicalizer::run(loco::Graph *graph)
+bool AvgPoolCanonicalizer::transform(TFAvgPool *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_node = dynamic_cast<moco::tf::TFAvgPool *>(node);
- if (tf_node != nullptr)
- {
- if (canonicalize_avgpool2d(graph, tf_node))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_avgpool2d(node->graph(), node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.h
index 7d7e6a80b..e9c56c868 100644
--- a/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_AVGPOOL_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Convert TFAvgPool to Canonical AvgPool2D
*/
-class AvgPoolCanonicalizer : public Transform
+class AvgPoolCanonicalizer : public SimpleNodeTransform<moco::TFAvgPool>
{
public:
const char *name(void) const final { return "AvgPoolCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(TFAvgPool *node) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.cpp
index 37b660e4a..a5568ce1a 100644
--- a/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.cpp
@@ -16,12 +16,9 @@
#include "BiasAddCanonicalizer.h"
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
-#include "Dialect/TFNodeVisitor.h"
-#include "Dialect/TFNodeImpl.h"
+#include <moco/IR/TFDialect.h>
-#include <moco/tf/Names.h>
+#include <moco/Names.h>
#include <moco/Log.h>
#include <plier/tf/Convert.h>
@@ -29,7 +26,7 @@ namespace
{
using plier::tf::DataLayout;
-bool canonicalize_biasadd(loco::Graph *graph, moco::tf::TFBiasAdd *node)
+bool canonicalize_biasadd(loco::Graph *graph, moco::TFBiasAdd *node)
{
LOGGER(l);
@@ -103,25 +100,9 @@ namespace moco
namespace tf
{
-bool BiasAddCanonicalizer::run(loco::Graph *graph)
+bool BiasAddCanonicalizer::transform(TFBiasAdd *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_biasadd = dynamic_cast<moco::tf::TFBiasAdd *>(node);
- if (tf_biasadd != nullptr)
- {
- if (canonicalize_biasadd(graph, tf_biasadd))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_biasadd(node->graph(), node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.h
index a30894708..ff4032ca9 100644
--- a/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_BIASADD_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Convert TFBiasAdd to Canonical BiasAdd
*/
-class BiasAddCanonicalizer : public Transform
+class BiasAddCanonicalizer final : public SimpleNodeTransform<moco::TFBiasAdd>
{
public:
const char *name(void) const final { return "BiasAddCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(TFBiasAdd *node) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.cpp
index e3939adb9..b59a3f3d7 100644
--- a/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.cpp
@@ -15,27 +15,39 @@
*/
#include "ConcatV2Canonicalizer.h"
-
#include "LogHelper.h"
-#include "Annotations/ConcatData.h"
-#include "Annotations/ShapeInferenceData.h"
-
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
-#include "Dialect/TFNodeVisitor.h"
-#include "Dialect/TFNodeImpl.h"
+#include <moco/IR/TFDialect.h>
+#include <moco/Support/TFShapeInferenceHelper.h>
#include <moco/Log.h>
+#include <loco/Service/ShapeInference.h>
+
#include <stdex/Memory.h>
+#include <oops/UserExn.h>
namespace
{
using namespace moco::tf;
-bool canonicalize_concat(loco::Graph *graph, moco::tf::TFConcatV2 *node)
+bool scalar_value(moco::TFConst *node, int32_t &ret)
+{
+ auto nodeshape = node_shape(node);
+ if (!(node->dtype() == loco::DataType::S32))
+ return false;
+
+ auto tensor_shape = nodeshape.as<loco::TensorShape>();
+ if (!(tensor_shape.rank() == 0 || tensor_shape.rank() == 1))
+ return false;
+
+ ret = node->at<loco::DataType::S32>(0);
+
+ return true;
+}
+
+bool canonicalize_concat(loco::Graph *graph, moco::TFConcatV2 *node)
{
LOGGER(l);
@@ -71,19 +83,43 @@ bool canonicalize_concat(loco::Graph *graph, moco::tf::TFConcatV2 *node)
const int num_values = node->num_values();
assert(num_values >= 2);
- // get axis value
- auto concat_data = node->annot<ConcatData>();
- assert(concat_data != nullptr);
- auto axis_value = concat_data->axis();
+ // get axis absolute value
+ auto value_a = node->values(0);
+ if (!loco::shape_known(value_a))
+ return false;
- auto shapedata = node->annot<ShapeInferenceData>();
- auto node_rank = shapedata->rank();
+ uint32_t node_rank = 0;
+ {
+ auto value_a_shape = moco::node_shape(value_a);
+ assert(value_a_shape.domain() == loco::Domain::Tensor);
+
+ auto value_a_tensor_shape = value_a_shape.as<loco::TensorShape>();
+ node_rank = value_a_tensor_shape.rank();
+ }
+ int32_t axis_value = 0;
+ {
+ // axis should be TFConst
+ auto axis_node = node->axis();
+ auto tfconst = dynamic_cast<moco::TFConst *>(axis_node);
+ if (tfconst == nullptr)
+ {
+ // TODO Check this: this error can be from TFOptimizatier.
+ throw oops::UserExn("ConcatV2 node has invalid input for axis", node->name());
+ }
+ auto result = scalar_value(tfconst, axis_value);
+ if (!result)
+ {
+ // TODO Check this: this error can be from TFOptimizatier.
+ throw oops::UserExn("ConcatV2 node has invalid input for axis", node->name());
+ }
+ }
uint32_t axis_absolute = (axis_value >= 0) ? axis_value : (int32_t)node_rank + axis_value;
INFO(l) << "canonicalize_concat axis(" << axis_absolute << "), value(" << axis_value << "), rank("
<< node_rank << ")";
+ // Convert series of TensorConcat if num_values > 2
auto concat_node = graph->nodes()->create<loco::TensorConcat>();
concat_node->lhs(node->values(0));
concat_node->rhs(node->values(1));
@@ -115,25 +151,9 @@ namespace moco
namespace tf
{
-bool ConcatV2Canonicalizer::run(loco::Graph *graph)
+bool ConcatV2Canonicalizer::transform(TFConcatV2 *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_node = dynamic_cast<moco::tf::TFConcatV2 *>(node);
- if (tf_node != nullptr)
- {
- if (canonicalize_concat(graph, tf_node))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_concat(node->graph(), node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.h b/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.h
index 4448ddb16..e6b471b89 100644
--- a/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_CONCATV2_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Convert TFConcatV2 to Canonical TensorConcat
*/
-class ConcatV2Canonicalizer : public Transform
+class ConcatV2Canonicalizer : public SimpleNodeTransform<moco::TFConcatV2>
{
public:
const char *name(void) const final { return "ConcatV2Canonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(moco::TFConcatV2 *node) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.cpp
index dea97f94a..60629cd5a 100644
--- a/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.cpp
@@ -16,18 +16,17 @@
#include "ConstCanonicalizer.h"
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
-#include "Dialect/TFNodeVisitor.h"
-#include "Dialect/TFNodeImpl.h"
+#include <moco/IR/TFDialect.h>
-#include <moco/tf/Names.h>
+#include <moco/Names.h>
#include <moco/Log.h>
+#include <oops/UserExn.h>
+
namespace
{
-bool canonicalize_const(loco::Graph *graph, moco::tf::TFConst *node)
+bool canonicalize_const(loco::Graph *graph, moco::TFConst *node)
{
LOGGER(l);
@@ -55,13 +54,27 @@ bool canonicalize_const(loco::Graph *graph, moco::tf::TFConst *node)
const_node->dtype(dtype);
auto rank = node->rank();
- const_node->rank(rank);
- for (uint32_t r = 0; r < rank; ++r)
+
+ if (rank == 0)
+ {
+ // This routine implements a workaround that converts a scalar constant (rank-0 tensor)
+ // into a rank-1 tensor of shape [1].
+ //
+ // TODO Revise this implementation later
+ const_node->rank(1);
+ const_node->dim(0) = 1;
+ }
+ else
{
- if (node->dim(r).known())
- const_node->dim(r) = node->dim(r);
- else
- const_node->dim(r).unset();
+ const_node->rank(rank);
+
+ for (uint32_t r = 0; r < rank; ++r)
+ {
+ if (node->dim(r).known())
+ const_node->dim(r) = node->dim(r);
+ else
+ const_node->dim(r).unset();
+ }
}
switch (dtype)
@@ -87,7 +100,7 @@ bool canonicalize_const(loco::Graph *graph, moco::tf::TFConst *node)
break;
}
default:
- throw std::runtime_error("NYI for this DataType");
+ throw oops::UserExn("Const has unsupported data type", node->name());
}
// update graph
@@ -105,25 +118,9 @@ namespace moco
namespace tf
{
-bool ConstCanonicalizer::run(loco::Graph *graph)
+bool ConstCanonicalizer::transform(TFConst *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_const = dynamic_cast<moco::tf::TFConst *>(node);
- if (tf_const != nullptr)
- {
- if (canonicalize_const(graph, tf_const))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_const(node->graph(), node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.h
index 53f3ca8e3..1b0b2b867 100644
--- a/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_CONST_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Convert TFConst to Canonical ConstGen
*/
-class ConstCanonicalizer : public Transform
+class ConstCanonicalizer : public SimpleNodeTransform<moco::TFConst>
{
public:
const char *name(void) const final { return "ConstCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(moco::TFConst *) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.cpp
new file mode 100644
index 000000000..d3cbd4ab3
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.cpp
@@ -0,0 +1,371 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Conv2DBackpropInputCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include "CodecHelper.h"
+
+#include <loco/IR/Stride.h>
+#include <loco/IR/Padding2D.h>
+#include <loco/Service/ShapeInference.h>
+
+#include <oops/UserExn.h>
+
+namespace
+{
+using plier::tf::DataLayout;
+
+void set_filter_enc(loco::FilterEncode *filter_enc)
+{
+ auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>();
+
+ // In TensorFlow, Conv2dBackpropInput's filter is a 4-D tensor of following shape:
+ // [filter_height, filter_width, out_channels, in_channels] or HWOI or HWNC (in/out in loco sense)
+ enc->perm()->axis(loco::FilterAxis::Height) = 0;
+ enc->perm()->axis(loco::FilterAxis::Width) = 1;
+ enc->perm()->axis(loco::FilterAxis::Count) = 2;
+ enc->perm()->axis(loco::FilterAxis::Depth) = 3;
+
+ filter_enc->encoder(std::move(enc));
+}
+
+} // namespace
+
+namespace
+{
+
+bool stride_2d_from_4d(loco::Stride<2> &ret, const std::vector<int64_t> &strides_4d,
+ const DataLayout data_layout)
+{
+ if (!(strides_4d.size() == 4))
+ return false;
+
+ switch (data_layout)
+ {
+ case DataLayout::NHWC:
+ ret.vertical(strides_4d.at(1));
+ ret.horizontal(strides_4d.at(2));
+ break;
+ case DataLayout::NCHW:
+ ret.vertical(strides_4d.at(2));
+ ret.horizontal(strides_4d.at(3));
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+struct PlaneShape
+{
+ loco::Dimension vertical;
+ loco::Dimension horizontal;
+};
+
+class Padding2DInference final
+{
+public:
+ Padding2DInference(const moco::TFNode *node) { _node = node; }
+
+public:
+ loco::Padding2D operator()(void);
+
+public:
+ PlaneShape &input() { return _input; }
+ PlaneShape &output() { return _output; }
+ loco::Stride<2> &stride() { return _stride; }
+ loco::Window<2> &window() { return _window; }
+ moco::TFPadding &padding() { return _padding; }
+
+private:
+ /// @brief Check whether ingredients set by non-default values
+ bool ready()
+ {
+ if (not input().vertical.known())
+ return false;
+ if (not input().horizontal.known())
+ return false;
+ if (not output().vertical.known())
+ return false;
+ if (not output().horizontal.known())
+ return false;
+ if (stride().vertical() == 0)
+ return false;
+ if (stride().horizontal() == 0)
+ return false;
+ if (window().vertical() == 0)
+ return false;
+ if (window().horizontal() == 0)
+ return false;
+ if (padding().empty())
+ return false;
+
+ return true;
+ }
+
+ inline uint32_t tight_output_for_valid_padding(uint32_t input, uint32_t stride, uint32_t filter)
+ {
+ return stride * (input - 1) + filter;
+ }
+
+ /**
+ * @note For Conv2DBackpropInput SAME padding, TensorFlow requires this condition to hold
+ *
+ * Reference: `::tensorflow::GetWindowedOutputSizeVerboseV2()` from TensorFlow project
+ */
+ inline bool same_padding_applicable(uint32_t input, uint32_t output, uint32_t stride)
+ {
+ // Here 'input' and 'output' means Conv2DBackpropInput's actual node input and output.
+ // Then these three conditions are equivalent:
+ //
+ // input == floor((output + stride - 1) / stride)
+ // input == ceil(output / stride)
+ // (stride * (input - 1) < output) and (output <= stride * input)
+ return (stride * (input - 1) < output) and (output <= stride * input);
+ }
+
+ inline uint32_t padding_needed(uint32_t input, uint32_t output, uint32_t stride, uint32_t filter)
+ {
+ return stride * (input - 1) + filter - output;
+ }
+
+private:
+ const moco::TFNode *_node;
+ PlaneShape _input;
+ PlaneShape _output;
+ loco::Stride<2> _stride;
+ loco::Window<2> _window;
+ moco::TFPadding _padding;
+};
+
+loco::Padding2D Padding2DInference::operator()(void)
+{
+ assert(ready());
+
+ if (padding() == "VALID")
+ {
+ // In case of VALID padding, TensorFlow accepts any size same or larger than
+ // 'tight fit' output. When output size (set by 'input sizes' node input) is
+ // larger than tight fit, extra spaces filled with zero.
+ auto tight_output_vertical = tight_output_for_valid_padding(
+ input().vertical.value(), stride().vertical(), window().vertical());
+ auto tight_output_horizontal = tight_output_for_valid_padding(
+ input().horizontal.value(), stride().horizontal(), window().horizontal());
+
+ if (output().vertical.value() < tight_output_vertical or
+ output().horizontal.value() < tight_output_horizontal)
+ throw oops::UserExn("input_sizes is too small", _node->name());
+
+ // Currently, only accept tight fit.
+ // TODO Support non-tight case by adding zero padding operation
+ assert(output().vertical.value() == tight_output_vertical);
+ assert(output().horizontal.value() == tight_output_horizontal);
+
+ return loco::Padding2D(0, 0, 0, 0);
+ }
+
+ if (padding() == "SAME")
+ {
+ // This condition is required by TensorFlow
+ if (not same_padding_applicable(input().vertical.value(), output().vertical.value(),
+ stride().vertical()) or
+ not same_padding_applicable(input().horizontal.value(), output().horizontal.value(),
+ stride().horizontal()))
+ throw oops::UserExn("Size mismatch for SAME padding", _node->name());
+
+ auto whole_pad_vertical = padding_needed(input().vertical.value(), output().vertical.value(),
+ stride().vertical(), window().vertical());
+ auto whole_pad_horizontal =
+ padding_needed(input().horizontal.value(), output().horizontal.value(),
+ stride().horizontal(), window().horizontal());
+
+ loco::Padding2D res;
+
+ res.top(whole_pad_vertical / 2);
+ res.bottom(whole_pad_vertical - res.top());
+ res.left(whole_pad_horizontal / 2);
+ res.right(whole_pad_horizontal - res.left());
+
+ return res;
+ }
+
+ throw oops::UserExn("Usupported padding " + padding(), _node->name());
+}
+
+/**
+ * @param[out] ret PlaneShape extracted from 'node' with given 'data_layout'
+ * @param[in] node
+ * @param[in] data_layout
+ *
+ * @return true on success
+ */
+bool set_plane_shape(PlaneShape &ret, const loco::Node *node, const DataLayout data_layout)
+{
+ auto tensor_shape = loco::shape_get(node).as<loco::TensorShape>();
+ if (!(tensor_shape.rank() == 4))
+ return false;
+
+ switch (data_layout)
+ {
+ case DataLayout::NHWC:
+ ret.vertical = tensor_shape.dim(1).value();
+ ret.horizontal = tensor_shape.dim(2).value();
+ break;
+ case DataLayout::NCHW:
+ ret.vertical = tensor_shape.dim(2).value();
+ ret.horizontal = tensor_shape.dim(3).value();
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * @param[out] ret 2D Window extracted from HW** filter node
+ * @param[in] filter_node
+ *
+ * @return true on success
+ */
+bool set_window(loco::Window<2> &ret, const loco::Node *filter_node)
+{
+ auto tensor_shape = loco::shape_get(filter_node).as<loco::TensorShape>();
+ assert(tensor_shape.rank() == 4);
+
+ ret.vertical(tensor_shape.dim(0).value());
+ ret.horizontal(tensor_shape.dim(1).value());
+
+ return true;
+}
+
+} // namespace
+
+namespace
+{
+
+bool canonicalize_conv2d_backprop_input(loco::Graph *graph,
+ moco::TFConv2DBackpropInput *conv2d_backprop)
+{
+ /**
+ * @note This will replace TFConv2DBackpropInput node with canonical
+ * FeatureEncode + FilterEncode + TransposedConv2D + FeatureDecode
+ *
+ * Before
+ * input_sizes ----
+ * \
+ * filter -------- TFConv2DBackpropInput --- output(s)
+ * /
+ * out_backprop ---
+ *
+ * After
+ * input_sizes ----
+ * \
+ * filter -------- TFConv2DBackpropInput ---
+ * /
+ * out_backprop ---
+ *
+ * filter ------ FilterEncode ------ TransposedConv2D --- FeatureDecode --- output(s)
+ * (as ker) /
+ * out_backprop --- FeatureEncode ---
+ * (as ifm)
+ */
+
+ if (!loco::shape_known(conv2d_backprop->out_backprop()))
+ return false;
+ if (!loco::shape_known(conv2d_backprop))
+ return false;
+ if (!loco::shape_known(conv2d_backprop->filter()))
+ return false;
+
+ auto data_layout = plier::tf::as_data_layout(conv2d_backprop->data_layout());
+
+ // Nodes to replace
+ auto feature_enc = graph->nodes()->create<loco::FeatureEncode>();
+ auto filter_enc = graph->nodes()->create<loco::FilterEncode>();
+ auto tr_conv2d = graph->nodes()->create<loco::TransposedConv2D>();
+ auto feature_dec = graph->nodes()->create<loco::FeatureDecode>();
+
+ set_feature_enc(feature_enc, data_layout);
+ set_filter_enc(filter_enc);
+ set_feature_dec(feature_dec, data_layout);
+
+ // Attributes for new TransposedConv2D
+ loco::Stride<2> stride;
+ loco::Padding2D pad;
+
+ // Get attributes
+ {
+ if (!stride_2d_from_4d(stride, conv2d_backprop->strides(), data_layout))
+ throw oops::UserExn("Unsupported strides", conv2d_backprop->name());
+
+ Padding2DInference infer_pad(conv2d_backprop);
+
+ if (!set_plane_shape(infer_pad.input(), conv2d_backprop->out_backprop(), data_layout))
+ throw oops::UserExn("Unsupported out_backprop data_format", conv2d_backprop->name());
+ if (!set_plane_shape(infer_pad.output(), conv2d_backprop, data_layout))
+ throw oops::UserExn("Unsupported data_format", conv2d_backprop->name());
+ if (!set_window(infer_pad.window(), conv2d_backprop->filter()))
+ throw oops::UserExn("Unsupported filter shape", conv2d_backprop->name());
+ infer_pad.stride() = stride;
+ infer_pad.padding() = conv2d_backprop->padding();
+
+ // Run padding infer_pad
+ pad = infer_pad();
+ }
+
+ // Set attributes
+ tr_conv2d->pad()->top(pad.top());
+ tr_conv2d->pad()->bottom(pad.bottom());
+ tr_conv2d->pad()->left(pad.left());
+ tr_conv2d->pad()->right(pad.right());
+
+ tr_conv2d->stride()->vertical(stride.vertical());
+ tr_conv2d->stride()->horizontal(stride.horizontal());
+
+ // Update graph
+ auto input_node = conv2d_backprop->out_backprop();
+ auto filter_node = conv2d_backprop->filter();
+
+ // Update connections
+ feature_enc->input(input_node);
+ filter_enc->input(filter_node);
+ tr_conv2d->ifm(feature_enc);
+ tr_conv2d->ker(filter_enc);
+ feature_dec->input(tr_conv2d);
+
+ // Replace old conv2d_backprop
+ replace(conv2d_backprop).with(feature_dec);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool Conv2DBackpropInputCanonicalizer::transform(TFConv2DBackpropInput *node) const
+{
+ return canonicalize_conv2d_backprop_input(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.h
new file mode 100644
index 000000000..bc37bb9cb
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_TF_CONV2DBACKPROPINPUT_CANONICALIZER_H__
+#define __MOCO_TF_CONV2DBACKPROPINPUT_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/// @brief Convert TFConv2DBackpropInput to Canonical TransposedConv2D
+class Conv2DBackpropInputCanonicalizer : public SimpleNodeTransform<moco::TFConv2DBackpropInput>
+{
+public:
+ const char *name(void) const final { return "Conv2DBackpropInputCanonicalizer"; }
+
+public:
+ bool transform(moco::TFConv2DBackpropInput *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_CONV2DBACKPROPINPUT_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.cpp
index f34339d0f..a955793a8 100644
--- a/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.cpp
@@ -16,46 +16,18 @@
#include "Conv2DCanonicalizer.h"
-#include "Annotations/PadData.h"
-#include "Annotations/StrideData.h"
+#include <moco/IR/TFDialect.h>
+#include <moco/Support/TFShapeInferenceHelper.h>
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
-#include "Dialect/TFNodeVisitor.h"
-#include "Dialect/TFNodeImpl.h"
+#include "CodecHelper.h"
#include <moco/Log.h>
-#include <plier/tf/Convert.h>
-
-#include <stdex/Memory.h>
namespace
{
using plier::tf::DataLayout;
-void set_feature_enc(loco::FeatureEncode *feature_enc, DataLayout data_layout)
-{
- auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>();
-
- if (data_layout == DataLayout::NHWC)
- {
- enc->perm()->axis(loco::FeatureAxis::Count) = 0;
- enc->perm()->axis(loco::FeatureAxis::Height) = 1;
- enc->perm()->axis(loco::FeatureAxis::Width) = 2;
- enc->perm()->axis(loco::FeatureAxis::Depth) = 3;
- }
- else if (data_layout == DataLayout::NCHW)
- {
- enc->perm()->axis(loco::FeatureAxis::Count) = 0;
- enc->perm()->axis(loco::FeatureAxis::Depth) = 1;
- enc->perm()->axis(loco::FeatureAxis::Height) = 2;
- enc->perm()->axis(loco::FeatureAxis::Width) = 3;
- }
-
- feature_enc->encoder(std::move(enc));
-}
-
-void set_filter_enc(loco::FilterEncode *filter_enc, DataLayout data_layout)
+void set_filter_enc(loco::FilterEncode *filter_enc)
{
auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>();
@@ -69,29 +41,7 @@ void set_filter_enc(loco::FilterEncode *filter_enc, DataLayout data_layout)
filter_enc->encoder(std::move(enc));
}
-void set_feature_dec(loco::FeatureDecode *feature_dec, DataLayout data_layout)
-{
- auto dec = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>();
-
- if (data_layout == DataLayout::NHWC)
- {
- dec->perm()->axis(loco::FeatureAxis::Count) = 0;
- dec->perm()->axis(loco::FeatureAxis::Height) = 1;
- dec->perm()->axis(loco::FeatureAxis::Width) = 2;
- dec->perm()->axis(loco::FeatureAxis::Depth) = 3;
- }
- else if (data_layout == DataLayout::NCHW)
- {
- dec->perm()->axis(loco::FeatureAxis::Count) = 0;
- dec->perm()->axis(loco::FeatureAxis::Depth) = 1;
- dec->perm()->axis(loco::FeatureAxis::Height) = 2;
- dec->perm()->axis(loco::FeatureAxis::Width) = 3;
- }
-
- feature_dec->decoder(std::move(dec));
-}
-
-bool canonicalize_conv2d(loco::Graph *graph, moco::tf::TFConv2D *node)
+bool canonicalize_conv2d(loco::Graph *graph, moco::TFConv2D *node)
{
LOGGER(l);
@@ -125,23 +75,29 @@ bool canonicalize_conv2d(loco::Graph *graph, moco::tf::TFConv2D *node)
auto feature_dec = graph->nodes()->create<loco::FeatureDecode>();
set_feature_enc(feature_enc, data_layout);
- set_filter_enc(filter_enc, data_layout);
+ set_filter_enc(filter_enc);
set_feature_dec(feature_dec, data_layout);
- // Set Conv2D attributes from TFConv2D
- auto pad_data = node->annot<moco::tf::PadData>();
- assert(pad_data != nullptr);
+ auto input_shape = moco::node_shape(node->input());
+ assert(input_shape.domain() != loco::Domain::Unknown);
+
+ auto ker_shape = moco::node_shape(node->filter());
+ auto ker_tensor_shape = ker_shape.as<loco::TensorShape>(); // in HWIO
+
+ auto node_stride = moco::stride_of(node->strides(), node->data_layout());
+ auto node_window = moco::window_of(ker_tensor_shape, "HWIO");
+
+ moco::Padding2DInference infer_padding2d;
- conv2d->pad()->top(pad_data->pad()->top());
- conv2d->pad()->bottom(pad_data->pad()->bottom());
- conv2d->pad()->left(pad_data->pad()->left());
- conv2d->pad()->right(pad_data->pad()->right());
+ infer_padding2d.padding(node->padding());
+ infer_padding2d.stride(node_stride);
+ infer_padding2d.window(node_window);
- auto stride_data = node->annot<moco::tf::StrideData>();
- assert(stride_data != nullptr);
+ auto input_feature_shape = moco::as_feature_shape(input_shape, node->data_layout());
+ auto input_plane_shape = moco::make_plane_shape(input_feature_shape);
- conv2d->stride()->vertical(stride_data->stride()->vertical());
- conv2d->stride()->horizontal(stride_data->stride()->horizontal());
+ *conv2d->pad() = infer_padding2d(input_plane_shape);
+ *conv2d->stride() = node_stride;
// update graph
auto node_A = node->input();
@@ -167,25 +123,9 @@ namespace moco
namespace tf
{
-bool Conv2DCanonicalizer::run(loco::Graph *graph)
+bool Conv2DCanonicalizer::transform(TFConv2D *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_node = dynamic_cast<moco::tf::TFConv2D *>(node);
- if (tf_node != nullptr)
- {
- if (canonicalize_conv2d(graph, tf_node))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_conv2d(node->graph(), node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.h
index 6be264f90..ea39667f3 100644
--- a/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_CONV2D_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Convert TFConv2D to Canonical Conv2D
*/
-class Conv2DCanonicalizer : public Transform
+class Conv2DCanonicalizer : public SimpleNodeTransform<TFConv2D>
{
public:
const char *name(void) const final { return "Conv2DCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(TFConv2D *) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp
index ee63efa2f..50dddf637 100644
--- a/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp
@@ -16,47 +16,18 @@
#include "DepthwiseConv2dNativeCanonicalizer.h"
-#include "Annotations/PadData.h"
-#include "Annotations/ShapeInferenceData.h"
-#include "Annotations/StrideData.h"
+#include <moco/IR/TFDialect.h>
+#include <moco/Support/TFShapeInferenceHelper.h>
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
-#include "Dialect/TFNodeVisitor.h"
-#include "Dialect/TFNodeImpl.h"
+#include "CodecHelper.h"
#include <moco/Log.h>
-#include <plier/tf/Convert.h>
-
-#include <stdex/Memory.h>
namespace
{
using plier::tf::DataLayout;
-void set_feature_enc(loco::FeatureEncode *feature_enc, DataLayout data_layout)
-{
- auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>();
-
- if (data_layout == DataLayout::NHWC)
- {
- enc->perm()->axis(loco::FeatureAxis::Count) = 0;
- enc->perm()->axis(loco::FeatureAxis::Height) = 1;
- enc->perm()->axis(loco::FeatureAxis::Width) = 2;
- enc->perm()->axis(loco::FeatureAxis::Depth) = 3;
- }
- else if (data_layout == DataLayout::NCHW)
- {
- enc->perm()->axis(loco::FeatureAxis::Count) = 0;
- enc->perm()->axis(loco::FeatureAxis::Depth) = 1;
- enc->perm()->axis(loco::FeatureAxis::Height) = 2;
- enc->perm()->axis(loco::FeatureAxis::Width) = 3;
- }
-
- feature_enc->encoder(std::move(enc));
-}
-
void set_filter_enc(loco::DepthwiseFilterEncode *filter_enc)
{
auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::DepthwiseFilter>>();
@@ -71,29 +42,7 @@ void set_filter_enc(loco::DepthwiseFilterEncode *filter_enc)
filter_enc->encoder(std::move(enc));
}
-void set_feature_dec(loco::FeatureDecode *feature_dec, DataLayout data_layout)
-{
- auto dec = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>();
-
- if (data_layout == DataLayout::NHWC)
- {
- dec->perm()->axis(loco::FeatureAxis::Count) = 0;
- dec->perm()->axis(loco::FeatureAxis::Height) = 1;
- dec->perm()->axis(loco::FeatureAxis::Width) = 2;
- dec->perm()->axis(loco::FeatureAxis::Depth) = 3;
- }
- else if (data_layout == DataLayout::NCHW)
- {
- dec->perm()->axis(loco::FeatureAxis::Count) = 0;
- dec->perm()->axis(loco::FeatureAxis::Depth) = 1;
- dec->perm()->axis(loco::FeatureAxis::Height) = 2;
- dec->perm()->axis(loco::FeatureAxis::Width) = 3;
- }
-
- feature_dec->decoder(std::move(dec));
-}
-
-bool canonicalize_depthwiseconv2dnative(loco::Graph *graph, moco::tf::TFDepthwiseConv2dNative *node)
+bool canonicalize_depthwiseconv2dnative(loco::Graph *graph, moco::TFDepthwiseConv2dNative *node)
{
LOGGER(l);
@@ -134,20 +83,24 @@ bool canonicalize_depthwiseconv2dnative(loco::Graph *graph, moco::tf::TFDepthwis
set_filter_enc(filter_enc);
set_feature_dec(feature_dec, data_layout);
- // Set DetphwiseConv2D attributes from TFDepthwiseConv2dNative
- auto pad_data = node->annot<moco::tf::PadData>();
- assert(pad_data != nullptr);
+ // Calculate Pad and Stride from inference
+ auto input_shape = moco::node_shape(node->input());
+ auto ker_shape = moco::node_shape(node->filter());
+ auto ker_tensor_shape = ker_shape.as<loco::TensorShape>();
+ auto node_stride = moco::stride_of(node->strides(), node->data_layout());
+ auto node_window = moco::window_of(ker_tensor_shape, "HWCM");
+
+ moco::Padding2DInference infer_padding2d;
- depthwiseconv2d->pad()->top(pad_data->pad()->top());
- depthwiseconv2d->pad()->bottom(pad_data->pad()->bottom());
- depthwiseconv2d->pad()->left(pad_data->pad()->left());
- depthwiseconv2d->pad()->right(pad_data->pad()->right());
+ infer_padding2d.padding(node->padding());
+ infer_padding2d.stride(node_stride);
+ infer_padding2d.window(node_window);
- auto stride_data = node->annot<moco::tf::StrideData>();
- assert(stride_data != nullptr);
+ auto input_feature_shape = moco::as_feature_shape(input_shape, node->data_layout());
+ auto input_plane_shape = moco::make_plane_shape(input_feature_shape);
- depthwiseconv2d->stride()->vertical(stride_data->stride()->vertical());
- depthwiseconv2d->stride()->horizontal(stride_data->stride()->horizontal());
+ *depthwiseconv2d->pad() = infer_padding2d(input_plane_shape);
+ *depthwiseconv2d->stride() = node_stride;
// update graph
auto node_A = node->input();
@@ -175,25 +128,9 @@ namespace moco
namespace tf
{
-bool DepthwiseConv2dNativeCanonicalizer::run(loco::Graph *graph)
+bool DepthwiseConv2dNativeCanonicalizer::transform(TFDepthwiseConv2dNative *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_node = dynamic_cast<moco::tf::TFDepthwiseConv2dNative *>(node);
- if (tf_node != nullptr)
- {
- if (canonicalize_depthwiseconv2dnative(graph, tf_node))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_depthwiseconv2dnative(node->graph(), node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.h
index 9bb8c5ad8..704e1ade9 100644
--- a/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_DEPTHWISE_CONV2D_NATIVE_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
namespace moco
{
@@ -27,13 +30,13 @@ namespace tf
/**
* @brief Convert TFDepthwiseConv2dNative to Canonical DepthwiseConv2D
*/
-class DepthwiseConv2dNativeCanonicalizer : public Transform
+class DepthwiseConv2dNativeCanonicalizer : public SimpleNodeTransform<moco::TFDepthwiseConv2dNative>
{
public:
const char *name(void) const final { return "DepthwiseConv2dNativeCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(moco::TFDepthwiseConv2dNative *) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.cpp
index c4d5d8063..3b680cf04 100644
--- a/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.cpp
@@ -18,18 +18,15 @@
#include "Convert.h"
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
-#include "Dialect/TFNodeVisitor.h"
-#include "Dialect/TFNodeImpl.h"
+#include <moco/IR/TFDialect.h>
-#include <moco/tf/Names.h>
+#include <moco/Names.h>
#include <moco/Log.h>
namespace
{
-bool canonicalize_identity(loco::Graph *graph, moco::tf::TFIdentity *node)
+bool canonicalize_identity(loco::Graph *graph, moco::TFIdentity *node)
{
LOGGER(l);
@@ -72,25 +69,9 @@ namespace moco
namespace tf
{
-bool IdentityCanonicalizer::run(loco::Graph *graph)
+bool IdentityCanonicalizer::transform(TFIdentity *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_identity = dynamic_cast<moco::tf::TFIdentity *>(node);
- if (tf_identity != nullptr)
- {
- if (canonicalize_identity(graph, tf_identity))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_identity(node->graph(), node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.h
index 81aee178a..59b2894c5 100644
--- a/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_IDENTITY_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Convert TFIdentity to Canonical Forward
*/
-class IdentityCanonicalizer : public Transform
+class IdentityCanonicalizer : public SimpleNodeTransform<moco::TFIdentity>
{
public:
const char *name(void) const final { return "IdentityCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(moco::TFIdentity *) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.cpp
index c46fbd208..06a605717 100644
--- a/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.cpp
@@ -16,70 +16,17 @@
#include "MaxPoolCanonicalizer.h"
-#include "Annotations/PadData.h"
-#include "Annotations/StrideData.h"
-#include "Annotations/WindowData.h"
+#include <moco/IR/TFDialect.h>
+#include <moco/Support/TFShapeInferenceHelper.h>
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
-#include "Dialect/TFNodeVisitor.h"
-#include "Dialect/TFNodeImpl.h"
+#include "CodecHelper.h"
#include <moco/Log.h>
-#include <plier/tf/Convert.h>
-
-#include <stdex/Memory.h>
namespace
{
-using plier::tf::DataLayout;
-
-void set_feature_enc(loco::FeatureEncode *feature_enc, DataLayout data_layout)
-{
- auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>();
-
- if (data_layout == DataLayout::NHWC)
- {
- enc->perm()->axis(loco::FeatureAxis::Count) = 0;
- enc->perm()->axis(loco::FeatureAxis::Height) = 1;
- enc->perm()->axis(loco::FeatureAxis::Width) = 2;
- enc->perm()->axis(loco::FeatureAxis::Depth) = 3;
- }
- else if (data_layout == DataLayout::NCHW)
- {
- enc->perm()->axis(loco::FeatureAxis::Count) = 0;
- enc->perm()->axis(loco::FeatureAxis::Depth) = 1;
- enc->perm()->axis(loco::FeatureAxis::Height) = 2;
- enc->perm()->axis(loco::FeatureAxis::Width) = 3;
- }
-
- feature_enc->encoder(std::move(enc));
-}
-
-void set_feature_dec(loco::FeatureDecode *feature_dec, DataLayout data_layout)
-{
- auto dec = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>();
-
- if (data_layout == DataLayout::NHWC)
- {
- dec->perm()->axis(loco::FeatureAxis::Count) = 0;
- dec->perm()->axis(loco::FeatureAxis::Height) = 1;
- dec->perm()->axis(loco::FeatureAxis::Width) = 2;
- dec->perm()->axis(loco::FeatureAxis::Depth) = 3;
- }
- else if (data_layout == DataLayout::NCHW)
- {
- dec->perm()->axis(loco::FeatureAxis::Count) = 0;
- dec->perm()->axis(loco::FeatureAxis::Depth) = 1;
- dec->perm()->axis(loco::FeatureAxis::Height) = 2;
- dec->perm()->axis(loco::FeatureAxis::Width) = 3;
- }
-
- feature_dec->decoder(std::move(dec));
-}
-
-bool canonicalize_maxpool2d(loco::Graph *graph, moco::tf::TFMaxPool *node)
+bool canonicalize_maxpool2d(loco::Graph *graph, moco::TFMaxPool *node)
{
LOGGER(l);
@@ -111,36 +58,31 @@ bool canonicalize_maxpool2d(loco::Graph *graph, moco::tf::TFMaxPool *node)
set_feature_dec(feature_dec, data_layout);
// paddata to pad
- auto pad_data = node->annot<moco::tf::PadData>();
- assert(pad_data != nullptr);
+ auto input_shape = moco::node_shape(node->input());
+ assert(input_shape.domain() != loco::Domain::Unknown);
- maxPool2d_node->pad()->top(pad_data->pad()->top());
- maxPool2d_node->pad()->bottom(pad_data->pad()->bottom());
- maxPool2d_node->pad()->left(pad_data->pad()->left());
- maxPool2d_node->pad()->right(pad_data->pad()->right());
+ auto node_stride = moco::stride_of(node->strides(), node->data_layout());
+ auto node_window = moco::window_of(node->ksize(), node->data_layout());
- // windowdata to window (ksize to window)
- auto window_data = node->annot<moco::tf::WindowData>();
- assert(window_data != nullptr);
+ moco::Padding2DInference infer_padding2d;
- auto window = maxPool2d_node->window();
- window->vertical(window_data->window()->vertical());
- window->horizontal(window_data->window()->horizontal());
+ infer_padding2d.padding(node->padding());
+ infer_padding2d.stride(node_stride);
+ infer_padding2d.window(node_window);
- // stridedata to stride (strides to stride)
- auto stride_data = node->annot<moco::tf::StrideData>();
- assert(stride_data != nullptr);
+ auto input_feature_shape = moco::as_feature_shape(input_shape, node->data_layout());
+ auto input_plane_shape = moco::make_plane_shape(input_feature_shape);
- auto stride = maxPool2d_node->stride();
- stride->vertical(stride_data->stride()->vertical());
- stride->horizontal(stride_data->stride()->horizontal());
+ *maxPool2d_node->pad() = infer_padding2d(input_plane_shape);
+ *maxPool2d_node->stride() = node_stride;
+ *maxPool2d_node->window() = node_window;
INFO(l) << "Canonicalize TFMaxPool pad = T " << maxPool2d_node->pad()->top() << ", L "
<< maxPool2d_node->pad()->left() << ", B " << maxPool2d_node->pad()->bottom() << ", R "
<< maxPool2d_node->pad()->right() << std::endl;
// update graph
- auto node_A = node->value();
+ auto node_A = node->input();
// update connections
feature_enc->input(node_A);
@@ -160,25 +102,9 @@ namespace moco
namespace tf
{
-bool MaxPoolCanonicalizer::run(loco::Graph *graph)
+bool MaxPoolCanonicalizer::transform(TFMaxPool *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_node = dynamic_cast<moco::tf::TFMaxPool *>(node);
- if (tf_node != nullptr)
- {
- if (canonicalize_maxpool2d(graph, tf_node))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_maxpool2d(node->graph(), node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.h
index a486c4caa..c58ade528 100644
--- a/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_MAXPOOL_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Convert TFMaxPool to Canonical MaxPool2D
*/
-class MaxPoolCanonicalizer : public Transform
+class MaxPoolCanonicalizer : public SimpleNodeTransform<moco::TFMaxPool>
{
public:
const char *name(void) const final { return "MaxPoolCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(moco::TFMaxPool *) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.cpp
new file mode 100644
index 000000000..92634d01f
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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 "MaximumCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include "TFEltwiseBinaryCanonicalzeHelper.h"
+
+namespace moco
+{
+namespace tf
+{
+
+bool MaximumCanonicalizer::transform(moco::TFMaximum *node) const
+{
+ return canonicalize_eltwise_binary_node(node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.h
new file mode 100644
index 000000000..baff4d7ad
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_TF_MAXIMUM_CANONICALIZER_H__
+#define __MOCO_TF_MAXIMUM_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFMaximum to Canonical EltwiseMax
+ */
+class MaximumCanonicalizer : public SimpleNodeTransform<moco::TFMaximum>
+{
+public:
+ const char *name(void) const final { return "MaximumCanonicalizer"; }
+
+public:
+ bool transform(moco::TFMaximum *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_MAXIMUM_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.cpp
new file mode 100644
index 000000000..69eaf7900
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MeanCanonicalizer.h"
+#include "TFReduceCanonicalzeHelper.h"
+
+namespace moco
+{
+namespace tf
+{
+
+bool MeanCanonicalizer::transform(moco::TFMean *node) const
+{
+ return canonicalize_reduce_node(node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.h
new file mode 100644
index 000000000..469d7e3cd
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_TF_MEAN_CANONICALIZER_H__
+#define __MOCO_TF_MEAN_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Canonicalize TF-dialect TFMean into canonical TensorReduce(Mean) node
+ */
+class MeanCanonicalizer : public SimpleNodeTransform<moco::TFMean>
+{
+public:
+ const char *name(void) const final { return "MeanCanonicalizer"; }
+
+public:
+ bool transform(moco::TFMean *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_MEAN_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.cpp
index 78d0ebc48..d02f71361 100644
--- a/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.cpp
@@ -16,8 +16,7 @@
#include "MulCanonicalizer.h"
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
+#include <moco/IR/TFDialect.h>
#include "TFEltwiseBinaryCanonicalzeHelper.h"
@@ -26,25 +25,9 @@ namespace moco
namespace tf
{
-bool MulCanonicalizer::run(loco::Graph *graph)
+bool MulCanonicalizer::transform(moco::TFMul *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_node = dynamic_cast<moco::tf::TFMul *>(node);
- if (tf_node != nullptr)
- {
- if (canonicalize_eltwise_binary_node(tf_node))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_eltwise_binary_node(node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.h
index 680f4c315..480eec700 100644
--- a/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_MUL_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Convert TFMul to Canonical EltwiseMul
*/
-class MulCanonicalizer : public Transform
+class MulCanonicalizer : public SimpleNodeTransform<moco::TFMul>
{
public:
const char *name(void) const final { return "MulCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(moco::TFMul *) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.cpp
new file mode 100644
index 000000000..36136aed4
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PadCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include "loco/Service/TypeInference.h"
+
+#include <stdex/Memory.h>
+
+namespace
+{
+
+bool canonicalize_pad(loco::Graph *graph, moco::TFPad *node)
+{
+ /**
+ * @note This will replace TFPad node with Canonical TensorConstantPad
+ *
+ * Before
+ * input --- TFPad -- C
+ * paddings --/
+ * After
+ * paddings ------- TFPad --
+ * /
+ * input ----------- TensorConstantPad -- C
+ * ConstGen --------/
+ * Where
+ * input : input of TFPad
+ * paddings : paddings of TFPad. it becomes TensorConstantPad's attribute.
+ * C : a node that uses TFPad as an input. TFPad is disconnected from C.
+ * ConstGen : constant value of Pad. TFPad has zero value by default.
+ */
+
+ auto pad_node = graph->nodes()->create<loco::TensorConstantPad>();
+
+ auto constant_node = graph->nodes()->create<loco::ConstGen>();
+
+ auto input_node = node->input();
+ // TODO: support other dtype.
+ assert(loco::dtype_get(input_node) == loco::DataType::FLOAT32);
+ constant_node->dtype(loco::DataType::FLOAT32);
+ // TODO: constant node changes to scalar when it is implemented.
+ constant_node->shape({1});
+ constant_node->size<loco::DataType::FLOAT32>(1);
+ constant_node->at<loco::DataType::FLOAT32>(0) = 0.0f;
+
+ auto const_paddings_node = loco::must_cast<loco::ConstGen *>(node->paddings());
+ // TODO: support S64 type.
+ assert(const_paddings_node->dtype() == loco::DataType::S32);
+ assert(const_paddings_node->rank() == 2);
+ assert(const_paddings_node->dim(1).value() == 2);
+
+ auto padding = pad_node->padding();
+ uint32_t padding_rank = const_paddings_node->dim(0).value();
+ padding->rank(padding_rank);
+
+ for (uint32_t i = 0; i < padding_rank; i++)
+ {
+ padding->front(i) = const_paddings_node->at<loco::DataType::S32>(i << 1);
+ padding->back(i) = const_paddings_node->at<loco::DataType::S32>((i << 1) + 1);
+ }
+
+ // update connections
+ pad_node->input(input_node);
+ pad_node->constant(constant_node);
+
+ // replace node
+ replace(node).with(pad_node);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool PadCanonicalizer::transform(TFPad *node) const
+{
+ return canonicalize_pad(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.h
new file mode 100644
index 000000000..64bb6041a
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_TF_PAD_CANONICALIZER_H__
+#define __MOCO_TF_PAD_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFPad to Canonical TensorConstantPad
+ */
+class PadCanonicalizer final : public SimpleNodeTransform<moco::TFPad>
+{
+public:
+ const char *name(void) const final { return "PadCanonicalizer"; }
+
+public:
+ bool transform(moco::TFPad *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_PAD_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.cpp
new file mode 100644
index 000000000..f568e909f
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PlaceholderCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include <moco/Names.h>
+#include <moco/Log.h>
+
+namespace
+{
+
+bool canonicalize_placeholder(loco::Graph *graph, moco::TFPlaceholder *node)
+{
+ LOGGER(l);
+
+ /**
+ * @note This will replace TFPlaceholder node with Canonical Pull
+ *
+ * Before
+ * TFPlaceholder -- C
+ *
+ * After
+ * TFPlaceholder -
+ * Pull -- C
+ *
+ * Where
+ * C : a node that uses TFPlaceholder as an input
+ * TFPlaceholder is disconnected from other nodes
+ */
+
+ INFO(l) << "PlaceholderCanonicalizer begin";
+
+ auto pull_node = graph->nodes()->create<loco::Pull>();
+
+ // copy properties
+ auto dtype = node->dtype();
+ pull_node->dtype(dtype);
+
+ auto rank = node->rank();
+
+ if (rank == 0)
+ {
+ // This routine implements a workaround that converts a scalar constant (rank-0 tensor)
+ // into a rank-1 tensor of shape [1].
+ //
+ // TODO Revise this implementation later
+ pull_node->rank(1);
+ pull_node->dim(0) = 1;
+ }
+ else
+ {
+ pull_node->rank(rank);
+
+ for (uint32_t r = 0; r < rank; ++r)
+ {
+ if (node->dim(r).known())
+ pull_node->dim(r) = node->dim(r);
+ else
+ pull_node->dim(r).unset();
+ }
+ }
+
+ // set loco::Pull GraphInputIndex
+ pull_node->index(moco::index(node));
+
+ // update graph
+ replace(node).with(pull_node);
+
+ INFO(l) << "PlaceholderCanonicalizer done";
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool PlaceholderCanonicalizer::transform(TFPlaceholder *node) const
+{
+ return canonicalize_placeholder(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.h
new file mode 100644
index 000000000..66eafe6af
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_TF_PLACEHOLDER_CANONICALIZER_H__
+#define __MOCO_TF_PLACEHOLDER_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/Nodes/TFPlaceholder.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFPlaceholder to Canonical Pull
+ *
+ * @note GraphInputIndex is copied to Pull
+ */
+class PlaceholderCanonicalizer : public SimpleNodeTransform<::moco::TFPlaceholder>
+{
+public:
+ const char *name(void) const final { return "PlaceholderCanonicalizer"; }
+
+public:
+ bool transform(moco::TFPlaceholder *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_PLACEHOLDER_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.cpp
index 9ad15150a..a448d85fa 100644
--- a/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.cpp
@@ -16,8 +16,7 @@
#include "RealDivCanonicalizer.h"
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
+#include <moco/IR/TFDialect.h>
#include "TFEltwiseBinaryCanonicalzeHelper.h"
@@ -26,25 +25,9 @@ namespace moco
namespace tf
{
-bool RealDivCanonicalizer::run(loco::Graph *graph)
+bool RealDivCanonicalizer::transform(moco::TFRealDiv *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_node = dynamic_cast<moco::tf::TFRealDiv *>(node);
- if (tf_node != nullptr)
- {
- if (canonicalize_eltwise_binary_node(tf_node))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_eltwise_binary_node(node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.h
index 8e6953396..76e1bd377 100644
--- a/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_REALDIV_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Convert TFRealDiv to Canonical EltwiseDiv
*/
-class RealDivCanonicalizer : public Transform
+class RealDivCanonicalizer : public SimpleNodeTransform<moco::TFRealDiv>
{
public:
const char *name(void) const final { return "RealDivCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(moco::TFRealDiv *) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.cpp
index 07657244b..c53a880a8 100644
--- a/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.cpp
@@ -16,17 +16,14 @@
#include "Relu6Canonicalizer.h"
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
-#include "Dialect/TFNodeVisitor.h"
-#include "Dialect/TFNodeImpl.h"
+#include <moco/IR/TFDialect.h>
#include <stdex/Memory.h>
namespace
{
-bool canonicalize_relu6(loco::Graph *graph, moco::tf::TFRelu6 *node)
+bool canonicalize_relu6(loco::Graph *graph, moco::TFRelu6 *node)
{
/**
* @note This will replace TFRelu6 node with Canonical ReLU6
@@ -64,25 +61,9 @@ namespace moco
namespace tf
{
-bool Relu6Canonicalizer::run(loco::Graph *graph)
+bool Relu6Canonicalizer::transform(TFRelu6 *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_node = dynamic_cast<moco::tf::TFRelu6 *>(node);
- if (tf_node != nullptr)
- {
- if (canonicalize_relu6(graph, tf_node))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_relu6(node->graph(), node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.h b/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.h
index aa1580f28..d8ad5db8e 100644
--- a/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_RELU6_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Convert TFRelu6 to Canonical ReLU6
*/
-class Relu6Canonicalizer : public Transform
+class Relu6Canonicalizer : public SimpleNodeTransform<moco::TFRelu6>
{
public:
const char *name(void) const final { return "Relu6Canonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(moco::TFRelu6 *) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.cpp
index 20cd0bab9..7965dc931 100644
--- a/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.cpp
@@ -16,17 +16,14 @@
#include "ReluCanonicalizer.h"
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
-#include "Dialect/TFNodeVisitor.h"
-#include "Dialect/TFNodeImpl.h"
+#include <moco/IR/TFDialect.h>
#include <stdex/Memory.h>
namespace
{
-bool canonicalize_relu(loco::Graph *graph, moco::tf::TFRelu *node)
+bool canonicalize_relu(loco::Graph *graph, moco::TFRelu *node)
{
/**
* @note This will replace TFRelu node with Canonical ReLU
@@ -64,25 +61,9 @@ namespace moco
namespace tf
{
-bool ReluCanonicalizer::run(loco::Graph *graph)
+bool ReluCanonicalizer::transform(TFRelu *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_node = dynamic_cast<moco::tf::TFRelu *>(node);
- if (tf_node != nullptr)
- {
- if (canonicalize_relu(graph, tf_node))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_relu(node->graph(), node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.h
index 97adba308..e27abe158 100644
--- a/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_RELU_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Convert TFRelu to Canonical ReLU
*/
-class ReluCanonicalizer : public Transform
+class ReluCanonicalizer : public SimpleNodeTransform<moco::TFRelu>
{
public:
const char *name(void) const final { return "ReluCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(moco::TFRelu *) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.cpp
index 3771d549a..b944568e0 100644
--- a/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.cpp
@@ -16,11 +16,11 @@
#include "ReshapeCanonicalizer.h"
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodeImpl.h"
+#include <moco/IR/TFDialect.h>
#include <moco/Log.h>
#include <plier/tf/Convert.h>
+#include <oops/UserExn.h>
#include <cassert>
@@ -31,7 +31,7 @@ using plier::tf::DataLayout;
/**
* @brief Check whether given 'new shape' arg is a fixed shape input for Reshape
*
- * ConstNode can be moco::tf::TFConst or loco::ConstGen
+ * ConstNode can be moco::TFConst or loco::ConstGen
*/
template <typename ConstNode> bool is_fixed_shape_input(ConstNode *const_shape_input)
{
@@ -54,13 +54,16 @@ template <typename ConstNode> bool is_fixed_shape_input(ConstNode *const_shape_i
// has wildcard dimension, i.e. dynamic reshape
return false;
}
- assert(shape_dim >= 1 && "Unknown behavior: New shape of Reshape has invalid dimension");
+ if (!(shape_dim >= 1))
+ {
+ throw oops::UserExn("New shape of Reshape has invalid dimension");
+ }
}
return true;
}
/// @note Currently only supports to canonicalize Fixed Reshape
-bool canonicalize_reshape(loco::Graph *graph, moco::tf::TFReshape *node)
+bool canonicalize_reshape(loco::Graph *graph, moco::TFReshape *node)
{
LOGGER(l);
INFO(l) << "TFNodeCanonicalize TFReshape begin";
@@ -99,14 +102,17 @@ bool canonicalize_reshape(loco::Graph *graph, moco::tf::TFReshape *node)
// Supports 2 cases for Reshape's shape input:
// TF-dialect TFConst or Canonical ConstGen
loco::Node *shape_input = node->shape();
- auto tfconst_shape_input = dynamic_cast<moco::tf::TFConst *>(shape_input);
+ auto tfconst_shape_input = dynamic_cast<moco::TFConst *>(shape_input);
auto constgen_shape_input = dynamic_cast<loco::ConstGen *>(shape_input);
if (tfconst_shape_input)
{
// Only support fixed reshape
// TODO support dynamic reshape
- assert(is_fixed_shape_input(tfconst_shape_input));
+ if (!(is_fixed_shape_input(tfconst_shape_input)))
+ {
+ throw oops::UserExn("Supports only fixed reshape", node->name());
+ }
auto rank = tfconst_shape_input->dim(0).value();
fixed_reshape->rank(rank);
@@ -118,7 +124,10 @@ bool canonicalize_reshape(loco::Graph *graph, moco::tf::TFReshape *node)
else if (constgen_shape_input)
{
// ditto
- assert(is_fixed_shape_input(constgen_shape_input));
+ if (!(is_fixed_shape_input(constgen_shape_input)))
+ {
+ throw oops::UserExn("Supports only fixed reshape", node->name());
+ }
auto rank = constgen_shape_input->dim(0).value();
fixed_reshape->rank(rank);
@@ -130,7 +139,7 @@ bool canonicalize_reshape(loco::Graph *graph, moco::tf::TFReshape *node)
else
{
// TODO support dynamic reshape from not const node
- throw std::runtime_error("ReshapeCanonicalizer: only support const node as input shape");
+ throw oops::UserExn("Supports only const node as input shape", node->name());
}
// replace
@@ -151,25 +160,9 @@ namespace moco
namespace tf
{
-bool ReshapeCanonicalizer::run(loco::Graph *graph)
+bool ReshapeCanonicalizer::transform(TFReshape *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_reshape = dynamic_cast<moco::tf::TFReshape *>(node);
- if (tf_reshape != nullptr)
- {
- if (canonicalize_reshape(graph, tf_reshape))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_reshape(node->graph(), node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.h
index c9deee7a4..1a792024e 100644
--- a/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_RESHAPE_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Convert TFReshape to Canonical Reshape
*/
-class ReshapeCanonicalizer : public Transform
+class ReshapeCanonicalizer : public SimpleNodeTransform<moco::TFReshape>
{
public:
const char *name(void) const final { return "ReshapeCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(moco::TFReshape *) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.cpp
index b4fbcac3c..c31dbf6d6 100644
--- a/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.cpp
@@ -16,29 +16,25 @@
#include "RsqrtCanonicalizer.h"
-#include "Annotations/ShapeInferenceData.h"
-
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
-#include "Dialect/TFNodeVisitor.h"
-#include "Dialect/TFNodeImpl.h"
+#include <moco/IR/TFDialect.h>
+#include <moco/Support/TFShapeInferenceHelper.h>
#include <moco/Log.h>
#include <loco/Service/TypeInference.h>
#include <stdex/Memory.h>
+#include <oops/UserExn.h>
namespace
{
template <typename T>
-void prepare_const_gen(loco::ConstGen *const_node, const moco::tf::ShapeInferenceData *shapedata,
- T value);
+bool prepare_const_gen(loco::ConstGen *const_node, const loco::TensorShape &tensorshape, T value);
template <>
-void prepare_const_gen<float>(loco::ConstGen *const_node,
- const moco::tf::ShapeInferenceData *shapedata, float value)
+bool prepare_const_gen<float>(loco::ConstGen *const_node, const loco::TensorShape &tensorshape,
+ float value)
{
LOGGER(l);
@@ -47,18 +43,18 @@ void prepare_const_gen<float>(loco::ConstGen *const_node,
auto dtype = loco::DataType::FLOAT32;
const_node->dtype(dtype);
- auto rank = shapedata->rank();
+ auto rank = tensorshape.rank();
const_node->rank(rank);
for (uint32_t r = 0; r < rank; ++r)
{
- if (shapedata->dim(r).known())
- const_node->dim(r) = shapedata->dim(r);
+ if (tensorshape.dim(r).known())
+ const_node->dim(r) = tensorshape.dim(r);
else
- throw std::runtime_error("Cannot handle unknown shape");
+ return false;
- assert(shapedata->dim(r).value() > 0);
+ assert(tensorshape.dim(r).value() > 0);
- const_num_elements *= shapedata->dim(r).value();
+ const_num_elements *= tensorshape.dim(r).value();
}
INFO(l) << "prepare_const_gen : Elements = " << const_num_elements;
@@ -68,9 +64,11 @@ void prepare_const_gen<float>(loco::ConstGen *const_node,
{
const_node->at<loco::DataType::FLOAT32>(i) = value;
}
+
+ return true;
}
-bool canonicalize_rsqrt(loco::Graph *graph, moco::tf::TFRsqrt *node)
+bool canonicalize_rsqrt(loco::Graph *graph, moco::TFRsqrt *node)
{
/**
* @note This will replace TFRsqrt node with Canonical EltwiseSqrt + EltwiseRealDiv
@@ -91,13 +89,14 @@ bool canonicalize_rsqrt(loco::Graph *graph, moco::tf::TFRsqrt *node)
* TFRsqrt is converted to 1 / EltwiseSqrt
*/
- auto rsqrt_shapedata = node->annot<moco::tf::ShapeInferenceData>();
- if (rsqrt_shapedata == nullptr)
+ auto nodeshape = moco::node_shape(node);
+ if (nodeshape.domain() == loco::Domain::Unknown)
{
// We need this shape information
assert(false); // this shouldn't happen, let's add an alarm
return false;
}
+ auto tensorshape = nodeshape.as<loco::TensorShape>();
if (!loco::dtype_known(node))
{
@@ -114,11 +113,12 @@ bool canonicalize_rsqrt(loco::Graph *graph, moco::tf::TFRsqrt *node)
switch (dtype)
{
case loco::DataType::FLOAT32:
- prepare_const_gen<float>(const_node, rsqrt_shapedata, 1.0f);
+ if (!prepare_const_gen<float>(const_node, tensorshape, 1.0f))
+ throw oops::UserExn("Cannot handle unknown shape", node->name());
break;
default:
- throw std::runtime_error("NYI for this DataType");
+ throw oops::UserExn("Unsupported data type", node->name());
}
auto node_A = node->x();
@@ -141,25 +141,9 @@ namespace moco
namespace tf
{
-bool RsqrtCanonicalizer::run(loco::Graph *graph)
+bool RsqrtCanonicalizer::transform(TFRsqrt *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_node = dynamic_cast<moco::tf::TFRsqrt *>(node);
- if (tf_node != nullptr)
- {
- if (canonicalize_rsqrt(graph, tf_node))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_rsqrt(node->graph(), node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.h
index a58c0adcb..7fd4ff697 100644
--- a/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_RSQRT_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Convert TFRsqrt to Canonical EltwiseDiv + EltwiseSqrt
*/
-class RsqrtCanonicalizer : public Transform
+class RsqrtCanonicalizer : public SimpleNodeTransform<moco::TFRsqrt>
{
public:
const char *name(void) const final { return "RsqrtCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(moco::TFRsqrt *) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.cpp
index 3b5043fa7..98af7b693 100644
--- a/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.cpp
@@ -16,19 +16,15 @@
#include "SoftmaxCanonicalizer.h"
-#include "Annotations/ShapeInferenceData.h"
-
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
-#include "Dialect/TFNodeVisitor.h"
-#include "Dialect/TFNodeImpl.h"
+#include <moco/IR/TFDialect.h>
+#include <moco/Support/TFShapeInferenceHelper.h>
#include <moco/Log.h>
namespace
{
-bool canonicalize_softmax(loco::Graph *graph, moco::tf::TFSoftmax *node)
+bool canonicalize_softmax(loco::Graph *graph, moco::TFSoftmax *node)
{
LOGGER(l);
@@ -46,12 +42,11 @@ bool canonicalize_softmax(loco::Graph *graph, moco::tf::TFSoftmax *node)
* In ---- TensorSoftmax ----- Out(s)
*/
- auto softmax_shape = node->annot<moco::tf::ShapeInferenceData>();
-
+ auto nodeshape = moco::node_shape(node);
// Canonicalization into TensorSoftmax is valid when softmax has shape info
- assert(softmax_shape);
+ assert(nodeshape.domain() != loco::Domain::Unknown);
- auto softmax_tensor_shape = softmax_shape->tensor_shape();
+ auto softmax_tensor_shape = nodeshape.as<loco::TensorShape>();
// Create loco node to replace
auto softmax = graph->nodes()->create<loco::TensorSoftmax>();
@@ -74,25 +69,9 @@ namespace moco
namespace tf
{
-bool SoftmaxCanonicalizer::run(loco::Graph *graph)
+bool SoftmaxCanonicalizer::transform(TFSoftmax *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_softmax = dynamic_cast<moco::tf::TFSoftmax *>(node);
- if (tf_softmax != nullptr)
- {
- if (canonicalize_softmax(graph, tf_softmax))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_softmax(node->graph(), node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h
index 6debf4194..ebaf04cfe 100644
--- a/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_SOFTMAx_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Canonicalize TF-dialect TFSoftmax into canonical Softmax node
*/
-class SoftmaxCanonicalizer : public Transform
+class SoftmaxCanonicalizer : public SimpleNodeTransform<moco::TFSoftmax>
{
public:
const char *name(void) const final { return "SoftmaxCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(moco::TFSoftmax *) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.cpp
index 347265121..89b9b8a44 100644
--- a/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.cpp
@@ -16,15 +16,12 @@
#include "SqrtCanonicalizer.h"
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
-#include "Dialect/TFNodeVisitor.h"
-#include "Dialect/TFNodeImpl.h"
+#include <moco/IR/TFDialect.h>
namespace
{
-bool canonicalize_sqrt(loco::Graph *graph, moco::tf::TFSqrt *node)
+bool canonicalize_sqrt(loco::Graph *graph, moco::TFSqrt *node)
{
/**
* @note This will replace TFSqrt node with Canonical EltwiseSqrt
@@ -62,25 +59,9 @@ namespace moco
namespace tf
{
-bool SqrtCanonicalizer::run(loco::Graph *graph)
+bool SqrtCanonicalizer::transform(TFSqrt *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_node = dynamic_cast<moco::tf::TFSqrt *>(node);
- if (tf_node != nullptr)
- {
- if (canonicalize_sqrt(graph, tf_node))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_sqrt(node->graph(), node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.h
index b4e6da09a..3f7ffead8 100644
--- a/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_SQRT_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Convert TFsqrt to Canonical EltwiseSqrt
*/
-class SqrtCanonicalizer : public Transform
+class SqrtCanonicalizer : public SimpleNodeTransform<moco::TFSqrt>
{
public:
const char *name(void) const final { return "SqrtCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(moco::TFSqrt *) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/SquaredDifferenceCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/SquaredDifferenceCanonicalizer.cpp
deleted file mode 100644
index 4eb7a7217..000000000
--- a/compiler/moco-tf/src/Canonicalization/SquaredDifferenceCanonicalizer.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "SquaredDifferenceCanonicalizer.h"
-
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
-#include "Dialect/TFNodeVisitor.h"
-#include "Dialect/TFNodeImpl.h"
-
-#include <loco/IR/NodeShape.h>
-#include <loco/Service/ShapeInference.h>
-
-#include <stdex/Memory.h>
-
-namespace
-{
-
-bool canonicalize_sqdiff(loco::Graph *graph, moco::tf::TFSquaredDifference *node)
-{
- /**
- * @note This will replace TFSquaredDifference node with Canonical EltwiseSub and EltwiseMul
- *
- * Before
- * A --- TFSquaredDifference -- C
- * B --/
- * After
- * A --- TFSquaredDifference --
- * B --/
- * A --- EltwiseSub == EltwiseMul -- C
- * B --/
- * Where
- * A : x of TFSquaredDifference
- * B : y of TFSquaredDifference
- * C : a node that uses TFSquaredDifference as an input
- * TFSquaredDifference is disconnected from C
- * A and B are drawn multiple times to simplify the diagram
- */
-
- auto node_A = node->x();
- auto node_B = node->y();
-
- if (!loco::shape_known(node_A) || !loco::shape_known(node_B))
- {
- // Wait for shape inference
- return false;
- }
-
- const auto &x_shape = loco::shape_get(node_A);
- const auto &y_shape = loco::shape_get(node_B);
-
- if (!(x_shape == y_shape))
- {
- // TODO support broadcast
- return false;
- }
-
- auto sub_node = graph->nodes()->create<loco::EltwiseSub>();
- auto mul_node = graph->nodes()->create<loco::EltwiseMul>();
-
- // update connections
- sub_node->lhs(node_A);
- sub_node->rhs(node_B);
- mul_node->lhs(sub_node);
- mul_node->rhs(sub_node);
-
- // replace node
- replace(node).with(mul_node);
-
- return true;
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-bool SquaredDifferenceCanonicalizer::run(loco::Graph *graph)
-{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_node = dynamic_cast<moco::tf::TFSquaredDifference *>(node);
- if (tf_node != nullptr)
- {
- if (canonicalize_sqdiff(graph, tf_node))
- changed = true;
- }
- }
- }
-
- return changed;
-}
-
-} // namespace tf
-} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/SquaredDifferenceCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/SquaredDifferenceCanonicalizer.h
deleted file mode 100644
index afd65be32..000000000
--- a/compiler/moco-tf/src/Canonicalization/SquaredDifferenceCanonicalizer.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_SQUAREDDIFFERENCE_CANONICALIZER_H__
-#define __MOCO_TF_SQUAREDDIFFERENCE_CANONICALIZER_H__
-
-#include "Transform.h"
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Convert TFSquaredDifference to Canonical EltwiseSub and EltwiseMul
- */
-class SquaredDifferenceCanonicalizer final : public Transform
-{
-public:
- const char *name(void) const final { return "SquaredDifferenceCanonicalizer"; }
-
-public:
- bool run(loco::Graph *graph) final;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_SQUAREDDIFFERENCE_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.cpp
index a3fcc3b47..f5b991206 100644
--- a/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.cpp
@@ -16,19 +16,15 @@
#include "SqueezeCanonicalizer.h"
-#include "Annotations/ShapeInferenceData.h"
-
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
-#include "Dialect/TFNodeVisitor.h"
-#include "Dialect/TFNodeImpl.h"
+#include <moco/IR/TFDialect.h>
+#include <moco/Support/TFShapeInferenceHelper.h>
#include <moco/Log.h>
namespace
{
-bool canonicalize_squeeze_to_reshape(loco::Graph *graph, moco::tf::TFSqueeze *node)
+bool canonicalize_squeeze_to_reshape(loco::Graph *graph, moco::TFSqueeze *node)
{
LOGGER(l);
@@ -46,12 +42,12 @@ bool canonicalize_squeeze_to_reshape(loco::Graph *graph, moco::tf::TFSqueeze *no
* In ---- FixedReshape ----- Out(s)
*/
- auto squeeze_shape = node->annot<moco::tf::ShapeInferenceData>();
+ auto nodeshape = moco::node_shape(node);
// canonicalize into FixedReshape is valid when squeeze has shape info
// TODO Support general Squeeze case
- assert(squeeze_shape);
+ assert(nodeshape.domain() != loco::Domain::Unknown);
- auto squeeze_tensor_shape = squeeze_shape->tensor_shape();
+ auto squeeze_tensor_shape = nodeshape.as<loco::TensorShape>();
// Create loco node to replace
auto reshape = graph->nodes()->create<loco::FixedReshape>();
@@ -81,25 +77,9 @@ namespace moco
namespace tf
{
-bool SqueezeCanonicalizer::run(loco::Graph *graph)
+bool SqueezeCanonicalizer::transform(TFSqueeze *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_squeeze = dynamic_cast<moco::tf::TFSqueeze *>(node);
- if (tf_squeeze != nullptr)
- {
- if (canonicalize_squeeze_to_reshape(graph, tf_squeeze))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_squeeze_to_reshape(node->graph(), node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.h
index dc5b2d7b1..28a1442bd 100644
--- a/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_SQUEEZE_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -31,13 +34,13 @@ namespace tf
*
* @note There is no canonical Squeeze node
*/
-class SqueezeCanonicalizer : public Transform
+class SqueezeCanonicalizer : public SimpleNodeTransform<moco::TFSqueeze>
{
public:
const char *name(void) const final { return "SqueezeCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(moco::TFSqueeze *) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.cpp
index a52af05a5..574fa3993 100644
--- a/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.cpp
@@ -16,17 +16,14 @@
#include "StopGradientCanonicalizer.h"
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
-#include "Dialect/TFNodeVisitor.h"
-#include "Dialect/TFNodeImpl.h"
+#include <moco/IR/TFDialect.h>
#include <moco/Log.h>
namespace
{
-bool canonicalize_stopgradient(loco::Graph *graph, moco::tf::TFStopGradient *node)
+bool canonicalize_stopgradient(loco::Graph *graph, moco::TFStopGradient *node)
{
LOGGER(l);
@@ -65,25 +62,9 @@ namespace moco
namespace tf
{
-bool StopGradientCanonicalizer::run(loco::Graph *graph)
+bool StopGradientCanonicalizer::transform(TFStopGradient *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_stopgradient = dynamic_cast<moco::tf::TFStopGradient *>(node);
- if (tf_stopgradient != nullptr)
- {
- if (canonicalize_stopgradient(graph, tf_stopgradient))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_stopgradient(node->graph(), node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.h
index a23a801f0..6a17728a6 100644
--- a/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_STOPGRADIENT_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Canonicalize TF-dialect TFStopGradient into canonical Forward node
*/
-class StopGradientCanonicalizer : public Transform
+class StopGradientCanonicalizer : public SimpleNodeTransform<moco::TFStopGradient>
{
public:
const char *name(void) const final { return "StopGradientCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(moco::TFStopGradient *) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.cpp
index 21f4210eb..c518b7d64 100644
--- a/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.cpp
@@ -16,8 +16,7 @@
#include "SubCanonicalizer.h"
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
+#include <moco/IR/TFDialect.h>
#include "TFEltwiseBinaryCanonicalzeHelper.h"
@@ -26,25 +25,9 @@ namespace moco
namespace tf
{
-bool SubCanonicalizer::run(loco::Graph *graph)
+bool SubCanonicalizer::transform(moco::TFSub *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_node = dynamic_cast<moco::tf::TFSub *>(node);
- if (tf_node != nullptr)
- {
- if (canonicalize_eltwise_binary_node(tf_node))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_eltwise_binary_node(node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.h
index 4ab470685..f715cc86c 100644
--- a/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_SUB_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Convert TFSub to Canonical EltwiseSub
*/
-class SubCanonicalizer : public Transform
+class SubCanonicalizer : public SimpleNodeTransform<moco::TFSub>
{
public:
const char *name(void) const final { return "SubCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(moco::TFSub *) const final;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.cpp
new file mode 100644
index 000000000..081e0e5f9
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TFPushCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include <stdex/Memory.h>
+
+namespace
+{
+
+bool canonicalize_push(loco::Graph *graph, moco::TFPush *node)
+{
+ /**
+ * @note This will replace TFRelu node with Canonical ReLU
+ *
+ * Before
+ * A --- TFPush
+ * After
+ * +- TFPush
+ * |
+ * A -+- Push
+ *
+ * Where
+ * A : from of TFPush
+ * TFPush will have no GraphOutputIndex
+ * Push will have GraphOutputIndex that from TFPush
+ */
+
+ auto push_node = graph->nodes()->create<loco::Push>();
+
+ auto node_A = node->from();
+
+ // update connections
+ push_node->from(node_A);
+
+ // update output index
+ push_node->index(node->index());
+ node->index_reset();
+
+ // replace node
+ replace(node).with(push_node);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool TFPushCanonicalizer::transform(TFPush *node) const
+{
+ return canonicalize_push(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.h
new file mode 100644
index 000000000..569a71f82
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_TF_PUSH_CANONICALIZER_H__
+#define __MOCO_TF_PUSH_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFPush to Canonical Push
+ */
+class TFPushCanonicalizer : public SimpleNodeTransform<moco::TFPush>
+{
+public:
+ const char *name(void) const final { return "TFPushCanonicalizer"; }
+
+public:
+ bool transform(moco::TFPush *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_PUSH_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.cpp
index 9b7b073e1..3f48a50fc 100644
--- a/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.cpp
@@ -16,17 +16,14 @@
#include "TanhCanonicalizer.h"
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
-#include "Dialect/TFNodeVisitor.h"
-#include "Dialect/TFNodeImpl.h"
+#include <moco/IR/TFDialect.h>
#include <stdex/Memory.h>
namespace
{
-bool canonicalize_tanh(loco::Graph *graph, moco::tf::TFTanh *node)
+bool canonicalize_tanh(loco::Graph *graph, moco::TFTanh *node)
{
/**
* @note This will replace TFTanh node with Canonical Tanh
@@ -64,25 +61,9 @@ namespace moco
namespace tf
{
-bool TanhCanonicalizer::run(loco::Graph *graph)
+bool TanhCanonicalizer::transform(TFTanh *node) const
{
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
- bool changed = false;
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- auto tf_node = dynamic_cast<moco::tf::TFTanh *>(node);
- if (tf_node != nullptr)
- {
- if (canonicalize_tanh(graph, tf_node))
- changed = true;
- }
- }
- }
-
- return changed;
+ return canonicalize_tanh(node->graph(), node);
}
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.h
index cf566a4d4..af5e79fb5 100644
--- a/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.h
+++ b/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.h
@@ -18,6 +18,9 @@
#define __MOCO_TF_TANH_CANONICALIZER_H__
#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
#include <loco.h>
@@ -29,13 +32,13 @@ namespace tf
/**
* @brief Convert TFTanh to Canonical Tanh
*/
-class TanhCanonicalizer : public Transform
+class TanhCanonicalizer : public SimpleNodeTransform<moco::TFTanh>
{
public:
const char *name(void) const final { return "TanhCanonicalizer"; }
public:
- bool run(loco::Graph *graph) override;
+ bool transform(moco::TFTanh *) const override;
};
} // namespace tf
diff --git a/compiler/moco-tf/src/Canonicalizer.cpp b/compiler/moco-tf/src/Canonicalizer.cpp
index c705d686b..04bc7c57a 100644
--- a/compiler/moco-tf/src/Canonicalizer.cpp
+++ b/compiler/moco-tf/src/Canonicalizer.cpp
@@ -27,11 +27,16 @@
#include "Canonicalization/BiasAddCanonicalizer.h"
#include "Canonicalization/ConcatV2Canonicalizer.h"
#include "Canonicalization/ConstCanonicalizer.h"
+#include "Canonicalization/Conv2DBackpropInputCanonicalizer.h"
#include "Canonicalization/Conv2DCanonicalizer.h"
#include "Canonicalization/DepthwiseConv2dNativeCanonicalizer.h"
#include "Canonicalization/IdentityCanonicalizer.h"
+#include "Canonicalization/MaximumCanonicalizer.h"
#include "Canonicalization/MaxPoolCanonicalizer.h"
+#include "Canonicalization/MeanCanonicalizer.h"
#include "Canonicalization/MulCanonicalizer.h"
+#include "Canonicalization/PadCanonicalizer.h"
+#include "Canonicalization/PlaceholderCanonicalizer.h"
#include "Canonicalization/RealDivCanonicalizer.h"
#include "Canonicalization/ReluCanonicalizer.h"
#include "Canonicalization/Relu6Canonicalizer.h"
@@ -39,14 +44,15 @@
#include "Canonicalization/RsqrtCanonicalizer.h"
#include "Canonicalization/SoftmaxCanonicalizer.h"
#include "Canonicalization/SqrtCanonicalizer.h"
-#include "Canonicalization/SquaredDifferenceCanonicalizer.h"
#include "Canonicalization/SqueezeCanonicalizer.h"
#include "Canonicalization/StopGradientCanonicalizer.h"
#include "Canonicalization/SubCanonicalizer.h"
#include "Canonicalization/TanhCanonicalizer.h"
+// For virtual nodes
+#include "Canonicalization/TFPushCanonicalizer.h"
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
+#include <moco/IR/TFDialect.h>
+#include <moco/IR/TFNodes.h>
#include <logo/Phase.h>
@@ -65,7 +71,7 @@ bool has_tf_nodes(loco::Graph *g)
auto active_nodes = loco::active_nodes(loco::output_nodes(g));
for (auto node : active_nodes)
{
- if (node->dialect() == moco::tf::TFDialect::get())
+ if (node->dialect() == moco::TFDialect::get())
{
return true;
}
@@ -96,12 +102,17 @@ void Canonicalizer::canonicalize(loco::Graph *g) const
phase.emplace_back(stdex::make_unique<ConcatV2Canonicalizer>());
if (moco::tf::get<moco::tf::Knob::CanonicalizeConst>())
phase.emplace_back(stdex::make_unique<ConstCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<Conv2DBackpropInputCanonicalizer>());
if (moco::tf::get<moco::tf::Knob::CanonicalizeConv2D>())
phase.emplace_back(stdex::make_unique<Conv2DCanonicalizer>());
phase.emplace_back(stdex::make_unique<DepthwiseConv2dNativeCanonicalizer>());
phase.emplace_back(stdex::make_unique<IdentityCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<MaximumCanonicalizer>());
phase.emplace_back(stdex::make_unique<MaxPoolCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<MeanCanonicalizer>());
phase.emplace_back(stdex::make_unique<MulCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<PadCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<PlaceholderCanonicalizer>());
phase.emplace_back(stdex::make_unique<RealDivCanonicalizer>());
phase.emplace_back(stdex::make_unique<ReluCanonicalizer>());
phase.emplace_back(stdex::make_unique<Relu6Canonicalizer>());
@@ -109,11 +120,13 @@ void Canonicalizer::canonicalize(loco::Graph *g) const
phase.emplace_back(stdex::make_unique<RsqrtCanonicalizer>());
phase.emplace_back(stdex::make_unique<SoftmaxCanonicalizer>());
phase.emplace_back(stdex::make_unique<SqrtCanonicalizer>());
- phase.emplace_back(stdex::make_unique<SquaredDifferenceCanonicalizer>());
+ // NOTE SquaredDifference is handled in ResolveSquaredDifference
phase.emplace_back(stdex::make_unique<SqueezeCanonicalizer>());
phase.emplace_back(stdex::make_unique<StopGradientCanonicalizer>());
phase.emplace_back(stdex::make_unique<SubCanonicalizer>());
phase.emplace_back(stdex::make_unique<TanhCanonicalizer>());
+ // For virtual nodes
+ phase.emplace_back(stdex::make_unique<TFPushCanonicalizer>());
/* TRANSFORM DECLARATION END */
ProgressReporter prog(g, logo::PhaseStrategy::Restart);
diff --git a/compiler/moco-tf/src/CodecHelper.h b/compiler/moco-tf/src/CodecHelper.h
new file mode 100644
index 000000000..85e4e2164
--- /dev/null
+++ b/compiler/moco-tf/src/CodecHelper.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CODEC_HELPER_H__
+#define __CODEC_HELPER_H__
+
+#include <plier/tf/Convert.h>
+#include <stdex/Memory.h>
+
+namespace
+{
+
+using plier::tf::DataLayout;
+
+void set_feature_enc(loco::FeatureEncode *feature_enc, DataLayout data_layout)
+{
+ auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>();
+
+ if (data_layout == DataLayout::NHWC)
+ {
+ enc->perm()->axis(loco::FeatureAxis::Count) = 0;
+ enc->perm()->axis(loco::FeatureAxis::Height) = 1;
+ enc->perm()->axis(loco::FeatureAxis::Width) = 2;
+ enc->perm()->axis(loco::FeatureAxis::Depth) = 3;
+ }
+ else if (data_layout == DataLayout::NCHW)
+ {
+ enc->perm()->axis(loco::FeatureAxis::Count) = 0;
+ enc->perm()->axis(loco::FeatureAxis::Depth) = 1;
+ enc->perm()->axis(loco::FeatureAxis::Height) = 2;
+ enc->perm()->axis(loco::FeatureAxis::Width) = 3;
+ }
+
+ feature_enc->encoder(std::move(enc));
+}
+
+void set_feature_dec(loco::FeatureDecode *feature_dec, DataLayout data_layout)
+{
+ auto dec = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>();
+
+ if (data_layout == DataLayout::NHWC)
+ {
+ dec->perm()->axis(loco::FeatureAxis::Count) = 0;
+ dec->perm()->axis(loco::FeatureAxis::Height) = 1;
+ dec->perm()->axis(loco::FeatureAxis::Width) = 2;
+ dec->perm()->axis(loco::FeatureAxis::Depth) = 3;
+ }
+ else if (data_layout == DataLayout::NCHW)
+ {
+ dec->perm()->axis(loco::FeatureAxis::Count) = 0;
+ dec->perm()->axis(loco::FeatureAxis::Depth) = 1;
+ dec->perm()->axis(loco::FeatureAxis::Height) = 2;
+ dec->perm()->axis(loco::FeatureAxis::Width) = 3;
+ }
+
+ feature_dec->decoder(std::move(dec));
+}
+
+} // namespace
+
+#endif // __CODEC_HELPER_H__
diff --git a/compiler/moco-tf/src/Dialect/TFDialect.cpp b/compiler/moco-tf/src/Dialect/TFDialect.cpp
deleted file mode 100644
index 730224753..000000000
--- a/compiler/moco-tf/src/Dialect/TFDialect.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TFDialect.h"
-
-namespace moco
-{
-namespace tf
-{
-
-loco::Dialect *TFDialect::get(void)
-{
- static TFDialect d;
- return &d;
-}
-
-} // namespace tf
-} // namespace moco
diff --git a/compiler/moco-tf/src/Dialect/TFDialect.h b/compiler/moco-tf/src/Dialect/TFDialect.h
deleted file mode 100644
index 9074e18c7..000000000
--- a/compiler/moco-tf/src/Dialect/TFDialect.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_DIALECT_TFDIALECT_H__
-#define __MOCO_TF_DIALECT_TFDIALECT_H__
-
-#include <loco/IR/Dialect.h>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief A singleton for TensorFlow Dialect
- */
-class TFDialect final : public loco::Dialect
-{
-private:
- TFDialect() = default;
-
-public:
- TFDialect(const TFDialect &) = delete;
- TFDialect(TFDialect &&) = delete;
-
-public:
- static loco::Dialect *get(void);
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_DIALECT_TFDIALECT_H__
diff --git a/compiler/moco-tf/src/Dialect/TFDialect.test.cpp b/compiler/moco-tf/src/Dialect/TFDialect.test.cpp
deleted file mode 100644
index f89eaaf96..000000000
--- a/compiler/moco-tf/src/Dialect/TFDialect.test.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFDialectTest, get)
-{
- auto d = moco::tf::TFDialect::get();
-
- // get() SHOULD return a valid(non-null) pointer
- ASSERT_NE(d, nullptr);
- // The return value SHOULD be stable across multiple invocations
- ASSERT_EQ(d, moco::tf::TFDialect::get());
-}
diff --git a/compiler/moco-tf/src/Dialect/TFNode.cpp b/compiler/moco-tf/src/Dialect/TFNode.cpp
deleted file mode 100644
index e9fc3149c..000000000
--- a/compiler/moco-tf/src/Dialect/TFNode.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TFNode.h"
-#include "TFDialect.h"
-
-namespace moco
-{
-namespace tf
-{
-
-const loco::Dialect *TFNode::dialect(void) const { return TFDialect::get(); }
-
-} // namespace tf
-} // namespace moco
diff --git a/compiler/moco-tf/src/Dialect/TFNode.h b/compiler/moco-tf/src/Dialect/TFNode.h
deleted file mode 100644
index 3cd12af23..000000000
--- a/compiler/moco-tf/src/Dialect/TFNode.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_DIALECT_TFNODE_H__
-#define __MOCO_TF_DIALECT_TFNODE_H__
-
-#include "TFNodeDecl.h"
-#include "TFNodeImpl.h"
-
-#endif // __MOCO_TF_DIALECT_TFNODE_H__
diff --git a/compiler/moco-tf/src/Dialect/TFNodeDecl.h b/compiler/moco-tf/src/Dialect/TFNodeDecl.h
deleted file mode 100644
index 922165b01..000000000
--- a/compiler/moco-tf/src/Dialect/TFNodeDecl.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_DIALECT_TFNODE_DECL_H__
-#define __MOCO_TF_DIALECT_TFNODE_DECL_H__
-
-#include <loco/IR/Node.h>
-#include <loco/IR/Dialect.h>
-
-#include "TFOpcode.h"
-#include "TFNodeVisitor.forward.h"
-
-#include <array>
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note Below alias may be introduced as separate class
-using TFDataLayout = std::string;
-using TFPadding = std::string;
-
-struct TFNode : public loco::Node
-{
- virtual ~TFNode() = default;
-
- const loco::Dialect *dialect(void) const final;
- virtual TFOpcode opcode(void) const = 0;
-
- template <typename T> T accept(TFNodeVisitorBase<T> *) const;
- template <typename T> T accept(TFNodeMutableVisitorBase<T> *);
-};
-
-template <TFOpcode Code> struct TFNodeImpl : public TFNode
-{
- virtual ~TFNodeImpl() = default;
-
- uint32_t opnum(void) const final { return static_cast<uint32_t>(Code); }
- TFOpcode opcode(void) const final { return Code; }
-};
-
-/**
- * @brief Nodes with the fixed number of inputs
- */
-template <unsigned N, typename Base> class FixedArityNode : public Base
-{
-public:
- FixedArityNode()
- {
- for (uint32_t n = 0; n < N; ++n)
- {
- _args[n] = std::unique_ptr<loco::Use>{new loco::Use{this}};
- }
- }
-
- virtual ~FixedArityNode() = default;
-
-public:
- unsigned arity(void) const final { return N; }
-
- loco::Node *arg(uint32_t n) const final { return _args.at(n)->node(); }
-
- void drop(void) final
- {
- for (uint32_t n = 0; n < N; ++n)
- {
- _args.at(n)->node(nullptr);
- }
- }
-
-protected:
- // This API allows inherited classes to access "_args" field.
- loco::Use *at(unsigned n) const { return _args.at(n).get(); }
-
-private:
- std::array<std::unique_ptr<loco::Use>, N> _args;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_DIALECT_TFNODE_DECL_H__
diff --git a/compiler/moco-tf/src/Dialect/TFNodeImpl.h b/compiler/moco-tf/src/Dialect/TFNodeImpl.h
deleted file mode 100644
index 39e8830b4..000000000
--- a/compiler/moco-tf/src/Dialect/TFNodeImpl.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_DIALECT_TFNODE_IMPL_H__
-#define __MOCO_TF_DIALECT_TFNODE_IMPL_H__
-
-#include "TFNodes.h"
-#include "TFNodeVisitor.h"
-
-#include <stdexcept>
-
-namespace moco
-{
-namespace tf
-{
-
-template <typename T> T TFNode::accept(TFNodeVisitorBase<T> *v) const
-{
- switch (this->opcode())
- {
-#define TENSORFLOW_NODE(OPCODE, CLASS) \
- case TFOpcode::OPCODE: \
- return v->visit(dynamic_cast<const CLASS *>(this));
-
-#include "TFNodes.lst"
-#undef TENSORFLOW_NODE
- default:
- break;
- }
-
- throw std::runtime_error{"NYI"};
-}
-
-template <typename T> T TFNode::accept(TFNodeMutableVisitorBase<T> *v)
-{
- switch (this->opcode())
- {
-#define TENSORFLOW_NODE(OPCODE, CLASS) \
- case TFOpcode::OPCODE: \
- return v->visit(dynamic_cast<CLASS *>(this));
-
-#include "TFNodes.lst"
-#undef TENSORFLOW_NODE
- default:
- break;
- }
-
- throw std::runtime_error{"NYI"};
-}
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_DIALECT_TFNODE_IMPL_H__
diff --git a/compiler/moco-tf/src/Dialect/TFNodeVisitor.forward.h b/compiler/moco-tf/src/Dialect/TFNodeVisitor.forward.h
deleted file mode 100644
index 513c0ae1e..000000000
--- a/compiler/moco-tf/src/Dialect/TFNodeVisitor.forward.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_DIALECT_TFNODE_VISITOR_FORWARD_H__
-#define __MOCO_TF_DIALECT_TFNODE_VISITOR_FORWARD_H__
-
-namespace moco
-{
-namespace tf
-{
-
-// NOTE These forward declarations SHOULD BE aligned with Node delcarations in
-// "TFNodeVisitor.h"
-template <typename T> struct TFNodeVisitorBase;
-template <typename T> struct TFNodeMutableVisitorBase;
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_DIALECT_TFNODE_VISITOR_FORWARD_H__
diff --git a/compiler/moco-tf/src/Dialect/TFNodeVisitor.h b/compiler/moco-tf/src/Dialect/TFNodeVisitor.h
deleted file mode 100644
index aff9bca4e..000000000
--- a/compiler/moco-tf/src/Dialect/TFNodeVisitor.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_DIALECT_TFNODE_VISITOR_H__
-#define __MOCO_TF_DIALECT_TFNODE_VISITOR_H__
-
-#include "TFNodes.h"
-
-#include <stdexcept>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * DO NOT use this class. Use TFNodeVisitor instead.
- */
-template <typename T> struct TFNodeVisitorBase
-{
- virtual ~TFNodeVisitorBase() = default;
-
-#define TENSORFLOW_NODE(OPCODE, CLASS) virtual T visit(const CLASS *) = 0;
-#include "TFNodes.lst"
-#undef TENSORFLOW_NODE
-};
-
-template <typename T> struct TFNodeVisitor : public TFNodeVisitorBase<T>
-{
- virtual ~TFNodeVisitor() = default;
-
-#define TENSORFLOW_NODE(OPCODE, CLASS) \
- virtual T visit(const CLASS *node) { return visit(static_cast<const TFNode *>(node)); }
-#include "TFNodes.lst"
-#undef TENSORFLOW_NODE
-
- /// @brief Default fallback
- virtual T visit(const TFNode *) { throw std::runtime_error{"Not implemented, yet"}; }
-};
-
-/**
- * DO NOT use this class. Use TFNodeMutableVisitor instead.
- */
-template <typename T> struct TFNodeMutableVisitorBase
-{
- virtual ~TFNodeMutableVisitorBase() = default;
-
-#define TENSORFLOW_NODE(OPCODE, CLASS) virtual T visit(CLASS *) = 0;
-#include "TFNodes.lst"
-#undef TENSORFLOW_NODE
-};
-
-template <typename T> struct TFNodeMutableVisitor : public TFNodeMutableVisitorBase<T>
-{
- virtual ~TFNodeMutableVisitor() = default;
-
-#define TENSORFLOW_NODE(OPCODE, CLASS) \
- virtual T visit(CLASS *node) { return visit(static_cast<TFNode *>(node)); }
-#include "TFNodes.lst"
-#undef TENSORFLOW_NODE
-
- /// @brief Default fallback
- virtual T visit(TFNode *) { throw std::runtime_error{"Not implemented, yet"}; }
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_DIALECT_TFNODE_VISITOR_H__
diff --git a/compiler/moco-tf/src/Dialect/TFNodes.h b/compiler/moco-tf/src/Dialect/TFNodes.h
deleted file mode 100644
index c7c63f0b6..000000000
--- a/compiler/moco-tf/src/Dialect/TFNodes.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_DIALECT_TFNODES_H__
-#define __MOCO_TF_DIALECT_TFNODES_H__
-
-#include "IR/TFAdd.h"
-#include "IR/TFAvgPool.h"
-#include "IR/TFBiasAdd.h"
-#include "IR/TFConcatV2.h"
-#include "IR/TFConst.h"
-#include "IR/TFConv2D.h"
-#include "IR/TFDepthwiseConv2dNative.h"
-#include "IR/TFFusedBatchNorm.h"
-#include "IR/TFIdentity.h"
-#include "IR/TFMaxPool.h"
-#include "IR/TFMean.h"
-#include "IR/TFMul.h"
-#include "IR/TFRealDiv.h"
-#include "IR/TFRelu.h"
-#include "IR/TFRelu6.h"
-#include "IR/TFReshape.h"
-#include "IR/TFRsqrt.h"
-#include "IR/TFShape.h"
-#include "IR/TFSoftmax.h"
-#include "IR/TFSqrt.h"
-#include "IR/TFSquaredDifference.h"
-#include "IR/TFSqueeze.h"
-#include "IR/TFStopGradient.h"
-#include "IR/TFSub.h"
-#include "IR/TFTanh.h"
-
-#endif // __MOCO_TF_DIALECT_TFNODES_H__
diff --git a/compiler/moco-tf/src/Dialect/TFNodes.lst b/compiler/moco-tf/src/Dialect/TFNodes.lst
deleted file mode 100644
index 20730bb69..000000000
--- a/compiler/moco-tf/src/Dialect/TFNodes.lst
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef TENSORFLOW_NODE
-#error "Define TENSORFLOW_NODE"
-#endif // TENSORFLOW_NODE
-
-//
-// PLEASE SORT NODE DECLS IN ALPHABETICAL ORDER
-//
-
-// TENSORFLOW_NODE(OPCODE, CLASS)
-TENSORFLOW_NODE(Add, TFAdd)
-TENSORFLOW_NODE(AvgPool, TFAvgPool)
-TENSORFLOW_NODE(BiasAdd, TFBiasAdd)
-TENSORFLOW_NODE(ConcatV2, TFConcatV2)
-TENSORFLOW_NODE(Const, TFConst)
-TENSORFLOW_NODE(Conv2D, TFConv2D)
-TENSORFLOW_NODE(DepthwiseConv2dNative, TFDepthwiseConv2dNative)
-TENSORFLOW_NODE(FusedBatchNorm, TFFusedBatchNorm)
-TENSORFLOW_NODE(Identity, TFIdentity)
-TENSORFLOW_NODE(MaxPool, TFMaxPool)
-TENSORFLOW_NODE(Mean, TFMean)
-TENSORFLOW_NODE(Mul, TFMul)
-TENSORFLOW_NODE(RealDiv, TFRealDiv)
-TENSORFLOW_NODE(Relu, TFRelu)
-TENSORFLOW_NODE(Relu6, TFRelu6)
-TENSORFLOW_NODE(Reshape, TFReshape)
-TENSORFLOW_NODE(Rsqrt, TFRsqrt)
-TENSORFLOW_NODE(Shape, TFShape)
-TENSORFLOW_NODE(Softmax, TFSoftmax)
-TENSORFLOW_NODE(Sqrt, TFSqrt)
-TENSORFLOW_NODE(SquaredDifference, TFSquaredDifference)
-TENSORFLOW_NODE(Squeeze, TFSqueeze)
-TENSORFLOW_NODE(StopGradient, TFStopGradient)
-TENSORFLOW_NODE(Sub, TFSub)
-TENSORFLOW_NODE(Tanh, TFTanh)
diff --git a/compiler/moco-tf/src/Dialect/TFOpcode.h b/compiler/moco-tf/src/Dialect/TFOpcode.h
deleted file mode 100644
index 13e9ca119..000000000
--- a/compiler/moco-tf/src/Dialect/TFOpcode.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_DIALECT_TFOPCODE_H__
-#define __MOCO_TF_DIALECT_TFOPCODE_H__
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief TensorFlow Node Opcode
- */
-enum class TFOpcode
-{
-#define TENSORFLOW_NODE(OPCODE, CLASS) OPCODE,
-#include "TFNodes.lst"
-#undef TENSORFLOW_NODE
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_DIALECT_TFOPCODE_H__
diff --git a/compiler/moco-tf/src/Dialect/TFShapeInferenceRule.cpp b/compiler/moco-tf/src/Dialect/TFShapeInferenceRule.cpp
deleted file mode 100644
index b25ad0c17..000000000
--- a/compiler/moco-tf/src/Dialect/TFShapeInferenceRule.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TFShapeInferenceRule.h"
-
-#include "TFDialect.h"
-#include "TFNode.h"
-
-#include "Annotations/ShapeInferenceData.h"
-
-#include <loco/IR/NodeShape.h>
-#include <loco/Service/ShapeInference.h>
-
-#include <cassert>
-
-namespace moco
-{
-namespace tf
-{
-
-bool TFShapeInferenceRule::recognize(const loco::Dialect *d) const
-{
- // handle only TensorFlow dialect
- return TFDialect::get() == d;
-}
-
-bool TFShapeInferenceRule::infer(const loco::Node *node, loco::NodeShape &shape) const
-{
- assert(node->dialect() == TFDialect::get());
- assert(dynamic_cast<const TFNode *>(node) != nullptr);
-
- if (auto shapedata = node->annot<ShapeInferenceData>())
- {
- assert(shapedata->domain() == loco::Domain::Tensor);
-
- shape.set(shapedata->tensor_shape());
-
- return true;
- }
-
- return false;
-}
-
-} // namespace tf
-} // namespace moco
diff --git a/compiler/moco-tf/src/Dialect/TFShapeInferenceRule.h b/compiler/moco-tf/src/Dialect/TFShapeInferenceRule.h
deleted file mode 100644
index 84b3b47c6..000000000
--- a/compiler/moco-tf/src/Dialect/TFShapeInferenceRule.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_SHAPE_INFERENCE_RULE_H__
-#define __MOCO_TF_SHAPE_INFERENCE_RULE_H__
-
-#include <loco/Service/ShapeInferenceRule.h>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Shape inference rule for TensorFlow dialect
- */
-struct TFShapeInferenceRule final : public loco::ShapeInferenceRule
-{
- bool recognize(const loco::Dialect *) const final;
- bool infer(const loco::Node *, loco::NodeShape &) const final;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_SHAPE_INFERENCE_RULE_H__
diff --git a/compiler/moco-tf/src/Dialect/TFTypeInferenceRule.cpp b/compiler/moco-tf/src/Dialect/TFTypeInferenceRule.cpp
deleted file mode 100644
index 8525768db..000000000
--- a/compiler/moco-tf/src/Dialect/TFTypeInferenceRule.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TFTypeInferenceRule.h"
-
-#include "TFDialect.h"
-#include "TFNodeVisitor.h"
-#include "TFNodes.h"
-
-#include "TFNodeImpl.h"
-
-#include <cassert>
-
-namespace
-{
-
-using namespace moco::tf;
-
-struct TypeForwardAlgorithm final : public moco::tf::TFNodeVisitor<loco::DataType>
-{
- loco::DataType visit(const TFAdd *node) { return dtype_get(node->x()); }
- loco::DataType visit(const TFAvgPool *node) { return dtype_get(node->value()); }
- loco::DataType visit(const TFBiasAdd *node) { return dtype_get(node->value()); }
- loco::DataType visit(const TFConcatV2 *node) { return dtype_get(node->values(0)); }
-
- loco::DataType visit(const TFConst *node) { return node->dtype(); }
-
- loco::DataType visit(const TFConv2D *node) { return dtype_get(node->input()); }
- loco::DataType visit(const TFDepthwiseConv2dNative *node) { return dtype_get(node->input()); }
- loco::DataType visit(const TFFusedBatchNorm *node) { return dtype_get(node->input()); }
- loco::DataType visit(const TFIdentity *node) { return dtype_get(node->input()); }
- loco::DataType visit(const TFMaxPool *node) { return dtype_get(node->value()); }
- loco::DataType visit(const TFMean *node) { return dtype_get(node->input()); }
- loco::DataType visit(const TFMul *node) { return dtype_get(node->x()); }
- loco::DataType visit(const TFRealDiv *node) { return dtype_get(node->x()); }
- loco::DataType visit(const TFRelu *node) { return dtype_get(node->features()); }
- loco::DataType visit(const TFRelu6 *node) { return dtype_get(node->features()); }
- loco::DataType visit(const TFReshape *node) { return dtype_get(node->tensor()); }
- loco::DataType visit(const TFRsqrt *node) { return dtype_get(node->x()); }
-
- loco::DataType visit(const TFShape *node) { return node->dtype(); }
-
- loco::DataType visit(const TFSoftmax *node) { return dtype_get(node->logits()); }
- loco::DataType visit(const TFSqrt *node) { return dtype_get(node->x()); }
- loco::DataType visit(const TFSquaredDifference *node) { return dtype_get(node->x()); }
- loco::DataType visit(const TFSqueeze *node) { return dtype_get(node->input()); }
- loco::DataType visit(const TFStopGradient *node) { return dtype_get(node->input()); }
- loco::DataType visit(const TFSub *node) { return dtype_get(node->x()); }
- loco::DataType visit(const TFTanh *node) { return dtype_get(node->x()); }
-};
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-bool TFTypeInferenceRule::recognize(const loco::Dialect *d) const
-{
- // This rule recognizes only "TFDialect" dialect!
- return TFDialect::get() == d;
-}
-
-bool TFTypeInferenceRule::infer(const loco::Node *node, loco::DataType &dtype) const
-{
- assert(node->dialect() == TFDialect::get());
-
- TypeForwardAlgorithm alg;
-
-// clang-format off
-#define TENSORFLOW_NODE(OPCODE,CLASS) \
- if (dynamic_cast<const moco::tf::CLASS *>(node)) \
- { \
- auto tfnode = dynamic_cast<const moco::tf::CLASS *>(node); \
- dtype = tfnode->accept(&alg); \
- assert(dtype != loco::DataType::Unknown); \
- return true; \
- }
-#include "Dialect/TFNodes.lst"
-#undef TENSORFLOW_NODE
- // clang-format on
-
- return false;
-}
-
-} // namespace tf
-} // namespace moco
diff --git a/compiler/moco-tf/src/Dialect/TFTypeInferenceRule.h b/compiler/moco-tf/src/Dialect/TFTypeInferenceRule.h
deleted file mode 100644
index 3e6a64712..000000000
--- a/compiler/moco-tf/src/Dialect/TFTypeInferenceRule.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_TYPE_INFERENCE_RULE_H__
-#define __MOCO_TF_TYPE_INFERENCE_RULE_H__
-
-#include <loco/Service/TypeInference.h>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Type Inference Rule for TFDialect
- */
-struct TFTypeInferenceRule final : public loco::TypeInferenceRule
-{
- bool recognize(const loco::Dialect *) const final;
-
- bool infer(const loco::Node *, loco::DataType &) const final;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_TYPE_INFERENCE_RULE_H__
diff --git a/compiler/moco-tf/src/Dialect/VariadicArityNode.h b/compiler/moco-tf/src/Dialect/VariadicArityNode.h
deleted file mode 100644
index 407b8314c..000000000
--- a/compiler/moco-tf/src/Dialect/VariadicArityNode.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __TF_DIALECT_VARIADIC_ARITY_NODE_H__
-#define __TF_DIALECT_VARIADIC_ARITY_NODE_H__
-
-#include <loco/IR/Node.h>
-#include <loco/IR/Use.h>
-
-#include <vector>
-#include <memory>
-#include <cassert>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Nodes with the variadic inputs
- */
-template <typename Base> class VariadicArityNode : public Base
-{
-public:
- VariadicArityNode(uint32_t arity)
- {
- for (uint32_t n = 0; n < arity; ++n)
- {
- _args.emplace_back(std::move(std::unique_ptr<loco::Use>{new loco::Use{this}}));
- }
- };
-
- virtual ~VariadicArityNode() = default;
-
-public:
- uint32_t arity(void) const final { return _args.size(); }
-
- loco::Node *arg(uint32_t n) const final
- {
- assert(n < _args.size());
- return _args.at(n)->node();
- }
-
- void drop(void) final
- {
- for (uint32_t n = 0; n < _args.size(); ++n)
- {
- _args.at(n)->node(nullptr);
- }
- }
-
-protected:
- // This API allows inherited classes to access "_args" field.
- loco::Use *at(uint32_t n) const
- {
- assert(n < _args.size());
- return _args.at(n).get();
- }
-
-private:
- std::vector<std::unique_ptr<loco::Use>> _args;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __TF_DIALECT_VARIADIC_ARITY_NODE_H__
diff --git a/compiler/moco-tf/src/Dialect/VariadicArityNode.test.cpp b/compiler/moco-tf/src/Dialect/VariadicArityNode.test.cpp
deleted file mode 100644
index 0b2d8795e..000000000
--- a/compiler/moco-tf/src/Dialect/VariadicArityNode.test.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "VariadicArityNode.h"
-
-#include <loco/IR/Nodes.h>
-
-#include <gtest/gtest.h>
-
-namespace
-{
-
-using namespace moco::tf;
-
-class ArbitraryInputNode : public VariadicArityNode<loco::Node>
-{
-public:
- ArbitraryInputNode(uint32_t arity) : VariadicArityNode<loco::Node>(arity) {}
-
- void input(uint32_t idx, loco::Node *node) { at(idx)->node(node); }
- loco::Node *input(uint32_t idx) const { return at(idx)->node(); }
-
- const loco::Dialect *dialect(void) const { return nullptr; } // this won't be called for testing
- uint32_t opnum(void) const { return -1; } // this won't be called for testing
-};
-
-} // namespace
-
-TEST(CustomOpTest, VariadicArityNode_arity_n)
-{
- loco::ConstGen cg0, cg1, cg2;
-
- ArbitraryInputNode a_node(3);
- a_node.input(0, &cg0);
- a_node.input(1, &cg1);
- a_node.input(2, &cg2);
-
- ASSERT_EQ(a_node.arity(), 3);
- ASSERT_EQ(a_node.input(0), &cg0);
- ASSERT_EQ(a_node.input(1), &cg1);
- ASSERT_EQ(a_node.input(2), &cg2);
-}
diff --git a/compiler/moco-tf/src/Frontend.cpp b/compiler/moco-tf/src/Frontend.cpp
index e76580785..a17d5dd0e 100644
--- a/compiler/moco-tf/src/Frontend.cpp
+++ b/compiler/moco-tf/src/Frontend.cpp
@@ -15,10 +15,13 @@
*/
#include <moco/tf/Frontend.h>
+#include <moco/Importer.h>
+#include <moco/IR/TFNode.h>
#include <moco/Log.h>
+#include <moco/Import/GraphBuilderRegistry.h>
+
#include "Canonicalizer.h"
-#include "Importer.h"
#include "Optimizer.h"
#include "TFOptimizer.h"
@@ -28,8 +31,8 @@
#include <loco/Service/ShapeInference.h>
-#include <cwrap/Fildes.h>
#include <stdex/Memory.h>
+#include <oops/UserExn.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
@@ -37,6 +40,7 @@
#include <iostream>
#include <sstream>
+#include <fstream>
#include <stdexcept>
#include <fcntl.h>
@@ -45,13 +49,6 @@
namespace
{
-bool load_text(const cwrap::Fildes &fildes, tensorflow::GraphDef &graph_def)
-{
- google::protobuf::io::FileInputStream fis(fildes.get());
-
- return google::protobuf::TextFormat::Parse(&fis, &graph_def);
-}
-
bool load_text(std::istream *stream, tensorflow::GraphDef &graph_def)
{
google::protobuf::io::IstreamInputStream iis(stream);
@@ -59,14 +56,6 @@ bool load_text(std::istream *stream, tensorflow::GraphDef &graph_def)
return google::protobuf::TextFormat::Parse(&iis, &graph_def);
}
-bool load_binary(const cwrap::Fildes &fildes, tensorflow::GraphDef &graph_def)
-{
- google::protobuf::io::FileInputStream fis(fildes.get());
- google::protobuf::io::CodedInputStream cis(&fis);
-
- return graph_def.ParseFromCodedStream(&cis);
-}
-
bool load_binary(std::istream *stream, tensorflow::GraphDef &graph_def)
{
google::protobuf::io::IstreamInputStream iis(stream);
@@ -75,42 +64,94 @@ bool load_binary(std::istream *stream, tensorflow::GraphDef &graph_def)
return graph_def.ParseFromCodedStream(&cis);
}
-void load_tf(const std::string &path, moco::tf::Frontend::FileType type,
+void load_tf(std::istream *stream, moco::tf::Frontend::FileType type,
tensorflow::GraphDef &graph_def)
{
- cwrap::Fildes fildes{open(path.c_str(), O_RDONLY)};
-
- if (fildes.get() < 0)
+ bool result = (type == moco::tf::Frontend::FileType::Text) ? load_text(stream, graph_def)
+ : load_binary(stream, graph_def);
+ if (!result)
{
- throw std::runtime_error{"Error: " + path + " not found"};
+ throw oops::UserExn("Failed to parse prototxt from stream");
}
+}
- bool result = (type == moco::tf::Frontend::FileType::Text) ? load_text(fildes, graph_def)
- : load_binary(fildes, graph_def);
- if (!result)
+// If Placeholder has no shape attribute, set unknown_rank property to true.
+void set_unknown_rank(tensorflow::GraphDef &tf_graph_def)
+{
+ for (auto &n : *tf_graph_def.mutable_node())
{
- throw std::runtime_error{"Error: Failed to parse prototxt " + path};
+ if (n.op().compare("Placeholder"))
+ continue;
+
+ auto iter = n.attr().find("shape");
+ if (iter == n.attr().end())
+ {
+ tensorflow::AttrValue attr;
+ attr.mutable_shape()->set_unknown_rank(true);
+ n.mutable_attr()->insert({"shape", attr});
+ }
}
}
-void load_tf(std::istream *stream, moco::tf::Frontend::FileType type,
- tensorflow::GraphDef &graph_def)
+/**
+ * @brief Set input shape according to signature if node has unknown shape in GraphDef.
+ *
+ * @note If shape you provided is wrong or not enough, it returns false.
+ */
+bool set_input_shape(const moco::ModelSignature &signature, tensorflow::GraphDef &tf_graph_def)
{
- bool result = (type == moco::tf::Frontend::FileType::Text) ? load_text(stream, graph_def)
- : load_binary(stream, graph_def);
- if (!result)
+ for (auto &n : *tf_graph_def.mutable_node())
{
- throw std::runtime_error{"Error: Failed to parse prototxt from stream"};
+ if (n.op().compare("Placeholder"))
+ continue;
+
+ auto node_shape = n.mutable_attr()->at("shape").mutable_shape();
+ auto sig_shape = signature.shape(n.name() + ":0");
+
+ if (node_shape->unknown_rank() || !node_shape->dim_size())
+ {
+ // If shape in GraphDef is unknown, user must provide the shape info.
+ if (sig_shape == nullptr)
+ return false;
+ node_shape->clear_unknown_rank();
+ for (uint32_t i = 0; i < sig_shape->rank(); i++)
+ node_shape->add_dim()->set_size(-1);
+ }
+
+ for (uint32_t d = 0; d < node_shape->dim_size(); d++)
+ {
+ if (node_shape->mutable_dim(d)->size() == -1)
+ {
+ if (sig_shape == nullptr)
+ return false;
+ node_shape->mutable_dim(d)->set_size(sig_shape->dim(d));
+ }
+ else
+ {
+ // If User provide shape info though it already exists in GraphDef, make sure it matches
+ // the shape of GraphDef.
+ if (sig_shape && node_shape->dim(d).size() != sig_shape->dim(d))
+ return false;
+ }
+ }
}
+ return true;
+}
+
+void transform_tf(const moco::ModelSignature &signature, tensorflow::GraphDef &tf_graph_def)
+{
+ set_unknown_rank(tf_graph_def);
+ if (!set_input_shape(signature, tf_graph_def))
+ oops::UserExn("Info you provided may be wrong or not enough. Please check the info file.");
}
/**
* @brief Returns GraphBuilderRegistry that looks up default registry and additions
* such as custom op
*/
-moco::tf::GraphBuilderRegistry make_graph_builder_registry(const moco::tf::ModelSignature &sig)
+moco::GraphBuilderRegistry make_graph_builder_registry(const moco::ModelSignature &sig)
{
- moco::tf::GraphBuilderRegistry registry{&moco::tf::GraphBuilderRegistry::get()};
+ moco::GraphBuilderRegistry registry{&moco::GraphBuilderRegistry::get()};
// build a COpCallGraphBuilder per custom op type
for (const auto &custom_op : sig.customops())
@@ -144,49 +185,6 @@ namespace moco
namespace tf
{
-void ModelSignature::add_customop(const std::string &op)
-{
- if (std::find(_customops.begin(), _customops.end(), op) == _customops.end())
- _customops.emplace_back(op);
- else
- throw std::runtime_error{"Duplicated custom op: " + op};
-}
-
-void ModelSignature::shape(const std::string &node_name,
- const nncc::core::ADT::tensor::Shape &shape)
-{
- if (_shapes.find(node_name) != _shapes.end())
- throw std::runtime_error{"Duplicated node name: " + node_name};
-
- _shapes[node_name] = shape;
-}
-
-const nncc::core::ADT::tensor::Shape *ModelSignature::shape(const std::string &node_name) const
-{
- auto res = _shapes.find(node_name);
- if (res == _shapes.end())
- return nullptr;
- else
- return &res->second;
-}
-
-void ModelSignature::dtype(const std::string &node_name, loco::DataType dtype)
-{
- if (_dtypes.find(node_name) != _dtypes.end())
- throw std::runtime_error{"Duplicated node name: " + node_name};
-
- _dtypes[node_name] = dtype;
-}
-
-loco::DataType ModelSignature::dtype(const std::string &node_name) const
-{
- auto res = _dtypes.find(node_name);
- if (res == _dtypes.end())
- return loco::DataType::Unknown;
- else
- return res->second;
-}
-
Frontend::Frontend()
{
// DO NOTHING
@@ -195,13 +193,9 @@ Frontend::Frontend()
std::unique_ptr<loco::Graph> Frontend::load(const ModelSignature &signature, const char *modelfile,
FileType type) const
{
- tensorflow::GraphDef tf_graph_def;
-
- load_tf(modelfile, type, tf_graph_def);
-
- auto graph = import(signature, tf_graph_def);
-
- return std::move(graph);
+ // Using c++ standard library, rather than file descriptor, makes these lines portable
+ std::ifstream ifs{modelfile, std::ios::in | std::ios::binary};
+ return load(signature, &ifs, type);
}
std::unique_ptr<loco::Graph> Frontend::load(const ModelSignature &signature, std::istream *stream,
@@ -211,25 +205,13 @@ std::unique_ptr<loco::Graph> Frontend::load(const ModelSignature &signature, std
load_tf(stream, type, tf_graph_def);
+ transform_tf(signature, tf_graph_def);
+
auto graph = import(signature, tf_graph_def);
return std::move(graph);
}
-void cleanup(loco::Graph *graph)
-{
- std::vector<std::unique_ptr<moco::tf::Transform>> finalize;
-
- finalize.emplace_back(stdex::make_unique<moco::tf::ClearAnnotTransform>());
- // TODO add more cleanup transformations
-
- // Run finalize to cleanup temporary annotations
- for (auto &tr : finalize)
- {
- tr->run(graph);
- }
-}
-
std::unique_ptr<loco::Graph> Frontend::import(const ModelSignature &signature,
tensorflow::GraphDef &tf_graph_def) const
{
@@ -259,7 +241,7 @@ std::unique_ptr<loco::Graph> Frontend::import(const ModelSignature &signature,
for (uint32_t n = 0; n < graph->inputs()->size(); ++n)
{
auto input = graph->inputs()->at(n);
- auto input_node = loco::pull_node(graph.get(), n);
+ auto input_node = moco::placeholder_node(graph.get(), n);
assert(input_node != nullptr);
input->shape(stdex::make_unique<loco::TensorShape>(tensor_shape(input_node)));
}
@@ -267,9 +249,9 @@ std::unique_ptr<loco::Graph> Frontend::import(const ModelSignature &signature,
for (uint32_t n = 0; n < graph->outputs()->size(); ++n)
{
auto output = graph->outputs()->at(n);
- auto output_node = loco::push_node(graph.get(), n);
+ auto output_node = moco::push_node(graph.get(), n);
assert(output_node != nullptr);
- output->shape(stdex::make_unique<loco::TensorShape>(tensor_shape(output_node)));
+ output->shape(stdex::make_unique<loco::TensorShape>(::tensor_shape(output_node)));
}
// Convert graph to hold only Canonical dialect
diff --git a/compiler/moco-tf/src/Frontend.test.cpp b/compiler/moco-tf/src/Frontend.test.cpp
index 57a9eb7f7..c665bd9e3 100644
--- a/compiler/moco-tf/src/Frontend.test.cpp
+++ b/compiler/moco-tf/src/Frontend.test.cpp
@@ -60,10 +60,11 @@ node {
TEST(FrontendTests, testcase_000)
{
moco::tf::Frontend frontend;
- moco::tf::ModelSignature signature;
+ moco::ModelSignature signature;
- signature.add_input(moco::tf::TensorName("Placeholder", 0));
- signature.add_output(moco::tf::TensorName("Identity", 0));
+ signature.add_input(moco::TensorName("Placeholder", 0));
+ signature.shape("Placeholder:0", angkor::TensorShape{4});
+ signature.add_output(moco::TensorName("Identity", 0));
std::stringstream ss{pbtxt_000};
diff --git a/compiler/moco-tf/src/GraphBuilder.h b/compiler/moco-tf/src/GraphBuilder.h
deleted file mode 100644
index b18bd2716..000000000
--- a/compiler/moco-tf/src/GraphBuilder.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __GRAPH_BUILDER_H__
-#define __GRAPH_BUILDER_H__
-
-#include "GraphBuilderContext.h"
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Interface of convert TF NodeDef to loco::Node (e.g., Conv2DGraphBuilder)
- */
-class GraphBuilder
-{
-public:
- virtual bool validate(const tensorflow::NodeDef &) const = 0;
- virtual void build(const tensorflow::NodeDef &, GraphBuilderContext *) const = 0;
- virtual ~GraphBuilder() {}
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __GRAPH_BUILDER_H__
diff --git a/compiler/moco-tf/src/GraphBuilderContext.cpp b/compiler/moco-tf/src/GraphBuilderContext.cpp
deleted file mode 100644
index 04fb8cd88..000000000
--- a/compiler/moco-tf/src/GraphBuilderContext.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "GraphBuilderContext.h"
-
-#include <stdexcept>
-#include <string>
-
-namespace moco
-{
-namespace tf
-{
-
-void NodeDefTable::enroll(const std::string &node_name, const tensorflow::NodeDef *node)
-{
- MapNameNode_t::iterator iter = _table.find(node_name);
-
- if (iter != _table.end())
- {
- throw std::runtime_error{"Error: Duplicate node name in TensorFlow GraphDef: " + node_name};
- }
-
- _table[node_name] = node;
-}
-
-const tensorflow::NodeDef *NodeDefTable::node(const std::string &node_name) const
-{
- MapNameNode_t::const_iterator iter = _table.find(node_name);
-
- if (iter == _table.end())
- {
- throw std::runtime_error{"Error: Cannot find node with name in TensorFlow GraphDef: " +
- node_name};
- }
-
- return iter->second;
-}
-
-void SymbolTable::enroll(const TensorName &tensor_name, loco::Node *node)
-{
- MapNameNode_t::iterator iter = _table.find(tensor_name);
-
- if (iter != _table.end())
- {
- throw std::runtime_error{"Error: Duplicate node name in Graph: " + tensor_name.name()};
- }
-
- _table[tensor_name] = node;
-}
-
-loco::Node *SymbolTable::node(const TensorName &tensor_name) const
-{
- MapNameNode_t::const_iterator iter = _table.find(tensor_name);
-
- if (iter == _table.end())
- {
- throw std::runtime_error{"Error: Cannot find node with name in Graph: " + tensor_name.name()};
- }
-
- return iter->second;
-}
-
-void UpdateQueue::enroll(std::unique_ptr<GraphUpdate> &&update)
-{
- _queue.push_back(std::move(update));
-}
-
-} // namespace tf
-} // namespace moco
diff --git a/compiler/moco-tf/src/GraphBuilderContext.h b/compiler/moco-tf/src/GraphBuilderContext.h
deleted file mode 100644
index ca474823c..000000000
--- a/compiler/moco-tf/src/GraphBuilderContext.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __GRAPHBUILDERCONTEXT_H__
-#define __GRAPHBUILDERCONTEXT_H__
-
-#include <moco/tf/Names.h>
-
-#include <loco.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Class to store and query tensorflow::NodeDef* with string name key
- */
-class NodeDefTable
-{
-public:
- /**
- * @brief Registers a name with corresponding tensorflow::NodeDef*
- */
- void enroll(const std::string &node_name, const tensorflow::NodeDef *node);
- /**
- * @brief Queries enrolled(registered) with name and return node if found
- * Will throw runtime_error if not found
- */
- const tensorflow::NodeDef *node(const std::string &node_name) const;
-
-private:
- using MapNameNode_t = std::map<std::string, const tensorflow::NodeDef *>;
-
- MapNameNode_t _table;
-};
-
-/**
- * @brief Class to store and query loco::Node* with string name key
- */
-class SymbolTable
-{
-public:
- /**
- * @brief Registers a name with corresponding loco::Node *
- */
- void enroll(const TensorName &tensor_name, loco::Node *node);
- /**
- * @brief Queries enrolled(registered) with name and return node if found
- * Will throw runtime_error if not found
- */
- loco::Node *node(const TensorName &tensor_name) const;
-
-private:
- using MapNameNode_t = std::map<TensorName, loco::Node *, TensorNameCompare>;
-
- MapNameNode_t _table;
-};
-
-/**
- * @brief Interface to connect the graph
- */
-class GraphUpdate
-{
-public:
- virtual ~GraphUpdate() = default;
-
-public:
- /**
- * @brief Do the graph input connections using the SymbolTable
- */
- virtual void input(const SymbolTable *) const = 0;
-};
-
-/**
- * @brief Class to store GraphUpdate objects
- */
-class UpdateQueue final
-{
-public:
- /**
- * @brief Registers GraphUpdate objects
- */
- void enroll(std::unique_ptr<GraphUpdate> &&update);
-
-public:
- using Queue = std::vector<std::unique_ptr<GraphUpdate>>;
-
- const Queue &queue() const { return _queue; }
-
-private:
- Queue _queue;
-};
-
-/**
- * @brief Class to store context to build loco graph IR from TensorFlow
- */
-class GraphBuilderContext
-{
-public:
- GraphBuilderContext(loco::Graph *g, NodeDefTable *nodedef, SymbolTable *tensor_names,
- UpdateQueue *updates)
- : _g(g), _nodedef(nodedef), _tensor_names(tensor_names), _updates(updates)
- {
- // DO NOTHING
- }
-
- GraphBuilderContext(const GraphBuilderContext &) = delete;
- GraphBuilderContext(GraphBuilderContext &&) = delete;
-
-public:
- loco::Graph *graph() { return _g; }
- NodeDefTable *nodedef() { return _nodedef; }
- SymbolTable *tensor_names() { return _tensor_names; }
- UpdateQueue *updates() { return _updates; }
-
-private:
- loco::Graph *_g;
- NodeDefTable *_nodedef;
- SymbolTable *_tensor_names;
- UpdateQueue *_updates;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __GRAPHBUILDERCONTEXT_H__
diff --git a/compiler/moco-tf/src/GraphBuilderContext.test.cpp b/compiler/moco-tf/src/GraphBuilderContext.test.cpp
deleted file mode 100644
index 03993b281..000000000
--- a/compiler/moco-tf/src/GraphBuilderContext.test.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "GraphBuilderContext.h"
-#include <moco/tf/Names.h>
-
-#include <loco.h>
-
-#include <gtest/gtest.h>
-
-TEST(GraphBuilderContext, ctor)
-{
- auto graph = loco::make_graph();
- moco::tf::NodeDefTable nodedef;
- moco::tf::SymbolTable nodes;
- moco::tf::UpdateQueue updates;
-
- moco::tf::GraphBuilderContext context(graph.get(), &nodedef, &nodes, &updates);
-
- ASSERT_EQ(context.graph(), graph.get());
- ASSERT_EQ(context.nodedef(), &nodedef);
- ASSERT_EQ(context.tensor_names(), &nodes);
- ASSERT_EQ(context.updates(), &updates);
-}
-
-TEST(SymbolTable, node_name)
-{
- moco::tf::SymbolTable table;
- loco::Pull pull_node;
- moco::tf::TensorName name("input", 0);
- moco::tf::TensorName invalid("invalid", 0);
-
- table.enroll(name, &pull_node);
- ASSERT_EQ(table.node(name), &pull_node);
- // duplicate name should throw
- EXPECT_THROW(table.enroll(name, &pull_node), std::runtime_error);
- // unregistered name should throw
- EXPECT_THROW(table.node(invalid), std::runtime_error);
-}
-
-namespace
-{
-
-class TestGraphUpdate final : public moco::tf::GraphUpdate
-{
-public:
- void input(const moco::tf::SymbolTable *) const override;
-};
-
-void TestGraphUpdate::input(const moco::tf::SymbolTable *) const {}
-
-} // namespace
-
-TEST(GraphUpdateQueue, queue)
-{
- std::unique_ptr<TestGraphUpdate> update(new TestGraphUpdate());
- moco::tf::UpdateQueue updates;
-
- updates.enroll(std::move(update));
- auto &queue = updates.queue();
- ASSERT_EQ(queue.size(), 1);
-}
diff --git a/compiler/moco-tf/src/GraphBuilderRegistry.h b/compiler/moco-tf/src/GraphBuilderRegistry.h
deleted file mode 100644
index f902ec228..000000000
--- a/compiler/moco-tf/src/GraphBuilderRegistry.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __GRAPH_BUILDER_REGISTRY_H__
-#define __GRAPH_BUILDER_REGISTRY_H__
-
-#include "GraphBuilder.h"
-
-#include <map>
-#include <memory>
-#include <string>
-
-namespace moco
-{
-namespace tf
-{
-
-struct GraphBuilderSource
-{
- virtual ~GraphBuilderSource() = default;
-
- /**
- * @brief Returns registered GraphBuilder pointer for operator (nullptr if not present)
- */
- virtual const GraphBuilder *lookup(const std::string &op) const = 0;
-};
-
-/**
- * @brief Class to return graph builder for TF nodes
- */
-class GraphBuilderRegistry final : public GraphBuilderSource
-{
-public:
- GraphBuilderRegistry() = default;
-
-public:
- GraphBuilderRegistry(const GraphBuilderSource *parent) : _parent{parent}
- {
- // DO NOTHING
- }
-
-public:
- /**
- * @brief Returns registered GraphBuilder pointer for operator or
- * nullptr if not registered
- */
- const GraphBuilder *lookup(const std::string &op) const final
- {
- if (_builder_map.find(op) == _builder_map.end())
- return (_parent == nullptr) ? nullptr : _parent->lookup(op);
-
- return _builder_map.at(op).get();
- }
-
- static GraphBuilderRegistry &get()
- {
- static GraphBuilderRegistry me;
- return me;
- }
-
-public:
- void add(const std::string op, std::unique_ptr<GraphBuilder> &&builder)
- {
- _builder_map[op] = std::move(builder);
- }
-
-private:
- const GraphBuilderSource *_parent = nullptr;
-
-private:
- std::map<const std::string, std::unique_ptr<GraphBuilder>> _builder_map;
-};
-
-} // namespace tf
-} // namespace mono
-
-#include <stdex/Memory.h>
-
-#define REGISTER_OP_BUILDER(NAME, BUILDER) \
- namespace \
- { \
- __attribute__((constructor)) void reg_op(void) \
- { \
- std::unique_ptr<moco::tf::BUILDER> builder = stdex::make_unique<moco::tf::BUILDER>(); \
- moco::tf::GraphBuilderRegistry::get().add(#NAME, std::move(builder)); \
- } \
- }
-
-#endif // __GRAPH_BUILDER_REGISTRY_H__
diff --git a/compiler/moco-tf/src/IR/TFAdd.h b/compiler/moco-tf/src/IR/TFAdd.h
deleted file mode 100644
index d2489fb5c..000000000
--- a/compiler/moco-tf/src/IR/TFAdd.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFADD_H__
-#define __MOCO_TF_IR_TFADD_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note TFAdd corresponds to the following GraphDef
-/*
-node {
- name: "add"
- op: "Add"
- input: "x"
- input: "y"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-*/
-
-class TFAdd final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Add>>
-{
-public:
- TFAdd() = default;
-
-public:
- Node *x(void) const { return at(0)->node(); }
- void x(Node *node) { at(0)->node(node); }
-
- Node *y(void) const { return at(1)->node(); }
- void y(Node *node) { at(1)->node(node); }
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFADD_H__
diff --git a/compiler/moco-tf/src/IR/TFAdd.test.cpp b/compiler/moco-tf/src/IR/TFAdd.test.cpp
deleted file mode 100644
index 3134f8610..000000000
--- a/compiler/moco-tf/src/IR/TFAdd.test.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFAdd.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFAddTest, constructor)
-{
- moco::tf::TFAdd add_node;
-
- ASSERT_EQ(add_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(add_node.opcode(), moco::tf::TFOpcode::Add);
-
- ASSERT_EQ(add_node.x(), nullptr);
- ASSERT_EQ(add_node.y(), nullptr);
-}
diff --git a/compiler/moco-tf/src/IR/TFAvgPool.h b/compiler/moco-tf/src/IR/TFAvgPool.h
deleted file mode 100644
index 93a72bb30..000000000
--- a/compiler/moco-tf/src/IR/TFAvgPool.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFAVGPOOL_H__
-#define __MOCO_TF_IR_TFAVGPOOL_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-#include <vector>
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note TFAvgPool corresponds to the following GraphDef
-/*
-node {
- name: "avgpool"
- op: "AvgPool"
- input: "placeholder"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "data_format"
- value {
- s: "NHWC"
- }
- }
- attr {
- key: "ksize"
- value {
- list {
- i: 1 i: 3 i: 3 i: 1
- }
- }
- }
- attr {
- key: "padding"
- value {
- s: "SAME"
- }
- }
- attr {
- key: "strides"
- value {
- list {
- i: 1 i: 1 i: 1 i: 1
- }
- }
- }
-}
-*/
-
-class TFAvgPool final : public FixedArityNode<1, TFNodeImpl<TFOpcode::AvgPool>>
-{
-public:
- TFAvgPool() = default;
-
-public:
- Node *value(void) const { return at(0)->node(); }
- void value(Node *node) { return at(0)->node(node); }
-
-public:
- const TFDataLayout &data_layout(void) const { return _data_layout; }
- void data_layout(const TFDataLayout &data_layout) { _data_layout = data_layout; }
-
- const TFPadding &padding(void) const { return _padding; }
- void padding(const TFPadding &padding) { _padding = padding; }
-
- const std::vector<int64_t> &ksize(void) const { return _ksize; }
- void ksize(const std::vector<int64_t> &ksize) { _ksize = ksize; }
-
- const std::vector<int64_t> &strides(void) const { return _strides; }
- void strides(const std::vector<int64_t> &strides) { _strides = strides; }
-
-private:
- TFDataLayout _data_layout;
- TFPadding _padding;
- std::vector<int64_t> _ksize;
- std::vector<int64_t> _strides;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFAVGPOOL_H__
diff --git a/compiler/moco-tf/src/IR/TFAvgPool.test.cpp b/compiler/moco-tf/src/IR/TFAvgPool.test.cpp
deleted file mode 100644
index 1e17122fd..000000000
--- a/compiler/moco-tf/src/IR/TFAvgPool.test.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFAvgPool.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFAvgPoolTest, constructor)
-{
- moco::tf::TFAvgPool avgpool;
-
- ASSERT_EQ(avgpool.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(avgpool.opcode(), moco::tf::TFOpcode::AvgPool);
-
- ASSERT_EQ(avgpool.value(), nullptr);
- ASSERT_EQ(avgpool.data_layout(), "");
- ASSERT_EQ(avgpool.padding(), "");
- ASSERT_EQ(avgpool.ksize(), std::vector<int64_t>({}));
- ASSERT_EQ(avgpool.strides(), std::vector<int64_t>({}));
-}
diff --git a/compiler/moco-tf/src/IR/TFBiasAdd.h b/compiler/moco-tf/src/IR/TFBiasAdd.h
deleted file mode 100644
index 468e02dad..000000000
--- a/compiler/moco-tf/src/IR/TFBiasAdd.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFBIASADD_H__
-#define __MOCO_TF_IR_TFBIASADD_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note TFBiasAdd corresponds to the following GraphDef
-/*
-node {
- name: "bias_add_01"
- op: "BiasAdd"
- input: "input_01"
- input: "bias_add_01/bias"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "data_format"
- value {
- s: "NHWC"
- }
- }
-}
-*/
-
-class TFBiasAdd final : public FixedArityNode<2, TFNodeImpl<TFOpcode::BiasAdd>>
-{
-public:
- TFBiasAdd() = default;
-
-public:
- Node *value(void) const { return at(0)->node(); }
- void value(Node *node) { return at(0)->node(node); }
-
- Node *bias(void) const { return at(1)->node(); }
- void bias(Node *node) { return at(1)->node(node); }
-
- const TFDataLayout data_layout(void) const { return _data_layout; }
- void data_layout(const TFDataLayout &data_layout) { _data_layout = data_layout; }
-
-private:
- TFDataLayout _data_layout;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFBIASADD_H__
diff --git a/compiler/moco-tf/src/IR/TFBiasAdd.test.cpp b/compiler/moco-tf/src/IR/TFBiasAdd.test.cpp
deleted file mode 100644
index 8fc1cdd6e..000000000
--- a/compiler/moco-tf/src/IR/TFBiasAdd.test.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFBiasAdd.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFBiasAddTest, constructor)
-{
- moco::tf::TFBiasAdd bias_add;
-
- ASSERT_EQ(bias_add.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(bias_add.opcode(), moco::tf::TFOpcode::BiasAdd);
-
- ASSERT_EQ(bias_add.value(), nullptr);
- ASSERT_EQ(bias_add.bias(), nullptr);
- ASSERT_EQ(bias_add.data_layout(), "");
-}
diff --git a/compiler/moco-tf/src/IR/TFConcatV2.h b/compiler/moco-tf/src/IR/TFConcatV2.h
deleted file mode 100644
index 1db44cdb0..000000000
--- a/compiler/moco-tf/src/IR/TFConcatV2.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFCONCATV2_H__
-#define __MOCO_TF_IR_TFCONCATV2_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-#include "Dialect/VariadicArityNode.h"
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note TFConcatV2 corresponds to the following GraphDef
-/*
-node {
- name: "Concat"
- op: "ConcatV2"
- input: "Input01"
- input: "Input02"
- input: "Axis"
- attr {
- key: "N"
- value {
- i: 2
- }
- }
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "Tidx"
- value {
- type: DT_INT32
- }
- }
-}
-*/
-
-class TFConcatV2 final : public VariadicArityNode<TFNodeImpl<TFOpcode::ConcatV2>>
-{
-public:
- TFConcatV2(uint32_t arity) : VariadicArityNode<TFNodeImpl<TFOpcode::ConcatV2>>(arity + 1)
- {
- // we add +1 for axis of VariadicArityNode ctor
- // at least one value is required
- assert(arity >= 1);
- }
-
-public:
- uint32_t num_values(void) const
- {
- // last one is for axis
- return arity() - 1;
- }
-
-public:
- Node *values(uint32_t index) const
- {
- assert(index < num_values());
- return at(index)->node();
- }
- void values(uint32_t index, Node *node)
- {
- assert(index < num_values());
- at(index)->node(node);
- }
-
- Node *axis(void) const { return at(num_values())->node(); }
- void axis(Node *node) { at(num_values())->node(node); }
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFCONCATV2_H__
diff --git a/compiler/moco-tf/src/IR/TFConcatV2.test.cpp b/compiler/moco-tf/src/IR/TFConcatV2.test.cpp
deleted file mode 100644
index 89eac1bce..000000000
--- a/compiler/moco-tf/src/IR/TFConcatV2.test.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFConcatV2.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFConcatV2Test, constructor)
-{
- moco::tf::TFConcatV2 concatv2_node(3); // num of values
-
- ASSERT_EQ(concatv2_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(concatv2_node.opcode(), moco::tf::TFOpcode::ConcatV2);
-
- ASSERT_EQ(concatv2_node.num_values(), 3);
- ASSERT_EQ(concatv2_node.values(0), nullptr);
- ASSERT_EQ(concatv2_node.values(1), nullptr);
- ASSERT_EQ(concatv2_node.values(2), nullptr);
- ASSERT_EQ(concatv2_node.axis(), nullptr);
-}
diff --git a/compiler/moco-tf/src/IR/TFConst.cpp b/compiler/moco-tf/src/IR/TFConst.cpp
deleted file mode 100644
index e59e6644a..000000000
--- a/compiler/moco-tf/src/IR/TFConst.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFConst.h"
-
-#include <cassert>
-
-namespace moco
-{
-namespace tf
-{
-
-template <loco::DataType DT> uint32_t TFConst::size(void) const
-{
- assert(dtype() == DT);
- assert(_data.size() % sizeof(typename loco::DataTypeImpl<DT>::Type) == 0);
- return _data.size() / sizeof(typename loco::DataTypeImpl<DT>::Type);
-}
-
-template <loco::DataType DT> void TFConst::size(uint32_t l)
-{
- assert(dtype() == DT);
- _data.resize(l * sizeof(typename loco::DataTypeImpl<DT>::Type));
-}
-
-template <loco::DataType DT>
-const typename loco::DataTypeImpl<DT>::Type &TFConst::at(uint32_t n) const
-{
- assert(dtype() == DT);
- assert(n < size<DT>());
- return *(reinterpret_cast<const typename loco::DataTypeImpl<DT>::Type *>(_data.data()) + n);
-}
-
-template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &TFConst::at(uint32_t n)
-{
- assert(dtype() == DT);
- assert(n < size<DT>());
- return *(reinterpret_cast<typename loco::DataTypeImpl<DT>::Type *>(_data.data()) + n);
-}
-
-#define INSTANTIATE(DT) \
- template uint32_t TFConst::size<DT>(void) const; \
- template void TFConst::size<DT>(uint32_t); \
- template const typename loco::DataTypeImpl<DT>::Type &TFConst::at<DT>(uint32_t) const; \
- template typename loco::DataTypeImpl<DT>::Type &TFConst::at<DT>(uint32_t);
-
-INSTANTIATE(loco::DataType::S32);
-INSTANTIATE(loco::DataType::FLOAT32);
-
-#undef INSTANTIATE
-
-} // namespace tf
-} // namespace moco
diff --git a/compiler/moco-tf/src/IR/TFConst.h b/compiler/moco-tf/src/IR/TFConst.h
deleted file mode 100644
index b63d37db7..000000000
--- a/compiler/moco-tf/src/IR/TFConst.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFCONSTANT_H__
-#define __MOCO_TF_IR_TFCONSTANT_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-#include <loco/IR/DataTypeTraits.h>
-#include <loco/IR/NodeMixins.h>
-
-#include <vector>
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note TFConst corresponds to the following GraphDef
-/*
-node {
- name: "val"
- op: "Const"
- attr {
- key: "dtype"
- value { type: DT_FLOAT }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim { size: 1 }
- dim { size: 3 }
- dim { size: 4 }
- dim { size: 4 }
- }
- float_val: 2.1
- }
- }
- }
-}
-*/
-
-/**
- * @brief IR for tf.constant
- *
- * @note Implementation for this class came from Canonical ConstGen
- * Read comments in loco::ConstGen for details
- */
-class TFConst final : public FixedArityNode<0, TFNodeImpl<TFOpcode::Const>>,
- public loco::NodeMixin<loco::NodeTrait::DataType>,
- public loco::NodeMixin<loco::NodeTrait::TensorShape>
-{
-public:
- TFConst() = default;
-
-public:
- template <loco::DataType DT> uint32_t size(void) const;
- template <loco::DataType DT> void size(uint32_t size);
-
- template <loco::DataType DT> const typename loco::DataTypeImpl<DT>::Type &at(uint32_t n) const;
- template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &at(uint32_t n);
-
-private:
- std::vector<uint8_t> _data;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFCONSTANT_H__
diff --git a/compiler/moco-tf/src/IR/TFConst.test.cpp b/compiler/moco-tf/src/IR/TFConst.test.cpp
deleted file mode 100644
index 6963122d8..000000000
--- a/compiler/moco-tf/src/IR/TFConst.test.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFConst.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFConstantTest, constructor)
-{
- moco::tf::TFConst constant;
-
- ASSERT_EQ(constant.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(constant.opcode(), moco::tf::TFOpcode::Const);
-
- ASSERT_EQ(constant.dtype(), loco::DataType::Unknown);
- ASSERT_EQ(constant.rank(), 0);
-
- constant.dtype(loco::DataType::FLOAT32);
- ASSERT_EQ(constant.dtype(), loco::DataType::FLOAT32);
-
- constant.rank(2);
- ASSERT_EQ(constant.rank(), 2);
-
- constant.dim(0) = 2;
- constant.dim(1) = 3;
-
- ASSERT_TRUE(constant.dim(0).known());
- ASSERT_TRUE(constant.dim(1).known());
-
- ASSERT_EQ(constant.dim(0), 2);
- ASSERT_EQ(constant.dim(1), 3);
-
- constant.size<loco::DataType::FLOAT32>(6);
-
- ASSERT_EQ(constant.size<loco::DataType::FLOAT32>(), 6);
-
- constant.at<loco::DataType::FLOAT32>(0) = 0.0f; // Set 0,0
- constant.at<loco::DataType::FLOAT32>(1) = 1.0f; // Set 0,1
- constant.at<loco::DataType::FLOAT32>(2) = 2.0f; // Set 0,2
- constant.at<loco::DataType::FLOAT32>(3) = 3.0f; // Set 1,0
- constant.at<loco::DataType::FLOAT32>(4) = 4.0f; // Set 1,1
- constant.at<loco::DataType::FLOAT32>(5) = 5.0f; // Set 1,2
-
- ASSERT_EQ(constant.at<loco::DataType::FLOAT32>(0), 0.0f);
- ASSERT_EQ(constant.at<loco::DataType::FLOAT32>(1), 1.0f);
- ASSERT_EQ(constant.at<loco::DataType::FLOAT32>(2), 2.0f);
- ASSERT_EQ(constant.at<loco::DataType::FLOAT32>(3), 3.0f);
- ASSERT_EQ(constant.at<loco::DataType::FLOAT32>(4), 4.0f);
- ASSERT_EQ(constant.at<loco::DataType::FLOAT32>(5), 5.0f);
-}
diff --git a/compiler/moco-tf/src/IR/TFConv2D.h b/compiler/moco-tf/src/IR/TFConv2D.h
deleted file mode 100644
index f9a9a1218..000000000
--- a/compiler/moco-tf/src/IR/TFConv2D.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFCONV2D_H__
-#define __MOCO_TF_IR_TFCONV2D_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-#include <vector>
-
-namespace moco
-{
-namespace tf
-{
-
-class TFConv2D final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Conv2D>>
-{
-public:
- loco::Node *input(void) const { return at(0)->node(); }
- void input(Node *node) { at(0)->node(node); }
-
- loco::Node *filter(void) const { return at(1)->node(); }
- void filter(Node *node) { at(1)->node(node); }
-
-public:
- const TFPadding &padding(void) const { return _padding; }
- void padding(const TFPadding &padding) { _padding = padding; }
-
- const TFDataLayout &data_layout(void) const { return _data_layout; }
- void data_layout(const TFDataLayout &data_layout) { _data_layout = data_layout; }
-
- const std::vector<int64_t> &strides(void) const { return _strides; }
- void strides(const std::vector<int64_t> &strides) { _strides = strides; }
-
-private:
- TFPadding _padding;
- TFDataLayout _data_layout;
- std::vector<int64_t> _strides;
- // TODO Support "Dilation"
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFCONV2D_H__
diff --git a/compiler/moco-tf/src/IR/TFConv2D.test.cpp b/compiler/moco-tf/src/IR/TFConv2D.test.cpp
deleted file mode 100644
index e1fa4ed6d..000000000
--- a/compiler/moco-tf/src/IR/TFConv2D.test.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFConv2D.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFConv2DTest, constructor)
-{
- moco::tf::TFConv2D conv2d_node;
-
- ASSERT_EQ(conv2d_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(conv2d_node.opcode(), moco::tf::TFOpcode::Conv2D);
-
- ASSERT_EQ(conv2d_node.input(), nullptr);
- ASSERT_EQ(conv2d_node.filter(), nullptr);
- ASSERT_EQ(conv2d_node.padding(), "");
- ASSERT_EQ(conv2d_node.data_layout(), "");
- ASSERT_EQ(conv2d_node.strides().size(), 0);
-}
diff --git a/compiler/moco-tf/src/IR/TFConv2DBackpropInput.h b/compiler/moco-tf/src/IR/TFConv2DBackpropInput.h
deleted file mode 100644
index af78d6abd..000000000
--- a/compiler/moco-tf/src/IR/TFConv2DBackpropInput.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFCONV2DBACKPROPINPUT_H__
-#define __MOCO_TF_IR_TFCONV2DBACKPROPINPUT_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-#include <vector>
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note TFConv2DBackpropInput corresponds to the following GraphDef
-/*
-node {
- name: "conv2d_backprop_input"
- op: "Conv2DBackpropInput"
- input: "input_sizes"
- input: "filter"
- input: "out_backprop"
- attr {
- key: "T"
- value { type: DT_FLOAT }
- }
- attr {
- key: "data_format"
- value { s: "NHWC" }
- }
- attr {
- key: "dilations"
- value {
- list { i: 1 i: 1 i: 1 i: 1 }
- }
- }
- attr {
- key: "padding"
- value { s: "SAME" }
- }
- attr {
- key: "strides"
- value {
- list { i: 1 i: 2 i: 2 i: 1 }
- }
- }
-}
-*/
-
-/**
- * @note For Tensorflow Conv2DBackpropInput, 'input' refers actual output of the
- * node, and 'input' refers actual input. The reasone of this is, as name
- * suggests, because it is inspired from backpropagation of convolution.
- * For example, 'out_backprop' of Conv2DBackpropInput is its actual input
- * feature map, and 'input_sizes' means desired output node's size.
- * Note that this convention is against loco canonical's convention.
- */
-class TFConv2DBackpropInput final
- : public FixedArityNode<3, TFNodeImpl<TFOpcode::Conv2DBackpropInput>>
-{
-public:
- loco::Node *input_sizes(void) const { return at(0)->node(); }
- void input_sizes(Node *node) { at(0)->node(node); }
-
- loco::Node *filter(void) const { return at(1)->node(); }
- void filter(Node *node) { at(1)->node(node); }
-
- loco::Node *out_backprop(void) const { return at(2)->node(); }
- void out_backprop(Node *node) { at(2)->node(node); }
-
-public:
- const TFPadding &padding(void) const { return _padding; }
- void padding(const TFPadding &padding) { _padding = padding; }
-
- const TFDataLayout &data_layout(void) const { return _data_layout; }
- void data_layout(const TFDataLayout &data_layout) { _data_layout = data_layout; }
-
- const std::vector<int64_t> &strides(void) const { return _strides; }
- void strides(const std::vector<int64_t> &strides) { _strides = strides; }
-
-private:
- TFPadding _padding;
- TFDataLayout _data_layout;
- std::vector<int64_t> _strides;
- // TODO Support "Dilation"
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFCONV2DBACKPROPINPUT_H__
diff --git a/compiler/moco-tf/src/IR/TFDepthwiseConv2dNative.h b/compiler/moco-tf/src/IR/TFDepthwiseConv2dNative.h
deleted file mode 100644
index 9ffc79281..000000000
--- a/compiler/moco-tf/src/IR/TFDepthwiseConv2dNative.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFDEPTHWISECONV2DNATIVE_H__
-#define __MOCO_TF_IR_TFDEPTHWISECONV2DNATIVE_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-#include "Convert.h"
-
-#include <loco/IR/Stride.h>
-#include <loco/IR/Padding2D.h>
-
-#include <string>
-#include <vector>
-
-namespace moco
-{
-namespace tf
-{
-
-class TFDepthwiseConv2dNative final
- : public FixedArityNode<2, TFNodeImpl<TFOpcode::DepthwiseConv2dNative>>
-{
-public:
- loco::Node *input(void) const { return at(0)->node(); }
- void input(Node *node) { at(0)->node(node); }
-
- loco::Node *filter(void) const { return at(1)->node(); }
- void filter(Node *node) { at(1)->node(node); }
-
-public:
- const TFPadding &padding(void) const { return _padding; }
- void padding(const TFPadding &padding) { _padding = padding; }
-
- const TFDataLayout &data_layout(void) const { return _data_layout; }
- void data_layout(const TFDataLayout &data_layout) { _data_layout = data_layout; }
-
- const std::vector<int64_t> &strides(void) const { return _strides; }
- void strides(const std::vector<int64_t> &strides) { _strides = strides; }
-
-private:
- TFPadding _padding;
- TFDataLayout _data_layout;
- std::vector<int64_t> _strides;
- // TODO Support "Dilation"
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFDEPTHWISECONV2DNATIVE_H__
diff --git a/compiler/moco-tf/src/IR/TFDepthwiseConv2dNative.test.cpp b/compiler/moco-tf/src/IR/TFDepthwiseConv2dNative.test.cpp
deleted file mode 100644
index 086145635..000000000
--- a/compiler/moco-tf/src/IR/TFDepthwiseConv2dNative.test.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFDepthwiseConv2dNative.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFDepthwiseConv2dNativeTest, constructor)
-{
- moco::tf::TFDepthwiseConv2dNative depthwiseConv2dnative_node;
-
- ASSERT_EQ(depthwiseConv2dnative_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(depthwiseConv2dnative_node.opcode(), moco::tf::TFOpcode::DepthwiseConv2dNative);
-
- ASSERT_EQ(depthwiseConv2dnative_node.input(), nullptr);
- ASSERT_EQ(depthwiseConv2dnative_node.filter(), nullptr);
- ASSERT_EQ(depthwiseConv2dnative_node.padding(), "");
- ASSERT_EQ(depthwiseConv2dnative_node.data_layout(), "");
- ASSERT_EQ(depthwiseConv2dnative_node.strides().size(), 0);
-}
diff --git a/compiler/moco-tf/src/IR/TFFusedBatchNorm.h b/compiler/moco-tf/src/IR/TFFusedBatchNorm.h
deleted file mode 100644
index 297f439a1..000000000
--- a/compiler/moco-tf/src/IR/TFFusedBatchNorm.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFFUSEDBATCHNORM_H__
-#define __MOCO_TF_IR_TFFUSEDBATCHNORM_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-namespace moco
-{
-namespace tf
-{
-
-class TFFusedBatchNorm final : public FixedArityNode<5, TFNodeImpl<TFOpcode::FusedBatchNorm>>
-{
-public:
- TFFusedBatchNorm() = default;
-
-public:
- Node *input(void) const { return at(0)->node(); }
- void input(Node *node) { at(0)->node(node); }
-
- Node *gamma(void) const { return at(1)->node(); }
- void gamma(Node *node) { at(1)->node(node); }
-
- Node *beta(void) const { return at(2)->node(); }
- void beta(Node *node) { at(2)->node(node); }
-
- Node *mean(void) const { return at(3)->node(); }
- void mean(Node *node) { at(3)->node(node); }
-
- Node *variance(void) const { return at(4)->node(); }
- void variance(Node *node) { at(4)->node(node); }
-
- float epsilon(void) const { return _epsilon; }
- void epsilon(float epsilon) { _epsilon = epsilon; }
-
-private:
- float _epsilon = 0.001f;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFFUSEDBATCHNORM_H__
diff --git a/compiler/moco-tf/src/IR/TFFusedBatchNorm.test.cpp b/compiler/moco-tf/src/IR/TFFusedBatchNorm.test.cpp
deleted file mode 100644
index 38db8cf33..000000000
--- a/compiler/moco-tf/src/IR/TFFusedBatchNorm.test.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFFusedBatchNorm.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFFusedBatchNormTest, constructor)
-{
- moco::tf::TFFusedBatchNorm fbn_node;
-
- ASSERT_EQ(fbn_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(fbn_node.opcode(), moco::tf::TFOpcode::FusedBatchNorm);
-
- ASSERT_EQ(fbn_node.input(), nullptr);
- ASSERT_EQ(fbn_node.gamma(), nullptr);
- ASSERT_EQ(fbn_node.beta(), nullptr);
- ASSERT_EQ(fbn_node.mean(), nullptr);
- ASSERT_EQ(fbn_node.variance(), nullptr);
- ASSERT_NE(fbn_node.epsilon(), 0.0f);
-}
diff --git a/compiler/moco-tf/src/IR/TFIdentity.h b/compiler/moco-tf/src/IR/TFIdentity.h
deleted file mode 100644
index 9eeab8d11..000000000
--- a/compiler/moco-tf/src/IR/TFIdentity.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFIDENTITY_H__
-#define __MOCO_TF_IR_TFIDENTITY_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note TFIdentity corresponds to the following GraphDef
-/*
-node {
- name: "identity"
- op: "Identity"
- input: "Placeholder"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-*/
-
-class TFIdentity final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Identity>>
-{
-public:
- TFIdentity() = default;
-
-public:
- Node *input(void) const { return at(0)->node(); }
- void input(Node *node) { at(0)->node(node); }
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFIDENTITY_H__
diff --git a/compiler/moco-tf/src/IR/TFIdentity.test.cpp b/compiler/moco-tf/src/IR/TFIdentity.test.cpp
deleted file mode 100644
index 4ea3e7acf..000000000
--- a/compiler/moco-tf/src/IR/TFIdentity.test.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFIdentity.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFIdentituTest, constructor)
-{
- moco::tf::TFIdentity identity_node;
-
- ASSERT_EQ(identity_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(identity_node.opcode(), moco::tf::TFOpcode::Identity);
-
- ASSERT_EQ(identity_node.input(), nullptr);
-}
diff --git a/compiler/moco-tf/src/IR/TFMaxPool.h b/compiler/moco-tf/src/IR/TFMaxPool.h
deleted file mode 100644
index 14dae7009..000000000
--- a/compiler/moco-tf/src/IR/TFMaxPool.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFMAXPOOL_H__
-#define __MOCO_TF_IR_TFMAXPOOL_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-#include <vector>
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note TFMaxPool corresponds to the following GraphDef
-/*
-node {
- name: "maxpool2d"
- op: "MaxPool"
- input: "placeholder"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "data_format"
- value {
- s: "NHWC"
- }
- }
- attr {
- key: "ksize"
- value {
- list {
- i: 1 i: 2 i: 2 i: 1
- }
- }
- }
- attr {
- key: "padding"
- value {
- s: "VALID"
- }
- }
- attr {
- key: "strides"
- value {
- list {
- i: 1 i: 1 i: 1 i: 1
- }
- }
- }
-}
-*/
-
-class TFMaxPool final : public FixedArityNode<1, TFNodeImpl<TFOpcode::MaxPool>>
-{
-public:
- TFMaxPool() = default;
-
-public:
- Node *value(void) const { return at(0)->node(); }
- void value(Node *node) { return at(0)->node(node); }
-
-public:
- const TFDataLayout &data_layout(void) const { return _data_layout; }
- void data_layout(const TFDataLayout &data_layout) { _data_layout = data_layout; }
-
- const TFPadding &padding(void) const { return _padding; }
- void padding(const TFPadding &padding) { _padding = padding; }
-
- const std::vector<int64_t> &ksize(void) const { return _ksize; }
- void ksize(const std::vector<int64_t> &ksize) { _ksize = ksize; }
-
- const std::vector<int64_t> &strides(void) const { return _strides; }
- void strides(const std::vector<int64_t> &strides) { _strides = strides; }
-
-private:
- TFDataLayout _data_layout;
- TFPadding _padding;
- std::vector<int64_t> _ksize;
- std::vector<int64_t> _strides;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFMAXPOOL_H__
diff --git a/compiler/moco-tf/src/IR/TFMaxPool.test.cpp b/compiler/moco-tf/src/IR/TFMaxPool.test.cpp
deleted file mode 100644
index b86e21eab..000000000
--- a/compiler/moco-tf/src/IR/TFMaxPool.test.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFMaxPool.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFMaxPoolTest, constructor)
-{
- moco::tf::TFMaxPool maxpool;
-
- ASSERT_EQ(maxpool.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(maxpool.opcode(), moco::tf::TFOpcode::MaxPool);
-
- ASSERT_EQ(maxpool.value(), nullptr);
- ASSERT_EQ(maxpool.data_layout(), "");
- ASSERT_EQ(maxpool.padding(), "");
- ASSERT_EQ(maxpool.ksize(), std::vector<int64_t>({}));
- ASSERT_EQ(maxpool.strides(), std::vector<int64_t>({}));
-}
diff --git a/compiler/moco-tf/src/IR/TFMean.h b/compiler/moco-tf/src/IR/TFMean.h
deleted file mode 100644
index 508887bd0..000000000
--- a/compiler/moco-tf/src/IR/TFMean.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFMEAN_H__
-#define __MOCO_TF_IR_TFMEAN_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-#include <vector>
-
-namespace moco
-{
-namespace tf
-{
-
-class TFMean final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Mean>>
-{
-public:
- TFMean() = default;
-
-public:
- Node *input(void) const { return at(0)->node(); }
- void input(Node *node) { at(0)->node(node); }
-
- Node *reduction_indices(void) const { return at(1)->node(); }
- void reduction_indices(Node *node) { at(1)->node(node); }
-
-public:
- bool keep_dims(void) const { return _keep_dims; }
- void keep_dims(bool keep_dims) { _keep_dims = keep_dims; }
-
-private:
- bool _keep_dims = false;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFMEAN_H__
diff --git a/compiler/moco-tf/src/IR/TFMean.test.cpp b/compiler/moco-tf/src/IR/TFMean.test.cpp
deleted file mode 100644
index 3c580c08e..000000000
--- a/compiler/moco-tf/src/IR/TFMean.test.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFMean.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFMeanTest, constructor)
-{
- moco::tf::TFMean mean_node;
-
- ASSERT_EQ(mean_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(mean_node.opcode(), moco::tf::TFOpcode::Mean);
-
- ASSERT_EQ(mean_node.input(), nullptr);
- ASSERT_EQ(mean_node.reduction_indices(), nullptr);
- ASSERT_EQ(mean_node.keep_dims(), false);
-}
diff --git a/compiler/moco-tf/src/IR/TFMul.h b/compiler/moco-tf/src/IR/TFMul.h
deleted file mode 100644
index 95826f05a..000000000
--- a/compiler/moco-tf/src/IR/TFMul.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFMUL_H__
-#define __MOCO_TF_IR_TFMUL_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note TFMul corresponds to the following GraphDef
-/*
-node {
- name: "mul"
- op: "Mul"
- input: "x"
- input: "y"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-*/
-
-class TFMul final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Mul>>
-{
-public:
- TFMul() = default;
-
-public:
- Node *x(void) const { return at(0)->node(); }
- void x(Node *node) { at(0)->node(node); }
-
- Node *y(void) const { return at(1)->node(); }
- void y(Node *node) { at(1)->node(node); }
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFMUL_H__
diff --git a/compiler/moco-tf/src/IR/TFMul.test.cpp b/compiler/moco-tf/src/IR/TFMul.test.cpp
deleted file mode 100644
index cc7c5880b..000000000
--- a/compiler/moco-tf/src/IR/TFMul.test.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFMul.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFMulTest, constructor)
-{
- moco::tf::TFMul mul_node;
-
- ASSERT_EQ(mul_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(mul_node.opcode(), moco::tf::TFOpcode::Mul);
-
- ASSERT_EQ(mul_node.x(), nullptr);
- ASSERT_EQ(mul_node.y(), nullptr);
-}
diff --git a/compiler/moco-tf/src/IR/TFRealDiv.h b/compiler/moco-tf/src/IR/TFRealDiv.h
deleted file mode 100644
index 8ef37861a..000000000
--- a/compiler/moco-tf/src/IR/TFRealDiv.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFREALDIV_H__
-#define __MOCO_TF_IR_TFREALDIV_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note TFRealDiv corresponds to the following GraphDef
-/*
-node {
- name: "div"
- op: "RealDiv"
- input: "x"
- input: "y"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-*/
-
-class TFRealDiv final : public FixedArityNode<2, TFNodeImpl<TFOpcode::RealDiv>>
-{
-public:
- TFRealDiv() = default;
-
-public:
- Node *x(void) const { return at(0)->node(); }
- void x(Node *node) { at(0)->node(node); }
-
- Node *y(void) const { return at(1)->node(); }
- void y(Node *node) { at(1)->node(node); }
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFREALDIV_H__
diff --git a/compiler/moco-tf/src/IR/TFRealDiv.test.cpp b/compiler/moco-tf/src/IR/TFRealDiv.test.cpp
deleted file mode 100644
index 1c7029f87..000000000
--- a/compiler/moco-tf/src/IR/TFRealDiv.test.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFRealDiv.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFRealDivTest, constructor)
-{
- moco::tf::TFRealDiv div_node;
-
- ASSERT_EQ(div_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(div_node.opcode(), moco::tf::TFOpcode::RealDiv);
-
- ASSERT_EQ(div_node.x(), nullptr);
- ASSERT_EQ(div_node.y(), nullptr);
-}
diff --git a/compiler/moco-tf/src/IR/TFRelu.h b/compiler/moco-tf/src/IR/TFRelu.h
deleted file mode 100644
index 7df958b11..000000000
--- a/compiler/moco-tf/src/IR/TFRelu.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFRELU_H__
-#define __MOCO_TF_IR_TFRELU_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-namespace moco
-{
-namespace tf
-{
-
-class TFRelu final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Relu>>
-{
-public:
- TFRelu() = default;
-
-public:
- Node *features(void) const { return at(0)->node(); }
- void features(Node *node) { at(0)->node(node); }
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFRELU_H__
diff --git a/compiler/moco-tf/src/IR/TFRelu.test.cpp b/compiler/moco-tf/src/IR/TFRelu.test.cpp
deleted file mode 100644
index 776207966..000000000
--- a/compiler/moco-tf/src/IR/TFRelu.test.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFRelu.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFReluTest, constructor)
-{
- moco::tf::TFRelu relu_node;
-
- ASSERT_EQ(relu_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(relu_node.opcode(), moco::tf::TFOpcode::Relu);
-
- ASSERT_EQ(relu_node.features(), nullptr);
-}
diff --git a/compiler/moco-tf/src/IR/TFRelu6.h b/compiler/moco-tf/src/IR/TFRelu6.h
deleted file mode 100644
index eba83a9f7..000000000
--- a/compiler/moco-tf/src/IR/TFRelu6.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFRELU6_H__
-#define __MOCO_TF_IR_TFRELU6_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-namespace moco
-{
-namespace tf
-{
-
-class TFRelu6 final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Relu6>>
-{
-public:
- TFRelu6() = default;
-
-public:
- Node *features(void) const { return at(0)->node(); }
- void features(Node *node) { at(0)->node(node); }
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFRELU6_H__
diff --git a/compiler/moco-tf/src/IR/TFRelu6.test.cpp b/compiler/moco-tf/src/IR/TFRelu6.test.cpp
deleted file mode 100644
index d342ccd5d..000000000
--- a/compiler/moco-tf/src/IR/TFRelu6.test.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFRelu6.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFRelu6Test, constructor)
-{
- moco::tf::TFRelu6 relu6_node;
-
- ASSERT_EQ(relu6_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(relu6_node.opcode(), moco::tf::TFOpcode::Relu6);
-
- ASSERT_EQ(relu6_node.features(), nullptr);
-}
diff --git a/compiler/moco-tf/src/IR/TFReshape.h b/compiler/moco-tf/src/IR/TFReshape.h
deleted file mode 100644
index 4359a49b5..000000000
--- a/compiler/moco-tf/src/IR/TFReshape.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFRESHAPE_H__
-#define __MOCO_TF_IR_TFRESHAPE_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note TFReshape corresponds to the following GraphDef
-/*
-node {
- name: "reshape"
- op: "Reshape"
- input: "tensor"
- input: "shape"
- attr {
- key: "T"
- value { type: DT_FLOAT }
- }
-}
-*/
-
-class TFReshape final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Reshape>>
-{
-public:
- TFReshape() = default;
-
-public:
- Node *tensor(void) const { return at(0)->node(); }
- void tensor(Node *node) { at(0)->node(node); }
-
- Node *shape(void) const { return at(1)->node(); }
- void shape(Node *node) { at(1)->node(node); }
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFRESHAPE_H__
diff --git a/compiler/moco-tf/src/IR/TFReshape.test.cpp b/compiler/moco-tf/src/IR/TFReshape.test.cpp
deleted file mode 100644
index 39d77e4b1..000000000
--- a/compiler/moco-tf/src/IR/TFReshape.test.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFReshape.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFReshapeTest, constructor)
-{
- moco::tf::TFReshape reshape_node;
-
- ASSERT_EQ(reshape_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(reshape_node.opcode(), moco::tf::TFOpcode::Reshape);
-
- ASSERT_EQ(reshape_node.tensor(), nullptr);
- ASSERT_EQ(reshape_node.shape(), nullptr);
-}
diff --git a/compiler/moco-tf/src/IR/TFRsqrt.h b/compiler/moco-tf/src/IR/TFRsqrt.h
deleted file mode 100644
index f371e39ab..000000000
--- a/compiler/moco-tf/src/IR/TFRsqrt.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFRSQRT_H__
-#define __MOCO_TF_IR_TFRSQRT_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note TFRsqrt corresponds to the following GraphDef
-/*
-node {
- name: "Rsqrt"
- op: "Rsqrt"
- input: "Placeholder"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-*/
-
-class TFRsqrt final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Rsqrt>>
-{
-public:
- TFRsqrt() = default;
-
-public:
- Node *x(void) const { return at(0)->node(); }
- void x(Node *node) { at(0)->node(node); }
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFRSQRT_H__
diff --git a/compiler/moco-tf/src/IR/TFRsqrt.test.cpp b/compiler/moco-tf/src/IR/TFRsqrt.test.cpp
deleted file mode 100644
index 7f92704ba..000000000
--- a/compiler/moco-tf/src/IR/TFRsqrt.test.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFRsqrt.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFRsqrtTest, constructor)
-{
- moco::tf::TFRsqrt rsqrt_node;
-
- ASSERT_EQ(rsqrt_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(rsqrt_node.opcode(), moco::tf::TFOpcode::Rsqrt);
-
- ASSERT_EQ(rsqrt_node.x(), nullptr);
-}
diff --git a/compiler/moco-tf/src/IR/TFShape.h b/compiler/moco-tf/src/IR/TFShape.h
deleted file mode 100644
index d50cabf79..000000000
--- a/compiler/moco-tf/src/IR/TFShape.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFSHAPE_H__
-#define __MOCO_TF_IR_TFSHAPE_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-#include <loco/IR/NodeMixins.h>
-
-#include <vector>
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note TFShape corresponds to the following GraphDef
-/*
-node {
- name: "Shape"
- op: "Shape"
- input: "some_input"
- attr {
- key: "T"
- value { type: DT_FLOAT }
- }
- attr {
- key: "out_type"
- value { type: DT_INT32 }
- }
-}
-*/
-
-/// @note Mixed in dtype() is for 'out_type' attribute
-class TFShape final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Shape>>,
- public loco::NodeMixin<loco::NodeTrait::DataType>
-{
-public:
- TFShape() = default;
-
-public:
- Node *input(void) const { return at(0)->node(); }
- void input(Node *node) { at(0)->node(node); }
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFSHAPE_H__
diff --git a/compiler/moco-tf/src/IR/TFShape.test.cpp b/compiler/moco-tf/src/IR/TFShape.test.cpp
deleted file mode 100644
index 6c68888cc..000000000
--- a/compiler/moco-tf/src/IR/TFShape.test.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFShape.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFShapeTest, constructor)
-{
- moco::tf::TFShape shape_node;
-
- ASSERT_EQ(shape_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(shape_node.opcode(), moco::tf::TFOpcode::Shape);
-
- ASSERT_EQ(shape_node.input(), nullptr);
- ASSERT_EQ(shape_node.dtype(), loco::DataType::Unknown);
-}
diff --git a/compiler/moco-tf/src/IR/TFSoftmax.h b/compiler/moco-tf/src/IR/TFSoftmax.h
deleted file mode 100644
index 22b7b9eca..000000000
--- a/compiler/moco-tf/src/IR/TFSoftmax.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFSOFTMAX_H__
-#define __MOCO_TF_IR_TFSOFTMAX_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-namespace moco
-{
-namespace tf
-{
-
-class TFSoftmax final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Softmax>>
-{
-public:
- TFSoftmax() = default;
-
-public:
- Node *logits(void) const { return at(0)->node(); }
- void logits(Node *node) { at(0)->node(node); }
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFSOFTMAX_H__
diff --git a/compiler/moco-tf/src/IR/TFSoftmax.test.cpp b/compiler/moco-tf/src/IR/TFSoftmax.test.cpp
deleted file mode 100644
index 99c7cbc3c..000000000
--- a/compiler/moco-tf/src/IR/TFSoftmax.test.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFSoftmax.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFSoftmaxTest, constructor)
-{
- moco::tf::TFSoftmax softmax_node;
-
- ASSERT_EQ(softmax_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(softmax_node.opcode(), moco::tf::TFOpcode::Softmax);
-
- ASSERT_EQ(softmax_node.logits(), nullptr);
-}
diff --git a/compiler/moco-tf/src/IR/TFSqrt.h b/compiler/moco-tf/src/IR/TFSqrt.h
deleted file mode 100644
index fda032e2d..000000000
--- a/compiler/moco-tf/src/IR/TFSqrt.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFSQRT_H__
-#define __MOCO_TF_IR_TFSQRT_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note TFSqrt corresponds to the following GraphDef
-/*
-node {
- name: "Sqrt"
- op: "Sqrt"
- input: "Placeholder"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-*/
-
-class TFSqrt final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Sqrt>>
-{
-public:
- TFSqrt() = default;
-
-public:
- Node *x(void) const { return at(0)->node(); }
- void x(Node *node) { at(0)->node(node); }
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFSQRT_H__
diff --git a/compiler/moco-tf/src/IR/TFSqrt.test.cpp b/compiler/moco-tf/src/IR/TFSqrt.test.cpp
deleted file mode 100644
index 9048d5729..000000000
--- a/compiler/moco-tf/src/IR/TFSqrt.test.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFSqrt.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFSqrtTest, constructor)
-{
- moco::tf::TFSqrt sqrt_node;
-
- ASSERT_EQ(sqrt_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(sqrt_node.opcode(), moco::tf::TFOpcode::Sqrt);
-
- ASSERT_EQ(sqrt_node.x(), nullptr);
-}
diff --git a/compiler/moco-tf/src/IR/TFSquaredDifference.h b/compiler/moco-tf/src/IR/TFSquaredDifference.h
deleted file mode 100644
index 83ecdb86b..000000000
--- a/compiler/moco-tf/src/IR/TFSquaredDifference.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFSQUAREDDIFFERENCE_H__
-#define __MOCO_TF_IR_TFSQUAREDDIFFERENCE_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note TFSquaredDifference corresponds to the following GraphDef
-/*
-node {
- name: "SquaredDifference"
- op: "SquaredDifference"
- input: "input_x"
- input: "input_y"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-*/
-
-class TFSquaredDifference final : public FixedArityNode<2, TFNodeImpl<TFOpcode::SquaredDifference>>
-{
-public:
- TFSquaredDifference() = default;
-
-public:
- Node *x(void) const { return at(0)->node(); }
- void x(Node *node) { at(0)->node(node); }
-
- Node *y(void) const { return at(1)->node(); }
- void y(Node *node) { at(1)->node(node); }
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFSQUAREDDIFFERENCE_H__
diff --git a/compiler/moco-tf/src/IR/TFSquaredDifference.test.cpp b/compiler/moco-tf/src/IR/TFSquaredDifference.test.cpp
deleted file mode 100644
index f83d28caf..000000000
--- a/compiler/moco-tf/src/IR/TFSquaredDifference.test.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFSquaredDifference.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFSquaredDifferenceTest, constructor)
-{
- moco::tf::TFSquaredDifference sd_node;
-
- ASSERT_EQ(sd_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(sd_node.opcode(), moco::tf::TFOpcode::SquaredDifference);
-
- ASSERT_EQ(sd_node.x(), nullptr);
- ASSERT_EQ(sd_node.y(), nullptr);
-}
diff --git a/compiler/moco-tf/src/IR/TFSqueeze.h b/compiler/moco-tf/src/IR/TFSqueeze.h
deleted file mode 100644
index e98644101..000000000
--- a/compiler/moco-tf/src/IR/TFSqueeze.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFSQUEEZE_H__
-#define __MOCO_TF_IR_TFSQUEEZE_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-#include <vector>
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note TFSqueeze corresponds to the following GraphDef
-/*
-node {
- name: "squeeze"
- op: "Squeeze"
- input: "x"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "squeeze_dims"
- value {
- list {
- i: a
- i: b
- ..
- }
- }
- }
-}
-*/
-
-class TFSqueeze final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Squeeze>>
-{
-public:
- TFSqueeze() = default;
-
-public:
- Node *input(void) const { return at(0)->node(); }
- void input(Node *node) { at(0)->node(node); }
-
-public:
- const std::vector<int64_t> &squeeze_dims(void) const { return _squeeze_dims; }
- void squeeze_dims(const std::vector<int64_t> &squeeze_dims) { _squeeze_dims = squeeze_dims; }
-
-private:
- std::vector<int64_t> _squeeze_dims;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFSQUEEZE_H__
diff --git a/compiler/moco-tf/src/IR/TFSqueeze.test.cpp b/compiler/moco-tf/src/IR/TFSqueeze.test.cpp
deleted file mode 100644
index 1ab219b2f..000000000
--- a/compiler/moco-tf/src/IR/TFSqueeze.test.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFSqueeze.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFSqueezeTest, constructor)
-{
- moco::tf::TFSqueeze squeeze_node;
-
- ASSERT_EQ(squeeze_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(squeeze_node.opcode(), moco::tf::TFOpcode::Squeeze);
-
- ASSERT_EQ(squeeze_node.input(), nullptr);
- ASSERT_EQ(squeeze_node.squeeze_dims().size(), 0);
-}
diff --git a/compiler/moco-tf/src/IR/TFStopGradient.h b/compiler/moco-tf/src/IR/TFStopGradient.h
deleted file mode 100644
index 4b8f1b843..000000000
--- a/compiler/moco-tf/src/IR/TFStopGradient.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFSTOPGRADIENT_H__
-#define __MOCO_TF_IR_TFSTOPGRADIENT_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note TFStopGradient corresponds to the following GraphDef
-/*
-node {
- name: "StopGradient"
- op: "StopGradient"
- input: "Placeholder"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-*/
-
-class TFStopGradient final : public FixedArityNode<1, TFNodeImpl<TFOpcode::StopGradient>>
-{
-public:
- TFStopGradient() = default;
-
-public:
- Node *input(void) const { return at(0)->node(); }
- void input(Node *node) { at(0)->node(node); }
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFSTOPGRADIENT_H__
diff --git a/compiler/moco-tf/src/IR/TFStopGradient.test.cpp b/compiler/moco-tf/src/IR/TFStopGradient.test.cpp
deleted file mode 100644
index dafd1a5e7..000000000
--- a/compiler/moco-tf/src/IR/TFStopGradient.test.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFStopGradient.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFStopGradientTest, constructor)
-{
- moco::tf::TFStopGradient node;
-
- ASSERT_EQ(node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(node.opcode(), moco::tf::TFOpcode::StopGradient);
-
- ASSERT_EQ(node.input(), nullptr);
-}
diff --git a/compiler/moco-tf/src/IR/TFSub.h b/compiler/moco-tf/src/IR/TFSub.h
deleted file mode 100644
index 5f4e48b63..000000000
--- a/compiler/moco-tf/src/IR/TFSub.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFSUB_H__
-#define __MOCO_TF_IR_TFSUB_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-namespace moco
-{
-namespace tf
-{
-
-/// @note TFSub corresponds to the following GraphDef
-/*
-node {
- name: "sub"
- op: "Sub"
- input: "x"
- input: "y"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-*/
-
-class TFSub final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Sub>>
-{
-public:
- TFSub() = default;
-
-public:
- Node *x(void) const { return at(0)->node(); }
- void x(Node *node) { at(0)->node(node); }
-
- Node *y(void) const { return at(1)->node(); }
- void y(Node *node) { at(1)->node(node); }
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFSUB_H__
diff --git a/compiler/moco-tf/src/IR/TFSub.test.cpp b/compiler/moco-tf/src/IR/TFSub.test.cpp
deleted file mode 100644
index 79f746681..000000000
--- a/compiler/moco-tf/src/IR/TFSub.test.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFSub.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFSubTest, constructor)
-{
- moco::tf::TFSub sub_node;
-
- ASSERT_EQ(sub_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(sub_node.opcode(), moco::tf::TFOpcode::Sub);
-
- ASSERT_EQ(sub_node.x(), nullptr);
- ASSERT_EQ(sub_node.y(), nullptr);
-}
diff --git a/compiler/moco-tf/src/IR/TFTanh.h b/compiler/moco-tf/src/IR/TFTanh.h
deleted file mode 100644
index c85663e69..000000000
--- a/compiler/moco-tf/src/IR/TFTanh.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_IR_TFTANH_H__
-#define __MOCO_TF_IR_TFTANH_H__
-
-#include "Dialect/TFNodeDecl.h"
-
-namespace moco
-{
-namespace tf
-{
-
-class TFTanh final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Tanh>>
-{
-public:
- TFTanh() = default;
-
-public:
- Node *x(void) const { return at(0)->node(); }
- void x(Node *node) { at(0)->node(node); }
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_IR_TFTANH_H__
diff --git a/compiler/moco-tf/src/IR/TFTanh.test.cpp b/compiler/moco-tf/src/IR/TFTanh.test.cpp
deleted file mode 100644
index 0ff1af6a4..000000000
--- a/compiler/moco-tf/src/IR/TFTanh.test.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFTanh.h"
-
-#include "Dialect/TFDialect.h"
-
-#include <gtest/gtest.h>
-
-TEST(TFTanhTest, constructor)
-{
- moco::tf::TFTanh tanh_node;
-
- ASSERT_EQ(tanh_node.dialect(), moco::tf::TFDialect::get());
- ASSERT_EQ(tanh_node.opcode(), moco::tf::TFOpcode::Tanh);
-
- ASSERT_EQ(tanh_node.x(), nullptr);
-}
diff --git a/compiler/moco-tf/src/ImportTarget.h b/compiler/moco-tf/src/ImportTarget.h
deleted file mode 100644
index cd169f53b..000000000
--- a/compiler/moco-tf/src/ImportTarget.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __IMPORT_TARGET_H__
-#define __IMPORT_TARGET_H__
-
-enum class ImportTarget
-{
- Canonical, // Emit Canonical Dialect
- TensorFlow, // Emit T/F Dialect
-};
-
-#endif // __IMPORT_TARGET_H__
diff --git a/compiler/moco-tf/src/Importer.cpp b/compiler/moco-tf/src/Importer.cpp
deleted file mode 100644
index 7899a4dcf..000000000
--- a/compiler/moco-tf/src/Importer.cpp
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Importer.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-#include "GraphBuilderRegistry.h"
-#include "Transforms.h"
-#include "ProgressReporter.h"
-
-#include "Annotations/ShapeInferenceData.h"
-
-#include <moco/Log.h>
-
-#include <loco/IR/Verifier.h>
-#include <locop/FormattedGraph.h>
-#include <stdex/Memory.h>
-
-#include <logo/Phase.h>
-
-#include <cassert>
-#include <sstream>
-#include <stdexcept>
-
-namespace
-{
-
-void convert_graph(const moco::tf::GraphBuilderSource &source,
- const moco::tf::ModelSignature &signature, tensorflow::GraphDef &tf_graph_def,
- loco::Graph *graph)
-{
- auto nodedef = stdex::make_unique<moco::tf::NodeDefTable>();
- auto tensor_names = stdex::make_unique<moco::tf::SymbolTable>();
- auto updates = stdex::make_unique<moco::tf::UpdateQueue>();
-
- moco::tf::GraphBuilderContext gb_context(graph, nodedef.get(), tensor_names.get(), updates.get());
-
- // Building a loco graph
- // 1. Convert all the nodes to loco::Node
- // 2. Connect inputs: set all node input(from a string) to actual node object
- // 3. Set graph input
- // 4. Create loco::Push node and set input and set graph output
-
- /**
- * @brief Prepare tensorflow::NodeDef search table from name
- */
- for (const auto &n : tf_graph_def.node())
- {
- nodedef->enroll(n.name(), &n);
- }
-
- /**
- * @brief 1. Convert all the nodes to loco::Node
- *
- * @note In each build for a TF node, four things happen
- * 1) create corresponding loco::Node(s)
- * 2) read and set the attributes to created loco::Node(s)
- * 3) register name-loco::Node(last one of Nodes) that will be used as the output
- * 4) queue a task to set the input of the loco::Node(first one of the Nodes)
- * this is done only for required nodes depending on the operator
- *
- * @example Placeholder("in") - Identity("out")
- * %1 = Pull --> 0x1001 (loco::Node* object address)
- * (symboltable: register %1, after the registeration table will contain as below;
- * "in" : 0x1001
- * )
- * (queue: this will be empty as Pull does not queue a task to set input;
- * )
- *
- * %2 = Forward --> 0x1002
- * (symboltable: register %2 and table will look like below;
- * "in" : 0x1001
- * "out" : 0x1002
- * )
- * (queue: Forward will queue a task with input "in";
- * 0x1002: {"in"}
- * )
- */
- for (const auto &n : tf_graph_def.node())
- {
- if (const auto *graph_builder = source.lookup(n.op()))
- {
- if (!graph_builder->validate(n))
- {
- throw std::runtime_error{"Invalid operator: " + n.op()};
- }
-
- graph_builder->build(n, &gb_context);
- }
- else
- {
- throw std::runtime_error{"Not supported: " + n.op()};
- }
- }
-
- /**
- * @brief 2. Connect inputs: Iterate updates and call each update input method
- *
- * @note Continue from above example graph, connecting inputs is done in following steps
- * a) iterate queue
- * b) call the input method for each update
- * c) each update has the loco::Node *node and names of the input to connect
- * node = 0x1002 and names = {"in"}
- * d) from symbol table, "in" will return 0x1001
- * e) set input of 0x1002 with 0x1001
- */
- for (auto &update : updates->queue())
- {
- update->input(tensor_names.get());
- }
-
- /**
- * @brief 3. Set graph input
- */
- for (auto input : signature.inputs())
- {
- auto node = tensor_names->node(input);
- assert(node != nullptr);
-
- auto graph_input = graph->inputs()->create();
-
- loco::Pull *pull_node = dynamic_cast<loco::Pull *>(node);
- assert(pull_node != nullptr);
-
- graph_input->name(input.nodeName());
- // This implementation works as "PlaceholderGraphBuilder in Op/PlaceholderGraphBuilder.cpp"
- // accepts only TF_FLOAT32 as of now.
- //
- // TODO Support other types
- graph_input->dtype(loco::DataType::FLOAT32);
- loco::link(graph_input, pull_node);
- }
-
- /**
- * @brief 4. Create loco::Push node and set graph input and output
- */
- for (auto output : signature.outputs())
- {
- auto output_node = tensor_names->node(output);
- assert(output_node);
-
- // create loco::Push for output of graph
- auto push_node = graph->nodes()->create<loco::Push>();
- push_node->from(output_node); // set input of Push to output node
-
- // set the graph output name and node object
- auto graph_output = graph->outputs()->create();
- graph_output->name(output.nodeName());
- // TODO Support other types
- graph_output->dtype(loco::DataType::FLOAT32);
- loco::link(graph_output, push_node);
- }
-
- // validate graph
- assert(loco::valid(graph));
-}
-
-void dump_shapeinferencedata(loco::Node *node, const std::string &name)
-{
- LOGGER(node_shapeinferencedata);
-
- const moco::tf::ShapeInferenceData *shapedata = node->annot<moco::tf::ShapeInferenceData>();
- if (shapedata == nullptr)
- {
- INFO(node_shapeinferencedata) << "ShapeInferenceData is null for " << name << ":" << node
- << std::endl;
- }
- else
- {
- std::stringstream ss;
-
- ss << "ShapeInferenceData for " << name << ":" << node;
- // clang-format off
- switch (shapedata->domain())
- {
- case loco::Domain::Tensor: ss << " (Tensor)"; break;
- case loco::Domain::Feature: ss << " (Feature)"; break;
- case loco::Domain::Filter: ss << " (Filter)"; break;
- case loco::Domain::Bias: ss << " (Bias)"; break;
- default: assert(false && "Unknown Domain"); break;
- }
- // clang-format on
- ss << " rank(" << shapedata->rank() << ") [";
- for (uint32_t index = 0; index < shapedata->rank(); ++index)
- {
- if (index)
- ss << ",";
- if (shapedata->dim(index).known())
- ss << shapedata->dim(index).value();
- else
- ss << "?";
- }
- ss << "]";
-
- INFO(node_shapeinferencedata) << ss.str() << std::endl;
- }
-}
-
-void transform_graph(loco::Graph *graph)
-{
- LOGGER(transform_graph);
-
- std::vector<std::unique_ptr<moco::tf::Transform>> prepare;
- logo::Phase transforms;
-
- // Transforms that run only once for preparation and finalization
- {
- // TODO add one time preparation when needed
- }
-
- // Transforms that run multiple times until there is no transform occured
- {
- transforms.emplace_back(stdex::make_unique<moco::tf::FixShapeTransform>());
- // TODO add more TensorFlow related transformations
- }
-
- // Run preparation
- for (auto &tr : prepare)
- {
- tr->run(graph);
- }
-
- moco::tf::ProgressReporter prog(graph, logo::PhaseStrategy::Saturate);
- logo::PhaseRunner<logo::PhaseStrategy::Saturate> runner{graph};
-
- runner.attach(&prog);
- runner.run(transforms);
-
- // TODO would be better to run this code only when log is enabled
- {
- for (uint32_t i = 0; i < graph->outputs()->size(); ++i)
- {
- loco::Node *node = loco::push_node(graph, i);
- std::string name = "Output(" + std::to_string(i) + ")";
- dump_shapeinferencedata(node, name);
- }
- }
-
- // validate graph
- assert(loco::valid(graph));
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-Importer::Importer()
-{
- // DO NOTHING
-}
-
-std::unique_ptr<loco::Graph> Importer::import(const ModelSignature &signature,
- tensorflow::GraphDef &tf_graph_def) const
-{
- auto graph = loco::make_graph();
-
- const GraphBuilderSource *source_ptr = &moco::tf::GraphBuilderRegistry::get();
-
- if (_source != nullptr)
- {
- // Use user-defined GraphBuilderSource
- source_ptr = _source;
- }
-
- convert_graph(*source_ptr, signature, tf_graph_def, graph.get());
-
- transform_graph(graph.get());
-
- return std::move(graph);
-}
-
-} // namespace tf
-} // namespace moco
diff --git a/compiler/moco-tf/src/Importer.h b/compiler/moco-tf/src/Importer.h
deleted file mode 100644
index e5faafd62..000000000
--- a/compiler/moco-tf/src/Importer.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __IMPORT_H__
-#define __IMPORT_H__
-
-#include <moco/tf/Frontend.h>
-#include <moco/tf/Names.h>
-
-#include "GraphBuilderRegistry.h"
-
-#include <loco.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <memory>
-
-namespace moco
-{
-namespace tf
-{
-
-class Importer final
-{
-public:
- Importer();
-
-public:
- explicit Importer(const GraphBuilderSource *source) : _source{source}
- {
- // DO NOTHING
- }
-
-public:
- std::unique_ptr<loco::Graph> import(const ModelSignature &, tensorflow::GraphDef &) const;
-
-private:
- const GraphBuilderSource *_source = nullptr;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __IMPORT_H__
diff --git a/compiler/moco-tf/src/Importer.test.cpp b/compiler/moco-tf/src/Importer.test.cpp
deleted file mode 100644
index 770984b0f..000000000
--- a/compiler/moco-tf/src/Importer.test.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Importer.h"
-
-#include "TestHelper.h"
-
-#include "IR/TFIdentity.h"
-#include "Op/Identity.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-using namespace moco::tf::test;
-
-TEST(TensorFlowImport, Dummy) { moco::tf::Importer import; }
-
-namespace
-{
-
-// clang-format off
-const char *basic_pbtxtdata = STRING_CONTENT(
-node {
- name: "Placeholder"
- op: "Placeholder"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "shape"
- value {
- shape {
- dim {
- size: 1
- }
- dim {
- size: 2
- }
- dim {
- size: 1
- }
- dim {
- size: 2
- }
- }
- }
- }
-}
-node {
- name: "output/identity"
- op: "Identity"
- input: "Placeholder"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, load_model_withio)
-{
- moco::tf::ModelSignature signature;
-
- signature.add_input(moco::tf::TensorName("Placeholder", 0));
- signature.add_output(moco::tf::TensorName("output/identity", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(basic_pbtxtdata, graph_def));
-
- using IdentityGraphBuilder = moco::tf::IdentityGraphBuilderImpl<ImportTarget::Canonical>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("Identity", stdex::make_unique<IdentityGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - import reads Pull
- // - import reads Forward
- // - attribute values should match
-
- auto pull = find_first_node_bytype<loco::Pull>(graph.get());
- ASSERT_NE(pull, nullptr);
- auto forward = find_first_node_bytype<loco::Forward>(graph.get());
- ASSERT_NE(forward, nullptr);
-
- ASSERT_EQ(pull->dtype(), loco::DataType::FLOAT32);
- ASSERT_EQ(pull->rank(), 4);
- loco::Dimension dim1 = 1;
- loco::Dimension dim2 = 2;
- ASSERT_EQ(pull->dim(0).value(), dim1.value());
- ASSERT_EQ(pull->dim(1).value(), dim2.value());
- ASSERT_EQ(pull->dim(2).value(), dim1.value());
- ASSERT_EQ(pull->dim(3).value(), dim2.value());
-}
-
-TEST(TensorFlowImport, load_model_withio_tf)
-{
- moco::tf::ModelSignature signature;
-
- signature.add_input(moco::tf::TensorName("Placeholder", 0));
- signature.add_output(moco::tf::TensorName("output/identity", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(basic_pbtxtdata, graph_def));
-
- using IdentityGraphBuilder = moco::tf::IdentityGraphBuilderImpl<ImportTarget::TensorFlow>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- // TODO add Placeholder
- r.add("Identity", stdex::make_unique<IdentityGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - import reads Placeholder
- // - import reads Identity
- // - attribute values should match
-
- auto tfidentity = find_first_node_bytype<moco::tf::TFIdentity>(graph.get());
- ASSERT_NE(tfidentity, nullptr);
- ASSERT_NE(tfidentity->input(), nullptr);
-}
diff --git a/compiler/moco-tf/src/Knob.lst b/compiler/moco-tf/src/Knob.lst
index fd8b4cd8a..b88e064c7 100644
--- a/compiler/moco-tf/src/Knob.lst
+++ b/compiler/moco-tf/src/Knob.lst
@@ -4,24 +4,19 @@
// KNOB_BOOL(NAME, DEFAULT_VALUE, DESCRIPTION)
-// Imports
-KNOB_BOOL(ImportAsTFAvgPool, true, Import AvgPool2D node as TFAvgPool node)
-KNOB_BOOL(ImportAsTFBiasAdd, true, Import BiasAdd node as TFBiasAdd node)
-KNOB_BOOL(ImportAsTFConcatV2, true, Import ConcatV2 node as TFConcatV2 node)
-KNOB_BOOL(ImportAsTFConst, true, Import Const node as TFConst node)
-KNOB_BOOL(ImportAsTFConv2D, true, Import Conv2D node as TFConv2D node)
-KNOB_BOOL(ImportAsTFIdentity, true, Import Identity node as TFIdentity node)
-KNOB_BOOL(ImportAsTFMaxPool, true, Import MaxPool node as TFMaxPool node)
-KNOB_BOOL(ImportAsTFRelu, true, Import Relu node as TFRelu node)
-KNOB_BOOL(ImportAsTFRelu6, true, Import Relu6 node as TFRelu6 node)
-
// TensorFlow dialect transforms
KNOB_BOOL(FuseBinaryIntoPreceding, true, Fuse Binary node to preceding node)
KNOB_BOOL(ResolveFusedBatchNorm, true, Enable ResolveFusedBatchNorm transform)
KNOB_BOOL(ResolveConstantShape, true, Replace determined TFShape to TFConst)
KNOB_BOOL(ResolveReshapeWildcardDim, true, Resolve wildcard dimension in TFReshape node)
-KNOB_BOOL(ResolveSquaredDifference, false, Resolve SquaredDifference node)
+KNOB_BOOL(ResolveSquaredDifference, true, Resolve SquaredDifference node)
KNOB_BOOL(RemoveTFIdentityNode, true, Enable RemoveTFIdentityNode optimization)
+KNOB_BOOL(SqueezeReduceNode, true, Insert TFSqueeze if ReduceNode do not keep dimensions)
+// Constant folding
+KNOB_BOOL(ConstantFoldAdd, false, Constant fold for Add node)
+KNOB_BOOL(ConstantFoldMul, false, Constant fold for Mul node)
+KNOB_BOOL(ConstantFoldPack, false, Constant fold for Pack node)
+KNOB_BOOL(ConstantFoldStridedSlice, false, Constant fold for StridedSlice node)
// Canonicalization
KNOB_BOOL(CanonicalizeBiasAdd, true, Enable Canonicalize for BiasAdd node)
@@ -37,8 +32,8 @@ KNOB_BOOL(ReorderDecodeReLU, true, Reorder FeatureDecode-ReLU)
KNOB_BOOL(ReorderDecodeTensorBiasAdd, true, Reorder FeatureDecode-TensorBiasAdd)
// END
KNOB_BOOL(SimplifyDomainConversion, true, Enable SimplifyDomainConversion optimization)
-KNOB_BOOL(ResolveDuplicateReshape, false, Resolve duplicated Reshape nodes)
-KNOB_BOOL(ResolveRedundantReshape, false, Resolve redundant Reshape node)
+KNOB_BOOL(ResolveDuplicateReshape, true, Resolve duplicated Reshape nodes)
+KNOB_BOOL(ResolveRedundantReshape, true, Resolve redundant Reshape node)
// Graph transformations
KNOB_BOOL(RemoveDeadNode, true, Enable RemoveDeadNode optimization)
diff --git a/compiler/moco-tf/src/LogHelper.cpp b/compiler/moco-tf/src/LogHelper.cpp
index 1a38eb7c3..92ff75569 100644
--- a/compiler/moco-tf/src/LogHelper.cpp
+++ b/compiler/moco-tf/src/LogHelper.cpp
@@ -56,22 +56,6 @@ std::ostream &operator<<(std::ostream &os, const loco::Padding2D &pad)
} // namespace loco
-namespace moco
-{
-namespace tf
-{
-
-std::ostream &operator<<(std::ostream &os, const moco::tf::PadData &pad_data)
-{
- os << "[TLBR " << pad_data.pad()->top() << "," << pad_data.pad()->left() << ","
- << pad_data.pad()->bottom() << "," << pad_data.pad()->right() << "]";
-
- return os;
-}
-
-} // namespace tf
-} // namespace moco
-
std::ostream &operator<<(std::ostream &os, const std::vector<int64_t> &vi64)
{
for (auto vi : vi64)
diff --git a/compiler/moco-tf/src/LogHelper.h b/compiler/moco-tf/src/LogHelper.h
index fc60f9fef..4e3cb5dac 100644
--- a/compiler/moco-tf/src/LogHelper.h
+++ b/compiler/moco-tf/src/LogHelper.h
@@ -23,8 +23,6 @@
#include <loco/IR/FilterShape.h>
#include <loco/IR/TensorShape.h>
-#include "Annotations/PadData.h"
-
#include <sstream>
#include <vector>
@@ -53,19 +51,6 @@ std::ostream &operator<<(std::ostream &os, const loco::Padding2D &pad);
} // namespace loco
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief dump moco::tf::PadData
- */
-std::ostream &operator<<(std::ostream &os, const moco::tf::PadData &pad_data);
-
-} // namespace tf
-} // namespace moco
-
/**
* @brief dump std::vector<int64_t> values to stream
*/
diff --git a/compiler/moco-tf/src/Op/Add.cpp b/compiler/moco-tf/src/Op/Add.cpp
deleted file mode 100644
index b957cf4dc..000000000
--- a/compiler/moco-tf/src/Op/Add.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFAdd.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-
-#include <loco.h>
-#include <stdex/Memory.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-namespace
-{
-
-using namespace moco::tf;
-
-/**
- * @brief GraphUpdate for TF Add node
- */
-class TFAddGraphUpdate final : public GraphUpdate
-{
-public:
- TFAddGraphUpdate(TFAdd *node, std::vector<TensorName> names) : _node(node), _names(names) {}
-
- void input(const SymbolTable *) const override;
-
-private:
- TFAdd *_node;
- std::vector<TensorName> _names;
-};
-
-void TFAddGraphUpdate::input(const SymbolTable *tensor_names) const
-{
- int num_inputs = _names.size();
- assert(num_inputs == 2);
-
- _node->x(tensor_names->node(_names[0]));
- _node->y(tensor_names->node(_names[1]));
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for Add node
- */
-class AddGraphBuilder final : public GraphBuilder
-{
-public:
- bool validate(const tensorflow::NodeDef &) const override;
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool AddGraphBuilder::validate(const tensorflow::NodeDef &node) const
-{
- assert(node.input_size() == 2);
-
- return true;
-}
-
-void AddGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // creating TF dialect Add node
- auto tf_add = graph->nodes()->create<TFAdd>();
-
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, tf_add);
-
- std::vector<TensorName> add_input_names;
- add_input_names.push_back(TensorName(node.input(0))); // x
- add_input_names.push_back(TensorName(node.input(1))); // y
-
- auto tf_add_update = stdex::make_unique<TFAddGraphUpdate>(tf_add, add_input_names);
- updates->enroll(std::move(tf_add_update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(Add, AddGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/Add.test.cpp b/compiler/moco-tf/src/Op/Add.test.cpp
deleted file mode 100644
index dc53f37b2..000000000
--- a/compiler/moco-tf/src/Op/Add.test.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-
-#include "IR/TFAdd.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <cstring>
-#include <memory>
-
-using namespace moco::tf::test;
-
-namespace
-{
-// clang-format off
-const char *add_basic_pbtxt = STRING_CONTENT(
-node {
- name: "input_01"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 1
- }
- dim {
- size: 3
- }
- dim {
- size: 4
- }
- }
- float_val: 1.0
- }
- }
- }
-}
-node {
- name: "input_02"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 1
- }
- dim {
- size: 3
- }
- dim {
- size: 4
- }
- }
- float_val: 2.0
- }
- }
- }
-}
-node {
- name: "ADD_01"
- op: "Add"
- input: "input_01"
- input: "input_02"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, tf_add_basic)
-{
- // load graph
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
- signature.add_output(moco::tf::TensorName("ADD_01", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(add_basic_pbtxt, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - TFAdd node should exist
- // - both inputs x() and y() should not be null
-
- auto add_node = moco::tf::test::find_first_node_bytype<moco::tf::TFAdd>(graph.get());
-
- ASSERT_NE(add_node, nullptr);
- ASSERT_NE(add_node->x(), nullptr);
- ASSERT_NE(add_node->y(), nullptr);
-}
diff --git a/compiler/moco-tf/src/Op/AvgPool.cpp b/compiler/moco-tf/src/Op/AvgPool.cpp
deleted file mode 100644
index dda7fce15..000000000
--- a/compiler/moco-tf/src/Op/AvgPool.cpp
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "AvgPool.h"
-
-#include "Convert.h"
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-#include "Knob.h"
-
-#include "IR/TFAvgPool.h"
-
-#include "Annotations/PaddingData.h"
-
-#include <moco/tf/Names.h>
-
-#include <loco/IR/PermutingCodec.h>
-#include <stdex/Memory.h>
-#include <plier/tf/Convert.h>
-
-#include <cassert>
-#include <stdexcept>
-
-using namespace plier::tf;
-
-namespace
-{
-
-using namespace moco::tf;
-
-class AvgPoolGraphUpdate final : public GraphUpdate
-{
-public:
- AvgPoolGraphUpdate(loco::FeatureEncode *node, const TensorName &name)
- : _encode_node(node), _input_name(name)
- {
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- loco::FeatureEncode *_encode_node;
- const TensorName _input_name;
-};
-
-class TFAvgPoolGraphUpdate final : public GraphUpdate
-{
-public:
- TFAvgPoolGraphUpdate(moco::tf::TFAvgPool *node, const TensorName &name)
- : _avgpool_node(node), _value_name(name)
- {
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- moco::tf::TFAvgPool *_avgpool_node;
- const TensorName _value_name;
-};
-
-void AvgPoolGraphUpdate::input(const SymbolTable *node_table) const
-{
- loco::Node *input_node = node_table->node(_input_name);
- _encode_node->input(input_node);
-}
-
-void TFAvgPoolGraphUpdate::input(const SymbolTable *node_table) const
-{
- loco::Node *value_node = node_table->node(_value_name);
- _avgpool_node->value(value_node);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for AvgPool node
- */
-class AvgPoolGraphBuilder final : public AvgPoolGraphBuilderBase
-{
-public:
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool AvgPoolGraphBuilderBase::validate(const tensorflow::NodeDef &node) const
-{
- // note: even though "data_format" is not entered when a model is written,
- // TF seems to generate "data_format" field into a pb file
- if (!plier::tf::has_attrs(node, {"T", "data_format", "ksize", "padding", "strides"}))
- return false;
-
- if (node.input_size() != 1)
- return false;
-
- return true;
-}
-
-void AvgPoolGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- if (moco::tf::get<moco::tf::Knob::ImportAsTFAvgPool>())
- {
- AvgPoolGraphBuilderImpl<ImportTarget::TensorFlow> builder;
- return builder.build(node, context);
- }
- else
- {
- AvgPoolGraphBuilderImpl<ImportTarget::Canonical> builder;
- return builder.build(node, context);
- }
-}
-
-void AvgPoolGraphBuilderImpl<ImportTarget::Canonical>::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- using plier::tf::DataLayout;
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // create loco nodes
- auto encode_node = graph->nodes()->create<loco::FeatureEncode>();
- auto avgPool2d_node = graph->nodes()->create<loco::AvgPool2D>();
- auto decode_node = graph->nodes()->create<loco::FeatureDecode>();
-
- // name of loco nodes
- ::std::string avgPool2d_name = node.name();
-
- // tensorflow padding convention is valid
- avgPool2d_node->convention(loco::AvgPool2D::Convention::Valid);
-
- // tensorflow data_format, e.g., NHWC, NCHW, etc.
- auto data_layout = plier::tf::get_data_layout(node, "data_format");
- if (!(data_layout == DataLayout::NHWC || data_layout == DataLayout::NCHW))
- {
- throw std::runtime_error("Not supported data layout at AvgPoolGraphBuilder");
- }
-
- // FeatureEncode
- {
- auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>();
-
- if (data_layout == DataLayout::NHWC)
- {
- enc->perm()->axis(loco::FeatureAxis::Count) = 0;
- enc->perm()->axis(loco::FeatureAxis::Height) = 1;
- enc->perm()->axis(loco::FeatureAxis::Width) = 2;
- enc->perm()->axis(loco::FeatureAxis::Depth) = 3;
- }
- else if (data_layout == DataLayout::NCHW)
- {
- enc->perm()->axis(loco::FeatureAxis::Count) = 0;
- enc->perm()->axis(loco::FeatureAxis::Depth) = 1;
- enc->perm()->axis(loco::FeatureAxis::Height) = 2;
- enc->perm()->axis(loco::FeatureAxis::Width) = 3;
- }
- else
- assert(false);
-
- encode_node->encoder(std::move(enc));
- }
-
- // AvgPool
- {
- // let's convert attrs:
- // TensorFlow attr : T, data_format, ksize, padding, strides
- // to loco attr: not defined, TBD, window, annot, stride
-
- // tf ksize -> loco window
- auto tf_ksize = plier::tf::get_list_attr(node, "ksize");
- auto window = avgPool2d_node->window();
-
- if (data_layout == DataLayout::NHWC)
- {
- window->vertical(tf_ksize.i(1));
- window->horizontal(tf_ksize.i(2));
- }
- else if (data_layout == DataLayout::NCHW)
- {
- window->vertical(tf_ksize.i(2));
- window->horizontal(tf_ksize.i(3));
- }
- else
- assert(false);
-
- // tf strides -> loco stride
- auto tf_strides = plier::tf::get_list_attr(node, "strides");
- auto stride = avgPool2d_node->stride();
-
- if (data_layout == DataLayout::NHWC)
- {
- stride->vertical(tf_strides.i(1));
- stride->horizontal(tf_strides.i(2));
- }
- else if (data_layout == DataLayout::NCHW)
- {
- stride->vertical(tf_strides.i(2));
- stride->horizontal(tf_strides.i(3));
- }
- else
- assert(false);
-
- // tf paddings -> PaddingData annotation
- auto tf_padding = moco::str_toupper(plier::tf::get_string_attr(node, "padding"));
- auto padding_data = stdex::make_unique<PaddingData>(tf_padding);
- avgPool2d_node->annot(std::move(padding_data));
- }
-
- // FeatureDecode
- {
- auto dec = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>();
-
- if (data_layout == DataLayout::NHWC)
- {
- dec->perm()->axis(loco::FeatureAxis::Count) = 0;
- dec->perm()->axis(loco::FeatureAxis::Height) = 1;
- dec->perm()->axis(loco::FeatureAxis::Width) = 2;
- dec->perm()->axis(loco::FeatureAxis::Depth) = 3;
- }
- else if (data_layout == DataLayout::NCHW)
- {
- dec->perm()->axis(loco::FeatureAxis::Count) = 0;
- dec->perm()->axis(loco::FeatureAxis::Depth) = 1;
- dec->perm()->axis(loco::FeatureAxis::Height) = 2;
- dec->perm()->axis(loco::FeatureAxis::Width) = 3;
- }
- else
- assert(false);
-
- decode_node->decoder(std::move(dec));
- }
-
- // link nodes
- avgPool2d_node->ifm(encode_node);
- decode_node->input(avgPool2d_node);
-
- // To set the input node of encode_node with avgPool2d_name
- TensorName output_name(avgPool2d_name, 0);
- tensor_names->enroll(output_name, decode_node);
-
- // Record ifm inputs to featureEncode_node
- auto update = stdex::make_unique<AvgPoolGraphUpdate>(encode_node, TensorName(node.input(0)));
-
- updates->enroll(std::move(update));
-}
-
-void AvgPoolGraphBuilderImpl<ImportTarget::TensorFlow>::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // name of loco nodes
- ::std::string avgPool2d_name = node.name();
-
- // tensorflow data_format: one of NHWC or NCHW.
- auto data_layout = get_string_attr(node, "data_format");
- auto avgPool_node = graph->nodes()->create<moco::tf::TFAvgPool>();
- avgPool_node->data_layout(data_layout);
-
- // padding
- auto padding = moco::str_toupper(get_string_attr(node, "padding"));
- avgPool_node->padding(padding);
-
- // ksize
- auto tf_ksize = get_list_attr(node, "ksize");
- auto ksize = as_int64_list(tf_ksize);
- if (ksize.size() != 4)
- {
- // TODO support ksize length for 1 and 2
- throw std::runtime_error("AvgPool only supports ksize length 4");
- }
- avgPool_node->ksize(ksize);
-
- // strides
- auto tf_strides = get_list_attr(node, "strides");
- auto strides = as_int64_list(tf_strides);
- if (strides.size() != 4)
- {
- // TODO support strides length for 1 and 2
- throw std::runtime_error("AvgPool only supports strides length 4");
- }
- avgPool_node->strides(strides);
-
- // To set the input node of encode_node with avgPool2d_name
- TensorName output_name(avgPool2d_name, 0);
- tensor_names->enroll(output_name, avgPool_node);
-
- // Record ifm inputs to featureEncode_node
- auto update = stdex::make_unique<TFAvgPoolGraphUpdate>(avgPool_node, TensorName(node.input(0)));
-
- updates->enroll(std::move(update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(AvgPool, AvgPoolGraphBuilder)
-
-// TODO Consider a case when TF AvgPool is for 3D.
-// AvgPool works for 2D and other Dimensions, such as 3D
-// So, in future, some other GraphBuilder decide if AvgPoolGraphBuilder is used or
-// other GraphBuilder is used for TF AvgPool
diff --git a/compiler/moco-tf/src/Op/AvgPool.h b/compiler/moco-tf/src/Op/AvgPool.h
deleted file mode 100644
index ec9075a81..000000000
--- a/compiler/moco-tf/src/Op/AvgPool.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __OP_AVG_POOL_H__
-#define __OP_AVG_POOL_H__
-
-#include "GraphBuilder.h"
-#include "ImportTarget.h"
-
-namespace moco
-{
-namespace tf
-{
-
-struct AvgPoolGraphBuilderBase : public GraphBuilder
-{
- virtual ~AvgPoolGraphBuilderBase() = default;
-
- bool validate(const tensorflow::NodeDef &) const final;
-};
-
-template <ImportTarget T> class AvgPoolGraphBuilderImpl;
-
-template <>
-struct AvgPoolGraphBuilderImpl<ImportTarget::Canonical> final : public AvgPoolGraphBuilderBase
-{
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
-};
-
-template <>
-struct AvgPoolGraphBuilderImpl<ImportTarget::TensorFlow> final : public AvgPoolGraphBuilderBase
-{
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __OP_AVG_POOL2D_H__
diff --git a/compiler/moco-tf/src/Op/AvgPool.test.cpp b/compiler/moco-tf/src/Op/AvgPool.test.cpp
deleted file mode 100644
index d5fb2082c..000000000
--- a/compiler/moco-tf/src/Op/AvgPool.test.cpp
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "AvgPool.h"
-
-#include "IR/TFAvgPool.h"
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-
-#include <loco.h>
-#include <loco/IR/TensorShape.h>
-#include <loco/IR/FeatureShape.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <memory>
-
-using namespace moco::tf;
-using namespace moco::tf::test;
-
-namespace
-{
-// clang-format off
-const char *avgpool_01_pbtxtdata = STRING_CONTENT(
-node {
- name: "const/float"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 1
- }
- dim {
- size: 3
- }
- dim {
- size: 3
- }
- dim {
- size: 1
- }
- }
- float_val: 1.1
- }
- }
- }
-}
-node {
- name: "avgpool"
- op: "AvgPool"
- input: "const/float"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "data_format"
- value {
- s: "NHWC"
- }
- }
- attr {
- key: "ksize"
- value {
- list {
- i: 1
- i: 2
- i: 3
- i: 1
- }
- }
- }
- attr {
- key: "padding"
- value {
- s: "VALID"
- }
- }
- attr {
- key: "strides"
- value {
- list {
- i: 1
- i: 3
- i: 2
- i: 1
- }
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, AvgPool_01)
-{
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
-
- signature.add_output(moco::tf::TensorName("avgpool", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(avgpool_01_pbtxtdata, graph_def));
-
- // Test "AvgPoolGraphBuilderImpl<ImportTarget::Canonical>"
- {
- // what to test:
- // - there should exist AvgPool2D
- // - input node should be FeatureEncode
- // - following node should be FeatureDecode
- // - stride values should match
- // - window values should match
-
- using AvgPoolGraphBuilder = AvgPoolGraphBuilderImpl<ImportTarget::Canonical>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("AvgPool", stdex::make_unique<AvgPoolGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- loco::AvgPool2D *avgpool2d_node =
- moco::tf::test::find_first_node_bytype<loco::AvgPool2D>(graph.get());
- ASSERT_NE(avgpool2d_node, nullptr);
-
- loco::Node *previous_node = avgpool2d_node->ifm();
- auto following_nodes = loco::succs(avgpool2d_node);
- ASSERT_EQ(following_nodes.size(), 1);
- loco::Node *following_node = *following_nodes.begin();
- ASSERT_NE(following_node, nullptr);
-
- loco::FeatureEncode *enc_node = dynamic_cast<loco::FeatureEncode *>(previous_node);
- loco::FeatureDecode *dec_node = dynamic_cast<loco::FeatureDecode *>(following_node);
-
- ASSERT_NE(enc_node, nullptr);
- ASSERT_NE(dec_node, nullptr);
-
- // attrs inside AvgPool2D
- auto avgpool2d = avgpool2d_node; // TODO remove this new variable
- // convention
- ASSERT_EQ(avgpool2d->convention(), loco::AvgPool2D::Convention::Valid);
-
- // stride
- ASSERT_EQ(avgpool2d->stride()->vertical(), 3);
- ASSERT_EQ(avgpool2d->stride()->horizontal(), 2);
-
- // window
- ASSERT_EQ(avgpool2d->window()->vertical(), 2);
- ASSERT_EQ(avgpool2d->window()->horizontal(), 3);
- }
-
- // Test "AvgPoolGraphBuilderImpl<ImportTarget::TensorFlow>"
- {
- // what to test:
- // - there should exist TFAvgPool
- // - attributes value should match
-
- using AvgPoolGraphBuilder = AvgPoolGraphBuilderImpl<ImportTarget::TensorFlow>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("AvgPool", stdex::make_unique<AvgPoolGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- moco::tf::TFAvgPool *avgpool_node =
- moco::tf::test::find_first_node_bytype<moco::tf::TFAvgPool>(graph.get());
- ASSERT_NE(avgpool_node, nullptr);
-
- loco::Node *previous_node = avgpool_node->value();
- auto following_nodes = loco::succs(avgpool_node);
- ASSERT_EQ(following_nodes.size(), 1);
- loco::Node *following_node = *following_nodes.begin();
- ASSERT_NE(following_node, nullptr);
-
- // attrs inside TFAvgPool2D
- ASSERT_EQ(avgpool_node->data_layout(), "NHWC");
- ASSERT_EQ(avgpool_node->padding(), "VALID");
- ASSERT_EQ(avgpool_node->ksize(), std::vector<int64_t>({1, 2, 3, 1}));
- ASSERT_EQ(avgpool_node->strides(), std::vector<int64_t>({1, 3, 2, 1}));
- }
-}
diff --git a/compiler/moco-tf/src/Op/BiasAdd.cpp b/compiler/moco-tf/src/Op/BiasAdd.cpp
deleted file mode 100644
index 6862a522d..000000000
--- a/compiler/moco-tf/src/Op/BiasAdd.cpp
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "BiasAdd.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-#include "Knob.h"
-
-#include "IR/TFBiasAdd.h"
-
-#include <moco/tf/Names.h>
-
-#include <loco.h>
-#include <loco/IR/PermutingCodec.h>
-#include <stdex/Memory.h>
-#include <plier/tf/Convert.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <cassert>
-#include <stdexcept>
-#include <vector>
-
-namespace
-{
-using namespace moco::tf;
-
-class ValueInputUpdate final : public GraphUpdate
-{
-public:
- ValueInputUpdate(loco::BiasAdd<loco::Domain::Tensor> *bias_add, const TensorName &&input_name)
- : _bias_add(bias_add), _input_name(input_name)
- {
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- loco::BiasAdd<loco::Domain::Tensor> *_bias_add;
- const TensorName _input_name;
-};
-
-void ValueInputUpdate::input(const SymbolTable *node_table) const
-{
- loco::Node *input_node = node_table->node(_input_name);
- _bias_add->value(input_node);
-}
-
-class BiasInputUpdate final : public GraphUpdate
-{
-public:
- BiasInputUpdate(loco::BiasEncode *bias_enc, const TensorName &&input_name)
- : _bias_enc(bias_enc), _input_name(input_name)
- {
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- loco::BiasEncode *_bias_enc;
- const TensorName _input_name;
-};
-
-void BiasInputUpdate::input(const SymbolTable *node_table) const
-{
- loco::Node *input_node = node_table->node(_input_name);
- _bias_enc->input(input_node);
-}
-
-class TFBiasAddGraphUpdate final : public GraphUpdate
-{
-public:
- TFBiasAddGraphUpdate(moco::tf::TFBiasAdd *biasadd, std::vector<TensorName> &names)
- : _biasadd(biasadd), _names(names)
- {
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- moco::tf::TFBiasAdd *_biasadd;
- std::vector<TensorName> _names;
-};
-
-void TFBiasAddGraphUpdate::input(const SymbolTable *node_table) const
-{
- assert(_names.size() == 2);
-
- auto value_node = node_table->node(_names[0]);
- auto bias_node = node_table->node(_names[1]);
- assert(value_node != nullptr);
- assert(bias_node != nullptr);
-
- _biasadd->value(value_node);
- _biasadd->bias(bias_node);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for BiasAdd node
- */
-class BiasAddGraphBuilder final : public BiasAddGraphBuilderBase
-{
-public:
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool BiasAddGraphBuilderBase::validate(const tensorflow::NodeDef &node) const
-{
- assert(node.input_size() == 2);
-
- // note: even though "data_format" is not entered when a model is written,
- // TF seems to generate "data_format" field into a pb file
- if (!plier::tf::has_attrs(node, {"T", "data_format"}))
- return false;
-
- // TODO add type check
- // type of input and bias should be same (except using quantization)
-
- // Note In case of TF.nn.bias_add,
- // "value may have any number of dimensions." ...
- // but "data_format: A string. 'NHWC' and 'NCHW' are supported."
- // Not sure if value should be 4-D tensor. Let's skip this check for now.
-
- return true;
-}
-
-void BiasAddGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- if (moco::tf::get<moco::tf::Knob::ImportAsTFBiasAdd>())
- {
- BiasAddGraphBuilderImpl<ImportTarget::TensorFlow> builder;
- return builder.build(node, context);
- }
- else
- {
- BiasAddGraphBuilderImpl<ImportTarget::Canonical> builder;
- return builder.build(node, context);
- }
-}
-
-void BiasAddGraphBuilderImpl<ImportTarget::Canonical>::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // tensorflow data_format: one of NHWC or NCHW.
- auto data_layout = plier::tf::get_data_layout(node, "data_format");
-
- // creating loco nodes
- auto bias_enc = graph->nodes()->create<loco::BiasEncode>();
-
- auto bias_add = graph->nodes()->create<loco::BiasAdd<loco::Domain::Tensor>>();
- {
- using plier::tf::DataLayout;
-
- if (data_layout == DataLayout::NHWC)
- {
- bias_add->axis(3);
- }
- else if (data_layout == DataLayout::NCHW)
- {
- bias_add->axis(1); // Channel
- // Note: the following descrition of TF 1.13 at
- // https://www.tensorflow.org/api_docs/python/tf/nn/bias_add seems wrong:
- // "bias: A 1-D Tensor with size matching the last dimension of value."
- // because providing the size of W (last dimension) to bias throws an error with TensorFlow
- }
- }
-
- // link nodes
- bias_add->bias(bias_enc);
-
- // To set the input node of encode_node with biasAdd_name
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, bias_add);
-
- // Record ifm inputs to featureEncode_node
- auto value_update = stdex::make_unique<ValueInputUpdate>(bias_add, TensorName(node.input(0)));
- auto bias_update = stdex::make_unique<BiasInputUpdate>(bias_enc, TensorName(node.input(1)));
-
- updates->enroll(std::move(value_update));
- updates->enroll(std::move(bias_update));
-}
-
-void BiasAddGraphBuilderImpl<ImportTarget::TensorFlow>::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // tensorflow data_format: one of NHWC or NCHW.
- auto data_layout = plier::tf::get_string_attr(node, "data_format");
- auto tf_bias_add = graph->nodes()->create<moco::tf::TFBiasAdd>();
-
- tf_bias_add->data_layout(data_layout);
-
- // To set the input node of encode_node with biasAdd_name
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, tf_bias_add);
-
- std::vector<TensorName> input_names;
- input_names.push_back(TensorName(node.input(0)));
- input_names.push_back(TensorName(node.input(1)));
-
- auto update = stdex::make_unique<TFBiasAddGraphUpdate>(tf_bias_add, input_names);
- updates->enroll(std::move(update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(BiasAdd, BiasAddGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/BiasAdd.h b/compiler/moco-tf/src/Op/BiasAdd.h
deleted file mode 100644
index 890ca65b1..000000000
--- a/compiler/moco-tf/src/Op/BiasAdd.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __OP_BIAS_ADD_H__
-#define __OP_BIAS_ADD_H__
-
-#include "GraphBuilder.h"
-#include "ImportTarget.h"
-
-namespace moco
-{
-namespace tf
-{
-
-struct BiasAddGraphBuilderBase : public GraphBuilder
-{
- virtual ~BiasAddGraphBuilderBase() = default;
-
- bool validate(const tensorflow::NodeDef &) const final;
-};
-
-template <ImportTarget T> class BiasAddGraphBuilderImpl;
-
-template <>
-struct BiasAddGraphBuilderImpl<ImportTarget::Canonical> final : public BiasAddGraphBuilderBase
-{
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
-};
-
-template <>
-struct BiasAddGraphBuilderImpl<ImportTarget::TensorFlow> final : public BiasAddGraphBuilderBase
-{
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __OP_BIAS_ADD_H__
diff --git a/compiler/moco-tf/src/Op/BiasAdd.test.cpp b/compiler/moco-tf/src/Op/BiasAdd.test.cpp
deleted file mode 100644
index 823b66f42..000000000
--- a/compiler/moco-tf/src/Op/BiasAdd.test.cpp
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "BiasAdd.h"
-#include "TestHelper.h"
-
-#include "Importer.h"
-#include "IR/TFBiasAdd.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-using namespace moco::tf;
-using namespace moco::tf::test;
-
-namespace
-{
-
-// clang-format off
-const char *bias_add_01_pbtxtdata = STRING_CONTENT(
-
-node {
- name: "val"
- op: "Const"
- attr {
- key: "dtype"
- value { type: DT_FLOAT }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim { size: 1 }
- dim { size: 5 }
- dim { size: 5 }
- dim { size: 3 }
- }
- float_val: 2.1
- }
- }
- }
-}
-node {
- name: "bias"
- op: "Const"
- attr {
- key: "dtype"
- value { type: DT_FLOAT }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim { size: 3 }
- }
- float_val: 1.1
- }
- }
- }
-}
-node {
- name: "out"
- op: "BiasAdd"
- input: "val"
- input: "bias"
- attr {
- key: "T"
- value { type: DT_FLOAT }
- }
- attr {
- key: "data_format"
- value { s: "NHWC" }
- }
-}
-
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, bias_add_01)
-{
- moco::tf::ModelSignature signature;
- signature.add_output(moco::tf::TensorName("out", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(bias_add_01_pbtxtdata, graph_def));
-
- // Test "BiasAddGraphBuilderImpl<ImportTarget::TensorFlow>"
- {
- using BiasAddGraphBuilder = BiasAddGraphBuilderImpl<ImportTarget::TensorFlow>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("BiasAdd", stdex::make_unique<BiasAddGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - there should exist TFBiasAdd
- // - value() should not be nullptr
- // - bias() should not be nullptr
- // - data_layout should match
-
- moco::tf::TFBiasAdd *bias_add =
- moco::tf::test::find_first_node_bytype<moco::tf::TFBiasAdd>(graph.get());
-
- ASSERT_NE(bias_add, nullptr);
-
- ASSERT_NE(bias_add->value(), nullptr);
- ASSERT_NE(bias_add->bias(), nullptr);
-
- ASSERT_TRUE(bias_add->data_layout() == "NHWC");
- }
- // Test "BiasAddGraphBuilderImpl<ImportTarget::Canonical>"
- {
- using BiasAddGraphBuilder = BiasAddGraphBuilderImpl<ImportTarget::Canonical>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("BiasAdd", stdex::make_unique<BiasAddGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - there should exist BiasAdd
- // - value() should not be nullptr
- // - bias() input should be BiasEncode
- // - axis should match
-
- // loco node : ------------+-- BiasAdd<Domain::Tensor> --
- // BiasEncode -/
-
- loco::BiasAdd<loco::Domain::Tensor> *bias_add =
- moco::tf::test::find_first_node_bytype<loco::BiasAdd<loco::Domain::Tensor>>(graph.get());
-
- ASSERT_NE(bias_add, nullptr);
- ASSERT_NE(bias_add->value(), nullptr);
-
- auto bias_enc = dynamic_cast<loco::BiasEncode *>(bias_add->bias());
- ASSERT_NE(bias_enc, nullptr);
-
- ASSERT_EQ(bias_add->axis(), 3); // NHWC
- }
-}
-
-namespace
-{
-
-// clang-format off
-const char *bias_add_NCHW_pbtxtdata = STRING_CONTENT(
-
-node {
- name: "val"
- op: "Const"
- attr {
- key: "dtype"
- value { type: DT_FLOAT }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim { size: 1 }
- dim { size: 3 }
- dim { size: 299 }
- dim { size: 299 }
- }
- float_val: 2.1
- }
- }
- }
-}
-node {
- name: "bias"
- op: "Const"
- attr {
- key: "dtype"
- value { type: DT_FLOAT }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim { size: 3 }
- }
- float_val: 1.1
- }
- }
- }
-}
-node {
- name: "out"
- op: "BiasAdd"
- input: "val"
- input: "bias"
- attr {
- key: "T"
- value { type: DT_FLOAT }
- }
- attr {
- key: "data_format"
- value { s: "NCHW" }
- }
-}
-
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, bias_add_NCHW_axis)
-{
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
- signature.add_output(moco::tf::TensorName("out", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(bias_add_NCHW_pbtxtdata, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // Test "BiasAddGraphBuilderImpl<ImportTarget::TensorFlow>"
- {
- using BiasAddGraphBuilder = BiasAddGraphBuilderImpl<ImportTarget::TensorFlow>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("BiasAdd", stdex::make_unique<BiasAddGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - there should exist TFBiasAdd
- // - value() should not be nullptr
- // - bias() should not be nullptr
- // - data_layout should match
-
- moco::tf::TFBiasAdd *bias_add =
- moco::tf::test::find_first_node_bytype<moco::tf::TFBiasAdd>(graph.get());
-
- ASSERT_NE(bias_add, nullptr);
-
- ASSERT_NE(bias_add->value(), nullptr);
- ASSERT_NE(bias_add->bias(), nullptr);
-
- ASSERT_TRUE(bias_add->data_layout() == "NCHW");
- }
- // Test "BiasAddGraphBuilderImpl<ImportTarget::Canonical>"
- {
- using BiasAddGraphBuilder = BiasAddGraphBuilderImpl<ImportTarget::Canonical>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("BiasAdd", stdex::make_unique<BiasAddGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - there should exist BiasAdd
- // - value() should not be nullptr
- // - bias() input should be BiasEncode
- // - axis should match
-
- // loco node : ------------+-- BiasAdd<Domain::Tensor> --
- // BiasEncode -/
-
- loco::BiasAdd<loco::Domain::Tensor> *bias_add =
- moco::tf::test::find_first_node_bytype<loco::BiasAdd<loco::Domain::Tensor>>(graph.get());
-
- ASSERT_NE(bias_add, nullptr);
- ASSERT_NE(bias_add->value(), nullptr);
-
- auto bias_enc = dynamic_cast<loco::BiasEncode *>(bias_add->bias());
- ASSERT_NE(bias_enc, nullptr);
-
- ASSERT_EQ(bias_add->axis(), 1); // NCHW
- }
-}
diff --git a/compiler/moco-tf/src/Op/COpCall.cpp b/compiler/moco-tf/src/Op/COpCall.cpp
index 2bd3fcdfc..801196f0f 100644
--- a/compiler/moco-tf/src/Op/COpCall.cpp
+++ b/compiler/moco-tf/src/Op/COpCall.cpp
@@ -17,17 +17,14 @@
#include "COpCall.h"
#include "Convert.h"
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
#include <locoex/COpCall.h>
#include <locoex/COpAttrTypes.h>
-#include <moco/tf/Names.h>
+#include <moco/Names.h>
#include <moco/tf/Frontend.h>
#include <loco.h>
#include <stdex/Memory.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
+#include <oops/UserExn.h>
#include <vector>
#include <cassert>
@@ -36,22 +33,22 @@
namespace
{
-class COpCallGraphUpdate final : public moco::tf::GraphUpdate
+class COpCallGraphUpdate final : public moco::GraphUpdate
{
public:
- COpCallGraphUpdate(locoex::COpCall *node, const std::vector<moco::tf::TensorName> &input_names)
+ COpCallGraphUpdate(locoex::COpCall *node, const std::vector<moco::TensorName> &input_names)
: _node(node), _input_names(input_names)
{
}
- void input(const moco::tf::SymbolTable *) const override;
+ void input(const moco::SymbolTable *) const override;
private:
locoex::COpCall *_node;
- const std::vector<moco::tf::TensorName> _input_names;
+ const std::vector<moco::TensorName> _input_names;
};
-void COpCallGraphUpdate::input(const moco::tf::SymbolTable *tensor_names) const
+void COpCallGraphUpdate::input(const moco::SymbolTable *tensor_names) const
{
for (int n = 0; n < _input_names.size(); n++)
{
@@ -106,7 +103,7 @@ void COpCallGraphBuilder::build(const tensorflow::NodeDef &tf_node,
// TODO define more types
else
{
- throw std::runtime_error("not supported attribute type");
+ throw oops::UserExn("Unsupported attribute type", tf_node.name());
}
}
}
diff --git a/compiler/moco-tf/src/Op/COpCall.h b/compiler/moco-tf/src/Op/COpCall.h
index ea81d3a98..0bb8a93c9 100644
--- a/compiler/moco-tf/src/Op/COpCall.h
+++ b/compiler/moco-tf/src/Op/COpCall.h
@@ -17,12 +17,9 @@
#ifndef __OP_COP_CALL_H__
#define __OP_COP_CALL_H__
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-
#include <moco/tf/Frontend.h>
-#include <tensorflow/core/framework/graph.pb.h>
+#include <moco/Import/GraphBuilder.h>
namespace moco
{
diff --git a/compiler/moco-tf/src/Op/COpCall.test.cpp b/compiler/moco-tf/src/Op/COpCall.test.cpp
index 7b6f8d5c4..f13118292 100644
--- a/compiler/moco-tf/src/Op/COpCall.test.cpp
+++ b/compiler/moco-tf/src/Op/COpCall.test.cpp
@@ -18,14 +18,16 @@
#include "TestHelper.h"
-#include "Importer.h"
#include "Canonicalizer.h"
+#include <moco/Importer.h>
+
#include <locoex/COpCall.h>
#include <locoex/COpAttrTypes.h>
#include <loco.h>
#include <plier/tf/TestHelper.h>
+#include <stdex/Memory.h>
#include <gtest/gtest.h>
@@ -75,10 +77,10 @@ node {
TEST(Call_Test, Call_01)
{
- moco::tf::ModelSignature signature;
+ moco::ModelSignature signature;
{
- signature.add_input(moco::tf::TensorName("input1", 0));
- signature.add_output(moco::tf::TensorName("my/customOp/000", 0));
+ signature.add_input(moco::TensorName("input1", 0));
+ signature.add_output(moco::TensorName("my/customOp/000", 0));
signature.add_customop("new_custom_op");
signature.dtype("my/customOp/000", loco::DataType::FLOAT32);
signature.shape("my/customOp/000", {1, 2});
@@ -88,10 +90,10 @@ TEST(Call_Test, Call_01)
EXPECT_TRUE(plier::tf::parse_graphdef(customop_01_pbtxtdata, graph_def));
// import
- moco::tf::GraphBuilderRegistry registry{&moco::tf::GraphBuilderRegistry::get()};
+ moco::GraphBuilderRegistry registry{&moco::GraphBuilderRegistry::get()};
registry.add("new_custom_op", stdex::make_unique<moco::tf::COpCallGraphBuilder>(&signature));
- moco::tf::Importer importer(&registry);
+ moco::Importer importer(&registry);
std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
// what to test:
diff --git a/compiler/moco-tf/src/Op/Concat.cpp b/compiler/moco-tf/src/Op/Concat.cpp
deleted file mode 100644
index 33788cdbf..000000000
--- a/compiler/moco-tf/src/Op/Concat.cpp
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Concat.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-#include "Knob.h"
-
-#include "IR/TFConcatV2.h"
-
-#include "Annotations/ConcatData.h"
-
-#include <moco/tf/Names.h>
-
-#include <loco.h>
-#include <stdex/Memory.h>
-#include <plier/tf/Convert.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <cassert>
-#include <stdexcept>
-
-namespace
-{
-
-using namespace moco::tf;
-
-class ConcatV2GraphUpdate final : public GraphUpdate
-{
-public:
- ConcatV2GraphUpdate(std::vector<loco::TensorConcat *> nodes, std::vector<TensorName> names)
- : _nodes(nodes), _names(names)
- {
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- std::vector<loco::TensorConcat *> _nodes;
- std::vector<TensorName> _names;
-};
-
-class TFConcatV2GraphUpdate final : public GraphUpdate
-{
-public:
- TFConcatV2GraphUpdate(moco::tf::TFConcatV2 *node, std::vector<TensorName> names)
- : _node(node), _names(names)
- {
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- moco::tf::TFConcatV2 *_node;
- std::vector<TensorName> _names;
-};
-
-void ConcatV2GraphUpdate::input(const SymbolTable *tensor_names) const
-{
- int num_inputs = _names.size();
- assert(num_inputs >= 2);
- assert(num_inputs == _nodes.size());
-
- loco::Node *target;
- // do "%0.lhs : %in[0].name" connection
- target = tensor_names->node(_names[0]);
- _nodes[0]->lhs(target);
-
- for (int i = 1; i < num_inputs; ++i)
- {
- // do "%i.rhs : %in[i].name" connections
- target = tensor_names->node(_names[i]);
- _nodes[i]->rhs(target);
- }
-}
-
-void TFConcatV2GraphUpdate::input(const SymbolTable *tensor_names) const
-{
- uint32_t num_values = _names.size() - 1; // exclude axis
- assert(num_values >= 1);
-
- for (uint32_t i = 0; i < num_values; ++i)
- {
- auto input_node = tensor_names->node(_names[i]);
- assert(input_node != nullptr);
- _node->values(i, input_node);
- }
- auto axis_node = tensor_names->node(_names[num_values]);
- assert(axis_node != nullptr);
- _node->axis(axis_node);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-bool ConcatV2GraphBuilderBase::validate(const tensorflow::NodeDef &node) const
-{
- if (!plier::tf::has_attrs(node, {"T", "N", "Tidx"}))
- return false;
-
- // Concat node SHOULD have 3 or more inputs, that is 2 + axis
- const int num_inputs = node.input_size() - 1;
- assert(num_inputs >= 2);
- assert(num_inputs == plier::tf::get_int_attr(node, "N"));
- return (num_inputs >= 2) && (num_inputs == plier::tf::get_int_attr(node, "N"));
-}
-
-/**
- * @brief GraphBuilder for Concat node of Tensor
- */
-class ConcatV2GraphBuilder final : public ConcatV2GraphBuilderBase
-{
-public:
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-void ConcatV2GraphBuilder::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- if (moco::tf::get<moco::tf::Knob::ImportAsTFConcatV2>())
- {
- ConcatV2GraphBuilderImpl<ImportTarget::TensorFlow> builder;
- return builder.build(node, context);
- }
- else
- {
- ConcatV2GraphBuilderImpl<ImportTarget::Canonical> builder;
- return builder.build(node, context);
- }
-}
-
-void ConcatV2GraphBuilderImpl<ImportTarget::Canonical>::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- loco::Graph *graph = context->graph();
- NodeDefTable *nodedef = context->nodedef();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // Concat has 2 or more inputs and loco TensorConcat is fixed to 2 inputs
- // for arbitrary N inputs (beginning from 0), TensorConcat will be created
- // as follows;
- // %0 = TensorConcat(%in[0], %in[1])
- // %1 = %0 --> this is to match index of input name
- // %2 = TensorConcat(%1, %in[2])
- // ...
- // %(N-1) = TensorConcat(%(N-2), %in[N-1]))
- // %N = TensorConcat(%(N-1), %in[N]))
- //
- // Output of this sub graph will be set to %N with node.name()
- //
- // As we know that each input exist, one of input(lhs) can be linked while creating
- // %2.lhs = %1
- // %3.lhs = %2
- // ...
- // %(N-1).lhs = %(N-2)
- // %N.lhs = %(N-1)
-
- const int num_inputs = node.input_size() - 1;
-
- std::vector<loco::TensorConcat *> concat_nodes;
- std::vector<TensorName> input_names;
-
- auto concat_node = graph->nodes()->create<loco::TensorConcat>();
- loco::TensorConcat *last_concat = concat_node;
-
- // Queue node input update
- concat_nodes.push_back(concat_node); // used for LHS of connection -> %0
- concat_nodes.push_back(concat_node); // used for RHS of connection -> %1
- input_names.push_back(TensorName(node.input(0))); // for first concat (%0) LHS
- input_names.push_back(TensorName(node.input(1))); // for first concat (%1) RHS
-
- for (int ni = 2; ni < num_inputs; ++ni)
- {
- auto concat_node_next = graph->nodes()->create<loco::TensorConcat>();
-
- concat_nodes.push_back(concat_node_next);
- input_names.push_back(TensorName(node.input(ni)));
-
- // connect LHS as we know the nodes
- concat_node_next->lhs(last_concat);
-
- // update last concat node
- last_concat = concat_node_next;
- }
-
- // register string-name to the last node as output of concat(s)
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, last_concat);
-
- // Find axis tensorflow::NodeDef and get the axis number
- std::string axis_name = node.input(num_inputs);
- const tensorflow::NodeDef *tfnode = nodedef->node(axis_name);
- // assume data type is int32
- assert(plier::tf::get_datatype_attr(*tfnode, "dtype") == tensorflow::DataType::DT_INT32);
- const auto &tensor = plier::tf::get_tensor_attr(*tfnode, "value");
- assert(tensor.int_val_size() == 1);
- auto axis_value_read = tensor.int_val(0);
-
- // set axis for all concat(s) as temporary data
- // as the first and the second items are actually the same one, skip it.
- std::vector<loco::TensorConcat *>::iterator iter = concat_nodes.begin();
- for (++iter; iter != concat_nodes.end(); ++iter)
- {
- auto concat_node = *iter;
- auto concat_data = stdex::make_unique<ConcatData>(axis_value_read);
-
- concat_node->annot(std::move(concat_data));
- }
-
- // Input name queue is created like this in 'concat_nodes' and 'input_names'
- // %0.lhs : %in[0].name
- // %1.rhs : %in[1].name (as %0 == %1)
- // %2.rhs : %in[2].name
- // %3.rhs : %in[3].name
- // ...
- // %(N-2).rhs : %in[N-2].name
- // %(N-1).rhs : %in[N-1].name
- auto update = stdex::make_unique<ConcatV2GraphUpdate>(concat_nodes, input_names);
- updates->enroll(std::move(update));
-}
-
-void ConcatV2GraphBuilderImpl<ImportTarget::TensorFlow>::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- loco::Graph *graph = context->graph();
- NodeDefTable *nodedef = context->nodedef();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- const int num_inputs = node.input_size() - 1;
- std::vector<TensorName> input_names;
- auto concat_node = graph->nodes()->create<TFConcatV2>(num_inputs);
-
- for (int ni = 0; ni < num_inputs; ++ni)
- {
- input_names.push_back(TensorName(node.input(ni)));
- }
- // last one is the axis
- input_names.push_back(TensorName(node.input(num_inputs)));
-
- // register string-name to the last node as output of concat(s)
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, concat_node);
-
- auto update = stdex::make_unique<TFConcatV2GraphUpdate>(concat_node, input_names);
- updates->enroll(std::move(update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(ConcatV2, ConcatV2GraphBuilder)
diff --git a/compiler/moco-tf/src/Op/Concat.h b/compiler/moco-tf/src/Op/Concat.h
deleted file mode 100644
index 6a5a857e3..000000000
--- a/compiler/moco-tf/src/Op/Concat.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __OP_CONCAT_H__
-#define __OP_CONCAT_H__
-
-#include "GraphBuilder.h"
-#include "ImportTarget.h"
-
-namespace moco
-{
-namespace tf
-{
-
-struct ConcatV2GraphBuilderBase : public GraphBuilder
-{
- virtual ~ConcatV2GraphBuilderBase() = default;
-
- bool validate(const tensorflow::NodeDef &) const final;
-};
-
-template <ImportTarget T> class ConcatV2GraphBuilderImpl;
-
-template <>
-struct ConcatV2GraphBuilderImpl<ImportTarget::Canonical> final : public ConcatV2GraphBuilderBase
-{
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
-};
-
-template <>
-struct ConcatV2GraphBuilderImpl<ImportTarget::TensorFlow> final : public ConcatV2GraphBuilderBase
-{
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __OP_CONCAT_H__
diff --git a/compiler/moco-tf/src/Op/Concat.test.cpp b/compiler/moco-tf/src/Op/Concat.test.cpp
deleted file mode 100644
index e9ddd6bea..000000000
--- a/compiler/moco-tf/src/Op/Concat.test.cpp
+++ /dev/null
@@ -1,449 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Concat.h"
-
-#include "IR/TFConcatV2.h"
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-using namespace moco::tf;
-using namespace moco::tf::test;
-
-namespace
-{
-
-// clang-format off
-const char *concat_01_pbtxtdata = STRING_CONTENT(
-node {
- name: "Input01"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 2
- }
- dim {
- size: 3
- }
- }
- float_val: 1
- float_val: 2
- float_val: 3
- float_val: 4
- float_val: 5
- float_val: 6
- }
- }
- }
-}
-node {
- name: "Input02"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 2
- }
- dim {
- size: 3
- }
- }
- float_val: 7
- float_val: 8
- float_val: 9
- float_val: 10
- float_val: 11
- float_val: 12
- }
- }
- }
-}
-node {
- name: "Axis"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_INT32
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_INT32
- tensor_shape {
- }
- int_val: 0
- }
- }
- }
-}
-node {
- name: "Concat"
- op: "ConcatV2"
- input: "Input01"
- input: "Input02"
- input: "Axis"
- attr {
- key: "N"
- value {
- i: 2
- }
- }
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "Tidx"
- value {
- type: DT_INT32
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, concat_01)
-{
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
-
- moco::tf::TensorName output("Concat", 0);
- signature.add_output(output);
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(concat_01_pbtxtdata, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // Test "ConcatV2GraphBuilderImpl<ImportTarget::Canonical>"
- {
- // TODO fix indent
- // clang-format off
-
- // what to test:
- // - there should exist TensorConcat
- // - lhs() should not be nullptr
- // - rhs() should not be nullptr
- // - axis() should match
-
- using ConcatV2GraphBuilder = ConcatV2GraphBuilderImpl<ImportTarget::Canonical>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("ConcatV2", stdex::make_unique<ConcatV2GraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- loco::TensorConcat *concat_node =
- moco::tf::test::find_first_node_bytype<loco::TensorConcat>(graph.get());
-
- ASSERT_NE(concat_node, nullptr);
- ASSERT_NE(concat_node->lhs(), nullptr);
- ASSERT_NE(concat_node->rhs(), nullptr);
- ASSERT_EQ(concat_node->axis(), 0);
-
- // clang-format on
- }
-
- // Test "ConcatV2GraphBuilderImpl<ImportTarget::Tensorflow>"
- {
- // what to test:
- // - there should exist TFConcatV2
- // - there should be two values
- // - values(idx) should not be nullptr
- // - axis() should not be nullptr
-
- using ConcatV2GraphBuilder = ConcatV2GraphBuilderImpl<ImportTarget::TensorFlow>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("ConcatV2", stdex::make_unique<ConcatV2GraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- auto concat_node = moco::tf::test::find_first_node_bytype<moco::tf::TFConcatV2>(graph.get());
-
- ASSERT_NE(concat_node, nullptr);
- ASSERT_EQ(concat_node->num_values(), 2);
- ASSERT_NE(concat_node->values(0), nullptr);
- ASSERT_NE(concat_node->values(1), nullptr);
- ASSERT_NE(concat_node->axis(), nullptr);
- }
-}
-
-namespace
-{
-
-// clang-format off
-const char *concat_02_pbtxtdata = STRING_CONTENT(
-node {
- name: "Input01"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 2
- }
- dim {
- size: 3
- }
- }
- float_val: 1
- float_val: 2
- float_val: 3
- float_val: 4
- float_val: 5
- float_val: 6
- }
- }
- }
-}
-node {
- name: "Input02"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 2
- }
- dim {
- size: 3
- }
- }
- float_val: 7
- float_val: 8
- float_val: 9
- float_val: 10
- float_val: 11
- float_val: 12
- }
- }
- }
-}
-node {
- name: "Input03"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 2
- }
- dim {
- size: 3
- }
- }
- float_val: 13
- float_val: 14
- float_val: 15
- float_val: 16
- float_val: 17
- float_val: 18
- }
- }
- }
-}
-node {
- name: "Axis"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_INT32
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_INT32
- tensor_shape {
- }
- int_val: 0
- }
- }
- }
-}
-node {
- name: "Concat"
- op: "ConcatV2"
- input: "Input01"
- input: "Input02"
- input: "Input03"
- input: "Axis"
- attr {
- key: "N"
- value {
- i: 3
- }
- }
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "Tidx"
- value {
- type: DT_INT32
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, concat_02)
-{
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
-
- moco::tf::TensorName output("Concat", 0);
- signature.add_output(output);
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(concat_02_pbtxtdata, graph_def));
-
- // Test "ConcatV2GraphBuilderImpl<ImportTarget::Canonical>"
- {
- // TODO fix indent
- // clang-format off
-
- // what to test: Concat has 3 inputs --> Importer creates 2 TensorConcat
- // - there should exist two TensorConcat
- // - lhs() of #1 should not be nullptr
- // - rhs() of #1 should not be nullptr
- // - lhs() of #2 should be #1
- // - rhs() of #2 should not be nullptr
- // - axis() should match
-
- using ConcatV2GraphBuilder = ConcatV2GraphBuilderImpl<ImportTarget::Canonical>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("ConcatV2", stdex::make_unique<ConcatV2GraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- std::vector<loco::TensorConcat *> concat_nodes =
- moco::tf::test::find_nodes_bytype<loco::TensorConcat>(graph.get());
- ASSERT_EQ(concat_nodes.size(), 2);
- loco::TensorConcat *concat_node0 = concat_nodes.at(0);
- loco::TensorConcat *concat_node1 = concat_nodes.at(1);
-
- ASSERT_NE(concat_node0, nullptr);
- ASSERT_NE(concat_node1, nullptr);
- ASSERT_NE(concat_node0->lhs(), nullptr);
- ASSERT_NE(concat_node0->rhs(), nullptr);
- ASSERT_NE(concat_node1->lhs(), nullptr);
- ASSERT_NE(concat_node1->rhs(), nullptr);
- ASSERT_TRUE(concat_node0->lhs() == concat_node1 || concat_node1->lhs() == concat_node0);
- ASSERT_EQ(concat_node0->axis(), 0);
- ASSERT_EQ(concat_node1->axis(), 0);
-
- // clang-format on
- }
-
- // Test "ConcatV2GraphBuilderImpl<ImportTarget::TensorFlow>"
- {
- // what to test: TFConcatV2 has 3 inputs
- // - there should exist TFConcatV2
- // - values(idx) should not be nullptr
- // - axis() should not be nullptr
-
- using ConcatV2GraphBuilder = ConcatV2GraphBuilderImpl<ImportTarget::TensorFlow>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("ConcatV2", stdex::make_unique<ConcatV2GraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- auto concat_node = moco::tf::test::find_first_node_bytype<moco::tf::TFConcatV2>(graph.get());
-
- ASSERT_NE(concat_node, nullptr);
- ASSERT_EQ(concat_node->num_values(), 3);
- ASSERT_NE(concat_node->values(0), nullptr);
- ASSERT_NE(concat_node->values(1), nullptr);
- ASSERT_NE(concat_node->values(2), nullptr);
- ASSERT_NE(concat_node->axis(), nullptr);
- }
-}
diff --git a/compiler/moco-tf/src/Op/Const.cpp b/compiler/moco-tf/src/Op/Const.cpp
deleted file mode 100644
index f69645187..000000000
--- a/compiler/moco-tf/src/Op/Const.cpp
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Const.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-#include "Knob.h"
-
-#include "IR/TFConst.h"
-
-#include <moco/tf/Names.h>
-#include <loco.h>
-#include <plier/tf/Convert.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <cassert>
-#include <stdexcept>
-#include <string>
-
-namespace
-{
-
-void read_value_int32(loco::ConstGen *const_node, int num_elements,
- const tensorflow::TensorProto &input_tensor)
-{
- const_node->size<loco::DataType::S32>(num_elements);
-
- int32_t input_elements = input_tensor.int_val_size();
-
- if (input_tensor.tensor_content().size() == num_elements * sizeof(int32_t))
- {
- const std::string &str_content = input_tensor.tensor_content();
- const int32_t *s32_ptr = reinterpret_cast<const int32_t *>(str_content.c_str());
- for (int32_t i = 0; i < num_elements; i++)
- {
- const_node->at<loco::DataType::S32>(i) = *(s32_ptr + i);
- }
- }
- else if (0 < input_elements && input_elements <= num_elements)
- {
- for (int32_t i = 0; i < input_elements; i++)
- {
- const_node->at<loco::DataType::S32>(i) = input_tensor.int_val(i);
- }
-
- for (int32_t i = input_elements; i < num_elements; i++)
- {
- const_node->at<loco::DataType::S32>(i) = input_tensor.int_val(input_elements - 1);
- }
- }
- else
- {
- throw std::runtime_error("Error: Invalid Const values");
- }
-}
-
-void read_value_float32(loco::ConstGen *const_node, int num_elements,
- const tensorflow::TensorProto &input_tensor)
-{
- const_node->size<loco::DataType::FLOAT32>(num_elements);
-
- int32_t input_elements = input_tensor.float_val_size();
-
- if (input_tensor.tensor_content().size() == num_elements * sizeof(float))
- {
- const std::string &str_content = input_tensor.tensor_content();
- const float *float_ptr = reinterpret_cast<const float *>(str_content.c_str());
- for (int32_t i = 0; i < num_elements; i++)
- {
- const_node->at<loco::DataType::FLOAT32>(i) = *(float_ptr + i);
- }
- }
- else if (0 < input_elements && input_elements <= num_elements)
- {
- for (int32_t i = 0; i < input_elements; i++)
- {
- const_node->at<loco::DataType::FLOAT32>(i) = input_tensor.float_val(i);
- }
-
- for (int32_t i = input_elements; i < num_elements; i++)
- {
- const_node->at<loco::DataType::FLOAT32>(i) = input_tensor.float_val(input_elements - 1);
- }
- }
- else
- {
- throw std::runtime_error("Error: Invalid Const values");
- }
-}
-
-} // namespace
-
-namespace
-{
-
-void read_value_int32(moco::tf::TFConst *const_node, int num_elements,
- const tensorflow::TensorProto &input_tensor)
-{
- const_node->size<loco::DataType::S32>(num_elements);
-
- int32_t input_elements = input_tensor.int_val_size();
-
- if (input_tensor.tensor_content().size() == num_elements * sizeof(int32_t))
- {
- const std::string &str_content = input_tensor.tensor_content();
- const int32_t *s32_ptr = reinterpret_cast<const int32_t *>(str_content.c_str());
- for (int32_t i = 0; i < num_elements; i++)
- {
- const_node->at<loco::DataType::S32>(i) = *(s32_ptr + i);
- }
- }
- else if (0 < input_elements && input_elements <= num_elements)
- {
- for (int32_t i = 0; i < input_elements; i++)
- {
- const_node->at<loco::DataType::S32>(i) = input_tensor.int_val(i);
- }
-
- for (int32_t i = input_elements; i < num_elements; i++)
- {
- const_node->at<loco::DataType::S32>(i) = input_tensor.int_val(input_elements - 1);
- }
- }
- else
- {
- throw std::runtime_error("Error: Invalid Const values");
- }
-}
-
-void read_value_float32(moco::tf::TFConst *const_node, int num_elements,
- const tensorflow::TensorProto &input_tensor)
-{
- const_node->size<loco::DataType::FLOAT32>(num_elements);
-
- int32_t input_elements = input_tensor.float_val_size();
-
- if (input_tensor.tensor_content().size() == num_elements * sizeof(float))
- {
- const std::string &str_content = input_tensor.tensor_content();
- const float *float_ptr = reinterpret_cast<const float *>(str_content.c_str());
- for (int32_t i = 0; i < num_elements; i++)
- {
- const_node->at<loco::DataType::FLOAT32>(i) = *(float_ptr + i);
- }
- }
- else if (0 < input_elements && input_elements <= num_elements)
- {
- for (int32_t i = 0; i < input_elements; i++)
- {
- const_node->at<loco::DataType::FLOAT32>(i) = input_tensor.float_val(i);
- }
-
- for (int32_t i = input_elements; i < num_elements; i++)
- {
- const_node->at<loco::DataType::FLOAT32>(i) = input_tensor.float_val(input_elements - 1);
- }
- }
- else
- {
- throw std::runtime_error("Error: Invalid Const values");
- }
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for Const node
- */
-class ConstGraphBuilder final : public ConstGraphBuilderBase
-{
-public:
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool ConstGraphBuilderBase::validate(const tensorflow::NodeDef &node) const
-{
- return plier::tf::has_attrs(node, {"dtype", "value"});
-}
-
-void ConstGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- if (moco::tf::get<moco::tf::Knob::ImportAsTFConst>())
- {
- ConstGraphBuilderImpl<ImportTarget::TensorFlow> builder;
- builder.build(node, context);
- }
- else
- {
- ConstGraphBuilderImpl<ImportTarget::Canonical> builder;
- builder.build(node, context);
- }
-}
-
-void ConstGraphBuilderImpl<ImportTarget::Canonical>::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
-
- // Create a "ConstGen" node for Const
- auto const_node = graph->nodes()->create<loco::ConstGen>();
-
- // set dtype
- auto dtype = plier::tf::as_loco_datatype(plier::tf::get_datatype_attr(node, "dtype"));
- const_node->dtype(dtype);
-
- // import shape and value
- const auto &input_tensor = plier::tf::get_tensor_attr(node, "value");
- const auto &input_shape = input_tensor.tensor_shape();
- const auto &input_dims = input_shape.dim();
- assert(input_shape.dim_size() <= 6);
- const_node->rank(input_shape.dim_size());
- int index = 0;
- bool zero_sized_shape = false;
- for (auto &d : input_dims)
- {
- if (d.size() > std::numeric_limits<int>::max())
- throw std::runtime_error("Shape element overflows");
- if (d.size() == 0)
- zero_sized_shape = true;
-
- if (d.size() >= 0)
- const_node->dim(index++) = d.size();
- else
- throw std::runtime_error{"Error: Unknown dim size for " + node.name()};
- }
-
- int num_elements = 1;
- if (zero_sized_shape)
- {
- const_node->rank(0);
- num_elements = 0;
- }
- else
- {
- for (int d = 0; d < const_node->rank(); d++)
- {
- num_elements *= const_node->dim(d).value();
- }
- }
-
- switch (dtype)
- {
- case loco::DataType::S32:
- read_value_int32(const_node, num_elements, input_tensor);
- break;
-
- case loco::DataType::FLOAT32:
- read_value_float32(const_node, num_elements, input_tensor);
- break;
-
- // TODO support other types
-
- default:
- throw std::runtime_error{"Error: Unsupported data type for " + node.name()};
- }
-
- // register string-name to node
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, const_node);
-}
-
-void ConstGraphBuilderImpl<ImportTarget::TensorFlow>::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
-
- // Create a "TFConstant" node for Const
- auto const_node = graph->nodes()->create<moco::tf::TFConst>();
-
- // set dtype
- auto dtype = plier::tf::as_loco_datatype(plier::tf::get_datatype_attr(node, "dtype"));
- const_node->dtype(dtype);
-
- // import shape and value
- const auto &input_tensor = plier::tf::get_tensor_attr(node, "value");
- const auto &input_shape = input_tensor.tensor_shape();
- const auto &input_dims = input_shape.dim();
- assert(input_shape.dim_size() <= 6);
- const_node->rank(input_shape.dim_size());
- int index = 0;
- bool zero_sized_shape = false;
- for (auto &d : input_dims)
- {
- if (d.size() > std::numeric_limits<int>::max())
- throw std::runtime_error("Shape element overflows");
- if (d.size() == 0)
- zero_sized_shape = true;
-
- if (d.size() >= 0)
- const_node->dim(index++) = d.size();
- else
- throw std::runtime_error{"Error: Unknown dim size for " + node.name()};
- }
-
- int num_elements = 1;
- if (zero_sized_shape)
- {
- const_node->rank(0);
- num_elements = 0;
- }
- else
- {
- for (int d = 0; d < const_node->rank(); d++)
- {
- num_elements *= const_node->dim(d).value();
- }
- }
-
- switch (dtype)
- {
- case loco::DataType::S32:
- read_value_int32(const_node, num_elements, input_tensor);
- break;
-
- case loco::DataType::FLOAT32:
- read_value_float32(const_node, num_elements, input_tensor);
- break;
-
- // TODO support other types
-
- default:
- throw std::runtime_error{"Error: Unsupported data type for " + node.name()};
- }
-
- // register string-name to node
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, const_node);
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(Const, ConstGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/Const.h b/compiler/moco-tf/src/Op/Const.h
deleted file mode 100644
index 4e727f06a..000000000
--- a/compiler/moco-tf/src/Op/Const.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __OP_CONST_H__
-#define __OP_CONST_H__
-
-#include "GraphBuilder.h"
-#include "ImportTarget.h"
-
-namespace moco
-{
-namespace tf
-{
-
-struct ConstGraphBuilderBase : public GraphBuilder
-{
- virtual ~ConstGraphBuilderBase() = default;
-
- bool validate(const tensorflow::NodeDef &) const final;
-};
-
-template <ImportTarget T> class ConstGraphBuilderImpl;
-
-template <>
-struct ConstGraphBuilderImpl<ImportTarget::Canonical> final : public ConstGraphBuilderBase
-{
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
-};
-
-template <>
-struct ConstGraphBuilderImpl<ImportTarget::TensorFlow> final : public ConstGraphBuilderBase
-{
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __OP_CONST_H__
diff --git a/compiler/moco-tf/src/Op/Const.test.cpp b/compiler/moco-tf/src/Op/Const.test.cpp
deleted file mode 100644
index 20c6c0e77..000000000
--- a/compiler/moco-tf/src/Op/Const.test.cpp
+++ /dev/null
@@ -1,464 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Const.h"
-#include "TestHelper.h"
-
-#include "Importer.h"
-
-#include "IR/TFConst.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <cstring>
-#include <memory>
-
-using namespace moco::tf;
-using namespace moco::tf::test;
-
-namespace
-{
-
-template <ImportTarget Target>
-std::unique_ptr<loco::Graph> import(const moco::tf::ModelSignature &sig, tensorflow::GraphDef &def)
-{
- using ConstGraphBuilder = ConstGraphBuilderImpl<Target>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("Const", stdex::make_unique<ConstGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- return importer.import(sig, def);
-}
-
-// Test case for "input_tensor.float_val_size() == num_elements"
-
-// clang-format off
-const char *const_float_01_pbtxtdata = STRING_CONTENT(
-node {
- name: "const/float"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 2
- }
- dim {
- size: 3
- }
- }
- float_val: 1.1
- float_val: 2.2
- float_val: 3.3
- float_val: 4.4
- float_val: 5.5
- float_val: 6.6
- }
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, const_float_01)
-{
- moco::tf::ModelSignature signature;
-
- signature.add_output(moco::tf::TensorName("const/float", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(const_float_01_pbtxtdata, graph_def));
-
- // Test "tf.GraphDef -> loco.TF" importer
- {
- auto graph = import<ImportTarget::TensorFlow>(signature, graph_def);
-
- moco::tf::TFConst *node0 =
- moco::tf::test::find_first_node_bytype<moco::tf::TFConst>(graph.get());
- ASSERT_NE(node0, nullptr);
-
- ASSERT_EQ(node0->size<loco::DataType::FLOAT32>(), 6);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(0), 1.1f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(1), 2.2f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(2), 3.3f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(3), 4.4f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(4), 5.5f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(5), 6.6f);
- }
-
- // Test "tf.GraphDef -> loco.Canonical" importer
- {
- auto graph = import<ImportTarget::Canonical>(signature, graph_def);
-
- loco::ConstGen *node0 = moco::tf::test::find_first_node_bytype<loco::ConstGen>(graph.get());
-
- ASSERT_EQ(node0->size<loco::DataType::FLOAT32>(), 6);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(0), 1.1f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(1), 2.2f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(2), 3.3f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(3), 4.4f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(4), 5.5f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(5), 6.6f);
- }
-}
-
-namespace
-{
-// Test case for "input_tensor.float_val_size() == 1"
-
-// clang-format off
-const char *const_float_02_pbtxtdata = STRING_CONTENT(
-node {
- name: "const/float"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 2
- }
- dim {
- size: 3
- }
- }
- float_val: 1.1
- }
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, const_float_02)
-{
- moco::tf::ModelSignature signature;
-
- signature.add_output(moco::tf::TensorName("const/float", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(const_float_02_pbtxtdata, graph_def));
-
- // Test "tf.GraphDef -> loco.TF" importer
- {
- auto graph = import<ImportTarget::TensorFlow>(signature, graph_def);
-
- moco::tf::TFConst *node0 =
- moco::tf::test::find_first_node_bytype<moco::tf::TFConst>(graph.get());
- ASSERT_NE(node0, nullptr);
-
- ASSERT_EQ(node0->size<loco::DataType::FLOAT32>(), 6);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(0), 1.1f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(1), 1.1f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(2), 1.1f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(3), 1.1f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(4), 1.1f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(5), 1.1f);
- }
-
- // Test "tf.GraphDef -> loco.Canonical" importer
- {
- auto graph = import<ImportTarget::Canonical>(signature, graph_def);
-
- loco::ConstGen *node0 = moco::tf::test::find_first_node_bytype<loco::ConstGen>(graph.get());
-
- ASSERT_EQ(node0->size<loco::DataType::FLOAT32>(), 6);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(0), 1.1f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(1), 1.1f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(2), 1.1f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(3), 1.1f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(4), 1.1f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(5), 1.1f);
- }
-}
-
-namespace
-{
-// Test case for "input_tensor.tensor_content().size() == num_elements * sizeof(float)"
-// Generated with tfkit tool: "cat ./test.pbtxt | ./tfkit pack"
-
-// clang-format off
-const char *const_float_03_pbtxtdata = STRING_CONTENT(
-node {
- name: "const/float"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 2
- }
- dim {
- size: 3
- }
- }
- tensor_content: "\315\314\214?\315\314\014@33S@\315\314\214@\000\000\260@33\323@"
- }
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, const_float_03)
-{
- moco::tf::ModelSignature signature;
-
- signature.add_output(moco::tf::TensorName("const/float", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(const_float_03_pbtxtdata, graph_def));
-
- // Test "tf.GraphDef -> loco.TF" importer
- {
- auto graph = import<ImportTarget::TensorFlow>(signature, graph_def);
-
- moco::tf::TFConst *node0 =
- moco::tf::test::find_first_node_bytype<moco::tf::TFConst>(graph.get());
- ASSERT_NE(node0, nullptr);
-
- ASSERT_EQ(node0->size<loco::DataType::FLOAT32>(), 6);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(0), 1.1f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(1), 2.2f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(2), 3.3f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(3), 4.4f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(4), 5.5f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(5), 6.6f);
- }
-
- // Test "tf.GraphDef -> loco.Canonical" importer
- {
- auto graph = import<ImportTarget::Canonical>(signature, graph_def);
-
- loco::ConstGen *node0 = moco::tf::test::find_first_node_bytype<loco::ConstGen>(graph.get());
-
- ASSERT_EQ(node0->size<loco::DataType::FLOAT32>(), 6);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(0), 1.1f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(1), 2.2f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(2), 3.3f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(3), 4.4f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(4), 5.5f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(5), 6.6f);
- }
-}
-
-namespace
-{
-// Test case for "input_tensor.float_val_size() < num_elements"
-
-// clang-format off
-const char *const_float_04_pbtxtdata = STRING_CONTENT(
-node {
- name: "const/float"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 2
- }
- dim {
- size: 3
- }
- }
- float_val: 1.1
- float_val: 2.2
- }
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, const_float_04)
-{
- moco::tf::ModelSignature signature;
-
- signature.add_output(moco::tf::TensorName("const/float", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(const_float_04_pbtxtdata, graph_def));
-
- // Test "tf.GraphDef -> loco.TF" importer
- {
- auto graph = import<ImportTarget::TensorFlow>(signature, graph_def);
-
- moco::tf::TFConst *node0 =
- moco::tf::test::find_first_node_bytype<moco::tf::TFConst>(graph.get());
-
- ASSERT_EQ(node0->size<loco::DataType::FLOAT32>(), 6);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(0), 1.1f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(1), 2.2f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(2), 2.2f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(3), 2.2f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(4), 2.2f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(5), 2.2f);
- }
-
- // Test "tf.GraphDef -> loco.Canonical" importer
- {
- auto graph = import<ImportTarget::Canonical>(signature, graph_def);
-
- loco::ConstGen *node0 = moco::tf::test::find_first_node_bytype<loco::ConstGen>(graph.get());
-
- ASSERT_EQ(node0->size<loco::DataType::FLOAT32>(), 6);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(0), 1.1f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(1), 2.2f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(2), 2.2f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(3), 2.2f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(4), 2.2f);
- ASSERT_EQ(node0->at<loco::DataType::FLOAT32>(5), 2.2f);
- }
-}
-
-namespace
-{
-// Test case for "input_tensor.int_val_size() < num_elements"
-
-// clang-format off
-const char *const_int32_04_pbtxtdata = STRING_CONTENT(
-node {
- name: "const/int"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_INT32
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_INT32
- tensor_shape {
- dim {
- size: 2
- }
- dim {
- size: 3
- }
- }
- int_val: 1
- int_val: 2
- }
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, const_int32_04)
-{
- moco::tf::ModelSignature signature;
-
- signature.add_output(moco::tf::TensorName("const/int", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(const_int32_04_pbtxtdata, graph_def));
-
-// TODO Re-enable this
-#if 0
- loco::Graph::OutputContext *outputs = graph->outputs();
- ASSERT_EQ(outputs->size(), 1);
- loco::GraphOutput *output = outputs->at(0);
- loco::Push *push = output->node();
-
- loco::Graph::NodeContext *nodes = graph->nodes();
- ASSERT_EQ(nodes->size(), 2);
-#endif
-
- // Test "tf.GraphDef -> loco.TF" importer
- {
- auto graph = import<ImportTarget::TensorFlow>(signature, graph_def);
-
- moco::tf::TFConst *node0 =
- moco::tf::test::find_first_node_bytype<moco::tf::TFConst>(graph.get());
- ASSERT_NE(node0, nullptr);
-
- ASSERT_EQ(node0->size<loco::DataType::S32>(), 6);
- ASSERT_EQ(node0->at<loco::DataType::S32>(0), 1);
- ASSERT_EQ(node0->at<loco::DataType::S32>(1), 2);
- ASSERT_EQ(node0->at<loco::DataType::S32>(2), 2);
- ASSERT_EQ(node0->at<loco::DataType::S32>(3), 2);
- ASSERT_EQ(node0->at<loco::DataType::S32>(4), 2);
- ASSERT_EQ(node0->at<loco::DataType::S32>(5), 2);
- }
-
- // Test "tf.GraphDef -> loco.Canonical" importer
- {
- auto graph = import<ImportTarget::Canonical>(signature, graph_def);
-
- loco::ConstGen *node0 = moco::tf::test::find_first_node_bytype<loco::ConstGen>(graph.get());
-
- ASSERT_EQ(node0->size<loco::DataType::S32>(), 6);
- ASSERT_EQ(node0->at<loco::DataType::S32>(0), 1);
- ASSERT_EQ(node0->at<loco::DataType::S32>(1), 2);
- ASSERT_EQ(node0->at<loco::DataType::S32>(2), 2);
- ASSERT_EQ(node0->at<loco::DataType::S32>(3), 2);
- ASSERT_EQ(node0->at<loco::DataType::S32>(4), 2);
- ASSERT_EQ(node0->at<loco::DataType::S32>(5), 2);
- }
-}
diff --git a/compiler/moco-tf/src/Op/Conv2D.cpp b/compiler/moco-tf/src/Op/Conv2D.cpp
deleted file mode 100644
index 7e011a7e1..000000000
--- a/compiler/moco-tf/src/Op/Conv2D.cpp
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Conv2D.h"
-
-#include "Convert.h"
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-#include "Knob.h"
-
-#include "IR/TFConv2D.h"
-
-#include "Annotations/PaddingData.h"
-#include "Annotations/PadData.h"
-
-#include <moco/tf/Names.h>
-
-#include <loco.h>
-#include <loco/IR/PermutingCodec.h>
-#include <stdex/Memory.h>
-#include <plier/tf/Convert.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <cassert>
-#include <stdexcept>
-
-namespace
-{
-using namespace moco::tf;
-
-class IFMUpdate final : public GraphUpdate
-{
-public:
- IFMUpdate(loco::FeatureEncode *ifm_enc, const TensorName &&ifm_name)
- : _ifm_enc(ifm_enc), _ifm_name(ifm_name)
- {
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- loco::FeatureEncode *_ifm_enc;
- const TensorName _ifm_name;
-};
-
-class KernelUpdate final : public GraphUpdate
-{
-public:
- KernelUpdate(loco::FilterEncode *ker_enc, const TensorName &&ker_name)
- : _ker_enc(ker_enc), _ker_name(ker_name)
- {
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- loco::FilterEncode *_ker_enc;
- const TensorName _ker_name;
-};
-
-void IFMUpdate::input(const SymbolTable *node_table) const
-{
- loco::Node *ifm_node = node_table->node(_ifm_name);
- _ifm_enc->input(ifm_node);
-}
-
-void KernelUpdate::input(const SymbolTable *node_table) const
-{
- loco::Node *ker_node = node_table->node(_ker_name);
- _ker_enc->input(ker_node);
-}
-
-class TFConv2DGraphUpdate final : public GraphUpdate
-{
-public:
- TFConv2DGraphUpdate(TFConv2D *node, std::vector<TensorName> names) : _node(node), _names(names) {}
-
- void input(const SymbolTable *) const override;
-
-private:
- TFConv2D *_node;
- std::vector<TensorName> _names;
-};
-
-void TFConv2DGraphUpdate::input(const SymbolTable *node_table) const
-{
- assert(_names.size() == 2);
-
- auto input_node = node_table->node(_names[0]);
- auto filter_node = node_table->node(_names[1]);
- assert(input_node != nullptr);
- assert(filter_node != nullptr);
-
- _node->input(input_node);
- _node->filter(filter_node);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for Conv2D node
- */
-class Conv2DGraphBuilder final : public Conv2DGraphBuilderBase
-{
-public:
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool Conv2DGraphBuilderBase::validate(const tensorflow::NodeDef &node) const
-{
- assert(node.input_size() == 2);
-
- // note: even though "data_format" is not entered when a model is written,
- // TF seems to generate "data_format" field into a pb file
- return plier::tf::has_attrs(node, {"T", "data_format", "dilations", "padding", "strides"});
-}
-
-void Conv2DGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- if (moco::tf::get<moco::tf::Knob::ImportAsTFConv2D>())
- {
- Conv2DGraphBuilderImpl<ImportTarget::TensorFlow> builder;
- builder.build(node, context);
- }
- else
- {
- Conv2DGraphBuilderImpl<ImportTarget::Canonical> builder;
- builder.build(node, context);
- }
-}
-
-void Conv2DGraphBuilderImpl<ImportTarget::Canonical>::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- using plier::tf::DataLayout;
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // name of loco nodes
- std::string conv2d_name = node.name();
-
- // tensorflow data_format, e.g., NHWC, NCHW, etc.
- auto data_layout = plier::tf::get_data_layout(node, "data_format");
-
- auto feature_enc = graph->nodes()->create<loco::FeatureEncode>();
- {
- auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>();
-
- if (data_layout == DataLayout::NHWC)
- {
- enc->perm()->axis(loco::FeatureAxis::Count) = 0;
- enc->perm()->axis(loco::FeatureAxis::Height) = 1;
- enc->perm()->axis(loco::FeatureAxis::Width) = 2;
- enc->perm()->axis(loco::FeatureAxis::Depth) = 3;
- }
- else if (data_layout == DataLayout::NCHW)
- {
- enc->perm()->axis(loco::FeatureAxis::Count) = 0;
- enc->perm()->axis(loco::FeatureAxis::Depth) = 1;
- enc->perm()->axis(loco::FeatureAxis::Height) = 2;
- enc->perm()->axis(loco::FeatureAxis::Width) = 3;
- }
- else
- throw std::runtime_error("Not yet supported");
-
- feature_enc->encoder(std::move(enc));
- }
-
- auto filter_enc = graph->nodes()->create<loco::FilterEncode>();
- {
- auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>();
-
- // In TensorFlow, conv2d filter is a 4-D tensor of following shape:
- // [filter_height, filter_width, in_channels, out_channels] -> HWIO (HWCN)
-
- enc->perm()->axis(loco::FilterAxis::Height) = 0;
- enc->perm()->axis(loco::FilterAxis::Width) = 1;
- enc->perm()->axis(loco::FilterAxis::Depth) = 2;
- enc->perm()->axis(loco::FilterAxis::Count) = 3;
-
- filter_enc->encoder(std::move(enc));
- }
-
- auto conv2d = graph->nodes()->create<loco::Conv2D>();
- {
- // let's convert attrs:
- // TensorFlow attr : T, data_format, dilations, padding, strides
- // to loco attr: not defined, TBD, TBD, TBD, stride
-
- // tf strides -> loco stride
- auto tf_strides = plier::tf::get_list_attr(node, "strides");
- auto stride = conv2d->stride();
-
- if (data_layout == DataLayout::NHWC)
- {
- stride->vertical(tf_strides.i(1));
- stride->horizontal(tf_strides.i(2));
- }
- else if (data_layout == DataLayout::NCHW)
- {
- stride->vertical(tf_strides.i(2));
- stride->horizontal(tf_strides.i(3));
- }
-
- // tf paddings -> PaddingData annotation
- auto tf_padding = moco::str_toupper(plier::tf::get_string_attr(node, "padding"));
- auto padding_data = stdex::make_unique<PaddingData>(tf_padding);
- conv2d->annot(std::move(padding_data));
- }
-
- auto feature_dec = graph->nodes()->create<loco::FeatureDecode>();
- {
- auto dec = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>();
-
- if (data_layout == DataLayout::NHWC)
- {
- dec->perm()->axis(loco::FeatureAxis::Count) = 0;
- dec->perm()->axis(loco::FeatureAxis::Height) = 1;
- dec->perm()->axis(loco::FeatureAxis::Width) = 2;
- dec->perm()->axis(loco::FeatureAxis::Depth) = 3;
- }
- else if (data_layout == DataLayout::NCHW)
- {
- dec->perm()->axis(loco::FeatureAxis::Count) = 0;
- dec->perm()->axis(loco::FeatureAxis::Depth) = 1;
- dec->perm()->axis(loco::FeatureAxis::Height) = 2;
- dec->perm()->axis(loco::FeatureAxis::Width) = 3;
- }
- else
- throw std::runtime_error("Not supported data layout");
-
- feature_dec->decoder(std::move(dec));
- }
-
- // link nodes
- conv2d->ifm(feature_enc);
- conv2d->ker(filter_enc);
- feature_dec->input(conv2d);
-
- // To set the input node of encode_node with conv2d_name
- TensorName output_name(conv2d_name, 0);
- tensor_names->enroll(output_name, feature_dec);
-
- // Record ifm inputs to featureEncode_node
- auto ifm_update = stdex::make_unique<IFMUpdate>(feature_enc, TensorName(node.input(0)));
- auto ker_update = stdex::make_unique<KernelUpdate>(filter_enc, TensorName(node.input(1)));
-
- updates->enroll(std::move(ifm_update));
- updates->enroll(std::move(ker_update));
-}
-
-void Conv2DGraphBuilderImpl<ImportTarget::TensorFlow>::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // name of loco nodes
- std::string conv2d_name = node.name();
-
- auto conv2d = graph->nodes()->create<TFConv2D>();
-
- // read attributes
- auto data_layout = plier::tf::get_string_attr(node, "data_format");
- if (!(data_layout == "NHWC" || data_layout == "NCHW"))
- {
- throw std::runtime_error("Not yet supported");
- }
- conv2d->data_layout(data_layout);
-
- auto tf_strides = plier::tf::get_list_attr(node, "strides");
- auto strides = plier::tf::as_int64_list(tf_strides);
- conv2d->strides(strides);
-
- auto padding = moco::str_toupper(plier::tf::get_string_attr(node, "padding"));
- assert(padding == "VALID" || padding == "SAME");
- conv2d->padding(padding);
-
- // save the name for graph link updates
- TensorName output_name(conv2d_name, 0);
- tensor_names->enroll(output_name, conv2d);
-
- std::vector<TensorName> input_names;
- input_names.push_back(TensorName(node.input(0))); // input
- input_names.push_back(TensorName(node.input(1))); // kernel
-
- // Record ifm inputs to featureEncode_node
- auto tfconv2d_update = stdex::make_unique<TFConv2DGraphUpdate>(conv2d, input_names);
-
- updates->enroll(std::move(tfconv2d_update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(Conv2D, Conv2DGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/Conv2D.h b/compiler/moco-tf/src/Op/Conv2D.h
deleted file mode 100644
index e88b8e399..000000000
--- a/compiler/moco-tf/src/Op/Conv2D.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __OP_CONV_2D_H__
-#define __OP_CONV_2D_H__
-
-#include "GraphBuilder.h"
-#include "ImportTarget.h"
-
-namespace moco
-{
-namespace tf
-{
-
-struct Conv2DGraphBuilderBase : public GraphBuilder
-{
- virtual ~Conv2DGraphBuilderBase() = default;
-
- bool validate(const tensorflow::NodeDef &) const final;
-};
-
-template <ImportTarget T> class Conv2DGraphBuilderImpl;
-
-template <>
-struct Conv2DGraphBuilderImpl<ImportTarget::Canonical> final : public Conv2DGraphBuilderBase
-{
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
-};
-
-template <>
-struct Conv2DGraphBuilderImpl<ImportTarget::TensorFlow> final : public Conv2DGraphBuilderBase
-{
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __OP_CONV_2D_H__
diff --git a/compiler/moco-tf/src/Op/Conv2D.test.cpp b/compiler/moco-tf/src/Op/Conv2D.test.cpp
deleted file mode 100644
index faf2977fc..000000000
--- a/compiler/moco-tf/src/Op/Conv2D.test.cpp
+++ /dev/null
@@ -1,513 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Conv2D.h"
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-#include "IR/TFConv2D.h"
-
-#include <loco.h>
-#include <loco/IR/TensorShape.h>
-#include <loco/IR/FeatureShape.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <memory>
-
-using namespace moco::tf;
-using namespace moco::tf::test;
-
-namespace
-{
-// clang-format off
-const char *conv2d_01_pbtxtdata = STRING_CONTENT(
-node {
- name: "ifm"
- op: "Const"
- attr { key: "dtype" value { type: DT_FLOAT } }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim { size: 1 }
- dim { size: 4 }
- dim { size: 4 }
- dim { size: 3 }
- }
- float_val: 1.1
- }
- }
- }
-}
-node {
- name: "ker"
- op: "Const"
- attr { key: "dtype" value { type: DT_FLOAT } }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim { size: 2 }
- dim { size: 2 }
- dim { size: 3 }
- dim { size: 100 }
- }
- float_val: 1.1
- }
- }
- }
-}
-node {
- name: "conv2d"
- op: "Conv2D"
- input: "ifm"
- input: "ker"
- attr { key: "T" value { type: DT_FLOAT } }
- attr { key: "data_format" value { s: "NHWC" } }
- attr { key: "dilations" value { list { i: 1 i: 1 i: 1 i: 1 } } }
- attr { key: "padding" value { s: "VALID" } }
- attr { key: "strides" value { list { i: 1 i: 2 i: 3 i: 1 } } }
- attr { key: "use_cudnn_on_gpu" value { b: false } }
-}
-);
-// clang-format on
-} // namespace
-
-namespace
-{
-
-void verify_Conv2D_01(loco::Graph *graph)
-{
- // what to test:
- // - Con2D node should exist
- // - ifm() should be FeatureEncode
- // - ker() should be FilterEncode
- // - following node should be FeatureDecode
- // - FeatureEncode encoder should encode Count-Height-Width-Depth order
- // - FeatureDecode decoder should decode Count-Height-Width-Depth order
-
- // test 1.
- // loco node : ConstGen - FeatureEncode -- Conv2D - FeatureDecode - Push
- // ConstGen - FilterEncode /
-
- loco::Conv2D *conv2d = moco::tf::test::find_first_node_bytype<loco::Conv2D>(graph);
- ASSERT_NE(conv2d, nullptr);
-
- loco::FeatureEncode *ifm_enc = dynamic_cast<loco::FeatureEncode *>(conv2d->ifm());
- loco::FilterEncode *ker_enc = dynamic_cast<loco::FilterEncode *>(conv2d->ker());
- ASSERT_NE(ifm_enc, nullptr);
- ASSERT_NE(ker_enc, nullptr);
-
- auto following_nodes = loco::succs(conv2d);
- ASSERT_EQ(following_nodes.size(), 1);
- loco::Node *following_node = *following_nodes.begin();
- ASSERT_NE(following_node, nullptr);
- loco::FeatureDecode *dec = dynamic_cast<loco::FeatureDecode *>(following_node);
- ASSERT_NE(dec, nullptr);
-
- // test 2.
- // attrs inside Conv2D
- {
- // stride
- ASSERT_EQ(conv2d->stride()->vertical(), 2);
- ASSERT_EQ(conv2d->stride()->horizontal(), 3);
-
- // TODO add padding test
- }
-
- // test 3.
- // attrs inside FeatureEncoder
- {
- auto ifm_encoder = ifm_enc->encoder();
- ASSERT_TRUE(ifm_encoder != nullptr);
-
- loco::TensorShape tensor_shape;
- tensor_shape.rank(4);
- tensor_shape.dim(0) = 1; // COUNT
- tensor_shape.dim(1) = 720; // HEIGHT
- tensor_shape.dim(2) = 1280; // WIDTH
- tensor_shape.dim(3) = 3; // DEPTH
-
- // Get the feature shape corresponding to a given image
- auto feature_shape = ifm_encoder->shape(tensor_shape);
-
- ASSERT_EQ(feature_shape.count().value(), 1);
- ASSERT_EQ(feature_shape.height().value(), 720);
- ASSERT_EQ(feature_shape.width().value(), 1280);
- ASSERT_EQ(feature_shape.depth().value(), 3);
- }
-
- // test 4.
- // attrs inside FilterEncoder
- {
- auto ker_encoder = ker_enc->encoder();
- ASSERT_TRUE(ker_encoder != nullptr);
-
- loco::TensorShape tensor_shape;
- tensor_shape.rank(4);
- tensor_shape.dim(0) = 2; // H
- tensor_shape.dim(1) = 4; // W
- tensor_shape.dim(2) = 3; // I (C)
- tensor_shape.dim(3) = 7; // O (N)
-
- // Get the feature shape corresponding to a given image
- auto ker_shape = ker_encoder->shape(tensor_shape);
-
- ASSERT_EQ(ker_shape.height().value(), 2);
- ASSERT_EQ(ker_shape.width().value(), 4);
- ASSERT_EQ(ker_shape.depth().value(), 3);
- ASSERT_EQ(ker_shape.count().value(), 7);
- }
-
- // test 5
- // attrs inside FeatureDecoder
- {
- auto decoder = dec->decoder();
- ASSERT_TRUE(decoder != nullptr);
-
- loco::FeatureShape feature_shape;
- feature_shape.count() = 1;
- feature_shape.height() = 720;
- feature_shape.width() = 1280;
- feature_shape.depth() = 3;
-
- // Get the tensor shape corresponding to a given image
- auto tensor_shape = decoder->shape(feature_shape);
-
- ASSERT_EQ(tensor_shape.rank(), 4);
- ASSERT_EQ(tensor_shape.dim(0).value(), 1); // COUNT
- ASSERT_EQ(tensor_shape.dim(1).value(), 720); // HEIGHT
- ASSERT_EQ(tensor_shape.dim(2).value(), 1280); // WIDTH
- ASSERT_EQ(tensor_shape.dim(3).value(), 3); // DEPTH
- }
-}
-
-void verify_TFConv2D_01(loco::Graph *graph)
-{
- // what to test:
- // - Con2D node should exist
- // - ifm() should not be nullptr
- // - ker() should not be nullptr
- // - attribute values should match
-
- // loco node : ConstGen - TFConv2D - Push
- // ConstGen /
- moco::tf::TFConv2D *tfconv2d = moco::tf::test::find_first_node_bytype<moco::tf::TFConv2D>(graph);
- ASSERT_NE(tfconv2d, nullptr);
- ASSERT_NE(tfconv2d->input(), nullptr);
- ASSERT_NE(tfconv2d->filter(), nullptr);
-
- // attrs inside TFConv2D
- ASSERT_EQ(tfconv2d->padding(), "VALID");
- ASSERT_EQ(tfconv2d->data_layout(), "NHWC");
- auto strides = tfconv2d->strides();
- ASSERT_EQ(strides.size(), 4);
- // TODO add verify dilation
-}
-
-} // namespace
-
-TEST(TensorFlowImport, Conv2D_01)
-{
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
-
- signature.add_output(moco::tf::TensorName("conv2d", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(conv2d_01_pbtxtdata, graph_def));
-
- // Test loco.TF Importer
- {
- using Conv2DGraphBuilder = Conv2DGraphBuilderImpl<ImportTarget::TensorFlow>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("Conv2D", stdex::make_unique<Conv2DGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- verify_TFConv2D_01(graph.get());
- }
-
- // Test loco.Canonical Importer
- {
- using Conv2DGraphBuilder = Conv2DGraphBuilderImpl<ImportTarget::Canonical>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("Conv2D", stdex::make_unique<Conv2DGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- verify_Conv2D_01(graph.get());
- }
-}
-
-namespace
-{
-// clang-format off
-const char *conv2d_inception_pbtxtdata = STRING_CONTENT(
-node {
- name: "input"
- op: "Placeholder"
- attr {
- key: "dtype" value { type: DT_FLOAT }
- }
- attr {
- key: "shape"
- value {
- shape {
- dim { size: 1 }
- dim { size: 299 }
- dim { size: 299 }
- dim { size: 3 }
- }
- }
- }
-}
-node {
- name: "InceptionV3/Conv2d_1a_3x3/weights/read/_3__cf__3"
- op: "Const"
- attr { key: "dtype" value { type: DT_FLOAT } }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim { size: 3 }
- dim { size: 3 }
- dim { size: 3 }
- dim { size: 32 }
- }
- float_val: 1.1
- }
- }
- }
-}
-node {
- name: "InceptionV3/InceptionV3/Conv2d_1a_3x3/Conv2D"
- op: "Conv2D"
- input: "input:0"
- input: "InceptionV3/Conv2d_1a_3x3/weights/read/_3__cf__3"
- attr {
- key: "T"
- value { type: DT_FLOAT }
- }
- attr {
- key: "data_format"
- value { s: "NHWC" }
- }
- attr {
- key: "dilations"
- value {
- list { i: 1 i: 1 i: 1 i: 1 }
- }
- }
- attr {
- key: "padding"
- value { s: "VALID" }
- }
- attr {
- key: "strides"
- value {
- list { i: 1 i: 2 i: 2 i: 1 }
- }
- }
- attr {
- key: "use_cudnn_on_gpu"
- value { b: true }
- }
-}
-);
-} // namespace
-
-namespace
-{
-
-void verify_Conv2D_inception_indexed_tensor_name(loco::Graph *graph)
-{
- // what to test: name with ':0' should be treated correctly
- // - Con2D node should exist
- // - ifm() should be FeatureEncode
- // - ker() should be FilterEncode
- // - following node should be FeatureDecode
- // - FeatureEncode encoder should encode Count-Height-Width-Depth order
- // - FeatureDecode decoder should decode Count-Height-Width-Depth order
-
- // test 1.
- // loco node : Pull - FeatureEncode -- Conv2D - FeatureDecode - Push
- // ConstGen - FilterEncode /
-
- loco::Conv2D *conv2d =
- moco::tf::test::find_first_node_bytype<loco::Conv2D>(graph);
- ASSERT_NE(conv2d, nullptr);
-
- loco::FeatureEncode *ifm_enc = dynamic_cast<loco::FeatureEncode *>(conv2d->ifm());
- loco::FilterEncode *ker_enc = dynamic_cast<loco::FilterEncode *>(conv2d->ker());
- ASSERT_NE(ifm_enc, nullptr);
- ASSERT_NE(ker_enc, nullptr);
-
- auto following_nodes = loco::succs(conv2d);
- ASSERT_EQ(following_nodes.size(), 1);
- loco::Node *following_node = *following_nodes.begin();
- ASSERT_NE(following_node, nullptr);
- loco::FeatureDecode *dec = dynamic_cast<loco::FeatureDecode *>(following_node);
- ASSERT_NE(dec, nullptr);
-
- // TODO remove below tests as it's duplicate as in verify_Conv2D_01
-
- // test 2.
- // attrs inside Conv2D
- {
- // stride
- ASSERT_EQ(conv2d->stride()->vertical(), 2);
- ASSERT_EQ(conv2d->stride()->horizontal(), 2);
-
- // TODO add padding test
- }
-
- // test 3.
- // attrs inside FeatureEncoder
- {
- auto ifm_encoder = ifm_enc->encoder();
- ASSERT_TRUE(ifm_encoder != nullptr);
-
- loco::TensorShape tensor_shape;
- tensor_shape.rank(4);
- tensor_shape.dim(0) = 1; // COUNT
- tensor_shape.dim(1) = 299; // HEIGHT
- tensor_shape.dim(2) = 299; // WIDTH
- tensor_shape.dim(3) = 3; // DEPTH
-
- // Get the feature shape corresponding to a given image
- auto feature_shape = ifm_encoder->shape(tensor_shape);
-
- ASSERT_EQ(feature_shape.count().value(), 1);
- ASSERT_EQ(feature_shape.height().value(), 299);
- ASSERT_EQ(feature_shape.width().value(), 299);
- ASSERT_EQ(feature_shape.depth().value(), 3);
- }
-
- // test 4.
- // attrs inside FilterEncoder
- {
- auto ker_encoder = ker_enc->encoder();
- ASSERT_TRUE(ker_encoder != nullptr);
-
- loco::TensorShape tensor_shape;
- tensor_shape.rank(4);
- tensor_shape.dim(0) = 3; // H
- tensor_shape.dim(1) = 3; // W
- tensor_shape.dim(2) = 3; // I (C)
- tensor_shape.dim(3) = 32; // O (N)
-
- // Get the feature shape corresponding to a given image
- auto ker_shape = ker_encoder->shape(tensor_shape);
-
- ASSERT_EQ(ker_shape.height().value(), 3);
- ASSERT_EQ(ker_shape.width().value(), 3);
- ASSERT_EQ(ker_shape.depth().value(), 3);
- ASSERT_EQ(ker_shape.count().value(), 32);
- }
-
- // test 5
- // attrs inside FeatureDecoder
- {
- auto decoder = dec->decoder();
- ASSERT_TRUE(decoder != nullptr);
-
- loco::FeatureShape feature_shape;
- feature_shape.count() = 1;
- feature_shape.height() = 299;
- feature_shape.width() = 299;
- feature_shape.depth() = 3;
-
- // Get the tensor shape corresponding to a given image
- auto tensor_shape = decoder->shape(feature_shape);
-
- ASSERT_EQ(tensor_shape.rank(), 4);
- ASSERT_EQ(tensor_shape.dim(0).value(), 1); // COUNT
- ASSERT_EQ(tensor_shape.dim(1).value(), 299); // HEIGHT
- ASSERT_EQ(tensor_shape.dim(2).value(), 299); // WIDTH
- ASSERT_EQ(tensor_shape.dim(3).value(), 3); // DEPTH
- }
-}
-
-void verify_TFConv2D_inception_indexed_tensor_name(loco::Graph *graph)
-{
- // what to test: name with ':0' should be treated correctly
- // - Con2D node should exist
- // - ifm() should not be nullptr
- // - ker() should not be nullptr
-
- // loco node : Pull - Conv2D - Push
- // ConstGen /
- moco::tf::TFConv2D *tfconv2d =
- moco::tf::test::find_first_node_bytype<moco::tf::TFConv2D>(graph);
- ASSERT_NE(tfconv2d, nullptr);
- ASSERT_NE(tfconv2d->input(), nullptr);
- ASSERT_NE(tfconv2d->filter(), nullptr);
-}
-
-} // namespace
-
-TEST(TensorFlowImport, Conv2D_inception_indexed_tensor_name)
-{
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
-
- signature.add_input(moco::tf::TensorName("input", 0));
- signature.add_output(moco::tf::TensorName("InceptionV3/InceptionV3/Conv2d_1a_3x3/Conv2D", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(conv2d_inception_pbtxtdata, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // Test loco.TF Importer
- {
- using Conv2DGraphBuilder = Conv2DGraphBuilderImpl<ImportTarget::TensorFlow>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("Conv2D", stdex::make_unique<Conv2DGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- verify_TFConv2D_inception_indexed_tensor_name(graph.get());
- }
-
- // Test loco.Canonical Importer
- {
- using Conv2DGraphBuilder = Conv2DGraphBuilderImpl<ImportTarget::Canonical>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("Conv2D", stdex::make_unique<Conv2DGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- verify_Conv2D_inception_indexed_tensor_name(graph.get());
- }
-}
diff --git a/compiler/moco-tf/src/Op/DepthwiseConv2dNative.cpp b/compiler/moco-tf/src/Op/DepthwiseConv2dNative.cpp
deleted file mode 100644
index 33f5fa4cd..000000000
--- a/compiler/moco-tf/src/Op/DepthwiseConv2dNative.cpp
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <plier/tf/Convert.h>
-#include "GraphBuilder.h"
-
-#include "IR/TFDepthwiseConv2dNative.h"
-
-#include "Annotations/PaddingData.h"
-#include "Annotations/PadData.h"
-
-#include <moco/tf/Names.h>
-
-#include <loco/IR/PermutingCodec.h>
-#include <stdex/Memory.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <cassert>
-
-using namespace plier::tf;
-
-namespace
-{
-using namespace moco::tf;
-
-class TFDepthwiseConv2dNativeGraphUpdate final : public GraphUpdate
-{
-public:
- TFDepthwiseConv2dNativeGraphUpdate(TFDepthwiseConv2dNative *node, std::vector<TensorName> names)
- : _node(node), _names(names)
- {
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- TFDepthwiseConv2dNative *_node;
- std::vector<TensorName> _names;
-};
-
-void TFDepthwiseConv2dNativeGraphUpdate::input(const SymbolTable *node_table) const
-{
- assert(_names.size() == 2);
-
- auto input_node = node_table->node(_names[0]);
- auto filter_node = node_table->node(_names[1]);
- assert(input_node != nullptr);
- assert(filter_node != nullptr);
-
- _node->input(input_node);
- _node->filter(filter_node);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for DepthwiseConv2dNative node
- */
-class DepthwiseConv2dNativeGraphBuilder final : public GraphBuilder
-{
-public:
- bool validate(const tensorflow::NodeDef &) const override;
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool DepthwiseConv2dNativeGraphBuilder::validate(const tensorflow::NodeDef &node) const
-{
- assert(node.input_size() == 2);
-
- auto data_layout = get_string_attr(node, "data_format");
- if (!(data_layout == "NHWC" || data_layout == "NCHW"))
- {
- throw std::runtime_error("Not yet supported");
- }
-
- auto padding = moco::str_toupper(get_string_attr(node, "padding"));
- assert(padding == "VALID" || padding == "SAME");
-
- auto tf_strides = get_list_attr(node, "strides");
- auto strides = as_int64_list(tf_strides);
- assert(strides.size() == 4);
- auto stride_n = strides.at(0);
- auto stride_h = strides.at(1);
- auto stride_w = strides.at(2);
- auto stride_c = strides.at(3);
- assert(stride_n == 1 && stride_c == 1);
- assert(stride_h == stride_w);
-
- // note: even though "data_format" and "dilations" are not entered when a model is written,
- // TF seems to generate those field into a pb file.
- return has_attrs(node, {"T", "data_format", "dilations", "padding", "strides"});
-}
-
-void DepthwiseConv2dNativeGraphBuilder::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- auto depthwiseconv2d_native_node = graph->nodes()->create<TFDepthwiseConv2dNative>();
-
- // read attributes
- auto data_layout = get_string_attr(node, "data_format");
- depthwiseconv2d_native_node->data_layout(data_layout);
-
- auto tf_strides = get_list_attr(node, "strides");
- auto strides = as_int64_list(tf_strides);
- depthwiseconv2d_native_node->strides(strides);
-
- auto padding = moco::str_toupper(get_string_attr(node, "padding"));
- depthwiseconv2d_native_node->padding(padding);
-
- // save the name for graph link updates
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, depthwiseconv2d_native_node);
-
- std::vector<TensorName> input_names;
- input_names.push_back(TensorName(node.input(0))); // input
- input_names.push_back(TensorName(node.input(1))); // kernel
-
- // Record ifm inputs to featureEncode_node
- auto tfdepthwiseconv2dnative_update = stdex::make_unique<TFDepthwiseConv2dNativeGraphUpdate>(
- depthwiseconv2d_native_node, input_names);
-
- updates->enroll(std::move(tfdepthwiseconv2dnative_update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(DepthwiseConv2dNative, DepthwiseConv2dNativeGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/DepthwiseConv2dNative.test.cpp b/compiler/moco-tf/src/Op/DepthwiseConv2dNative.test.cpp
deleted file mode 100644
index 64ae27da8..000000000
--- a/compiler/moco-tf/src/Op/DepthwiseConv2dNative.test.cpp
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-#include "IR/TFDepthwiseConv2dNative.h"
-
-#include <loco/IR/TensorShape.h>
-#include <loco/IR/FeatureShape.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <memory>
-
-using namespace moco::tf::test;
-
-namespace
-{
-// clang-format off
-const char *depthwise_conv2d_native_01_pbtxtdata = STRING_CONTENT(
-node {
- name: "input"
- op: "Placeholder"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "shape"
- value {
- shape {
- dim {
- size: 1
- }
- dim {
- size: 4
- }
- dim {
- size: 4
- }
- dim {
- size: 3
- }
- }
- }
- }
-}
-node {
- name: "filter"
- op: "Placeholder"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "shape"
- value {
- shape {
- dim {
- size: 2
- }
- dim {
- size: 2
- }
- dim {
- size: 3
- }
- dim {
- size: 2
- }
- }
- }
- }
-}
-node {
- name: "depthwise/Shape"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_INT32
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_INT32
- tensor_shape {
- dim {
- size: 4
- }
- }
- int_val: 2
- int_val: 2
- int_val: 3
- int_val: 2
- }
- }
- }
-}
-node {
- name: "depthwise/dilation_rate"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_INT32
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_INT32
- tensor_shape {
- dim {
- size: 2
- }
- }
- int_val: 1
- int_val: 1
- }
- }
- }
-}
-node {
- name: "depthwise"
- op: "DepthwiseConv2dNative"
- input: "input"
- input: "filter"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "data_format"
- value {
- s: "NHWC"
- }
- }
- attr {
- key: "dilations"
- value {
- list {
- i: 1
- i: 1
- i: 1
- i: 1
- }
- }
- }
- attr {
- key: "padding"
- value {
- s: "VALID"
- }
- }
- attr {
- key: "strides"
- value {
- list {
- i: 1
- i: 1
- i: 1
- i: 1
- }
- }
- }
-}
-);
-// clang-format on
-} // namespace
-
-TEST(TensorFlowImport, Depthwise_conv2d_native)
-{
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
-
- signature.add_input(moco::tf::TensorName("input", 0));
- signature.add_output(moco::tf::TensorName("depthwise", 0));
-
- tensorflow::GraphDef graph_def;
-
- EXPECT_TRUE(plier::tf::parse_graphdef(depthwise_conv2d_native_01_pbtxtdata, graph_def));
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- moco::tf::TFDepthwiseConv2dNative *tfdepthwiseconv2dnative =
- moco::tf::test::find_first_node_bytype<moco::tf::TFDepthwiseConv2dNative>(graph.get());
- ASSERT_NE(tfdepthwiseconv2dnative, nullptr);
- ASSERT_NE(tfdepthwiseconv2dnative->input(), nullptr);
- ASSERT_NE(tfdepthwiseconv2dnative->filter(), nullptr);
-
- ASSERT_EQ(tfdepthwiseconv2dnative->padding(), "VALID");
- ASSERT_EQ(tfdepthwiseconv2dnative->data_layout(), "NHWC");
- ASSERT_EQ(tfdepthwiseconv2dnative->strides().size(), 4);
-}
diff --git a/compiler/moco-tf/src/Op/FusedBatchNorm.cpp b/compiler/moco-tf/src/Op/FusedBatchNorm.cpp
deleted file mode 100644
index d22b690bf..000000000
--- a/compiler/moco-tf/src/Op/FusedBatchNorm.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFFusedBatchNorm.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-
-#include <loco.h>
-#include <stdex/Memory.h>
-#include <plier/tf/Convert.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-namespace
-{
-
-using namespace moco::tf;
-
-/**
- * @brief GraphUpdate for FusedBatchNorm node
- */
-class FusedBatchNormGraphUpdate final : public GraphUpdate
-{
-public:
- FusedBatchNormGraphUpdate(TFFusedBatchNorm *node, std::vector<TensorName> names)
- : _node(node), _names(names)
- {
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- TFFusedBatchNorm *_node;
- std::vector<TensorName> _names;
-};
-
-void FusedBatchNormGraphUpdate::input(const SymbolTable *tensor_names) const
-{
- int num_inputs = _names.size();
- assert(num_inputs == 5);
-
- _node->input(tensor_names->node(_names[0]));
- _node->gamma(tensor_names->node(_names[1]));
- _node->beta(tensor_names->node(_names[2]));
- _node->mean(tensor_names->node(_names[3]));
- _node->variance(tensor_names->node(_names[4]));
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for FusedBatchNorm node
- */
-class FusedBatchNormGraphBuilder final : public GraphBuilder
-{
-public:
- bool validate(const tensorflow::NodeDef &) const override;
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool FusedBatchNormGraphBuilder::validate(const tensorflow::NodeDef &node) const
-{
- assert(node.input_size() == 5);
-
- return plier::tf::has_attrs(node, {"epsilon"});
-}
-
-void FusedBatchNormGraphBuilder::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- float epsilon = plier::tf::get_float_attr(node, "epsilon");
-
- // creating TF dialect FusedBatchNorm node
- auto tf_fbn = graph->nodes()->create<TFFusedBatchNorm>();
- tf_fbn->epsilon(epsilon);
-
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, tf_fbn);
-
- std::vector<TensorName> fbn_input_names;
- fbn_input_names.push_back(TensorName(node.input(0))); // input
- fbn_input_names.push_back(TensorName(node.input(1))); // scale
- fbn_input_names.push_back(TensorName(node.input(2))); // offset
- fbn_input_names.push_back(TensorName(node.input(3))); // mean
- fbn_input_names.push_back(TensorName(node.input(4))); // variance
-
- auto tf_fbn_update = stdex::make_unique<FusedBatchNormGraphUpdate>(tf_fbn, fbn_input_names);
- updates->enroll(std::move(tf_fbn_update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(FusedBatchNorm, FusedBatchNormGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/FusedBatchNorm.test.cpp b/compiler/moco-tf/src/Op/FusedBatchNorm.test.cpp
deleted file mode 100644
index d9c45bca0..000000000
--- a/compiler/moco-tf/src/Op/FusedBatchNorm.test.cpp
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-
-#include "IR/TFFusedBatchNorm.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <cstring>
-#include <memory>
-
-using namespace moco::tf::test;
-
-namespace
-{
-// clang-format off
-const char *fbn_basic_pbtxt = STRING_CONTENT(
-node {
- name: "input"
- op: "Const"
- attr {
- key: "dtype"
- value { type: DT_FLOAT }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim { size: 1 }
- dim { size: 4 }
- dim { size: 4 }
- dim { size: 1 }
- }
- float_val: 1.0
- }
- }
- }
-}
-node {
- name: "gamma"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 1
- }
- }
- float_val: 1.0
- }
- }
- }
-}
-node {
- name: "beta"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 1
- }
- }
- float_val: 1.0
- }
- }
- }
-}
-node {
- name: "FBN_01/mean"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 1
- }
- }
- float_val: 1.0
- }
- }
- }
-}
-node {
- name: "FBN_01/variance"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 1
- }
- }
- float_val: 1.0
- }
- }
- }
-}
-node {
- name: "FBN_01"
- op: "FusedBatchNorm"
- input: "input"
- input: "gamma"
- input: "beta"
- input: "FBN_01/mean"
- input: "FBN_01/variance"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "data_format"
- value {
- s: "NHWC"
- }
- }
- attr {
- key: "epsilon"
- value {
- f: 0.001
- }
- }
- attr {
- key: "is_training"
- value {
- b: false
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, tf_fbn_basic)
-{
- // load graph
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
- signature.add_output(moco::tf::TensorName("FBN_01", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(fbn_basic_pbtxt, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - there should exist a TFFusedBatchNorm
- // - input() should not be nullptr
- // - gamma() should not be nullptr
- // - beta() should not be nullptr
- // - mean() should not be nullptr
- // - variance() should not be nullptr
- // - epsilon() value should match
-
- moco::tf::TFFusedBatchNorm *fbn_node =
- moco::tf::test::find_first_node_bytype<moco::tf::TFFusedBatchNorm>(graph.get());
-
- ASSERT_NE(fbn_node->input(), nullptr);
- ASSERT_NE(fbn_node->gamma(), nullptr);
- ASSERT_NE(fbn_node->beta(), nullptr);
- ASSERT_NE(fbn_node->mean(), nullptr);
- ASSERT_NE(fbn_node->variance(), nullptr);
- ASSERT_EQ(fbn_node->epsilon(), 0.001f);
-}
diff --git a/compiler/moco-tf/src/Op/Identity.cpp b/compiler/moco-tf/src/Op/Identity.cpp
deleted file mode 100644
index 03dac6d26..000000000
--- a/compiler/moco-tf/src/Op/Identity.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Identity.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-#include "Knob.h"
-
-#include "IR/TFIdentity.h"
-
-#include <moco/tf/Names.h>
-#include <loco.h>
-#include <stdex/Memory.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <cassert>
-#include <stdexcept>
-#include <string>
-#include <vector>
-
-namespace
-{
-
-using namespace moco::tf;
-
-class IdentityGraphUpdate final : public GraphUpdate
-{
-public:
- IdentityGraphUpdate(loco::Forward *node, const std::vector<TensorName> &names)
- : _node(node), _names(names)
- {
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- loco::Forward *_node;
- const std::vector<TensorName> _names;
-};
-
-class TFIdentityGraphUpdate final : public GraphUpdate
-{
-public:
- TFIdentityGraphUpdate(moco::tf::TFIdentity *node, const std::vector<TensorName> &names)
- : _node(node), _names(names)
- {
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- moco::tf::TFIdentity *_node;
- const std::vector<TensorName> _names;
-};
-
-void IdentityGraphUpdate::input(const SymbolTable *tensor_names) const
-{
- for (auto &name : _names)
- {
- loco::Node *target = tensor_names->node(name);
- _node->input(target);
- }
-}
-
-void TFIdentityGraphUpdate::input(const SymbolTable *tensor_names) const
-{
- for (auto &name : _names)
- {
- loco::Node *target = tensor_names->node(name);
- _node->input(target);
- }
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for Identity node
- */
-class IdentityGraphBuilder final : public IdentityGraphBuilderBase
-{
-public:
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool IdentityGraphBuilderBase::validate(const tensorflow::NodeDef &node) const
-{
- if (node.input_size() < 1) // from TensorFlow lite toco
- return false;
-
- return true;
-}
-
-void IdentityGraphBuilder::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- if (moco::tf::get<moco::tf::Knob::ImportAsTFIdentity>())
- {
- IdentityGraphBuilderImpl<ImportTarget::TensorFlow> builder;
- return builder.build(node, context);
- }
- else
- {
- IdentityGraphBuilderImpl<ImportTarget::Canonical> builder;
- return builder.build(node, context);
- }
-}
-
-void IdentityGraphBuilderImpl<ImportTarget::Canonical>::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // Create a "Forward" node for Identity
- auto forward_node = graph->nodes()->create<loco::Forward>();
-
- // register string-name to node
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, forward_node);
-
- // Queue node input update
- // TODO: Check if we really need multiple input handlings
- std::vector<TensorName> names;
- for (int i = 0; i < node.input_size(); ++i)
- {
- names.emplace_back(TensorName(node.input(i)));
- }
- auto update = stdex::make_unique<IdentityGraphUpdate>(forward_node, names);
- updates->enroll(std::move(update));
-}
-
-void IdentityGraphBuilderImpl<ImportTarget::TensorFlow>::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // Create a Identity node
- auto identity_node = graph->nodes()->create<moco::tf::TFIdentity>();
-
- // register string-name to node
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, identity_node);
-
- // Queue node input update
- // TODO: Check if we really need multiple input handlings
- std::vector<TensorName> names;
- for (int i = 0; i < node.input_size(); ++i)
- {
- names.emplace_back(TensorName(node.input(i)));
- }
- auto update = stdex::make_unique<TFIdentityGraphUpdate>(identity_node, names);
- updates->enroll(std::move(update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(Identity, IdentityGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/Identity.h b/compiler/moco-tf/src/Op/Identity.h
deleted file mode 100644
index 55da0070e..000000000
--- a/compiler/moco-tf/src/Op/Identity.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __OP_IDENTITY_H__
-#define __OP_IDENTITY_H__
-
-#include "GraphBuilder.h"
-#include "ImportTarget.h"
-
-namespace moco
-{
-namespace tf
-{
-
-struct IdentityGraphBuilderBase : public GraphBuilder
-{
- virtual ~IdentityGraphBuilderBase() = default;
-
- bool validate(const tensorflow::NodeDef &) const final;
-};
-
-template <ImportTarget T> class IdentityGraphBuilderImpl;
-
-template <>
-struct IdentityGraphBuilderImpl<ImportTarget::Canonical> final : public IdentityGraphBuilderBase
-{
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
-};
-
-template <>
-struct IdentityGraphBuilderImpl<ImportTarget::TensorFlow> final : public IdentityGraphBuilderBase
-{
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __OP_IDENTITY_H__
diff --git a/compiler/moco-tf/src/Op/MaxPool.cpp b/compiler/moco-tf/src/Op/MaxPool.cpp
deleted file mode 100644
index 079d91448..000000000
--- a/compiler/moco-tf/src/Op/MaxPool.cpp
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "MaxPool.h"
-
-#include "Convert.h"
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-#include "Knob.h"
-
-#include "IR/TFMaxPool.h"
-
-#include "Annotations/PaddingData.h"
-
-#include <moco/tf/Names.h>
-#include <loco.h>
-#include <loco/IR/PermutingCodec.h>
-#include <stdex/Memory.h>
-#include <plier/tf/Convert.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <cassert>
-#include <stdexcept>
-
-namespace
-{
-
-using namespace moco::tf;
-
-class MaxPoolGraphUpdate final : public GraphUpdate
-{
-public:
- MaxPoolGraphUpdate(loco::FeatureEncode *node, const TensorName &&name)
- : _encode_node(node), _input_name(name)
- {
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- loco::FeatureEncode *_encode_node;
- const TensorName _input_name;
-};
-
-class TFMaxPoolGraphUpdate final : public GraphUpdate
-{
-public:
- TFMaxPoolGraphUpdate(moco::tf::TFMaxPool *node, const TensorName &name)
- : _maxpool_node(node), _value_name(name)
- {
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- moco::tf::TFMaxPool *_maxpool_node;
- const TensorName _value_name;
-};
-
-void MaxPoolGraphUpdate::input(const SymbolTable *tensor_names) const
-{
- loco::Node *input_node = tensor_names->node(_input_name);
- _encode_node->input(input_node);
-}
-
-void TFMaxPoolGraphUpdate::input(const SymbolTable *node_table) const
-{
- loco::Node *value_node = node_table->node(_value_name);
- _maxpool_node->value(value_node);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for MaxPool node
- */
-class MaxPoolGraphBuilder final : public MaxPoolGraphBuilderBase
-{
-public:
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool MaxPoolGraphBuilderBase::validate(const tensorflow::NodeDef &node) const
-{
- // note: even though "data_format" is not entered when a model is written,
- // TF seems to generate "data_format" field into a pb file
- return plier::tf::has_attrs(node, {"T", "data_format", "ksize", "padding", "strides"});
-}
-
-void MaxPoolGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- if (moco::tf::get<moco::tf::Knob::ImportAsTFMaxPool>())
- {
- MaxPoolGraphBuilderImpl<ImportTarget::TensorFlow> builder;
- return builder.build(node, context);
- }
- else
- {
- MaxPoolGraphBuilderImpl<ImportTarget::Canonical> builder;
- return builder.build(node, context);
- }
-}
-
-void MaxPoolGraphBuilderImpl<ImportTarget::Canonical>::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- using plier::tf::DataLayout;
-
- assert(context != nullptr);
- assert(node.input_size() == 1);
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // create loco nodes
- auto encode_node = graph->nodes()->create<loco::FeatureEncode>();
- auto maxpool2d_node = graph->nodes()->create<loco::MaxPool2D>();
- auto decode_node = graph->nodes()->create<loco::FeatureDecode>();
-
- // name of loco nodes
- ::std::string maxpool2d_name = node.name();
-
- // tensorflow data_format, e.g., NHWC, NCHW, etc.
- auto data_layout = plier::tf::get_data_layout(node, "data_format");
-
- // FeatureEncode
- {
- auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>();
-
- if (data_layout == DataLayout::NHWC)
- {
- enc->perm()->axis(loco::FeatureAxis::Count) = 0;
- enc->perm()->axis(loco::FeatureAxis::Height) = 1;
- enc->perm()->axis(loco::FeatureAxis::Width) = 2;
- enc->perm()->axis(loco::FeatureAxis::Depth) = 3;
- }
- else
- throw std::runtime_error("Not supported data layout");
-
- encode_node->encoder(std::move(enc));
- }
-
- // MaxPool
- {
- // let's convert attrs:
- // TensorFlow attr : T, data_format, ksize, padding, strides
- // to loco attr: not defined, TBD, window, TBD, stride
-
- // tf ksize -> loco window
- auto tf_ksize = plier::tf::get_list_attr(node, "ksize");
- auto window = maxpool2d_node->window();
-
- if (data_layout == DataLayout::NHWC)
- {
- window->vertical(tf_ksize.i(1));
- window->horizontal(tf_ksize.i(2));
- }
- else if (data_layout == DataLayout::NCHW)
- {
- window->vertical(tf_ksize.i(2));
- window->horizontal(tf_ksize.i(3));
- }
-
- // tf strides -> loco stride
- auto tf_strides = plier::tf::get_list_attr(node, "strides");
- auto stride = maxpool2d_node->stride();
-
- if (data_layout == DataLayout::NHWC)
- {
- stride->vertical(tf_strides.i(1));
- stride->horizontal(tf_strides.i(2));
- }
- else if (data_layout == DataLayout::NCHW)
- {
- stride->vertical(tf_strides.i(2));
- stride->horizontal(tf_strides.i(3));
- }
-
- // tf paddings -> PaddingData annotation
- auto tf_padding = plier::tf::get_string_attr(node, "padding");
- auto padding_data = stdex::make_unique<PaddingData>(tf_padding);
- maxpool2d_node->annot(std::move(padding_data));
- }
-
- // FeatureDecode
- {
- auto dec = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>();
-
- if (data_layout == DataLayout::NHWC)
- {
- dec->perm()->axis(loco::FeatureAxis::Count) = 0;
- dec->perm()->axis(loco::FeatureAxis::Height) = 1;
- dec->perm()->axis(loco::FeatureAxis::Width) = 2;
- dec->perm()->axis(loco::FeatureAxis::Depth) = 3;
- }
- else
- throw std::runtime_error("Not supported data layout");
-
- decode_node->decoder(std::move(dec));
- }
-
- // link nodes
- maxpool2d_node->ifm(encode_node);
- decode_node->input(maxpool2d_node);
-
- // To set the input node of encode_node with maxpool2d_name
- TensorName output_name(maxpool2d_name, 0);
- tensor_names->enroll(output_name, decode_node);
-
- // Record ifm inputs to featureEncode_node
- auto update = stdex::make_unique<MaxPoolGraphUpdate>(encode_node, TensorName(node.input(0)));
-
- updates->enroll(std::move(update));
-}
-
-void MaxPoolGraphBuilderImpl<ImportTarget::TensorFlow>::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // name of loco nodes
- ::std::string node_name = node.name();
-
- // tensorflow data_format: one of NHWC or NCHW.
- auto data_layout = plier::tf::get_string_attr(node, "data_format");
- auto maxPool_node = graph->nodes()->create<moco::tf::TFMaxPool>();
- maxPool_node->data_layout(data_layout);
-
- // padding
- auto padding = moco::str_toupper(plier::tf::get_string_attr(node, "padding"));
- maxPool_node->padding(padding);
-
- // ksize
- auto tf_ksize = plier::tf::get_list_attr(node, "ksize");
- auto ksize = plier::tf::as_int64_list(tf_ksize);
- if (ksize.size() != 4)
- {
- // TODO support ksize length for 1 and 2
- throw std::runtime_error("MaxPool only supports ksize length 4");
- }
- maxPool_node->ksize(ksize);
-
- // strides
- auto tf_strides = plier::tf::get_list_attr(node, "strides");
- auto strides = plier::tf::as_int64_list(tf_strides);
- if (strides.size() != 4)
- {
- // TODO support strides length for 1 and 2
- throw std::runtime_error("MaxPool only supports strides length 4");
- }
- maxPool_node->strides(strides);
-
- // To set the input node of encode_node with node_name
- TensorName output_name(node_name, 0);
- tensor_names->enroll(output_name, maxPool_node);
-
- // Record ifm inputs to featureEncode_node
- auto update = stdex::make_unique<TFMaxPoolGraphUpdate>(maxPool_node, TensorName(node.input(0)));
-
- updates->enroll(std::move(update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(MaxPool, MaxPoolGraphBuilder)
-
-// TODO Consider a case when TF MaxPool is for 3D.
-// MaxPool works for 2D and other Dimensions, such as 3D
-// So, in future, some other GraphBuilder decide if MaxPoolGraphBuilder is used or
-// other GraphBuilder is used for TF MaxPool
diff --git a/compiler/moco-tf/src/Op/MaxPool.h b/compiler/moco-tf/src/Op/MaxPool.h
deleted file mode 100644
index e95f19e31..000000000
--- a/compiler/moco-tf/src/Op/MaxPool.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __OP_MAX_POOL_H__
-#define __OP_MAX_POOL_H__
-
-#include "GraphBuilder.h"
-#include "ImportTarget.h"
-
-namespace moco
-{
-namespace tf
-{
-
-struct MaxPoolGraphBuilderBase : public GraphBuilder
-{
- virtual ~MaxPoolGraphBuilderBase() = default;
-
- bool validate(const tensorflow::NodeDef &) const final;
-};
-
-template <ImportTarget T> class MaxPoolGraphBuilderImpl;
-
-template <>
-struct MaxPoolGraphBuilderImpl<ImportTarget::Canonical> final : public MaxPoolGraphBuilderBase
-{
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
-};
-
-template <>
-struct MaxPoolGraphBuilderImpl<ImportTarget::TensorFlow> final : public MaxPoolGraphBuilderBase
-{
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __OP_MAX_POOL_H__
diff --git a/compiler/moco-tf/src/Op/MaxPool.test.cpp b/compiler/moco-tf/src/Op/MaxPool.test.cpp
deleted file mode 100644
index 308520d46..000000000
--- a/compiler/moco-tf/src/Op/MaxPool.test.cpp
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "MaxPool.h"
-
-#include "IR/TFMaxPool.h"
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-
-#include <loco.h>
-#include <loco/IR/TensorShape.h>
-#include <loco/IR/FeatureShape.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <memory>
-
-using namespace moco::tf;
-using namespace moco::tf::test;
-
-namespace
-{
-// clang-format off
-const char *maxpool_01_pbtxtdata = STRING_CONTENT(
-node {
- name: "const/float"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 1
- }
- dim {
- size: 3
- }
- dim {
- size: 3
- }
- dim {
- size: 1
- }
- }
- float_val: 1.1
- }
- }
- }
-}
-node {
- name: "maxpool"
- op: "MaxPool"
- input: "const/float"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "data_format"
- value {
- s: "NHWC"
- }
- }
- attr {
- key: "ksize"
- value {
- list {
- i: 1
- i: 2
- i: 3
- i: 1
- }
- }
- }
- attr {
- key: "padding"
- value {
- s: "VALID"
- }
- }
- attr {
- key: "strides"
- value {
- list {
- i: 1
- i: 3
- i: 2
- i: 1
- }
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, MaxPool_01)
-{
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
-
- signature.add_output(moco::tf::TensorName("maxpool", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(maxpool_01_pbtxtdata, graph_def));
-
- // Test "MaxPoolGraphBuilderImpl<ImportTarget::Canonical>"
- {
- // what to test:
- // - there should exist MaxPool2D
- // - ifm node should be FeatureEncode
- // - following node should be FeatureDecode
- // - stride values should match
- // - window values should match
-
- using MaxPoolGraphBuilder = MaxPoolGraphBuilderImpl<ImportTarget::Canonical>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("MaxPool", stdex::make_unique<MaxPoolGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- loco::MaxPool2D *maxpool2d_node =
- moco::tf::test::find_first_node_bytype<loco::MaxPool2D>(graph.get());
- ASSERT_NE(maxpool2d_node, nullptr);
-
- loco::Node *previous_node = maxpool2d_node->ifm();
- auto following_nodes = loco::succs(maxpool2d_node);
- ASSERT_EQ(following_nodes.size(), 1);
- loco::Node *following_node = *following_nodes.begin();
- ASSERT_NE(following_node, nullptr);
-
- loco::FeatureEncode *enc_node = dynamic_cast<loco::FeatureEncode *>(previous_node);
- loco::FeatureDecode *dec_node = dynamic_cast<loco::FeatureDecode *>(following_node);
-
- ASSERT_NE(enc_node, nullptr);
- ASSERT_NE(dec_node, nullptr);
-
- // attrs inside MaxPool2D
- auto maxpool2d = maxpool2d_node; // TODO remove this new variable
-
- // stride
- ASSERT_EQ(maxpool2d->stride()->vertical(), 3);
- ASSERT_EQ(maxpool2d->stride()->horizontal(), 2);
-
- // window
- ASSERT_EQ(maxpool2d->window()->vertical(), 2);
- ASSERT_EQ(maxpool2d->window()->horizontal(), 3);
- }
-
- // Test "MaxPoolGraphBuilderImpl<ImportTarget::TensorFlow>"
- {
- // what to test:
- // - there should exist TFMaxPool
- // - attributes value should match
-
- using MaxPoolGraphBuilder = MaxPoolGraphBuilderImpl<ImportTarget::TensorFlow>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("MaxPool", stdex::make_unique<MaxPoolGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- moco::tf::TFMaxPool *maxpool_node =
- moco::tf::test::find_first_node_bytype<moco::tf::TFMaxPool>(graph.get());
- ASSERT_NE(maxpool_node, nullptr);
-
- loco::Node *previous_node = maxpool_node->value();
- auto following_nodes = loco::succs(maxpool_node);
- ASSERT_EQ(following_nodes.size(), 1);
- loco::Node *following_node = *following_nodes.begin();
- ASSERT_NE(following_node, nullptr);
-
- // attrs inside TFMaxPool
- ASSERT_EQ(maxpool_node->data_layout(), "NHWC");
- ASSERT_EQ(maxpool_node->padding(), "VALID");
- ASSERT_EQ(maxpool_node->ksize(), std::vector<int64_t>({1, 2, 3, 1}));
- ASSERT_EQ(maxpool_node->strides(), std::vector<int64_t>({1, 3, 2, 1}));
- }
-}
-
-TEST(TensorFlowImport, MaxPool_02)
-{
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
-
- signature.add_output(moco::tf::TensorName("maxpool", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(maxpool_01_pbtxtdata, graph_def));
-
- // Test "MaxPoolGraphBuilderImpl<ImportTarget::Canonical>"
- {
- // TODO: fix indentation
- // clang-format off
-
- // what to test: Encoder and Decoder dimension order
- // - there should exist MaxPool2D
- // - ifm node should be FeatureEncode
- // - following node should be FeatureDecode
- // - FeatureEncode encoder should encode Count-Height-Width-Depth order
- // - FeatureDecode decoder should decode Count-Height-Width-Depth order
-
- using MaxPoolGraphBuilder = MaxPoolGraphBuilderImpl<ImportTarget::Canonical>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("MaxPool", stdex::make_unique<MaxPoolGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- loco::MaxPool2D *maxpool2d_node =
- moco::tf::test::find_first_node_bytype<loco::MaxPool2D>(graph.get());
- ASSERT_NE(maxpool2d_node, nullptr);
-
- loco::Node *previous_node = maxpool2d_node->ifm();
- auto following_nodes = loco::succs(maxpool2d_node);
- ASSERT_EQ(following_nodes.size(), 1);
- loco::Node *following_node = *following_nodes.begin();
- ASSERT_NE(following_node, nullptr);
-
- loco::FeatureEncode *enc_node = dynamic_cast<loco::FeatureEncode *>(previous_node);
- loco::FeatureDecode *dec_node = dynamic_cast<loco::FeatureDecode *>(following_node);
-
- ASSERT_NE(enc_node, nullptr);
- ASSERT_NE(dec_node, nullptr);
-
- // attrs inside FeatureEncoder
- auto encoder = enc_node->encoder();
- ASSERT_TRUE(encoder != nullptr);
-
- loco::TensorShape tensor_shape;
- tensor_shape.rank(4);
- tensor_shape.dim(0) = 1; // COUNT
- tensor_shape.dim(1) = 720; // HEIGHT
- tensor_shape.dim(2) = 1280; // WIDTH
- tensor_shape.dim(3) = 3; // DEPTH
-
- // Get the feature shape corresponding to a given image
- auto feature_shape = encoder->shape(tensor_shape);
-
- ASSERT_EQ(feature_shape.count().value(), 1);
- ASSERT_EQ(feature_shape.depth().value(), 3);
- ASSERT_EQ(feature_shape.height().value(), 720);
- ASSERT_EQ(feature_shape.width().value(), 1280);
-
- // attrs inside FeatureDecoder
- auto decoder = dec_node->decoder();
- ASSERT_TRUE(decoder != nullptr);
-
- feature_shape.count() = 1;
- feature_shape.depth() = 3;
- feature_shape.height() = 720;
- feature_shape.width() = 1280;
-
- // Get the tensor shape corresponding to a given image
- tensor_shape = decoder->shape(feature_shape);
-
- ASSERT_EQ(tensor_shape.rank(), 4);
- ASSERT_EQ(tensor_shape.dim(0).value(), 1); // COUNT
- ASSERT_EQ(tensor_shape.dim(1).value(), 720); // HEIGHT
- ASSERT_EQ(tensor_shape.dim(2).value(), 1280); // WIDTH
- ASSERT_EQ(tensor_shape.dim(3).value(), 3); // DEPTH
-
- // clang-format on
- }
-
- // Skip Test "AvgPoolGraphBuilderImpl<ImportTarget::TensorFlow>"
- // There is no FeatureEncode nor FeatureDecode to test
-}
diff --git a/compiler/moco-tf/src/Op/Mul.cpp b/compiler/moco-tf/src/Op/Mul.cpp
deleted file mode 100644
index 5fa5b68aa..000000000
--- a/compiler/moco-tf/src/Op/Mul.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFMul.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-
-#include <loco.h>
-#include <stdex/Memory.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-namespace
-{
-
-using namespace moco::tf;
-
-/**
- * @brief GraphUpdate for TF Mul node
- */
-class TFMulGraphUpdate final : public GraphUpdate
-{
-public:
- TFMulGraphUpdate(TFMul *node, std::vector<TensorName> names) : _node(node), _names(names) {}
-
- void input(const SymbolTable *) const override;
-
-private:
- TFMul *_node;
- std::vector<TensorName> _names;
-};
-
-void TFMulGraphUpdate::input(const SymbolTable *tensor_names) const
-{
- int num_inputs = _names.size();
- assert(num_inputs == 2);
-
- _node->x(tensor_names->node(_names[0]));
- _node->y(tensor_names->node(_names[1]));
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for Mul node
- */
-class MulGraphBuilder final : public GraphBuilder
-{
-public:
- bool validate(const tensorflow::NodeDef &) const override;
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool MulGraphBuilder::validate(const tensorflow::NodeDef &node) const
-{
- assert(node.input_size() == 2);
-
- return true;
-}
-
-void MulGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // creating TF dialect Mul node
- auto tf_mul = graph->nodes()->create<TFMul>();
-
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, tf_mul);
-
- std::vector<TensorName> add_input_names;
- add_input_names.push_back(TensorName(node.input(0))); // x
- add_input_names.push_back(TensorName(node.input(1))); // y
-
- auto tf_mul_update = stdex::make_unique<TFMulGraphUpdate>(tf_mul, add_input_names);
- updates->enroll(std::move(tf_mul_update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(Mul, MulGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/Mul.test.cpp b/compiler/moco-tf/src/Op/Mul.test.cpp
deleted file mode 100644
index 7bc138656..000000000
--- a/compiler/moco-tf/src/Op/Mul.test.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-
-#include "IR/TFMul.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <cstring>
-#include <memory>
-
-using namespace moco::tf::test;
-
-namespace
-{
-// clang-format off
-const char *mul_basic_pbtxt = STRING_CONTENT(
-node {
- name: "input_01"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 1
- }
- dim {
- size: 3
- }
- dim {
- size: 4
- }
- }
- float_val: 1.0
- }
- }
- }
-}
-node {
- name: "input_02"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 1
- }
- dim {
- size: 3
- }
- dim {
- size: 4
- }
- }
- float_val: 2.0
- }
- }
- }
-}
-node {
- name: "MUL_01"
- op: "Mul"
- input: "input_01"
- input: "input_02"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, tf_mul_basic)
-{
- // load graph
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
- signature.add_output(moco::tf::TensorName("MUL_01", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(mul_basic_pbtxt, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - TFMul node should exist
- // - both inputs x() and y() should not be null
-
- auto mul_node = moco::tf::test::find_first_node_bytype<moco::tf::TFMul>(graph.get());
-
- ASSERT_NE(mul_node, nullptr);
- ASSERT_NE(mul_node->x(), nullptr);
- ASSERT_NE(mul_node->y(), nullptr);
-}
diff --git a/compiler/moco-tf/src/Op/Placeholder.cpp b/compiler/moco-tf/src/Op/Placeholder.cpp
deleted file mode 100644
index e0b24d5df..000000000
--- a/compiler/moco-tf/src/Op/Placeholder.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-
-#include <moco/tf/Names.h>
-#include <plier/tf/Convert.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <cassert>
-#include <stdexcept>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for Placeholder node
- */
-class PlaceholderGraphBuilder final : public GraphBuilder
-{
-public:
- bool validate(const tensorflow::NodeDef &) const override;
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool PlaceholderGraphBuilder::validate(const tensorflow::NodeDef &node) const
-{
- return plier::tf::has_attrs(node, {"dtype", "shape"});
-}
-
-void PlaceholderGraphBuilder::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
-
- loco::DataType dtype = plier::tf::as_loco_datatype(plier::tf::get_datatype_attr(node, "dtype"));
- const auto &shape = plier::tf::get_shape_attr(node, "shape");
- // TODO handle for unknown rank
- assert(!shape.unknown_rank());
- int64_t num_dims = shape.dim_size();
-
- // TODO support other types
- assert(dtype == loco::DataType::FLOAT32);
-
- // Create a "pull" node as an input
- auto pull_node = graph->nodes()->create<loco::Pull>();
-
- pull_node->dtype(dtype);
-
- // Setting shape info.
- pull_node->rank(num_dims);
- for (int64_t d = 0; d < num_dims; d++)
- {
- assert(shape.dim(d).size() < std::numeric_limits<uint32_t>::max());
- int64_t dim_value = shape.dim(d).size();
- if (dim_value >= 0ULL)
- {
- uint32_t dim_value32 = static_cast<uint32_t>(dim_value);
- pull_node->dim(d) = dim_value32;
- }
- else
- {
- pull_node->dim(d).unset();
- // TODO Remove assert() and do implement
- // NOTE Current implementation assumes dim is all know
- assert(false);
- }
- }
-
- // register string-name to node
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, pull_node);
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(Placeholder, PlaceholderGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/Placeholder.test.cpp b/compiler/moco-tf/src/Op/Placeholder.test.cpp
deleted file mode 100644
index 0fe32af37..000000000
--- a/compiler/moco-tf/src/Op/Placeholder.test.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-
-#include <loco.h>
-#include <loco/IR/TensorShape.h>
-#include <loco/IR/FeatureShape.h>
-#include <nncc/core/ADT/tensor/Shape.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <memory>
-
-using namespace moco::tf::test;
-
-namespace
-{
-// clang-format off
-const char *known_batch_pbtxt = STRING_CONTENT(
-node {
- name: "placeholder"
- op: "Placeholder"
- attr {
- key: "dtype" value { type: DT_FLOAT }
- }
- attr {
- key: "shape"
- value {
- shape {
- dim { size: 1024 }
- dim { size: 2 }
- dim { size: 3 }
- dim { size: 4 }
- }
- }
- }
-}
-node {
- name: "output"
- op: "Identity"
- input: "placeholder"
- attr {
- key: "T" value { type: DT_FLOAT }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, placeholder_knwon_batch)
-{
- // load graph
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
- signature.add_output(moco::tf::TensorName("output", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(known_batch_pbtxt, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // get loco::Pull
- loco::Graph::NodeContext *loco_nodes = graph->nodes();
- loco::Pull *pull_node = dynamic_cast<loco::Pull *>(loco_nodes->at(0));
-
- // Check dim
- ASSERT_TRUE(pull_node->dim(0).known() && pull_node->dim(0).value() == 1024);
- ASSERT_TRUE(pull_node->dim(1).known() && pull_node->dim(1).value() == 2);
- ASSERT_TRUE(pull_node->dim(2).known() && pull_node->dim(2).value() == 3);
- ASSERT_TRUE(pull_node->dim(3).known() && pull_node->dim(3).value() == 4);
-}
diff --git a/compiler/moco-tf/src/Op/RealDiv.cpp b/compiler/moco-tf/src/Op/RealDiv.cpp
deleted file mode 100644
index 4d96f7457..000000000
--- a/compiler/moco-tf/src/Op/RealDiv.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFRealDiv.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-
-#include <loco.h>
-#include <stdex/Memory.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-namespace
-{
-
-using namespace moco::tf;
-
-/**
- * @brief GraphUpdate for TF RealDiv node
- */
-class TFRealDivGraphUpdate final : public GraphUpdate
-{
-public:
- TFRealDivGraphUpdate(TFRealDiv *node, std::vector<TensorName> names) : _node(node), _names(names)
- {
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- TFRealDiv *_node;
- std::vector<TensorName> _names;
-};
-
-void TFRealDivGraphUpdate::input(const SymbolTable *tensor_names) const
-{
- int num_inputs = _names.size();
- assert(num_inputs == 2);
-
- _node->x(tensor_names->node(_names[0]));
- _node->y(tensor_names->node(_names[1]));
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for RealDiv node
- */
-class RealDivGraphBuilder final : public GraphBuilder
-{
-public:
- bool validate(const tensorflow::NodeDef &) const override;
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool RealDivGraphBuilder::validate(const tensorflow::NodeDef &node) const
-{
- assert(node.input_size() == 2);
-
- return true;
-}
-
-void RealDivGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // creating TF dialect RealDiv node
- auto tf_div = graph->nodes()->create<TFRealDiv>();
-
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, tf_div);
-
- std::vector<TensorName> div_input_names;
- div_input_names.push_back(TensorName(node.input(0))); // x
- div_input_names.push_back(TensorName(node.input(1))); // y
-
- auto tf_div_update = stdex::make_unique<TFRealDivGraphUpdate>(tf_div, div_input_names);
- updates->enroll(std::move(tf_div_update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(RealDiv, RealDivGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/RealDiv.test.cpp b/compiler/moco-tf/src/Op/RealDiv.test.cpp
deleted file mode 100644
index 40e55b276..000000000
--- a/compiler/moco-tf/src/Op/RealDiv.test.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-
-#include "IR/TFRealDiv.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <cstring>
-#include <memory>
-
-using namespace moco::tf::test;
-
-namespace
-{
-// clang-format off
-const char *div_basic_pbtxt = STRING_CONTENT(
-node {
- name: "input_01"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 1
- }
- dim {
- size: 3
- }
- dim {
- size: 4
- }
- }
- float_val: 1.0
- }
- }
- }
-}
-node {
- name: "input_02"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 1
- }
- dim {
- size: 3
- }
- dim {
- size: 4
- }
- }
- float_val: 2.0
- }
- }
- }
-}
-node {
- name: "DIV_01"
- op: "RealDiv"
- input: "input_01"
- input: "input_02"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, tf_div_basic)
-{
- // load graph
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
- signature.add_output(moco::tf::TensorName("DIV_01", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(div_basic_pbtxt, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - TFRealDiv node should exist
- // - both inputs x() and y() should not be null
-
- auto div_node = moco::tf::test::find_first_node_bytype<moco::tf::TFRealDiv>(graph.get());
-
- ASSERT_NE(div_node, nullptr);
- ASSERT_NE(div_node->x(), nullptr);
- ASSERT_NE(div_node->y(), nullptr);
-}
diff --git a/compiler/moco-tf/src/Op/Relu.cpp b/compiler/moco-tf/src/Op/Relu.cpp
deleted file mode 100644
index 0c3ceeec6..000000000
--- a/compiler/moco-tf/src/Op/Relu.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Relu.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-#include "Knob.h"
-
-#include "IR/TFRelu.h"
-
-#include <moco/tf/Names.h>
-#include <loco.h>
-#include <stdex/Memory.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <cassert>
-#include <stdexcept>
-
-namespace
-{
-
-using namespace moco::tf;
-
-class ReLUGraphUpdate final : public GraphUpdate
-{
-public:
- ReLUGraphUpdate(loco::ReLU *node, const TensorName &&name) : _node(node), _name(name) {}
-
- void input(const SymbolTable *) const override;
-
-private:
- loco::ReLU *_node;
- const TensorName _name;
-};
-
-class TFReluGraphUpdate final : public GraphUpdate
-{
-public:
- TFReluGraphUpdate(moco::tf::TFRelu *node, const TensorName &&name) : _node(node), _name(name) {}
-
- void input(const SymbolTable *) const override;
-
-private:
- moco::tf::TFRelu *_node;
- const TensorName _name;
-};
-
-void ReLUGraphUpdate::input(const SymbolTable *table) const
-{
- loco::Node *target = table->node(_name);
- _node->input(target);
-}
-
-void TFReluGraphUpdate::input(const SymbolTable *table) const
-{
- loco::Node *target = table->node(_name);
- _node->features(target);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for Relu node
- */
-class ReluGraphBuilder final : public ReluGraphBuilderBase
-{
-public:
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool ReluGraphBuilderBase::validate(const tensorflow::NodeDef &node) const
-{
- // ReLU node SHOULD have only one input
- if (node.input_size() != 1)
- return false;
-
- return true;
-}
-
-void ReluGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- if (moco::tf::get<moco::tf::Knob::ImportAsTFRelu>())
- {
- ReluGraphBuilderImpl<ImportTarget::TensorFlow> builder;
- return builder.build(node, context);
- }
- else
- {
- ReluGraphBuilderImpl<ImportTarget::Canonical> builder;
- return builder.build(node, context);
- }
-}
-
-void ReluGraphBuilderImpl<ImportTarget::Canonical>::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // Create a "ReLU" node for Relu
- auto relu_node = graph->nodes()->create<loco::ReLU>();
-
- // register string-name to node
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, relu_node);
-
- // Queue node input update
- auto update = stdex::make_unique<ReLUGraphUpdate>(relu_node, TensorName(node.input(0)));
- updates->enroll(std::move(update));
-}
-
-void ReluGraphBuilderImpl<ImportTarget::TensorFlow>::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // Create a "TFRelu" node for Relu
- auto relu_node = graph->nodes()->create<moco::tf::TFRelu>();
-
- // register string-name to node
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, relu_node);
-
- // Queue node input update
- auto update = stdex::make_unique<TFReluGraphUpdate>(relu_node, TensorName(node.input(0)));
- updates->enroll(std::move(update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(Relu, ReluGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/Relu.h b/compiler/moco-tf/src/Op/Relu.h
deleted file mode 100644
index 7d75f8a03..000000000
--- a/compiler/moco-tf/src/Op/Relu.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __OP_RELU_H__
-#define __OP_RELU_H__
-
-#include "GraphBuilder.h"
-#include "ImportTarget.h"
-
-namespace moco
-{
-namespace tf
-{
-
-struct ReluGraphBuilderBase : public GraphBuilder
-{
- virtual ~ReluGraphBuilderBase() = default;
-
- bool validate(const tensorflow::NodeDef &) const final;
-};
-
-template <ImportTarget T> class ReluGraphBuilderImpl;
-
-template <> struct ReluGraphBuilderImpl<ImportTarget::Canonical> final : public ReluGraphBuilderBase
-{
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
-};
-
-template <>
-struct ReluGraphBuilderImpl<ImportTarget::TensorFlow> final : public ReluGraphBuilderBase
-{
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __OP_RELU_H__
diff --git a/compiler/moco-tf/src/Op/Relu.test.cpp b/compiler/moco-tf/src/Op/Relu.test.cpp
deleted file mode 100644
index bdd1152c3..000000000
--- a/compiler/moco-tf/src/Op/Relu.test.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Relu.h"
-
-#include "IR/TFRelu.h"
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <memory>
-
-using namespace moco::tf;
-using namespace moco::tf::test;
-
-namespace
-{
-
-// clang-format off
-const char *relu_01_pbtxtdata = STRING_CONTENT(
-node {
- name: "Placeholder"
- op: "Placeholder"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "shape"
- value {
- shape {
- dim {
- size: 2
- }
- dim {
- size: 3
- }
- }
- }
- }
-}
-node {
- name: "ReLU"
- op: "Relu"
- input: "Placeholder"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, relu_01)
-{
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
-
- signature.add_input(moco::tf::TensorName("Placeholder", 0));
- signature.add_output(moco::tf::TensorName("ReLU", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(relu_01_pbtxtdata, graph_def));
-
- // Test "ReluGraphBuilderImpl<ImportTarget::Canonical>"
- {
- // TODO: fix indentation
- // clang-format off
-
- using ReluGraphBuilder = ReluGraphBuilderImpl<ImportTarget::Canonical>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("Relu", stdex::make_unique<ReluGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - there should exist ReLU
- // - input node should not be nullptr
-
- loco::ReLU *relu_node = moco::tf::test::find_first_node_bytype<loco::ReLU>(graph.get());
-
- ASSERT_NE(relu_node, nullptr);
- ASSERT_NE(relu_node->input(), nullptr);
- // clang-format on
- }
-
- // Test "ReluGraphBuilderImpl<ImportTarget::TensorFlow>"
- {
- using ReluGraphBuilder = ReluGraphBuilderImpl<ImportTarget::TensorFlow>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("Relu", stdex::make_unique<ReluGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - there should exist TFRelu
- // - features node should not be nullptr
-
- auto relu_node = moco::tf::test::find_first_node_bytype<moco::tf::TFRelu>(graph.get());
-
- ASSERT_NE(relu_node, nullptr);
- ASSERT_NE(relu_node->features(), nullptr);
- }
-}
diff --git a/compiler/moco-tf/src/Op/Relu6.cpp b/compiler/moco-tf/src/Op/Relu6.cpp
deleted file mode 100644
index 8f697cc6f..000000000
--- a/compiler/moco-tf/src/Op/Relu6.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Relu6.h"
-
-#include "GraphBuilder.h"
-#include "Knob.h"
-
-#include "IR/TFRelu6.h"
-
-#include <stdex/Memory.h>
-
-namespace
-{
-
-using namespace moco::tf;
-
-class ReLU6GraphUpdate final : public GraphUpdate
-{
-public:
- ReLU6GraphUpdate(loco::ReLU6 *node, const TensorName &&name) : _node(node), _name(name) {}
-
- void input(const SymbolTable *) const override;
-
-private:
- loco::ReLU6 *_node;
- const TensorName _name;
-};
-
-class TFRelu6GraphUpdate final : public GraphUpdate
-{
-public:
- TFRelu6GraphUpdate(moco::tf::TFRelu6 *node, const TensorName &&name) : _node(node), _name(name) {}
-
- void input(const SymbolTable *) const override;
-
-private:
- moco::tf::TFRelu6 *_node;
- const TensorName _name;
-};
-
-void ReLU6GraphUpdate::input(const SymbolTable *table) const
-{
- loco::Node *target = table->node(_name);
- _node->input(target);
-}
-
-void TFRelu6GraphUpdate::input(const SymbolTable *table) const
-{
- loco::Node *target = table->node(_name);
- _node->features(target);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-/**
- * @brief GraphBuilder for Relu6 node
- */
-class Relu6GraphBuilder final : public Relu6GraphBuilderBase
-{
-public:
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool Relu6GraphBuilderBase::validate(const tensorflow::NodeDef &node) const
-{
- // ReLU6 node SHOULD have only one input
- if (node.input_size() != 1)
- return false;
- return true;
-}
-
-void Relu6GraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- if (moco::tf::get<moco::tf::Knob::ImportAsTFRelu6>())
- {
- Relu6GraphBuilderImpl<ImportTarget::TensorFlow> builder;
- return builder.build(node, context);
- }
- else
- {
- Relu6GraphBuilderImpl<ImportTarget::Canonical> builder;
- return builder.build(node, context);
- }
-}
-
-void Relu6GraphBuilderImpl<ImportTarget::Canonical>::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // Create a "ReLU6" node for Relu6
- auto relu6_node = graph->nodes()->create<loco::ReLU6>();
-
- // register string-name to node
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, relu6_node);
-
- // Queue node input update
- auto update = stdex::make_unique<ReLU6GraphUpdate>(relu6_node, TensorName(node.input(0)));
- updates->enroll(std::move(update));
-}
-
-void Relu6GraphBuilderImpl<ImportTarget::TensorFlow>::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // Create a "TFRelu6" node for Relu
- auto relu_node = graph->nodes()->create<moco::tf::TFRelu6>();
-
- // register string-name to node
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, relu_node);
-
- // Queue node input update
- auto update = stdex::make_unique<TFRelu6GraphUpdate>(relu_node, TensorName(node.input(0)));
- updates->enroll(std::move(update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(Relu6, Relu6GraphBuilder)
diff --git a/compiler/moco-tf/src/Op/Relu6.h b/compiler/moco-tf/src/Op/Relu6.h
deleted file mode 100644
index 8bbadee1d..000000000
--- a/compiler/moco-tf/src/Op/Relu6.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __OP_RELU6_H__
-#define __OP_RELU6_H__
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-#include "ImportTarget.h"
-
-namespace moco
-{
-namespace tf
-{
-
-struct Relu6GraphBuilderBase : public GraphBuilder
-{
- virtual ~Relu6GraphBuilderBase() = default;
-
- bool validate(const tensorflow::NodeDef &) const final;
-};
-
-template <ImportTarget T> class Relu6GraphBuilderImpl;
-
-template <>
-struct Relu6GraphBuilderImpl<ImportTarget::Canonical> final : public Relu6GraphBuilderBase
-{
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
-};
-
-template <>
-struct Relu6GraphBuilderImpl<ImportTarget::TensorFlow> final : public Relu6GraphBuilderBase
-{
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __OP_RELU6_H__
diff --git a/compiler/moco-tf/src/Op/Relu6.test.cpp b/compiler/moco-tf/src/Op/Relu6.test.cpp
deleted file mode 100644
index 4d6832353..000000000
--- a/compiler/moco-tf/src/Op/Relu6.test.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Relu6.h"
-
-#include "IR/TFRelu6.h"
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <memory>
-
-using namespace moco::tf;
-using namespace moco::tf::test;
-
-namespace
-{
-
-// clang-format off
-const char *relu6_01_pbtxtdata = STRING_CONTENT(
-node {
- name: "Placeholder"
- op: "Placeholder"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "shape"
- value {
- shape {
- dim {
- size: 2
- }
- dim {
- size: 3
- }
- }
- }
- }
-}
-node {
- name: "ReLU6"
- op: "Relu6"
- input: "Placeholder"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, relu6_01)
-{
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
-
- signature.add_input(moco::tf::TensorName("Placeholder", 0));
- signature.add_output(moco::tf::TensorName("ReLU6", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(relu6_01_pbtxtdata, graph_def));
-
- // Test "Relu6GraphBuilderImpl<ImportTarget::Canonical>"
- {
- // TODO: fix indentation
- // clang-format off
-
- using ReluGraphBuilder = Relu6GraphBuilderImpl<ImportTarget::Canonical>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("Relu6", stdex::make_unique<ReluGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - there should exist ReLU6
- // - input node should not be nullptr
-
- loco::ReLU6 *relu6_node = moco::tf::test::find_first_node_bytype<loco::ReLU6>(graph.get());
-
- ASSERT_NE(relu6_node, nullptr);
- ASSERT_NE(relu6_node->input(), nullptr);
- // clang-format on
- }
-
- // Test "ReluGraphBuilderImpl<ImportTarget::TensorFlow>"
- {
- using ReluGraphBuilder = Relu6GraphBuilderImpl<ImportTarget::TensorFlow>;
-
- moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
- r.add("Relu6", stdex::make_unique<ReluGraphBuilder>());
- moco::tf::Importer importer{&r};
-
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - there should exist TFRelu6
- // - features node should not be null
-
- auto relu_node = moco::tf::test::find_first_node_bytype<moco::tf::TFRelu6>(graph.get());
-
- ASSERT_NE(relu_node, nullptr);
- ASSERT_NE(relu_node->features(), nullptr);
- }
-}
diff --git a/compiler/moco-tf/src/Op/Reshape.cpp b/compiler/moco-tf/src/Op/Reshape.cpp
deleted file mode 100644
index 08931f7e5..000000000
--- a/compiler/moco-tf/src/Op/Reshape.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-
-#include "IR/TFReshape.h"
-
-#include <moco/tf/Names.h>
-#include <plier/tf/Convert.h>
-#include <loco.h>
-#include <stdex/Memory.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <cassert>
-#include <stdexcept>
-
-namespace
-{
-using namespace moco::tf;
-
-class ReshapeGraphUpdate final : public GraphUpdate
-{
-public:
- ReshapeGraphUpdate(TFReshape *node, std::vector<TensorName> names) : _node(node), _names(names) {}
-
- void input(const SymbolTable *) const override;
-
-private:
- TFReshape *_node;
- std::vector<TensorName> _names;
-};
-
-void ReshapeGraphUpdate::input(const SymbolTable *node_table) const
-{
- assert(_names.size() == 2);
-
- auto tensor_node = node_table->node(_names[0]);
- auto shape_node = node_table->node(_names[1]);
-
- assert(tensor_node != nullptr);
- assert(shape_node != nullptr);
-
- _node->tensor(tensor_node);
- _node->shape(shape_node);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/// @brief GraphBuilder for Reshape node
-class ReshapeGraphBuilder final : public GraphBuilder
-{
-public:
- bool validate(const tensorflow::NodeDef &) const override;
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool ReshapeGraphBuilder::validate(const tensorflow::NodeDef &node) const
-{
- // Tensorflow Reshape has 2 inputs: tensor & shape
- if (node.input_size() != 2)
- return false;
-
- // TODO Assert Tshape value is DT_INT32?
- return plier::tf::has_attrs(node, {"T", "Tshape"});
-}
-
-void ReshapeGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // name of loco nodes
- std::string reshape_name = node.name();
-
- auto reshape = graph->nodes()->create<TFReshape>();
-
- // save the name for graph link updates
- TensorName output_name(reshape_name, 0);
- tensor_names->enroll(output_name, reshape);
-
- std::vector<TensorName> input_names;
- input_names.push_back(TensorName(node.input(0))); // tensor
- input_names.push_back(TensorName(node.input(1))); // shape
-
- // Queue node input update
- auto update = stdex::make_unique<ReshapeGraphUpdate>(reshape, input_names);
-
- updates->enroll(std::move(update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(Reshape, ReshapeGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/Reshape.test.cpp b/compiler/moco-tf/src/Op/Reshape.test.cpp
deleted file mode 100644
index 66d4f0054..000000000
--- a/compiler/moco-tf/src/Op/Reshape.test.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-#include "IR/TFReshape.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <memory>
-
-namespace
-{
-
-// clang-format off
-const char *reshape_01_pbtxtdata = STRING_CONTENT(
-node {
- name: "placeholder"
- op: "Placeholder"
- attr {
- key: "dtype"
- value { type: DT_FLOAT }
- }
- attr {
- key: "shape"
- value {
- shape {
- dim { size: 4 }
- }
- }
- }
-}
-node {
- name: "shape"
- op: "Const"
- attr {
- key: "dtype"
- value { type: DT_INT32 }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_INT32
- tensor_shape {
- dim { size: 2 }
- }
- int_val: 2
- int_val: 2
- }
- }
- }
-}
-node {
- name: "reshape"
- op: "Reshape"
- input: "placeholder"
- input: "shape"
- attr {
- key: "T"
- value { type: DT_FLOAT }
- }
- attr {
- key: "Tshape"
- value { type: DT_INT32 }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, reshape_01)
-{
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
-
- signature.add_input(moco::tf::TensorName("placeholder", 0));
- signature.add_output(moco::tf::TensorName("reshape", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(reshape_01_pbtxtdata, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- moco::tf::TFReshape *reshape_node =
- moco::tf::test::find_first_node_bytype<moco::tf::TFReshape>(graph.get());
-
- ASSERT_NE(reshape_node, nullptr);
- ASSERT_NE(reshape_node->tensor(), nullptr);
- ASSERT_NE(reshape_node->shape(), nullptr);
-}
diff --git a/compiler/moco-tf/src/Op/Rsqrt.cpp b/compiler/moco-tf/src/Op/Rsqrt.cpp
deleted file mode 100644
index e3b7fcc98..000000000
--- a/compiler/moco-tf/src/Op/Rsqrt.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFRsqrt.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-
-#include <loco.h>
-#include <stdex/Memory.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-namespace
-{
-
-using namespace moco::tf;
-
-/**
- * @brief GraphUpdate for TF Rsqrt node
- */
-class TFRsqrtGraphUpdate final : public GraphUpdate
-{
-public:
- TFRsqrtGraphUpdate(TFRsqrt *node, TensorName &&name) : _node(node), _name(name) {}
-
- void input(const SymbolTable *) const override;
-
-private:
- TFRsqrt *_node;
- TensorName _name;
-};
-
-void TFRsqrtGraphUpdate::input(const SymbolTable *table) const
-{
- loco::Node *target = table->node(_name);
- _node->x(target);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for Rsqrt node
- */
-class RsqrtGraphBuilder final : public GraphBuilder
-{
-public:
- bool validate(const tensorflow::NodeDef &) const override;
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool RsqrtGraphBuilder::validate(const tensorflow::NodeDef &node) const
-{
- assert(node.input_size() == 1);
-
- return true;
-}
-
-void RsqrtGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // creating TF dialect Rsqrt node
- auto tf_rsqrt = graph->nodes()->create<TFRsqrt>();
-
- // register string-name to node
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, tf_rsqrt);
-
- // Queue node input update
- auto tf_rsqrt_update =
- stdex::make_unique<TFRsqrtGraphUpdate>(tf_rsqrt, TensorName(node.input(0)));
- updates->enroll(std::move(tf_rsqrt_update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(Rsqrt, RsqrtGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/Rsqrt.test.cpp b/compiler/moco-tf/src/Op/Rsqrt.test.cpp
deleted file mode 100644
index 0fd76d472..000000000
--- a/compiler/moco-tf/src/Op/Rsqrt.test.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-
-#include "IR/TFRsqrt.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <cstring>
-#include <memory>
-
-using namespace moco::tf::test;
-
-namespace
-{
-// clang-format off
-const char *rsqrt_basic_pbtxt = STRING_CONTENT(
-node {
- name: "Placeholder"
- op: "Placeholder"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "shape"
- value {
- shape {
- dim {
- size: 1
- }
- dim {
- size: 2
- }
- dim {
- size: 3
- }
- dim {
- size: 4
- }
- }
- }
- }
-}
-node {
- name: "RSQRT_01"
- op: "Rsqrt"
- input: "Placeholder"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, tf_rsqrt_basic)
-{
- // load graph
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
- signature.add_output(moco::tf::TensorName("RSQRT_01", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(rsqrt_basic_pbtxt, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - TFRsqrt node should exist
- // - input x() should not be null
-
- auto rsqrt_node = moco::tf::test::find_first_node_bytype<moco::tf::TFRsqrt>(graph.get());
-
- ASSERT_NE(rsqrt_node, nullptr);
- ASSERT_NE(rsqrt_node->x(), nullptr);
-}
diff --git a/compiler/moco-tf/src/Op/Shape.cpp b/compiler/moco-tf/src/Op/Shape.cpp
deleted file mode 100644
index 9e2f00bb4..000000000
--- a/compiler/moco-tf/src/Op/Shape.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFShape.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-
-#include <loco.h>
-#include <stdex/Memory.h>
-#include <plier/tf/Convert.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-namespace
-{
-using namespace moco::tf;
-
-/**
- * @brief GraphUpdate for Shape node
- */
-class ShapeGraphUpdate final : public GraphUpdate
-{
-public:
- ShapeGraphUpdate(TFShape *node, const TensorName &&input_name)
- : _node(node), _input_name(input_name)
- {
- // DO NOTHING
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- TFShape *_node;
- const TensorName _input_name;
-};
-
-void ShapeGraphUpdate::input(const SymbolTable *table) const
-{
- loco::Node *input_node = table->node(_input_name);
- _node->input(input_node);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for Shape node
- */
-class ShapeGraphBuilder final : public GraphBuilder
-{
-public:
- bool validate(const tensorflow::NodeDef &) const override;
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool ShapeGraphBuilder::validate(const tensorflow::NodeDef &node) const
-{
- assert(node.input_size() == 1);
-
- return plier::tf::has_attrs(node, {"T"});
-}
-
-void ShapeGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // create TF dialect Shape node
- auto tf_shape = graph->nodes()->create<TFShape>();
-
- if (plier::tf::has_attrs(node, {"out_type"}))
- {
- auto dtype = plier::tf::as_loco_datatype(plier::tf::get_datatype_attr(node, "out_type"));
- // TODO Support other dtype like S64
- assert(dtype == loco::DataType::S32);
-
- tf_shape->dtype(dtype);
- }
- else
- {
- // Set to S32, TF-documented default value for 'out_type'
- tf_shape->dtype(loco::DataType::S32);
- }
-
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, tf_shape);
-
- auto update = stdex::make_unique<ShapeGraphUpdate>(tf_shape, TensorName(node.input(0)));
- updates->enroll(std::move(update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(Shape, ShapeGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/Shape.test.cpp b/compiler/moco-tf/src/Op/Shape.test.cpp
deleted file mode 100644
index 6abefb071..000000000
--- a/compiler/moco-tf/src/Op/Shape.test.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-#include "IR/TFShape.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <memory>
-
-namespace
-{
-
-// clang-format off
-const char *shape_000_pbtxtdata = STRING_CONTENT(
-node {
- name: "Placeholder"
- op: "Placeholder"
- attr {
- key: "dtype"
- value { type: DT_FLOAT }
- }
- attr {
- key: "shape"
- value {
- shape {
- dim { size: 1 }
- dim { size: 2 }
- dim { size: 2 }
- dim { size: 3 }
- }
- }
- }
-}
-node {
- name: "Shape"
- op: "Shape"
- input: "Placeholder"
- attr {
- key: "T"
- value { type: DT_FLOAT }
- }
- attr {
- key: "out_type"
- value { type: DT_INT32 }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, shape_000)
-{
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
-
- signature.add_input(moco::tf::TensorName("Placeholder", 0));
- signature.add_output(moco::tf::TensorName("Shape", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(shape_000_pbtxtdata, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - there should exist TFShape
- // - input node should not be null
- // - dtype attribute is set same as out_type attribute of pbtxt
-
- moco::tf::TFShape *shape_node =
- moco::tf::test::find_first_node_bytype<moco::tf::TFShape>(graph.get());
-
- ASSERT_NE(shape_node, nullptr);
- ASSERT_NE(shape_node->input(), nullptr);
- ASSERT_EQ(shape_node->dtype(), loco::DataType::S32);
-}
diff --git a/compiler/moco-tf/src/Op/Softmax.cpp b/compiler/moco-tf/src/Op/Softmax.cpp
deleted file mode 100644
index d813b9d3d..000000000
--- a/compiler/moco-tf/src/Op/Softmax.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFSoftmax.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-
-#include <loco.h>
-#include <stdex/Memory.h>
-#include <plier/tf/Convert.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-namespace
-{
-using namespace moco::tf;
-
-/**
-* @brief GraphUpdate for Softmax node
-*/
-class SoftmaxGraphUpdate final : public GraphUpdate
-{
-public:
- SoftmaxGraphUpdate(TFSoftmax *node, const TensorName &&input_name)
- : _node(node), _input_name(input_name)
- {
- // DO NOTHING
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- TFSoftmax *_node;
- const TensorName _input_name;
-};
-
-void SoftmaxGraphUpdate::input(const SymbolTable *table) const
-{
- loco::Node *input_node = table->node(_input_name);
- _node->logits(input_node);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
-* @brief GraphBuilder for Softmax node
-*/
-class SoftmaxGraphBuilder final : public GraphBuilder
-{
-public:
- bool validate(const tensorflow::NodeDef &) const override;
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool SoftmaxGraphBuilder::validate(const tensorflow::NodeDef &node) const
-{
- assert(node.input_size() == 1);
-
- return plier::tf::has_attrs(node, {"T"});
-}
-
-void SoftmaxGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // creating TF dialect Softmax node
- auto tf_softmax = graph->nodes()->create<TFSoftmax>();
-
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, tf_softmax);
-
- auto update = stdex::make_unique<SoftmaxGraphUpdate>(tf_softmax, TensorName(node.input(0)));
- updates->enroll(std::move(update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(Softmax, SoftmaxGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/Softmax.test.cpp b/compiler/moco-tf/src/Op/Softmax.test.cpp
deleted file mode 100644
index d4f9fc1c2..000000000
--- a/compiler/moco-tf/src/Op/Softmax.test.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-#include "IR/TFSoftmax.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <memory>
-
-namespace
-{
-
-// clang-format off
-const char *softmax_2d_pbtxtdata = STRING_CONTENT(
-node {
- name: "Placeholder"
- op: "Placeholder"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "shape"
- value {
- shape {
- dim {
- size: 2
- }
- dim {
- size: 3
- }
- }
- }
- }
-}
-node {
- name: "Softmax"
- op: "Softmax"
- input: "Placeholder"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, softmax_2d)
-{
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
-
- signature.add_input(moco::tf::TensorName("Placeholder", 0));
- signature.add_output(moco::tf::TensorName("Softmax", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(softmax_2d_pbtxtdata, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - there should exist TFSoftmax
- // - logits node should not be null
-
- moco::tf::TFSoftmax *softmax_node =
- moco::tf::test::find_first_node_bytype<moco::tf::TFSoftmax>(graph.get());
-
- ASSERT_NE(softmax_node, nullptr);
- ASSERT_NE(softmax_node->logits(), nullptr);
-}
diff --git a/compiler/moco-tf/src/Op/Sqrt.cpp b/compiler/moco-tf/src/Op/Sqrt.cpp
deleted file mode 100644
index 6b7dec61b..000000000
--- a/compiler/moco-tf/src/Op/Sqrt.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFSqrt.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-
-#include <loco.h>
-#include <stdex/Memory.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-namespace
-{
-
-using namespace moco::tf;
-
-/**
- * @brief GraphUpdate for TF Sqrt node
- */
-class TFSqrtGraphUpdate final : public GraphUpdate
-{
-public:
- TFSqrtGraphUpdate(TFSqrt *node, TensorName &&name) : _node(node), _name(name) {}
-
- void input(const SymbolTable *) const override;
-
-private:
- TFSqrt *_node;
- TensorName _name;
-};
-
-void TFSqrtGraphUpdate::input(const SymbolTable *table) const
-{
- loco::Node *target = table->node(_name);
- _node->x(target);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for Sqrt node
- */
-class SqrtGraphBuilder final : public GraphBuilder
-{
-public:
- bool validate(const tensorflow::NodeDef &) const override;
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool SqrtGraphBuilder::validate(const tensorflow::NodeDef &node) const
-{
- assert(node.input_size() == 1);
-
- return true;
-}
-
-void SqrtGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // creating TF dialect Sqrt node
- auto tf_sqrt = graph->nodes()->create<TFSqrt>();
-
- // register string-name to node
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, tf_sqrt);
-
- // Queue node input update
- auto tf_sqrt_update = stdex::make_unique<TFSqrtGraphUpdate>(tf_sqrt, TensorName(node.input(0)));
- updates->enroll(std::move(tf_sqrt_update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(Sqrt, SqrtGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/Sqrt.test.cpp b/compiler/moco-tf/src/Op/Sqrt.test.cpp
deleted file mode 100644
index 2c55c602e..000000000
--- a/compiler/moco-tf/src/Op/Sqrt.test.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-
-#include "IR/TFSqrt.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <cstring>
-#include <memory>
-
-using namespace moco::tf::test;
-
-namespace
-{
-// clang-format off
-const char *sqrt_basic_pbtxt = STRING_CONTENT(
-node {
- name: "Placeholder"
- op: "Placeholder"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "shape"
- value {
- shape {
- dim {
- size: 1
- }
- dim {
- size: 2
- }
- dim {
- size: 3
- }
- dim {
- size: 4
- }
- }
- }
- }
-}
-node {
- name: "SQRT_01"
- op: "Sqrt"
- input: "Placeholder"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, tf_sqrt_basic)
-{
- // load graph
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
- signature.add_output(moco::tf::TensorName("SQRT_01", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(sqrt_basic_pbtxt, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - TFSqrt node should exist
- // - input x() should not be null
-
- auto sqrt_node = moco::tf::test::find_first_node_bytype<moco::tf::TFSqrt>(graph.get());
-
- ASSERT_NE(sqrt_node, nullptr);
- ASSERT_NE(sqrt_node->x(), nullptr);
-}
diff --git a/compiler/moco-tf/src/Op/SquaredDifference.cpp b/compiler/moco-tf/src/Op/SquaredDifference.cpp
deleted file mode 100644
index bbccad757..000000000
--- a/compiler/moco-tf/src/Op/SquaredDifference.cpp
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFSquaredDifference.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-
-#include <loco.h>
-#include <stdex/Memory.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-namespace
-{
-
-using namespace moco::tf;
-
-/**
- * @brief GraphUpdate for TF SquaredDifference node
- */
-class TFSquaredDifferenceGraphUpdate final : public GraphUpdate
-{
-public:
- TFSquaredDifferenceGraphUpdate(TFSquaredDifference *node, std::vector<TensorName> names)
- : _node(node), _names(names)
- {
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- TFSquaredDifference *_node;
- std::vector<TensorName> _names;
-};
-
-void TFSquaredDifferenceGraphUpdate::input(const SymbolTable *table) const
-{
- int num_inputs = _names.size();
- assert(num_inputs == 2);
-
- _node->x(table->node(_names[0]));
- _node->y(table->node(_names[1]));
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for SquaredDifference node
- */
-class SquaredDifferenceGraphBuilder final : public GraphBuilder
-{
-public:
- bool validate(const tensorflow::NodeDef &) const override;
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool SquaredDifferenceGraphBuilder::validate(const tensorflow::NodeDef &node) const
-{
- assert(node.input_size() == 2);
-
- return true;
-}
-
-void SquaredDifferenceGraphBuilder::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // creating TF dialect SquaredDifference node
- auto tf_sqdiff = graph->nodes()->create<TFSquaredDifference>();
-
- // register string-name to node
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, tf_sqdiff);
-
- std::vector<TensorName> add_input_names;
- add_input_names.push_back(TensorName(node.input(0))); // x
- add_input_names.push_back(TensorName(node.input(1))); // y
-
- // Queue node input update
- auto tf_sqrt_update =
- stdex::make_unique<TFSquaredDifferenceGraphUpdate>(tf_sqdiff, add_input_names);
- updates->enroll(std::move(tf_sqrt_update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(SquaredDifference, SquaredDifferenceGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/SquaredDifference.test.cpp b/compiler/moco-tf/src/Op/SquaredDifference.test.cpp
deleted file mode 100644
index 1efe2ef48..000000000
--- a/compiler/moco-tf/src/Op/SquaredDifference.test.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-
-#include "IR/TFSquaredDifference.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <cstring>
-#include <memory>
-
-using namespace moco::tf::test;
-
-namespace
-{
-// clang-format off
-const char *sqdiff_basic_pbtxt = STRING_CONTENT(
-node {
- name: "input_01"
- op: "Placeholder"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "shape"
- value {
- shape {
- dim {
- size: 1
- }
- dim {
- size: 3
- }
- dim {
- size: 3
- }
- dim {
- size: 1
- }
- }
- }
- }
-}
-node {
- name: "input_02"
- op: "Placeholder"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "shape"
- value {
- shape {
- dim {
- size: 1
- }
- dim {
- size: 3
- }
- dim {
- size: 3
- }
- dim {
- size: 1
- }
- }
- }
- }
-}
-node {
- name: "squared_difference"
- op: "SquaredDifference"
- input: "input_01"
- input: "input_02"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, tf_squdiff_basic)
-{
- // load graph
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
- signature.add_output(moco::tf::TensorName("squared_difference", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(sqdiff_basic_pbtxt, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - TFSquaredDifference node should exist
- // - input x() should not be null
- // - input y() should not be null
-
- auto sqdiff_node =
- moco::tf::test::find_first_node_bytype<moco::tf::TFSquaredDifference>(graph.get());
-
- ASSERT_NE(sqdiff_node, nullptr);
- ASSERT_NE(sqdiff_node->x(), nullptr);
- ASSERT_NE(sqdiff_node->y(), nullptr);
-}
diff --git a/compiler/moco-tf/src/Op/Squeeze.cpp b/compiler/moco-tf/src/Op/Squeeze.cpp
deleted file mode 100644
index a7aca3790..000000000
--- a/compiler/moco-tf/src/Op/Squeeze.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFSqueeze.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-
-#include <loco.h>
-#include <stdex/Memory.h>
-#include <plier/tf/Convert.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-namespace
-{
-using namespace moco::tf;
-
-/**
- * @brief GraphUpdate for Squeeze node
- */
-class SqueezeGraphUpdate final : public GraphUpdate
-{
-public:
- SqueezeGraphUpdate(TFSqueeze *node, const TensorName &&input_name)
- : _node(node), _input_name(input_name)
- {
- // DO NOTHING
- }
-
- void input(const SymbolTable *) const override;
-
-private:
- TFSqueeze *_node;
- const TensorName _input_name;
-};
-
-void SqueezeGraphUpdate::input(const SymbolTable *table) const
-{
- loco::Node *input_node = table->node(_input_name);
- _node->input(input_node);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for Squeeze node
- */
-class SqueezeGraphBuilder final : public GraphBuilder
-{
-public:
- bool validate(const tensorflow::NodeDef &) const override;
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool SqueezeGraphBuilder::validate(const tensorflow::NodeDef &node) const
-{
- assert(node.input_size() == 1);
-
- return plier::tf::has_attrs(node, {"T"});
-}
-
-void SqueezeGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- if (plier::tf::has_attrs(node, {"axis"}))
- {
- // TODO support 'axis' attribute
- std::runtime_error("Import Squeeze: 'axis' attribute is not supported yet");
- }
-
- std::vector<int64_t> squeeze_dims;
- if (plier::tf::has_attrs(node, {"squeeze_dims"}))
- {
- auto squeeze_dim_list = plier::tf::get_list_attr(node, {"squeeze_dims"});
- // TODO assert squeeze_dims are mutually different?
- squeeze_dims = plier::tf::as_int64_list(squeeze_dim_list);
- }
- // Note that it is possible that NodeDef does not have squeeze_dims attribute.
- // In that case, TFSqueeze also has empty squeeze_dims,
-
- // creating TF dialect Squeeze node
- auto tf_squeeze = graph->nodes()->create<TFSqueeze>();
- tf_squeeze->squeeze_dims(squeeze_dims);
-
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, tf_squeeze);
-
- auto update = stdex::make_unique<SqueezeGraphUpdate>(tf_squeeze, TensorName(node.input(0)));
- updates->enroll(std::move(update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(Squeeze, SqueezeGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/Squeeze.test.cpp b/compiler/moco-tf/src/Op/Squeeze.test.cpp
deleted file mode 100644
index 179183b6c..000000000
--- a/compiler/moco-tf/src/Op/Squeeze.test.cpp
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-#include "IR/TFSqueeze.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <memory>
-
-namespace
-{
-
-// clang-format off
-const char *squeeze_all_pbtxtdata = STRING_CONTENT(
-node {
- name: "Placeholder"
- op: "Placeholder"
- attr {
- key: "dtype"
- value { type: DT_FLOAT }
- }
- attr {
- key: "shape"
- value {
- shape {
- dim { size: 2 }
- dim { size: 1 }
- dim { size: 3 }
- dim { size: 1 }
- }
- }
- }
-}
-node {
- name: "Squeeze"
- op: "Squeeze"
- input: "Placeholder"
- attr {
- key: "T"
- value { type: DT_FLOAT }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, squeeze_all)
-{
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
-
- signature.add_input(moco::tf::TensorName("Placeholder", 0));
- signature.add_output(moco::tf::TensorName("Squeeze", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(squeeze_all_pbtxtdata, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - there should exist TFSqueeze
- // - input node should not be null
- // - squeeze_dims attribute is set same as pbtxt
-
- moco::tf::TFSqueeze *squeeze_node =
- moco::tf::test::find_first_node_bytype<moco::tf::TFSqueeze>(graph.get());
-
- ASSERT_NE(squeeze_node, nullptr);
- ASSERT_NE(squeeze_node->input(), nullptr);
- ASSERT_EQ(squeeze_node->squeeze_dims().size(), 0);
-}
-
-namespace
-{
-
-// clang-format off
-const char *squeeze_some_pbtxtdata = STRING_CONTENT(
-node {
- name: "Placeholder"
- op: "Placeholder"
- attr {
- key: "dtype"
- value { type: DT_FLOAT }
- }
- attr {
- key: "shape"
- value {
- shape {
- dim { size: 2 }
- dim { size: 1 }
- dim { size: 3 }
- dim { size: 1 }
- }
- }
- }
-}
-node {
- name: "Squeeze"
- op: "Squeeze"
- input: "Placeholder"
- attr {
- key: "T"
- value { type: DT_FLOAT }
- }
- attr {
- key: "squeeze_dims"
- value {
- list { i: 1 }
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, squeeze_some)
-{
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
-
- signature.add_input(moco::tf::TensorName("Placeholder", 0));
- signature.add_output(moco::tf::TensorName("Squeeze", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(squeeze_some_pbtxtdata, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - there should exist TFSqueeze
- // - input node should not be null
- // - squeeze_dims attribute is set same as pbtxt
-
- moco::tf::TFSqueeze *squeeze_node =
- moco::tf::test::find_first_node_bytype<moco::tf::TFSqueeze>(graph.get());
-
- ASSERT_NE(squeeze_node, nullptr);
- ASSERT_NE(squeeze_node->input(), nullptr);
- ASSERT_EQ(squeeze_node->squeeze_dims().size(), 1);
- ASSERT_EQ(squeeze_node->squeeze_dims().at(0), 1);
-}
-
-// TODO Add test case for negative squeeze dim
diff --git a/compiler/moco-tf/src/Op/StopGradient.cpp b/compiler/moco-tf/src/Op/StopGradient.cpp
deleted file mode 100644
index dc28d6dfb..000000000
--- a/compiler/moco-tf/src/Op/StopGradient.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFStopGradient.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-
-#include <loco.h>
-#include <plier/tf/Convert.h>
-#include <stdex/Memory.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-namespace
-{
-
-using namespace moco::tf;
-
-/**
- * @brief GraphUpdate for TF StopGradient node
- */
-class TFStopGradientGraphUpdate final : public GraphUpdate
-{
-public:
- TFStopGradientGraphUpdate(TFStopGradient *node, TensorName &&name) : _node(node), _name(name) {}
-
- void input(const SymbolTable *) const override;
-
-private:
- TFStopGradient *_node;
- TensorName _name;
-};
-
-void TFStopGradientGraphUpdate::input(const SymbolTable *table) const
-{
- loco::Node *target = table->node(_name);
- _node->input(target);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for StopGradient node
- */
-class StopGradientGraphBuilder final : public GraphBuilder
-{
-public:
- bool validate(const tensorflow::NodeDef &) const override;
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool StopGradientGraphBuilder::validate(const tensorflow::NodeDef &node) const
-{
- assert(node.input_size() == 1);
-
- return plier::tf::has_attrs(node, {"T"});
-}
-
-void StopGradientGraphBuilder::build(const tensorflow::NodeDef &node,
- GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // creating TF dialect StopGradient node
- auto tf_stopgradient = graph->nodes()->create<TFStopGradient>();
-
- // register string-name to node
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, tf_stopgradient);
-
- // Queue node input update
- auto tf_stopgradient_update =
- stdex::make_unique<TFStopGradientGraphUpdate>(tf_stopgradient, TensorName(node.input(0)));
- updates->enroll(std::move(tf_stopgradient_update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(StopGradient, StopGradientGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/StopGradient.test.cpp b/compiler/moco-tf/src/Op/StopGradient.test.cpp
deleted file mode 100644
index dd92fb8f8..000000000
--- a/compiler/moco-tf/src/Op/StopGradient.test.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-
-#include "IR/TFStopGradient.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-using namespace moco::tf::test;
-
-namespace
-{
-// clang-format off
-const char *stopgradient_basic_pbtxt = STRING_CONTENT(
-node {
- name: "Placeholder"
- op: "Placeholder"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "shape"
- value {
- shape {
- dim {
- size: 1
- }
- dim {
- size: 2
- }
- dim {
- size: 3
- }
- dim {
- size: 4
- }
- }
- }
- }
-}
-node {
- name: "StopGradient_01"
- op: "StopGradient"
- input: "Placeholder"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, tf_stopgradient_basic)
-{
- // load graph
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
- signature.add_output(moco::tf::TensorName("StopGradient_01", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(stopgradient_basic_pbtxt, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - TFStopGradient node should exist
- // - input() should not be null
-
- auto node = moco::tf::test::find_first_node_bytype<moco::tf::TFStopGradient>(graph.get());
-
- ASSERT_NE(node, nullptr);
- ASSERT_NE(node->input(), nullptr);
-}
diff --git a/compiler/moco-tf/src/Op/Sub.cpp b/compiler/moco-tf/src/Op/Sub.cpp
deleted file mode 100644
index 2629b5aa8..000000000
--- a/compiler/moco-tf/src/Op/Sub.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFSub.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-
-#include <loco.h>
-#include <stdex/Memory.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-namespace
-{
-
-using namespace moco::tf;
-
-/**
- * @brief GraphUpdate for TF Sub node
- */
-class TFSubGraphUpdate final : public GraphUpdate
-{
-public:
- TFSubGraphUpdate(TFSub *node, std::vector<TensorName> names) : _node(node), _names(names) {}
-
- void input(const SymbolTable *) const override;
-
-private:
- TFSub *_node;
- std::vector<TensorName> _names;
-};
-
-void TFSubGraphUpdate::input(const SymbolTable *tensor_names) const
-{
- int num_inputs = _names.size();
- assert(num_inputs == 2);
-
- _node->x(tensor_names->node(_names[0]));
- _node->y(tensor_names->node(_names[1]));
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for Sub node
- */
-class SubGraphBuilder final : public GraphBuilder
-{
-public:
- bool validate(const tensorflow::NodeDef &) const override;
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool SubGraphBuilder::validate(const tensorflow::NodeDef &node) const
-{
- assert(node.input_size() == 2);
-
- return true;
-}
-
-void SubGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // creating TF dialect Sub node
- auto tf_sub = graph->nodes()->create<TFSub>();
-
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, tf_sub);
-
- std::vector<TensorName> sub_input_names;
- sub_input_names.push_back(TensorName(node.input(0))); // x
- sub_input_names.push_back(TensorName(node.input(1))); // y
-
- auto tf_sub_update = stdex::make_unique<TFSubGraphUpdate>(tf_sub, sub_input_names);
- updates->enroll(std::move(tf_sub_update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(Sub, SubGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/Sub.test.cpp b/compiler/moco-tf/src/Op/Sub.test.cpp
deleted file mode 100644
index ad2ad55fc..000000000
--- a/compiler/moco-tf/src/Op/Sub.test.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-
-#include "IR/TFSub.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <cstring>
-#include <memory>
-
-using namespace moco::tf::test;
-
-namespace
-{
-// clang-format off
-const char *sub_basic_pbtxt = STRING_CONTENT(
-node {
- name: "input_01"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 1
- }
- dim {
- size: 3
- }
- dim {
- size: 4
- }
- }
- float_val: 1.0
- }
- }
- }
-}
-node {
- name: "input_02"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 1
- }
- dim {
- size: 3
- }
- dim {
- size: 4
- }
- }
- float_val: 2.0
- }
- }
- }
-}
-node {
- name: "SUB_01"
- op: "Sub"
- input: "input_01"
- input: "input_02"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, tf_sub_basic)
-{
- // load graph
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
- signature.add_output(moco::tf::TensorName("SUB_01", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(sub_basic_pbtxt, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - TFSub node should exist
- // - both inputs x() and y() should not be null
-
- auto sub_node = moco::tf::test::find_first_node_bytype<moco::tf::TFSub>(graph.get());
-
- ASSERT_NE(sub_node, nullptr);
- ASSERT_NE(sub_node->x(), nullptr);
- ASSERT_NE(sub_node->y(), nullptr);
-}
diff --git a/compiler/moco-tf/src/Op/Tanh.cpp b/compiler/moco-tf/src/Op/Tanh.cpp
deleted file mode 100644
index b465401d1..000000000
--- a/compiler/moco-tf/src/Op/Tanh.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IR/TFTanh.h"
-
-#include "GraphBuilder.h"
-#include "GraphBuilderContext.h"
-
-#include <loco.h>
-#include <stdex/Memory.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-namespace
-{
-
-using namespace moco::tf;
-
-/**
- * @brief GraphUpdate for TF Tanh node
- */
-class TFTanhGraphUpdate final : public GraphUpdate
-{
-public:
- TFTanhGraphUpdate(TFTanh *node, TensorName &&name) : _node(node), _name(name) {}
-
- void input(const SymbolTable *) const override;
-
-private:
- TFTanh *_node;
- TensorName _name;
-};
-
-void TFTanhGraphUpdate::input(const SymbolTable *table) const
-{
- loco::Node *target = table->node(_name);
- _node->x(target);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief GraphBuilder for Tanh node
- */
-class TanhGraphBuilder final : public GraphBuilder
-{
-public:
- bool validate(const tensorflow::NodeDef &) const override;
- void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
-};
-
-bool TanhGraphBuilder::validate(const tensorflow::NodeDef &node) const
-{
- assert(node.input_size() == 1);
-
- return true;
-}
-
-void TanhGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
-{
- assert(context != nullptr);
-
- loco::Graph *graph = context->graph();
- SymbolTable *tensor_names = context->tensor_names();
- UpdateQueue *updates = context->updates();
-
- // creating TF dialect Tanh node
- auto tf_tanh = graph->nodes()->create<TFTanh>();
-
- // register string-name to node
- TensorName output_name(node.name(), 0);
- tensor_names->enroll(output_name, tf_tanh);
-
- // Queue node input update
- auto tf_tanh_update = stdex::make_unique<TFTanhGraphUpdate>(tf_tanh, TensorName(node.input(0)));
- updates->enroll(std::move(tf_tanh_update));
-}
-
-} // namespace tf
-} // namespace moco
-
-#include "GraphBuilderRegistry.h"
-
-REGISTER_OP_BUILDER(Tanh, TanhGraphBuilder)
diff --git a/compiler/moco-tf/src/Op/Tanh.test.cpp b/compiler/moco-tf/src/Op/Tanh.test.cpp
deleted file mode 100644
index 578ef2211..000000000
--- a/compiler/moco-tf/src/Op/Tanh.test.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestHelper.h"
-
-#include "Importer.h"
-
-#include "IR/TFTanh.h"
-
-#include <loco.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-#include <tensorflow/core/framework/graph.pb.h>
-
-#include <cstring>
-#include <memory>
-
-using namespace moco::tf::test;
-
-namespace
-{
-// clang-format off
-const char *tanh_basic_pbtxt = STRING_CONTENT(
-node {
- name: "Placeholder"
- op: "Placeholder"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "shape"
- value {
- shape {
- dim {
- size: 1
- }
- dim {
- size: 2
- }
- dim {
- size: 1
- }
- dim {
- size: 2
- }
- }
- }
- }
-}
-node {
- name: "output/tanh"
- op: "Tanh"
- input: "Placeholder"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-TEST(TensorFlowImport, tf_tanh_basic)
-{
- // load graph
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
- signature.add_output(moco::tf::TensorName("output/tanh", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(tanh_basic_pbtxt, graph_def));
- std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
-
- // what to test:
- // - TFTanh node should exist
- // - input x() should not be null
-
- auto tanh_node = moco::tf::test::find_first_node_bytype<moco::tf::TFTanh>(graph.get());
-
- ASSERT_NE(tanh_node, nullptr);
- ASSERT_NE(tanh_node->x(), nullptr);
-}
diff --git a/compiler/moco-tf/src/Phase.cpp b/compiler/moco-tf/src/Phase.cpp
deleted file mode 100644
index 6764691c7..000000000
--- a/compiler/moco-tf/src/Phase.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Phase.h"
-#include "LogHelper.h"
-
-#include <moco/Log.h>
-
-namespace
-{
-
-char to_char(bool b) { return b ? 'Y' : 'N'; }
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-void PhaseRunner<PhaseStrategy::Saturate>::run(const Phase &phase) const
-{
- LOGGER(l);
-
- INFO(l) << "==============================================================";
- INFO(l) << "PhaseRunner<Saturate>";
-
- INFO(l) << "Initial graph";
- INFO(l) << fmt(_graph);
-
- for (bool changed = true; changed;)
- {
- changed = false;
-
- for (auto &tr : phase)
- {
- bool chg_one = false;
-
- INFO(l) << "--------------------------------------------------------------";
- INFO(l) << "Before " << transform_name(tr.get());
-
- if (tr->run(_graph))
- {
- changed = true;
- chg_one = true;
- }
-
- INFO(l) << "After " << transform_name(tr.get()) << " (changed: " << to_char(chg_one) << ")";
- INFO(l) << fmt(_graph);
- }
- }
-
- INFO(l) << "PhaseRunner<Saturate> - done";
-}
-
-void PhaseRunner<PhaseStrategy::Restart>::run(const Phase &phase) const
-{
- LOGGER(l);
-
- INFO(l) << "==============================================================";
- INFO(l) << "PhaseRunner<Restart>";
-
- INFO(l) << "Initial graph";
- INFO(l) << fmt(_graph);
-
- for (bool changed = true; changed;)
- {
- changed = false;
-
- for (auto &tr : phase)
- {
- INFO(l) << "--------------------------------------------------------------";
- INFO(l) << "Before " << transform_name(tr.get());
-
- if (tr->run(_graph))
- {
- changed = true;
- }
-
- INFO(l) << "After " << transform_name(tr.get()) << " (changed: " << to_char(changed) << ")";
- INFO(l) << fmt(_graph);
-
- if (changed)
- {
- break;
- }
- }
- }
-
- INFO(l) << "PhaseRunner<Restart> - done";
-}
-
-} // namespace tf
-} // namespace moco
diff --git a/compiler/moco-tf/src/Phase.h b/compiler/moco-tf/src/Phase.h
deleted file mode 100644
index cb1854b59..000000000
--- a/compiler/moco-tf/src/Phase.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_PHASE_H__
-#define __MOCO_TF_PHASE_H__
-
-#include "Transform.h"
-
-#include <loco.h>
-
-#include <vector>
-#include <memory>
-
-namespace moco
-{
-namespace tf
-{
-
-// Phase is a collection of Transform(s)
-using Phase = std::vector<std::unique_ptr<Transform>>;
-
-enum class PhaseStrategy
-{
- // Run all the transforms until there is no transform that makes a change
- Saturate,
- // Same as Saturate but will restart from the first when there is a change
- Restart,
-};
-
-template <PhaseStrategy S> class PhaseRunner;
-
-template <> class PhaseRunner<PhaseStrategy::Saturate>
-{
-public:
- PhaseRunner(loco::Graph *graph) : _graph{graph}
- {
- // DO NOTHING
- }
-
-public:
- void run(const Phase &) const;
-
-private:
- loco::Graph *_graph;
-};
-
-template <> class PhaseRunner<PhaseStrategy::Restart>
-{
-public:
- PhaseRunner(loco::Graph *graph) : _graph{graph}
- {
- // DO NOTHING
- }
-
-public:
- void run(const Phase &) const;
-
-private:
- loco::Graph *_graph;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_PHASE_H__
diff --git a/compiler/moco-tf/src/SimpleNodeTransform.h b/compiler/moco-tf/src/SimpleNodeTransform.h
new file mode 100644
index 000000000..b69cbad6b
--- /dev/null
+++ b/compiler/moco-tf/src/SimpleNodeTransform.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_TF_SIMPLE_NODE_TRANSFORM_H__
+#define __MOCO_TF_SIMPLE_NODE_TRANSFORM_H__
+
+#include "Transform.h"
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Per-Node Transform
+ */
+template <typename ConcreteNode> struct SimpleNodeTransform : public Transform
+{
+ SimpleNodeTransform() = default;
+
+ virtual ~SimpleNodeTransform() = default;
+
+ // NOTE Users SHOULD implement this method
+ virtual bool transform(ConcreteNode *node) const = 0;
+
+ bool run(loco::Graph *graph) final
+ {
+ using loco::active_nodes;
+ using loco::output_nodes;
+
+ bool changed = false;
+
+ for (auto node : active_nodes(output_nodes(graph)))
+ {
+ if (auto casted = dynamic_cast<ConcreteNode *>(node))
+ {
+ if (transform(casted))
+ {
+ changed = true;
+ }
+ }
+ }
+
+ return changed;
+ }
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_SIMPLE_NODE_TRANSFORM_H__
diff --git a/compiler/moco-tf/src/SimpleNodeTransform.test.cpp b/compiler/moco-tf/src/SimpleNodeTransform.test.cpp
new file mode 100644
index 000000000..781a48781
--- /dev/null
+++ b/compiler/moco-tf/src/SimpleNodeTransform.test.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SimpleNodeTransform.h"
+
+#include <set>
+
+#include <gtest/gtest.h>
+
+TEST(SimpleNodeTransformTests, run)
+{
+ class Transform final : public moco::tf::SimpleNodeTransform<loco::Push>
+ {
+ public:
+ Transform(std::multiset<loco::Node *> *out) : _out{out}
+ {
+ // DO NOTHING
+ }
+
+ public:
+ bool transform(loco::Push *node) const final
+ {
+ _out->insert(node);
+ return false;
+ }
+
+ private:
+ std::multiset<loco::Node *> *_out;
+ };
+
+ auto g = loco::make_graph();
+ auto output_0 = g->outputs()->create();
+ auto push = g->nodes()->create<loco::Push>();
+ loco::link(output_0, push);
+
+ std::multiset<loco::Node *> nodes;
+ Transform transform{&nodes};
+
+ transform.run(g.get());
+
+ ASSERT_EQ(nodes.size(), 1);
+ ASSERT_EQ(nodes.count(push), 1);
+}
diff --git a/compiler/moco-tf/src/TFEltwiseBinaryCanonicalzeHelper.h b/compiler/moco-tf/src/TFEltwiseBinaryCanonicalzeHelper.h
index c86f6b9a0..df9aec144 100644
--- a/compiler/moco-tf/src/TFEltwiseBinaryCanonicalzeHelper.h
+++ b/compiler/moco-tf/src/TFEltwiseBinaryCanonicalzeHelper.h
@@ -1,8 +1,24 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#ifndef __TF_ELTWISE_BINARY_CANONICALIZE_HELPER_H__
#define __TF_ELTWISE_BINARY_CANONICALIZE_HELPER_H__
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
+#include <moco/IR/TFDialect.h>
+#include <moco/IR/TFNodes.h>
#include "CanonicalEltwiseInputConnector.h"
#include "BroadcastHelper.h"
@@ -18,22 +34,27 @@ namespace
template <typename TFNodeT> struct EltwiseBinaryCanonicalizationRule;
-template <> struct EltwiseBinaryCanonicalizationRule<moco::tf::TFAdd>
+template <> struct EltwiseBinaryCanonicalizationRule<moco::TFAdd>
{
using CanonicalNode = loco::EltwiseAdd;
};
-template <> struct EltwiseBinaryCanonicalizationRule<moco::tf::TFSub>
+template <> struct EltwiseBinaryCanonicalizationRule<moco::TFSub>
{
using CanonicalNode = loco::EltwiseSub;
};
-template <> struct EltwiseBinaryCanonicalizationRule<moco::tf::TFMul>
+template <> struct EltwiseBinaryCanonicalizationRule<moco::TFMaximum>
+{
+ using CanonicalNode = loco::EltwiseMax;
+};
+
+template <> struct EltwiseBinaryCanonicalizationRule<moco::TFMul>
{
using CanonicalNode = loco::EltwiseMul;
};
-template <> struct EltwiseBinaryCanonicalizationRule<moco::tf::TFRealDiv>
+template <> struct EltwiseBinaryCanonicalizationRule<moco::TFRealDiv>
{
using CanonicalNode = loco::EltwiseDiv;
};
@@ -46,25 +67,34 @@ template <typename TFNode> bool canonicalize_eltwise_binary_node(TFNode *node)
* This will replace T/F Eltwise Binary node with a corresponding Canonical Eltwise node
*
* BEFORE
- * A --- T/F Node -- C
- * B --/
+ * A --- T/F Node --- C
+ * /
+ * B ----
*
* AFTER
- * +------ T/F Node --
- * | /
- * B -------
- * | \
- * A -+------ Canonical Node -- C
+ * A --- T/F Node ---
+ * /
+ * B ----
+ *
+ * A --- [FixedReshape] --- [TensorBroadcast] --- Canonical Node -- C
+ * /
+ * B --- [FixedReshape] --- [TensorBroadcast] ----
*
- * NOTE T/F Node is disconnected from C after transformation
+ * NOTE
+ * - [...] means optional node. They may or may not be created during this procedure.
+ * - T/F Node is disconnected from C after transformation.
*/
using CanonicalNodeT = typename EltwiseBinaryCanonicalizationRule<TFNode>::CanonicalNode;
- // check condition: shape should be same
auto node_A = node->x();
auto node_B = node->y();
+ if (!loco::shape_known(node_A) || !loco::shape_known(node_B))
+ return false;
+ if (!loco::shape_known(node))
+ return false;
+
auto out_shape = loco::shape_get(node).template as<loco::TensorShape>();
// Create a node
diff --git a/compiler/moco-tf/src/TFFormattedGraph.cpp b/compiler/moco-tf/src/TFFormattedGraph.cpp
index 08fad7a50..2ea514a2b 100644
--- a/compiler/moco-tf/src/TFFormattedGraph.cpp
+++ b/compiler/moco-tf/src/TFFormattedGraph.cpp
@@ -16,15 +16,14 @@
#include "TFFormattedGraph.h"
-#include "Annotations/ShapeInferenceData.h"
-
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNodes.h"
+#include <moco/IR/TFDialect.h>
+#include <moco/IR/TFNodes.h>
#include "LogHelper.h"
#include <pepper/str.h>
#include <locoex/Service/COpFormattedGraph.h>
+#include <oops/InternalExn.h>
#include <sstream>
@@ -35,12 +34,12 @@ std::string opname(uint32_t opnum)
{
static std::string prefix{"tf."};
- switch (static_cast<moco::tf::TFOpcode>(opnum))
+ switch (static_cast<moco::TFOpcode>(opnum))
{
#define TENSORFLOW_NODE(OPCODE, CLASS) \
- case moco::tf::TFOpcode::OPCODE: \
+ case moco::TFOpcode::OPCODE: \
return prefix + #OPCODE;
-#include "Dialect/TFNodes.lst"
+#include <moco/IR/TFNodes.lst>
#undef TENSORFLOW_NODE
default:
break;
@@ -49,6 +48,7 @@ std::string opname(uint32_t opnum)
return prefix + "Invalid";
}
+using namespace moco;
using namespace moco::tf;
/// TFNodeSummaryBuilder with default implementation
@@ -71,7 +71,7 @@ protected:
s.state(locop::NodeSummary::State::PartiallyKnown); \
return true; \
}
-#include "Dialect/TFNodes.lst"
+#include <moco/IR/TFNodes.lst>
#undef TENSORFLOW_NODE
protected:
@@ -99,18 +99,24 @@ private:
IMPLEMENT(TFConcatV2);
IMPLEMENT(TFConst);
IMPLEMENT(TFConv2D);
+ IMPLEMENT(TFConv2DBackpropInput);
IMPLEMENT(TFDepthwiseConv2dNative);
IMPLEMENT(TFFusedBatchNorm);
+ IMPLEMENT(TFMaximum);
IMPLEMENT(TFMaxPool);
IMPLEMENT(TFMean);
IMPLEMENT(TFMul);
+ IMPLEMENT(TFPack);
IMPLEMENT(TFReshape);
IMPLEMENT(TFRsqrt);
IMPLEMENT(TFShape);
IMPLEMENT(TFSoftmax);
IMPLEMENT(TFSqueeze);
IMPLEMENT(TFStopGradient);
+ IMPLEMENT(TFStridedSlice);
IMPLEMENT(TFTanh);
+ // For virtual nodes
+ IMPLEMENT(TFPush);
#undef IMPLEMENT
};
@@ -125,7 +131,7 @@ bool TFNodeSummaryBuilderBase::build(const loco::Node *node, locop::NodeSummary
s.opname(opname(node->opnum())); \
return summary(dynamic_cast<const CLASS *>(node), s); \
}
-#include "Dialect/TFNodes.lst"
+#include <moco/IR/TFNodes.lst>
#undef TENSORFLOW_NODE
return false;
@@ -177,10 +183,6 @@ bool TFNodeSummaryBuilder::summary(const TFConst *node, locop::NodeSummary &s) c
{
std::ostringstream ss;
- auto shapedata = node->annot<ShapeInferenceData>();
- // TODO show real numbers like [1,2,3,4]
- s.args().append("shape", shapedata ? "OK" : "?");
-
auto dtype = node->dtype();
switch (dtype)
{
@@ -191,7 +193,7 @@ bool TFNodeSummaryBuilder::summary(const TFConst *node, locop::NodeSummary &s) c
ss << node->size<loco::DataType::FLOAT32>();
break;
default:
- throw std::runtime_error("NYI for this DataType");
+ INTERNAL_EXN_V("Unsupported data type", node->name());
}
s.args().append("size", ss.str());
s.state(locop::NodeSummary::State::PartiallyKnown);
@@ -209,6 +211,18 @@ bool TFNodeSummaryBuilder::summary(const TFConv2D *node, locop::NodeSummary &s)
return true;
}
+bool TFNodeSummaryBuilder::summary(const TFConv2DBackpropInput *node, locop::NodeSummary &s) const
+{
+ s.args().append("input_sizes", tbl()->lookup(node->input_sizes()));
+ s.args().append("filter", tbl()->lookup(node->filter()));
+ s.args().append("out_backprop", tbl()->lookup(node->out_backprop()));
+ s.args().append("padding", node->padding());
+ s.args().append("data_layout", node->data_layout());
+ s.args().append("strides", pepper::str(node->strides()));
+ s.state(locop::NodeSummary::State::PartiallyKnown);
+ return true;
+}
+
bool TFNodeSummaryBuilder::summary(const TFDepthwiseConv2dNative *node, locop::NodeSummary &s) const
{
s.args().append("input", tbl()->lookup(node->input()));
@@ -222,9 +236,9 @@ bool TFNodeSummaryBuilder::summary(const TFDepthwiseConv2dNative *node, locop::N
bool TFNodeSummaryBuilder::summary(const TFFusedBatchNorm *node, locop::NodeSummary &s) const
{
- s.args().append("input", tbl()->lookup(node->input()));
- s.args().append("gamma", tbl()->lookup(node->gamma()));
- s.args().append("beta", tbl()->lookup(node->beta()));
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.args().append("scale", tbl()->lookup(node->scale()));
+ s.args().append("offset", tbl()->lookup(node->offset()));
s.args().append("mean", tbl()->lookup(node->mean()));
s.args().append("variance", tbl()->lookup(node->variance()));
s.args().append("epsilon", pepper::str(node->epsilon()));
@@ -232,9 +246,17 @@ bool TFNodeSummaryBuilder::summary(const TFFusedBatchNorm *node, locop::NodeSumm
return true;
}
+bool TFNodeSummaryBuilder::summary(const TFMaximum *node, locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.args().append("y", tbl()->lookup(node->y()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
bool TFNodeSummaryBuilder::summary(const TFMaxPool *node, locop::NodeSummary &s) const
{
- s.args().append("value", tbl()->lookup(node->value()));
+ s.args().append("input", tbl()->lookup(node->input()));
s.args().append("ksize", pepper::str(node->ksize()));
s.args().append("strides", pepper::str(node->strides()));
s.args().append("padding", node->padding());
@@ -261,6 +283,16 @@ bool TFNodeSummaryBuilder::summary(const TFMul *node, locop::NodeSummary &s) con
return true;
}
+bool TFNodeSummaryBuilder::summary(const TFPack *node, locop::NodeSummary &s) const
+{
+ s.args().append("N", pepper::str(node->N()));
+ s.args().append("axis", pepper::str(node->axis()));
+ for (uint32_t n = 0; n < node->N(); ++n)
+ s.args().append("values", tbl()->lookup(node->values(n)));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
bool TFNodeSummaryBuilder::summary(const TFReshape *node, locop::NodeSummary &s) const
{
s.args().append("tensor", tbl()->lookup(node->tensor()));
@@ -305,6 +337,22 @@ bool TFNodeSummaryBuilder::summary(const TFStopGradient *node, locop::NodeSummar
return true;
}
+bool TFNodeSummaryBuilder::summary(const TFStridedSlice *node, locop::NodeSummary &s) const
+{
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.args().append("begin", tbl()->lookup(node->begin()));
+ s.args().append("end", tbl()->lookup(node->end()));
+ if (node->strides() != nullptr)
+ s.args().append("strides", tbl()->lookup(node->strides()));
+ s.args().append("begin_mask", pepper::str(node->begin_mask()));
+ s.args().append("end_mask", pepper::str(node->end_mask()));
+ s.args().append("ellipsis_mask", pepper::str(node->ellipsis_mask()));
+ s.args().append("new_axis_mask", pepper::str(node->new_axis_mask()));
+ s.args().append("shrink_axis_mask", pepper::str(node->shrink_axis_mask()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
bool TFNodeSummaryBuilder::summary(const TFTanh *node, locop::NodeSummary &s) const
{
s.args().append("x", tbl()->lookup(node->x()));
@@ -312,6 +360,15 @@ bool TFNodeSummaryBuilder::summary(const TFTanh *node, locop::NodeSummary &s) co
return true;
}
+// For virtual nodes
+bool TFNodeSummaryBuilder::summary(const TFPush *node, locop::NodeSummary &s) const
+{
+ s.args().append("index", node->indexed() ? pepper::str(node->index()) : "?");
+ s.args().append("from", tbl()->lookup(node->from()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
} // namespace
namespace moco
diff --git a/compiler/moco-tf/src/TFOptimizer.cpp b/compiler/moco-tf/src/TFOptimizer.cpp
index f6f76a718..2256b99b8 100644
--- a/compiler/moco-tf/src/TFOptimizer.cpp
+++ b/compiler/moco-tf/src/TFOptimizer.cpp
@@ -36,31 +36,37 @@ void TFOptimizer::optimize(loco::Graph *g) const
/* TRANSFORM DECLARATION BEGIN */
if (moco::tf::get<moco::tf::Knob::ResolveFusedBatchNorm>())
{
- phase.emplace_back(stdex::make_unique<moco::tf::ResolveFusedBatchNorm>());
+ phase.emplace_back(stdex::make_unique<moco::ResolveFusedBatchNorm>());
}
if (moco::tf::get<moco::tf::Knob::FuseBinaryIntoPreceding>())
{
- phase.emplace_back(stdex::make_unique<moco::tf::FuseBinaryIntoPreceding>());
+ phase.emplace_back(stdex::make_unique<moco::FuseBinaryIntoPreceding>());
}
if (moco::tf::get<moco::tf::Knob::ResolveConstantShape>())
{
- phase.emplace_back(stdex::make_unique<moco::tf::ResolveConstantShape>());
+ phase.emplace_back(stdex::make_unique<moco::ResolveConstantShape>());
}
if (moco::tf::get<moco::tf::Knob::ResolveReshapeWildcardDim>())
{
- phase.emplace_back(stdex::make_unique<moco::tf::ResolveReshapeWildcardDim>());
+ phase.emplace_back(stdex::make_unique<moco::ResolveReshapeWildcardDim>());
+ }
+ if (moco::tf::get<moco::tf::Knob::ResolveSquaredDifference>())
+ {
+ phase.emplace_back(stdex::make_unique<moco::ResolveSquaredDifference>());
}
if (moco::tf::get<moco::tf::Knob::RemoveTFIdentityNode>())
{
- phase.emplace_back(stdex::make_unique<moco::tf::RemoveTFIdentityNodeTransform>());
+ phase.emplace_back(stdex::make_unique<moco::RemoveTFIdentityNode>());
}
if (moco::tf::get<moco::tf::Knob::RemoveDeadNode>())
{
phase.emplace_back(stdex::make_unique<logo::RemoveDeadNodePass>());
}
- // Fix shape and pad for added nodes doing above transformations
- // TODO need to merge or remove the ones in importer
- phase.emplace_back(stdex::make_unique<moco::tf::FixShapeTransform>());
+ if (moco::tf::get<moco::tf::Knob::SqueezeReduceNode>())
+ {
+ phase.emplace_back(stdex::make_unique<moco::SqueezeReduceNode>());
+ }
+ // Shape inference is needed for added nodes doing above transformations
phase.emplace_back(stdex::make_unique<moco::tf::ShapeInferencePass>());
phase.emplace_back(stdex::make_unique<moco::tf::TypeInferencePass>());
/* TRANSFORM DECLARATION END */
diff --git a/compiler/moco-tf/src/TFReduceCanonicalzeHelper.h b/compiler/moco-tf/src/TFReduceCanonicalzeHelper.h
new file mode 100644
index 000000000..abd24cec8
--- /dev/null
+++ b/compiler/moco-tf/src/TFReduceCanonicalzeHelper.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TF_REDUCE_CANONICALIZE_HELPER_H__
+#define __TF_REDUCE_CANONICALIZE_HELPER_H__
+
+#include <moco/IR/TFDialect.h>
+#include <moco/IR/TFNodes.h>
+
+#include <loco/Service/ShapeInference.h>
+
+#include <moco/Log.h>
+
+namespace
+{
+
+template <typename TFNodeT> loco::ReduceFunc reduceFunc(void);
+
+template <> loco::ReduceFunc reduceFunc<moco::TFMean>(void) { return loco::ReduceFunc::Mean; }
+
+template <typename TFNode> bool canonicalize_reduce_node(TFNode *node)
+{
+ LOGGER(l);
+
+ INFO(l) << "TFNodeCanonicalize ReduceNode begin";
+
+ auto graph = node->graph();
+
+ /**
+ * This will replace T/F Reduce node with a corresponding Canonical Reduce node
+ *
+ * BEFORE
+ * reduction_indices -------- T/F Node -- C
+ * input -------/
+ *
+ * AFTER
+ * +------ T/F Node --
+ * | /
+ * reduction_indices -------
+ * | \
+ * input -+------ Canonical Node -- C
+ *
+ * NOTE
+ * - T/F Node is disconnected from C after transformation
+ */
+
+ // TFSqueeze had to be inserted if keep_dims() was false
+ assert(node->keep_dims());
+
+ auto axes_node = node->reduction_indices();
+ assert(axes_node != nullptr);
+
+ auto node_tensor_shape = loco::shape_get(node).template as<loco::TensorShape>();
+
+ // Canonicalization into TensorReduce is valid when reduction indices is constant
+ // TODO Support general TensorReduce case
+ std::vector<int32_t> axes_values;
+ if (auto const_axes = dynamic_cast<moco::TFConst *>(axes_node))
+ {
+ // TODO Support S64 type
+ assert(const_axes->dtype() == loco::DataType::S32);
+
+ for (uint32_t i = 0; i < const_axes->size<loco::DataType::S32>(); ++i)
+ {
+ int32_t axis = const_axes->at<loco::DataType::S32>(i);
+ if (axis < 0)
+ axis += node_tensor_shape.rank();
+ axes_values.push_back(axis);
+ }
+ }
+ else if (auto const_axes = dynamic_cast<loco::ConstGen *>(axes_node))
+ {
+ // TODO Support S64 type
+ assert(const_axes->dtype() == loco::DataType::S32);
+
+ for (uint32_t i = 0; i < const_axes->size<loco::DataType::S32>(); ++i)
+ {
+ int32_t axis = const_axes->at<loco::DataType::S32>(i);
+ if (axis < 0)
+ axis += node_tensor_shape.rank();
+ axes_values.push_back(axis);
+ }
+ }
+ else
+ return false;
+
+ // Create loco node to replace
+ auto reduce = graph->nodes()->template create<loco::TensorReduce>();
+
+ // replace
+ reduce->func(reduceFunc<TFNode>());
+ reduce->input(node->input());
+ for (uint32_t i = 0; i < axes_values.size(); ++i)
+ reduce->axes()->insert(axes_values.at(i));
+
+ replace(node).with(reduce);
+
+ INFO(l) << "TFNodeCanonicalize ReduceNode done";
+
+ return true;
+}
+
+} // namespace
+
+#endif // __TF_REDUCE_CANONICALIZE_HELPER_H__
diff --git a/compiler/moco-tf/src/TestHelper.cpp b/compiler/moco-tf/src/TestHelper.cpp
deleted file mode 100644
index 412556a68..000000000
--- a/compiler/moco-tf/src/TestHelper.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestHelper.h"
-
-#include <google/protobuf/io/coded_stream.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
-#include <google/protobuf/text_format.h>
-
-#include <cstring>
-
-namespace moco
-{
-namespace tf
-{
-namespace test
-{
-
-void setup_output_node(loco::Graph *graph, loco::Node *last_node)
-{
- // add push as output
- auto push_node = graph->nodes()->create<loco::Push>();
- push_node->from(last_node);
-
- // set the graph output name and node object
- auto graph_output = graph->outputs()->create();
- graph_output->name("output");
- graph_output->dtype(loco::DataType::FLOAT32);
- loco::link(graph_output, push_node);
-}
-
-} // namespace test
-} // namespace tf
-} // namespace moco
diff --git a/compiler/moco-tf/src/TestHelper.h b/compiler/moco-tf/src/TestHelper.h
index 6978efaab..dd32d4433 100644
--- a/compiler/moco-tf/src/TestHelper.h
+++ b/compiler/moco-tf/src/TestHelper.h
@@ -21,8 +21,6 @@
#include <tensorflow/core/framework/graph.pb.h>
-#include <istream>
-
#define STRING_CONTENT(content) #content
namespace moco
@@ -75,4 +73,41 @@ void setup_output_node(loco::Graph *graph, loco::Node *last_node);
} // namespace tf
} // namespace moco
+#include <moco/IR/TFNode.h>
+
+#include <moco/Import/GraphBuilder.h>
+
+#include <plier/tf/TestHelper.h>
+
+namespace moco
+{
+namespace tf
+{
+namespace test
+{
+
+class TFNodeBuildTester
+{
+public:
+ TFNodeBuildTester();
+
+public:
+ void inputs(const std::vector<std::string> &names);
+ void output(const char *name);
+ moco::TFNode *output(void);
+
+ void run(tensorflow::NodeDef &node_def, moco::GraphBuilder &graph_builder);
+
+private:
+ std::unique_ptr<moco::SymbolTable> _tensor_names;
+ std::unique_ptr<loco::Graph> _graph;
+
+ std::vector<moco::TFNode *> _inputs;
+ const char *_output{nullptr};
+};
+
+} // namespace test
+} // namespace tf
+} // namespace moco
+
#endif // __TEST_HELPER_H__
diff --git a/compiler/moco-tf/src/TestHelper.test.cpp b/compiler/moco-tf/src/TestHelper.test.cpp
new file mode 100644
index 000000000..1e8c38e36
--- /dev/null
+++ b/compiler/moco-tf/src/TestHelper.test.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestHelper.h"
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/text_format.h>
+
+#include <cstring>
+
+namespace moco
+{
+namespace tf
+{
+namespace test
+{
+
+void setup_output_node(loco::Graph *graph, loco::Node *last_node)
+{
+ // add push as output
+ auto push_node = graph->nodes()->create<loco::Push>();
+ push_node->from(last_node);
+
+ // set the graph output name and node object
+ auto graph_output = graph->outputs()->create();
+ graph_output->name("output");
+ graph_output->dtype(loco::DataType::FLOAT32);
+ loco::link(graph_output, push_node);
+}
+
+} // namespace test
+} // namespace tf
+} // namespace moco
+
+#include <moco/IR/Nodes/TFConst.h>
+
+#include <stdex/Memory.h>
+
+#include <gtest/gtest.h>
+
+namespace moco
+{
+namespace tf
+{
+namespace test
+{
+
+TFNodeBuildTester::TFNodeBuildTester()
+{
+ _graph = loco::make_graph();
+ _tensor_names = stdex::make_unique<moco::SymbolTable>();
+}
+
+void TFNodeBuildTester::inputs(const std::vector<std::string> &names)
+{
+ for (auto name : names)
+ {
+ auto input = _graph->nodes()->create<moco::TFConst>();
+ moco::TensorName name_01(name, 0);
+ _tensor_names->enroll(name_01, input);
+
+ _inputs.push_back(input);
+ }
+}
+
+void TFNodeBuildTester::output(const char *name) { _output = name; }
+
+moco::TFNode *TFNodeBuildTester::output(void)
+{
+ assert(_output != nullptr);
+
+ moco::TensorName tname(_output, 0);
+ return static_cast<moco::TFNode *>(_tensor_names->node(tname));
+}
+
+void TFNodeBuildTester::run(tensorflow::NodeDef &nodedef, moco::GraphBuilder &graphbuilder)
+{
+ assert(_output != nullptr);
+
+ auto node_defs = stdex::make_unique<moco::NodeDefTable>();
+ auto updates = stdex::make_unique<moco::UpdateQueue>();
+
+ moco::GraphBuilderContext gb_context(_graph.get(), node_defs.get(), _tensor_names.get(),
+ updates.get());
+
+ EXPECT_TRUE(graphbuilder.validate(nodedef));
+ graphbuilder.build(nodedef, &gb_context);
+
+ for (auto &update : updates->queue())
+ {
+ update->input(_tensor_names.get());
+ }
+
+ auto tfnode = output();
+ ASSERT_NE(tfnode, nullptr);
+
+ int idx = 0;
+ ASSERT_EQ(tfnode->arity(), _inputs.size());
+ for (auto input : _inputs)
+ {
+ ASSERT_EQ(tfnode->arg(idx++), input);
+ }
+}
+
+} // namespace test
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Transforms.h b/compiler/moco-tf/src/Transforms.h
index 653adbf3a..f14b81675 100644
--- a/compiler/moco-tf/src/Transforms.h
+++ b/compiler/moco-tf/src/Transforms.h
@@ -17,16 +17,10 @@
#ifndef __MOCO_TF_TRANSFORMS_H__
#define __MOCO_TF_TRANSFORMS_H__
-#include "Transforms/ClearAnnotTransform.h"
-#include "Transforms/FixShapeTransform.h"
-#include "Transforms/FuseBinaryIntoPreceding.h"
-#include "Transforms/RemoveTFIdentityNodeTransform.h"
-#include "Transforms/ResolveConstantShape.h"
-#include "Transforms/ResolveFusedBatchNorm.h"
-#include "Transforms/ResolveReshapeWildcardDim.h"
#include "Transforms/ShapeInferencePass.h"
#include "Transforms/TypeInferencePass.h"
#include <logo/Passes.h>
+#include <moco/Pass/Passes.h>
#endif // __MOCO_TF_TRANSFORMS_H__
diff --git a/compiler/moco-tf/src/Transforms/ClearAnnotTransform.cpp b/compiler/moco-tf/src/Transforms/ClearAnnotTransform.cpp
deleted file mode 100644
index 37873cb04..000000000
--- a/compiler/moco-tf/src/Transforms/ClearAnnotTransform.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ClearAnnotTransform.h"
-
-#include "Annotations/ConcatData.h"
-#include "Annotations/PadData.h"
-#include "Annotations/PaddingData.h"
-#include "Annotations/ShapeInferenceData.h"
-#include "Annotations/StrideData.h"
-
-#include <loco.h>
-
-namespace moco
-{
-namespace tf
-{
-
-bool ClearAnnotTransform::run(loco::Graph *graph)
-{
- for (auto node : loco::all_nodes(graph))
- {
-// clang-format off
-#define MOCOANNOT(TYPE_NAME) \
- { \
- auto annot_data = node->annot<TYPE_NAME>(); \
- if (annot_data != nullptr) \
- { \
- node->annot<TYPE_NAME>(nullptr); \
- } \
- }
-MOCOANNOT(ConcatData)
-MOCOANNOT(PadData)
-MOCOANNOT(PaddingData)
-MOCOANNOT(ShapeInferenceData)
-MOCOANNOT(StrideData)
- // TODO add more annotation(s) to clear when defined
-#undef MOCOANNOT
- // clang-format on
- }
-
- /** @note Current design requires to return boolean for changed but we don't
- * need for this one-time-execution.
- * It would be better to separate two transform types later.
- */
- return false;
-}
-
-} // namespace tf
-} // namespace moco
diff --git a/compiler/moco-tf/src/Transforms/ClearAnnotTransform.h b/compiler/moco-tf/src/Transforms/ClearAnnotTransform.h
deleted file mode 100644
index ab56097e9..000000000
--- a/compiler/moco-tf/src/Transforms/ClearAnnotTransform.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_CLEAR_ANNOT_TRANSFORM_H__
-#define __MOCO_TF_CLEAR_ANNOT_TRANSFORM_H__
-
-#include "Transform.h"
-
-#include <loco.h>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Clear(delete) annotation data no needed anymore
- */
-class ClearAnnotTransform : public Transform
-{
-public:
- const char *name(void) const final { return "ClearAnnotTransform"; }
-
-public:
- bool run(loco::Graph *graph) override;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_CLEAR_ANNOT_TRANSFORM_H__
diff --git a/compiler/moco-tf/src/Transforms/ClearAnnotTransform.test.cpp b/compiler/moco-tf/src/Transforms/ClearAnnotTransform.test.cpp
deleted file mode 100644
index f2ea39be0..000000000
--- a/compiler/moco-tf/src/Transforms/ClearAnnotTransform.test.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ClearAnnotTransform.h"
-
-#include <loco.h>
-
-#include <gtest/gtest.h>
-
-TEST(ClearAnnotTransform, ctor)
-{
- moco::tf::ClearAnnotTransform catransform;
- loco::Graph graph;
-
- ASSERT_FALSE(catransform.run(&graph));
-}
diff --git a/compiler/moco-tf/src/Transforms/FixShapeTransform.cpp b/compiler/moco-tf/src/Transforms/FixShapeTransform.cpp
deleted file mode 100644
index 93570dbbc..000000000
--- a/compiler/moco-tf/src/Transforms/FixShapeTransform.cpp
+++ /dev/null
@@ -1,1539 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "FixShapeTransform.h"
-
-#include "LogHelper.h"
-
-#include "Annotations/ConcatData.h"
-#include "Annotations/PadData.h"
-#include "Annotations/ShapeInferenceData.h"
-#include "Annotations/StrideData.h"
-#include "Annotations/WindowData.h"
-#include "Dialect/TFNodes.h"
-
-#include <loco.h>
-#include <loco/IR/NodeShape.h>
-#include <loco/Service/ShapeInference.h>
-#include <moco/Log.h>
-#include <stdex/Memory.h>
-#include <plier/tf/Convert.h>
-#include <locoex/COpCall.h>
-
-#include <cassert>
-#include <sstream>
-#include <stdexcept>
-
-namespace
-{
-
-using namespace moco::tf;
-
-/**
- * @brief Return true if node has shape inference data for checking shape
- * inference is done or not
- */
-bool shape_inference_done(const loco::Node *node)
-{
- auto shapedata = node->annot<ShapeInferenceData>();
- return (shapedata != nullptr);
-}
-
-/**
- * @brief Copy ShapeInferenceData values from src to dst
- *
- * @note T can be ShapeInferenceData or loco::Node based class having shape
- * attributes like ConstGen, Pull and so on
- */
-template <class T> void copy_shape_values(const T *src, ShapeInferenceData *dst)
-{
- assert(src != nullptr);
- assert(dst != nullptr);
-
- uint32_t rank = src->rank();
- dst->rank(rank);
- for (uint32_t index = 0; index < rank; ++index)
- {
- if (src->dim(index).known())
- dst->dim(index) = src->dim(index).value();
- else
- dst->dim(index).unset();
- }
-}
-
-/**
- * @brief Make copy of ShapeInferenceData from src
- *
- * @note T can be ShapeInferenceData or loco::Node based class having shape
- * attributes like TFConst, COpCall and so on
- */
-template <class T> std::unique_ptr<ShapeInferenceData> make_shape_inference_data(const T *src)
-{
- assert(src != nullptr);
-
- auto shape_data = stdex::make_unique<ShapeInferenceData>();
-
- uint32_t rank = src->rank();
- shape_data->rank(rank);
- for (uint32_t index = 0; index < rank; ++index)
- {
- if (src->dim(index).known())
- shape_data->dim(index) = src->dim(index).value();
- else
- shape_data->dim(index).unset();
- }
-
- return std::move(shape_data);
-}
-
-std::unique_ptr<ShapeInferenceData> make_shape_inference_data(const loco::NodeShape &src)
-{
- auto shape_data = stdex::make_unique<ShapeInferenceData>();
-
- switch (src.domain())
- {
- case loco::Domain::Tensor:
- shape_data->tensor_shape(src.as<loco::TensorShape>());
- break;
-
- case loco::Domain::Feature:
- shape_data->feature_shape(src.as<loco::FeatureShape>());
- break;
-
- case loco::Domain::Filter:
- shape_data->filter_shape(src.as<loco::FilterShape>());
- break;
-
- case loco::Domain::DepthwiseFilter:
- shape_data->depthwisefilter_shape(src.as<loco::DepthwiseFilterShape>());
- break;
-
- case loco::Domain::Bias:
- shape_data->bias_shape(src.as<loco::BiasShape>());
- break;
-
- default:
- throw std::runtime_error("Unsupported Domain in make_shape_inference_data");
- }
-
- return std::move(shape_data);
-}
-
-loco::NodeShape as_node_shape(const ShapeInferenceData *shapedata)
-{
- switch (shapedata->domain())
- {
- case loco::Domain::Tensor:
- return loco::NodeShape({shapedata->tensor_shape()});
-
- case loco::Domain::Feature:
- return loco::NodeShape({shapedata->feature_shape()});
-
- case loco::Domain::Filter:
- return loco::NodeShape({shapedata->filter_shape()});
-
- case loco::Domain::DepthwiseFilter:
- return loco::NodeShape({shapedata->depthwisefilter_shape()});
-
- case loco::Domain::Bias:
- return loco::NodeShape({shapedata->bias_shape()});
- }
-
- throw std::runtime_error("Unsupported Domain in as_node_shape");
-}
-
-/**
- * @brief Create a higher-rank TensorShape following NumPy broadcasting semantics
- *
- * HOW TO USE:
- *
- * auto expanded_tensor_shape = expand(tensor_shape).to(N);
- */
-class TensorShapeExpander
-{
-public:
- TensorShapeExpander(const loco::TensorShape &shape) : _shape{shape}
- {
- // DO NOTHING
- }
-
-public:
- loco::TensorShape to(uint32_t output_rank)
- {
- auto const &input_shape = _shape;
- uint32_t const input_rank = input_shape.rank();
-
- assert(input_rank <= output_rank && "Cannot shrink rank");
- uint32_t const axis_shift = output_rank - input_rank;
-
- loco::TensorShape output_shape;
-
- output_shape.rank(output_rank);
- for (uint32_t axis = 0; axis < output_rank; ++axis)
- {
- output_shape.dim(axis) = (axis < axis_shift) ? 1 : input_shape.dim(axis - axis_shift);
- }
-
- return output_shape;
- }
-
-private:
- const loco::TensorShape _shape;
-};
-
-/**
- * @breif Expand shape x and y to same rank by align right and filling with 1
- */
-void expand_rank(loco::TensorShape &x, loco::TensorShape &y)
-{
- auto x_rank = x.rank();
- auto y_rank = y.rank();
-
- if (x_rank == y_rank)
- return;
-
- TensorShapeExpander x_exp(x);
- TensorShapeExpander y_exp(y);
-
- auto xy_rank = std::max(x_rank, y_rank);
-
- x = x_rank > y_rank ? x : x_exp.to(xy_rank);
- y = y_rank > x_rank ? y : y_exp.to(xy_rank);
-}
-
-/**
- * @breif Returns shape of expanded dimension of input x and y having same rank
- */
-loco::TensorShape expand_dimension(const loco::TensorShape &x, const loco::TensorShape &y)
-{
- assert(x.rank() == y.rank());
-
- auto rank = x.rank();
-
- loco::TensorShape output_shape;
-
- output_shape.rank(rank);
- for (auto axis = 0; axis < rank; ++axis)
- {
- assert(x.dim(axis).known() && y.dim(axis).known());
-
- auto x_dim = x.dim(axis).value();
- auto y_dim = y.dim(axis).value();
-
- // each dimension of x and y should be same or one must be 1 if different
- if (!((x_dim == y_dim) || (x_dim == 1 || y_dim == 1)))
- throw std::runtime_error("Cannot produce expand_dimension of two shapes");
-
- output_shape.dim(axis) = std::max(x_dim, y_dim);
- }
-
- return output_shape;
-}
-
-loco::TensorShape broadcast_shape(const loco::TensorShape &x, const loco::TensorShape &y)
-{
- auto x_match = x;
- auto y_match = y;
-
- expand_rank(x_match, y_match);
-
- auto output_shape = expand_dimension(x_match, y_match);
-
- return output_shape;
-}
-
-/**
- * @brief Copy ShapeInferenceData from loco::Node pointer src to dst
- */
-bool copy_shapedata(const loco::Node *src, loco::Node *dst)
-{
- // if dst already has ShapeInferenceData, skip
- if (shape_inference_done(dst))
- return false;
-
- // if src has loco::NodeShape, use it
- if (loco::shape_known(src))
- {
- auto shape_data = make_shape_inference_data(loco::shape_get(src));
- dst->annot(std::move(shape_data));
-
- return true;
- }
-
- // if src doesn't have ShapeInferenceData, skip
- if (!shape_inference_done(src))
- return false;
-
- auto src_shapedata = src->annot<ShapeInferenceData>();
- auto shape_data = make_shape_inference_data(src_shapedata);
- dst->annot(std::move(shape_data));
-
- return true;
-}
-
-/**
- * @note This will find broadcast shape from two inputs lhs and rhs using
- * broadcast_shape() and return that shape to dst
- */
-bool copy_shapedata(const loco::Node *lhs, const loco::Node *rhs, loco::Node *dst)
-{
- // if dst already has ShapeInferenceData, skip
- if (shape_inference_done(dst))
- return false;
-
- auto get_node_shape = [](const loco::Node *node, loco::NodeShape &out) {
- if (loco::shape_known(node))
- {
- out = loco::shape_get(node);
- }
- else
- {
- if (!shape_inference_done(node))
- return false;
-
- out = as_node_shape(node->annot<ShapeInferenceData>());
- }
-
- return true;
- };
-
- loco::NodeShape lhs_shape;
- loco::NodeShape rhs_shape;
-
- if (loco::shape_known(lhs))
- {
- lhs_shape = loco::shape_get(lhs);
- }
- else
- {
- if (!shape_inference_done(lhs))
- return false;
-
- lhs_shape = as_node_shape(lhs->annot<ShapeInferenceData>());
- }
-
- if (loco::shape_known(rhs))
- {
- rhs_shape = loco::shape_get(rhs);
- }
- else
- {
- if (!shape_inference_done(rhs))
- return false;
-
- rhs_shape = as_node_shape(rhs->annot<ShapeInferenceData>());
- }
-
- if (lhs_shape.domain() != loco::Domain::Tensor || rhs_shape.domain() != loco::Domain::Tensor)
- {
- throw std::runtime_error("copy_shapedata supports only for Tensor");
- }
-
- loco::TensorShape lhs_tensorshape = lhs_shape.as<loco::TensorShape>();
- loco::TensorShape rhs_tensorshape = rhs_shape.as<loco::TensorShape>();
- loco::TensorShape sum_tensorshape = broadcast_shape(lhs_tensorshape, rhs_tensorshape);
-
- loco::NodeShape sum_shape({sum_tensorshape});
- auto shape_data = make_shape_inference_data(sum_shape);
- dst->annot(std::move(shape_data));
-
- LOGGER(l);
-
- INFO(l) << "copy_shapedata " << lhs_tensorshape << " or " << rhs_tensorshape << " -> "
- << sum_tensorshape << std::endl;
-
- return true;
-}
-
-/**
- * @note While in shape inference, Node maybe Canonical, TF dialect or other dialects
- * This will provide common loco::NodeShape as shape information
- */
-bool node_shape(const loco::Node *node, loco::NodeShape &nodeshape)
-{
- if (loco::shape_known(node))
- {
- nodeshape = loco::shape_get(node);
- return true;
- }
-
- if (!shape_inference_done(node))
- return false;
-
- auto shapedata = node->annot<ShapeInferenceData>();
-
- switch (shapedata->domain())
- {
- case loco::Domain::Tensor:
- nodeshape.set(shapedata->tensor_shape());
- break;
-
- case loco::Domain::Feature:
- nodeshape.set(shapedata->feature_shape());
- break;
-
- case loco::Domain::Filter:
- nodeshape.set(shapedata->filter_shape());
- break;
-
- case loco::Domain::DepthwiseFilter:
- nodeshape.set(shapedata->depthwisefilter_shape());
- break;
-
- case loco::Domain::Bias:
- nodeshape.set(shapedata->bias_shape());
- break;
-
- default:
- throw std::runtime_error("Unsupported Domain in node_shape()");
- }
- return true;
-}
-
-loco::FeatureShape as_feature_shape(const loco::NodeShape &nodeshape,
- const TFDataLayout &data_layout)
-{
- if (nodeshape.domain() == loco::Domain::Feature)
- return nodeshape.as<loco::FeatureShape>();
-
- loco::FeatureShape feature_shape;
-
- // only convert from tensor to feature
- if (nodeshape.domain() != loco::Domain::Tensor)
- {
- throw std::runtime_error("as_feature_shape: domain is not tensor");
- }
-
- loco::TensorShape tensor_shape = nodeshape.as<loco::TensorShape>();
-
- if (tensor_shape.rank() != 4)
- {
- throw std::runtime_error("as_feature_shape: rank is not 4");
- }
-
- if (data_layout == "NHWC")
- {
- feature_shape.count() = tensor_shape.dim(0);
- feature_shape.height() = tensor_shape.dim(1);
- feature_shape.width() = tensor_shape.dim(2);
- feature_shape.depth() = tensor_shape.dim(3);
- }
- else if (data_layout == "NCHW")
- {
- feature_shape.count() = tensor_shape.dim(0);
- feature_shape.depth() = tensor_shape.dim(1);
- feature_shape.height() = tensor_shape.dim(2);
- feature_shape.width() = tensor_shape.dim(3);
- }
- else
- {
- // TODO support for other data_layout if needed
- throw std::runtime_error("as_feature_shape: only supports NHWC or NCHW");
- }
-
- return feature_shape;
-}
-
-struct FixPadContext
-{
- uint32_t input_height;
- uint32_t input_width;
- uint32_t output_height;
- uint32_t output_width;
- uint32_t stride_height;
- uint32_t stride_width;
- uint32_t effective_window_height;
- uint32_t effective_window_width;
-};
-
-PadData calc_paddata(const FixPadContext &ctx)
-{
- assert(ctx.output_height > 0);
- assert(ctx.output_width > 0);
-
- // calculate padding height, width
- int64_t i_height = (int64_t)(ctx.output_height - 1) * (int64_t)ctx.stride_height +
- (int64_t)ctx.effective_window_height - (int64_t)ctx.input_height;
- int64_t i_width = (int64_t)(ctx.output_width - 1) * (int64_t)ctx.stride_width +
- (int64_t)ctx.effective_window_width - (int64_t)ctx.input_width;
- uint32_t pad_height = i_height >= 0 ? (uint32_t)i_height : 0U;
- uint32_t pad_width = i_width >= 0 ? (uint32_t)i_width : 0U;
-
- PadData pad_data;
-
- pad_data.pad()->top(pad_height / 2);
- pad_data.pad()->bottom(pad_height - pad_data.pad()->top());
- pad_data.pad()->left(pad_width / 2);
- pad_data.pad()->right(pad_width - pad_data.pad()->left());
-
- return pad_data;
-}
-
-template <class T> void calc_annot_paddata(T *node, const FixPadContext &ctx)
-{
- assert(node != nullptr);
-
- PadData pd = calc_paddata(ctx);
-
- // annotation of pad data
- auto pad_data = stdex::make_unique<PadData>(pd);
-
- node->annot(std::move(pad_data));
-
- assert(node->template annot<PadData>() != nullptr);
-}
-
-template <class T> void update_stride_data(T *node)
-{
- auto stride_data = stdex::make_unique<StrideData>();
- auto strides = node->strides();
- auto data_layout = plier::tf::as_data_layout(node->data_layout());
- if (data_layout == plier::tf::DataLayout::NHWC)
- {
- stride_data->stride()->vertical(strides[1]);
- stride_data->stride()->horizontal(strides[2]);
- }
- else if (data_layout == plier::tf::DataLayout::NCHW)
- {
- stride_data->stride()->vertical(strides[2]);
- stride_data->stride()->horizontal(strides[3]);
- }
- node->annot(std::move(stride_data));
-}
-
-template <class T> void update_window_data(T *node)
-{
- auto window_data = stdex::make_unique<WindowData>();
- auto ksize = node->ksize();
- auto data_layout = plier::tf::as_data_layout(node->data_layout());
- if (data_layout == plier::tf::DataLayout::NHWC)
- {
- window_data->window()->vertical(ksize[1]);
- window_data->window()->horizontal(ksize[2]);
- }
- else if (data_layout == plier::tf::DataLayout::NCHW)
- {
- window_data->window()->vertical(ksize[2]);
- window_data->window()->horizontal(ksize[3]);
- }
- node->annot(std::move(window_data));
-}
-
-bool fix_shape(loco::Pull *node)
-{
- if (shape_inference_done(node))
- return false;
-
- // Pull itself has shape information, copy them
- auto shape_data = make_shape_inference_data(node);
- node->annot(std::move(shape_data));
-
- return true;
-}
-
-bool fix_shape(loco::Push *node)
-{
- // Output shape is same as the from
- auto from = node->from();
- return copy_shapedata(from, node);
-}
-
-bool fix_shape(moco::tf::TFAdd *node)
-{
- auto x = node->x();
- auto y = node->y();
- loco::NodeShape x_shape;
- loco::NodeShape y_shape;
-
- if (!node_shape(x, x_shape))
- return false;
- if (!node_shape(y, y_shape))
- return false;
-
- // Output shape is same as the input
- return copy_shapedata(x, y, node);
-}
-
-bool fix_shape(moco::tf::TFAvgPool *node)
-{
- LOGGER(l);
-
- if (shape_inference_done(node))
- return false;
-
- auto value = node->value();
- loco::NodeShape value_shape;
- if (!node_shape(value, value_shape))
- {
- // input node shape inference is not ready
- return false;
- }
-
- auto padding = node->padding();
- assert(padding == "VALID" || padding == "SAME");
-
- update_stride_data(node);
- update_window_data(node);
-
- auto value_feature_shape = as_feature_shape(value_shape, node->data_layout());
-
- auto stride_data = node->annot<StrideData>();
- assert(stride_data != nullptr);
- auto window_data = node->annot<WindowData>();
- assert(window_data != nullptr);
-
- uint32_t input_height = value_feature_shape.height().value();
- uint32_t input_width = value_feature_shape.width().value();
- uint32_t stride_height = stride_data->stride()->vertical();
- uint32_t stride_width = stride_data->stride()->horizontal();
- uint32_t window_height = window_data->window()->vertical();
- uint32_t window_width = window_data->window()->horizontal();
- uint32_t dilation_height = 1; // dilation is 1
- uint32_t dilation_width = 1;
- uint32_t effective_window_height = dilation_height * (window_height - 1) + 1;
- uint32_t effective_window_width = dilation_width * (window_width - 1) + 1;
- uint32_t output_height;
- uint32_t output_width;
-
- if (padding == "VALID")
- {
- output_height = (input_height + stride_height - effective_window_height) / stride_height;
- output_width = (input_width + stride_width - effective_window_width) / stride_width;
- }
- else if (padding == "SAME")
- {
- output_height = (input_height + stride_height - 1) / stride_height;
- output_width = (input_width + stride_width - 1) / stride_width;
- }
-
- loco::FeatureShape ofm_feature_shape;
- ofm_feature_shape.count() = value_feature_shape.count();
- ofm_feature_shape.height() = output_height;
- ofm_feature_shape.width() = output_width;
- ofm_feature_shape.depth() = value_feature_shape.depth();
-
- auto shape_data = stdex::make_unique<ShapeInferenceData>();
- as_tensor_shape(*shape_data.get(), ofm_feature_shape, node->data_layout());
- node->annot(std::move(shape_data));
-
- FixPadContext ctx = {
- input_height, input_width, output_height, output_width,
- stride_height, stride_width, effective_window_height, effective_window_width};
-
- calc_annot_paddata(node, ctx);
-
- INFO(l) << "Fix TFAvgPool shape = ifm" << value_feature_shape << " --> ofm" << ofm_feature_shape;
- INFO(l) << " pad = " << *node->annot<PadData>();
-
- return true;
-}
-
-bool fix_shape(moco::tf::TFBiasAdd *node)
-{
- auto value = node->value();
- auto bias = node->bias();
- loco::NodeShape value_shape;
- loco::NodeShape bias_shape;
- if (!node_shape(value, value_shape) || !node_shape(bias, bias_shape))
- {
- return false;
- }
-
- // Output shape is same as the value shape
- return copy_shapedata(value, node);
-}
-
-template <class CONST_CLASS> bool valid_scala_value(CONST_CLASS *node)
-{
- LOGGER(l);
-
- loco::NodeShape nodeshape;
- if (!node_shape(node, nodeshape))
- {
- return false;
- }
-
- if (node->dtype() != loco::DataType::S32)
- {
- INFO(l) << "valid_scala_value not S32";
- return false;
- }
-
- auto tensor_shape = nodeshape.as<loco::TensorShape>();
- if (!(tensor_shape.rank() == 0 || tensor_shape.rank() == 1))
- {
- INFO(l) << "valid_scala_value rank not 0/1 : " << tensor_shape.rank();
- return false;
- }
-
- return true;
-}
-
-template <class CONST_CLASS> int32_t scala_value(CONST_CLASS *node)
-{
- loco::NodeShape nodeshape;
- if (!node_shape(node, nodeshape))
- {
- return false;
- }
-
- assert(node->dtype() == loco::DataType::S32);
-
- auto tensor_shape = nodeshape.as<loco::TensorShape>();
- assert(tensor_shape.rank() == 0 || tensor_shape.rank() == 1);
-
- return node->template at<loco::DataType::S32>(0);
-}
-
-bool fix_shape(moco::tf::TFConcatV2 *node)
-{
- LOGGER(l);
-
- if (shape_inference_done(node))
- {
- INFO(l) << "Fix shape TFConcatV2 already done";
- return false;
- }
- // ConcatData should be null
- assert(node->annot<ConcatData>() == nullptr);
-
- // Check shape inference data are all ready
- // Check shape rank are all same
- auto value_a = node->values(0);
- loco::NodeShape value_a_shape;
- if (!node_shape(value_a, value_a_shape))
- {
- // shape inference is not ready for this value
- INFO(l) << "Fix shape TFConcatV2 value 0 shape_data not ready";
- return false;
- }
- assert(value_a_shape.domain() == loco::Domain::Tensor);
- auto value_a_tensor_shape = value_a_shape.as<loco::TensorShape>();
- uint32_t a_rank = value_a_tensor_shape.rank();
-
- uint32_t num_values = node->num_values();
- for (uint32_t ni = 1; ni < num_values; ++ni)
- {
- auto value_b = node->values(ni);
- loco::NodeShape value_b_shape;
- if (!node_shape(value_b, value_b_shape))
- {
- // shape inference is not ready for this value
- INFO(l) << "Fix shape TFConcatV2 value " << ni << " shape_data not ready";
- return false;
- }
- assert(value_b_shape.domain() == loco::Domain::Tensor);
- auto value_b_tensor_shape = value_b_shape.as<loco::TensorShape>();
- uint32_t b_rank = value_b_tensor_shape.rank();
- assert(a_rank == b_rank);
- }
-
- // check for axis
- auto axis_node = node->axis();
- loco::NodeShape axis_shape;
- if (!node_shape(axis_node, axis_shape))
- {
- // shape inference is not ready for axis_node
- INFO(l) << "Fix shape TFConcatV2 axis shape_data not ready";
- return false;
- }
-
- int32_t axis_value = 0;
- bool axis_available = false;
- {
- // check for axis is TFConst
- auto tfconst = dynamic_cast<moco::tf::TFConst *>(axis_node);
- if (tfconst != nullptr)
- {
- if (valid_scala_value(tfconst))
- {
- axis_value = scala_value(tfconst);
- axis_available = true;
- }
- }
- }
- {
- // check for axis is ConstGen
- auto constgen = dynamic_cast<loco::ConstGen *>(axis_node);
- if (constgen != nullptr)
- {
- if (valid_scala_value(constgen))
- {
- axis_value = scala_value(constgen);
- axis_available = true;
- }
- }
- }
- if (!axis_available)
- {
- // we cannot find a valid axis value
- INFO(l) << "Fix shape TFConcatV2 axis_available false";
- return false;
- }
-
- auto concat_data = stdex::make_unique<ConcatData>(axis_value);
- node->annot(std::move(concat_data));
-
- uint32_t axis_absolute = (axis_value >= 0) ? axis_value : (int32_t)a_rank + axis_value;
-
- auto shape_data = stdex::make_unique<ShapeInferenceData>();
- shape_data->rank(a_rank);
-
- for (uint32_t index = 0; index < a_rank; ++index)
- {
- if (value_a_tensor_shape.dim(index).known())
- {
- uint32_t dim = value_a_tensor_shape.dim(index).value();
- if (index == axis_absolute)
- {
- uint32_t dim_acc = dim;
- for (uint32_t ni = 1; ni < num_values; ++ni)
- {
- auto value_b = node->values(ni);
- loco::NodeShape value_b_shape;
- node_shape(value_b, value_b_shape);
- assert(value_b_shape.domain() == loco::Domain::Tensor);
- auto value_b_tensor_shape = value_b_shape.as<loco::TensorShape>();
- assert(value_b_tensor_shape.dim(index).known());
- dim_acc += value_b_tensor_shape.dim(index).value();
- }
- dim = dim_acc;
- }
- shape_data->dim(index) = dim;
- }
- else
- shape_data->dim(index).unset();
- }
- node->annot(std::move(shape_data));
-
- INFO(l) << "Fix TFConcat shape = " << node->annot<ShapeInferenceData>();
-
- return true;
-}
-
-bool fix_shape(moco::tf::TFConst *node)
-{
- if (shape_inference_done(node))
- return false;
-
- // TFConst itself has shape information, copy them
- auto shape_data = make_shape_inference_data(node);
- node->annot(std::move(shape_data));
-
- {
- LOGGER(l);
- auto shapedata = node->annot<ShapeInferenceData>();
- assert(shapedata != nullptr);
- INFO(l) << "Fix TFConst shape = " << shapedata->tensor_shape();
- }
-
- return true;
-}
-
-bool fix_shape(moco::tf::TFConv2D *node)
-{
- LOGGER(l);
-
- if (shape_inference_done(node))
- return false;
-
- auto ifm = node->input();
- loco::NodeShape ifm_shape;
- if (!node_shape(ifm, ifm_shape))
- {
- // input node shape inference is not ready
- return false;
- }
-
- auto ker = node->filter();
- loco::NodeShape ker_shape;
- if (!node_shape(ker, ker_shape))
- {
- return false;
- }
-
- auto padding = node->padding();
- assert(padding == "VALID" || padding == "SAME");
-
- update_stride_data(node);
-
- auto stride_data = node->annot<StrideData>();
- assert(stride_data != nullptr);
- // TODO add and use 'stride_data->stride()' stream out
- INFO(l) << "Fix TFConv2D strides = " << stride_data->stride()->vertical() << ", "
- << stride_data->stride()->horizontal();
-
- auto ifm_tensor_shape = ifm_shape.as<loco::TensorShape>(); // in NHWC
- auto ker_tensor_shape = ker_shape.as<loco::TensorShape>(); // in HWIO
- assert(ifm_tensor_shape.rank() == 4);
- assert(ker_tensor_shape.rank() == 4);
-
- uint32_t input_height = ifm_tensor_shape.dim(1).value();
- uint32_t input_width = ifm_tensor_shape.dim(2).value();
- uint32_t stride_height = stride_data->stride()->vertical();
- uint32_t stride_width = stride_data->stride()->horizontal();
- uint32_t ker_height = ker_tensor_shape.dim(0).value();
- uint32_t ker_width = ker_tensor_shape.dim(1).value();
- uint32_t dilation_height = 1; // TODO Consider dilation
- uint32_t dilation_width = 1;
- uint32_t effective_ker_height = dilation_height * (ker_height - 1) + 1;
- uint32_t effective_ker_width = dilation_width * (ker_width - 1) + 1;
- uint32_t output_height;
- uint32_t output_width;
-
- if (padding == "VALID")
- {
- output_height = (input_height + stride_height - effective_ker_height) / stride_height;
- output_width = (input_width + stride_width - effective_ker_width) / stride_width;
- }
- else if (padding == "SAME")
- {
- output_height = (input_height + stride_height - 1) / stride_height;
- output_width = (input_width + stride_width - 1) / stride_width;
- }
- else
- {
- assert(false && "Unknown padding in fix_shape for TFConv2D");
- }
-
- loco::TensorShape ofm_tensor_shape;
- ofm_tensor_shape.rank(4);
- ofm_tensor_shape.dim(0) = ifm_tensor_shape.dim(0);
- ofm_tensor_shape.dim(1) = output_height;
- ofm_tensor_shape.dim(2) = output_width;
- ofm_tensor_shape.dim(3) = ker_tensor_shape.dim(3);
-
- auto shape_data = stdex::make_unique<ShapeInferenceData>();
- shape_data->tensor_shape(ofm_tensor_shape);
- node->annot(std::move(shape_data));
-
- FixPadContext ctx = {input_height, input_width, output_height, output_width,
- stride_height, stride_width, effective_ker_height, effective_ker_width};
-
- calc_annot_paddata(node, ctx);
-
- INFO(l) << "Fix TFConv2D shape = ifm" << ifm_tensor_shape << " ker" << ker_tensor_shape
- << " --> ofm" << ofm_tensor_shape;
- INFO(l) << " pad = " << *node->annot<PadData>();
-
- return true;
-}
-
-bool fix_shape(moco::tf::TFDepthwiseConv2dNative *node)
-{
- LOGGER(l);
-
- if (shape_inference_done(node))
- return false;
-
- auto ifm = node->input();
- loco::NodeShape ifm_shape;
- if (!node_shape(ifm, ifm_shape))
- {
- // input node shape inference is not ready
- return false;
- }
-
- auto ker = node->filter();
- loco::NodeShape ker_shape;
- if (!node_shape(ker, ker_shape))
- {
- return false;
- }
-
- update_stride_data(node);
-
- auto stride_data = node->annot<StrideData>();
- assert(stride_data != nullptr);
-
- INFO(l) << "FixShape TFDepthwiseConv2dNative strides = " << stride_data->stride()->vertical()
- << ", " << stride_data->stride()->horizontal();
-
- auto ifm_tensor_shape = ifm_shape.as<loco::TensorShape>(); // in NHWC
- auto ker_tensor_shape = ker_shape.as<loco::TensorShape>(); // in HWCM
- assert(ifm_tensor_shape.rank() == 4);
- assert(ker_tensor_shape.rank() == 4);
-
- uint32_t input_height = ifm_tensor_shape.dim(1).value();
- uint32_t input_width = ifm_tensor_shape.dim(2).value();
- uint32_t stride_height = stride_data->stride()->vertical();
- uint32_t stride_width = stride_data->stride()->horizontal();
- uint32_t ker_height = ker_tensor_shape.dim(0).value();
- uint32_t ker_width = ker_tensor_shape.dim(1).value();
- uint32_t dilation_height = 1; // TODO Consider dilation
- uint32_t dilation_width = 1;
- uint32_t effective_ker_height = dilation_height * (ker_height - 1) + 1;
- uint32_t effective_ker_width = dilation_width * (ker_width - 1) + 1;
- uint32_t output_height;
- uint32_t output_width;
-
- auto padding = node->padding();
- assert(padding == "VALID" || padding == "SAME");
-
- if (padding == "VALID")
- {
- output_height = (input_height + stride_height - effective_ker_height) / stride_height;
- output_width = (input_width + stride_width - effective_ker_width) / stride_width;
- }
- else // padding == "SAME"
- {
- output_height = (input_height + stride_height - 1) / stride_height;
- output_width = (input_width + stride_width - 1) / stride_width;
- }
-
- loco::TensorShape ofm_tensor_shape;
- ofm_tensor_shape.rank(4);
- ofm_tensor_shape.dim(0) = ifm_tensor_shape.dim(0);
- ofm_tensor_shape.dim(1) = output_height;
- ofm_tensor_shape.dim(2) = output_width;
- ofm_tensor_shape.dim(3) =
- loco::Dimension(ker_tensor_shape.dim(2).value() * ker_tensor_shape.dim(3).value());
-
- auto shape_data = stdex::make_unique<ShapeInferenceData>();
- shape_data->tensor_shape(ofm_tensor_shape);
- node->annot(std::move(shape_data));
-
- FixPadContext ctx = {input_height, input_width, output_height, output_width,
- stride_height, stride_width, effective_ker_height, effective_ker_width};
-
- calc_annot_paddata(node, ctx);
-
- INFO(l) << "Fix TFDepthwiseConv2dNative shape = ifm" << ifm_tensor_shape << " ker"
- << ker_tensor_shape << " --> ofm" << ofm_tensor_shape;
- INFO(l) << " pad = " << *node->annot<PadData>();
-
- return true;
-}
-
-bool fix_shape(moco::tf::TFFusedBatchNorm *node)
-{
- // Output shape is same as the input
- auto input = node->input();
- return copy_shapedata(input, node);
-}
-
-bool fix_shape(moco::tf::TFIdentity *node)
-{
- // Output shape is same as the input
- auto input = node->input();
- return copy_shapedata(input, node);
-}
-
-bool fix_shape(moco::tf::TFMaxPool *node)
-{
- LOGGER(l);
-
- if (shape_inference_done(node))
- return false;
-
- auto value = node->value();
- loco::NodeShape value_shape;
- if (!node_shape(value, value_shape))
- {
- // input node shape inference is not ready
- return false;
- }
-
- auto padding = node->padding();
- assert(padding == "VALID" || padding == "SAME");
-
- update_stride_data(node);
- update_window_data(node);
-
- auto stride_data = node->annot<StrideData>();
- assert(stride_data != nullptr);
- auto window_data = node->annot<WindowData>();
- assert(window_data != nullptr);
-
- auto value_feature_shape = as_feature_shape(value_shape, node->data_layout());
-
- uint32_t input_height = value_feature_shape.height().value();
- uint32_t input_width = value_feature_shape.width().value();
- uint32_t stride_height = stride_data->stride()->vertical();
- uint32_t stride_width = stride_data->stride()->horizontal();
- uint32_t window_height = window_data->window()->vertical();
- uint32_t window_width = window_data->window()->horizontal();
- uint32_t dilation_height = 1; // dilation for MaxPool is 1
- uint32_t dilation_width = 1;
- uint32_t effective_window_height = dilation_height * (window_height - 1) + 1;
- uint32_t effective_window_width = dilation_width * (window_width - 1) + 1;
- uint32_t output_height;
- uint32_t output_width;
-
- if (padding == "VALID")
- {
- output_height = (input_height + stride_height - effective_window_height) / stride_height;
- output_width = (input_width + stride_width - effective_window_width) / stride_width;
- }
- else if (padding == "SAME")
- {
- output_height = (input_height + stride_height - 1) / stride_height;
- output_width = (input_width + stride_width - 1) / stride_width;
- }
-
- loco::FeatureShape ofm_feature_shape;
- ofm_feature_shape.count() = value_feature_shape.count();
- ofm_feature_shape.height() = output_height;
- ofm_feature_shape.width() = output_width;
- ofm_feature_shape.depth() = value_feature_shape.depth();
-
- auto shape_data = stdex::make_unique<ShapeInferenceData>();
- as_tensor_shape(*shape_data.get(), ofm_feature_shape, node->data_layout());
- node->annot(std::move(shape_data));
-
- FixPadContext ctx = {
- input_height, input_width, output_height, output_width,
- stride_height, stride_width, effective_window_height, effective_window_width};
-
- calc_annot_paddata(node, ctx);
-
- INFO(l) << "Fix TFMaxPool shape = ifm" << value_feature_shape << " --> ofm" << ofm_feature_shape;
- INFO(l) << " pad = " << *node->annot<PadData>();
-
- return true;
-}
-
-bool fix_shape(moco::tf::TFMul *node)
-{
- auto x = node->x();
- auto y = node->y();
- loco::NodeShape x_shape;
- loco::NodeShape y_shape;
-
- if (!node_shape(x, x_shape))
- return false;
- if (!node_shape(y, y_shape))
- return false;
-
- // Output shape is same as the input
- return copy_shapedata(x, y, node);
-}
-
-bool fix_shape(moco::tf::TFMean *node)
-{
- if (shape_inference_done(node))
- return false;
-
- LOGGER(l);
-
- auto input = node->input();
- auto reduction_indices = node->reduction_indices();
- loco::NodeShape input_shape;
- loco::NodeShape reduction_indices_shape;
-
- if (!node_shape(input, input_shape) || !node_shape(reduction_indices, reduction_indices_shape))
- {
- // Input and reduction_indices shape are required for TFMean shape inference
- return false;
- }
-
- // Get constant values if reduction_indeces is const
- std::vector<int32_t> reduction_values;
- if (auto tfconst = dynamic_cast<moco::tf::TFConst *>(reduction_indices))
- {
- assert(tfconst->dtype() == loco::DataType::S32);
- auto const_size = tfconst->size<loco::DataType::S32>();
- for (uint32_t i = 0; i < const_size; ++i)
- {
- int32_t axis = tfconst->at<loco::DataType::S32>(i);
- if (axis < 0)
- axis += input_shape.as<loco::TensorShape>().rank();
- reduction_values.push_back(axis);
- }
- }
- else
- {
- // we cannot find a valid reduction indices value
- INFO(l) << "Fix shape TFMean fail : reduction indeces are not constant or not valid";
- return false;
- }
-
- loco::TensorShape shape_data;
- loco::TensorShape input_tensor_shape = input_shape.as<loco::TensorShape>();
-
- if (node->keep_dims())
- {
- shape_data.rank(input_tensor_shape.rank());
- for (uint32_t i = 0; i < input_tensor_shape.rank(); ++i)
- shape_data.dim(i) = input_tensor_shape.dim(i);
- for (uint32_t i = 0; i < reduction_values.size(); ++i)
- shape_data.dim(reduction_values.at(i)) = 1;
- }
- else
- {
- std::vector<bool> check_reduce(input_tensor_shape.rank(), false);
- for (uint32_t i = 0; i < reduction_values.size(); ++i)
- check_reduce.at(reduction_values.at(i)) = true;
-
- uint32_t reduce_cnt = 0;
- for (uint32_t i = 0; i < check_reduce.size(); ++i)
- if (check_reduce.at(i))
- ++reduce_cnt;
-
- shape_data.rank(input_tensor_shape.rank() - reduce_cnt);
- for (uint32_t i = 0, j = 0; i < check_reduce.size(); ++i)
- if (check_reduce.at(i) == false)
- shape_data.dim(j++) = i;
- }
-
- auto shape_annot = stdex::make_unique<ShapeInferenceData>();
- shape_annot->tensor_shape(shape_data);
- node->annot(std::move(shape_annot));
-
- return true;
-}
-
-bool fix_shape(moco::tf::TFRealDiv *node)
-{
- auto x = node->x();
- auto y = node->y();
- loco::NodeShape x_shape;
- loco::NodeShape y_shape;
-
- if (!node_shape(x, x_shape))
- return false;
- if (!node_shape(y, y_shape))
- return false;
-
- // Output shape is same as the input
- return copy_shapedata(x, y, node);
-}
-
-bool fix_shape(moco::tf::TFRelu *node)
-{
- // Output shape is same as the features
- auto features = node->features();
- return copy_shapedata(features, node);
-}
-
-bool fix_shape(moco::tf::TFRelu6 *node)
-{
- // Output shape is same as the features
- auto features = node->features();
- return copy_shapedata(features, node);
-}
-
-bool fix_shape(moco::tf::TFReshape *node)
-{
- if (shape_inference_done(node))
- return false;
-
- // For now, we only consider Fixed Reshape, i.e. Reshape with determined
- // 'shape' input. So here we only support case when 'shape' input of
- // TFReshape is TFConst. If 'shape' input is not TFConst, another
- // transform (e.g. constant folding) should be done beforehand to make
- // it TFConst.
- // TODO Support dynamic Reshape
- // Note that 'shape()' here is 'shape' input, not node's shape information
- auto const_shape_input = dynamic_cast<moco::tf::TFConst *>(node->shape());
- if (!const_shape_input)
- {
- // 'shape' input of TFReshape is not TFConst, try next time when it becomes TFConst
- return false;
- }
-
- // 'Shape' input should be integer tensor of rank 1, e.g. [2, 3, 4] or [3, -1]
- assert(const_shape_input->dtype() == loco::DataType::S32);
- assert(const_shape_input->rank() == 1);
-
- auto shape_rank = const_shape_input->dim(0).value();
- assert(shape_rank > 0);
-
- loco::TensorShape shape_data;
- shape_data.rank(shape_rank);
- for (uint32_t axis = 0; axis < shape_rank; ++axis)
- {
- auto shape_dim = const_shape_input->at<loco::DataType::S32>(axis);
- if (shape_dim == -1)
- {
- // Reshape's new shape has wildcard dimension, i.e. dynamic reshape
- return false;
- }
- assert(shape_dim >= 1);
- shape_data.dim(axis) = shape_dim;
- }
-
- // TODO Compare 'tensor' input and validate coherency?
- // Not sure this is appropriate stage for this task.
-
- auto shape_annot = stdex::make_unique<ShapeInferenceData>();
- shape_annot->tensor_shape(shape_data);
- node->annot(std::move(shape_annot));
-
- {
- LOGGER(l);
- auto shapedata = node->annot<ShapeInferenceData>();
- assert(shapedata != nullptr);
- INFO(l) << "Fix TFReshape shape = " << shapedata->tensor_shape();
- }
-
- return true;
-}
-
-bool fix_shape(moco::tf::TFRsqrt *node)
-{
- // Output shape is same as the input x
- auto x = node->x();
- return copy_shapedata(x, node);
-}
-
-bool fix_shape(moco::tf::TFShape *node)
-{
- if (shape_inference_done(node))
- return false;
-
- auto input = node->input();
- loco::NodeShape input_shape;
- if (!node_shape(input, input_shape))
- {
- // Input shape is required for TFShape shape inference
- return false;
- }
- loco::TensorShape input_tensor_shape = input_shape.as<loco::TensorShape>();
-
- loco::TensorShape node_shape;
-
- // Note that input shape becomes node(TFShape)'s value
- node_shape.rank(1);
- node_shape.dim(0) = input_tensor_shape.rank();
-
- auto shape_annot = stdex::make_unique<ShapeInferenceData>();
- shape_annot->tensor_shape(node_shape);
- node->annot(std::move(shape_annot));
-
- LOGGER(l);
- INFO(l) << "Fix TFShape shape = " << node_shape;
-
- return true;
-}
-
-bool fix_shape(moco::tf::TFSqrt *node)
-{
- // Output shape is same as the input x
- auto x = node->x();
- return copy_shapedata(x, node);
-}
-
-bool fix_shape(moco::tf::TFSoftmax *node)
-{
- // Output shape is same as the input x
- auto logits = node->logits();
- return copy_shapedata(logits, node);
-}
-
-bool fix_shape(moco::tf::TFSquaredDifference *node)
-{
- auto x = node->x();
- auto y = node->y();
- return copy_shapedata(x, y, node);
-}
-
-bool fix_shape(moco::tf::TFSqueeze *node)
-{
- if (shape_inference_done(node))
- return false;
-
- auto input = node->input();
- loco::NodeShape input_shape;
- if (!node_shape(input, input_shape))
- {
- // Input shape is required for TFSqueeze shape inference
- return false;
- }
-
- // TODO Not sure Squeeze only get input as Tensor
- // Note that tensor_shape() has assertion in it
- auto input_tensor_shape = input_shape.as<loco::TensorShape>();
-
- auto squeeze_dims_vec = node->squeeze_dims();
- std::set<int64_t> squeeze_dims(squeeze_dims_vec.cbegin(), squeeze_dims_vec.cend());
-
- loco::TensorShape node_shape;
- uint32_t node_rank = 0;
-
- if (squeeze_dims.empty())
- {
- // Remove all dimensions whose value is 1
- for (uint32_t axis = 0; axis < input_tensor_shape.rank(); ++axis)
- {
- assert(input_tensor_shape.dim(axis).known());
- auto dim = input_tensor_shape.dim(axis).value();
- if (dim != 1)
- {
- assert(dim > 1);
- node_shape.rank(++node_rank);
- node_shape.dim(node_rank - 1) = dim;
- }
- }
- }
- else
- {
- uint32_t input_rank = input_tensor_shape.rank();
-
- // Sanity check for 'squeeze_dims'
- auto is_valid_squeeze_dims = [&squeeze_dims, &input_rank]() {
- if (!(squeeze_dims.size() < input_rank))
- return false;
- for (auto squeeze_dim : squeeze_dims)
- {
- if (!(squeeze_dim >= -(int64_t)input_rank))
- return false;
- if (!(squeeze_dim < (int64_t)input_rank))
- return false;
- }
- return true;
- };
-
- if (!is_valid_squeeze_dims())
- {
- throw std::runtime_error("Fix shape for TFSqueeze: invalid squeeze dimension");
- }
-
- // Resolve negative squeeze dimension
- std::set<int64_t> resolved_squeeze_dims;
- for (auto squeeze_dim : squeeze_dims)
- {
- if (squeeze_dim < 0)
- resolved_squeeze_dims.insert(squeeze_dim + (int64_t)input_rank);
- else
- resolved_squeeze_dims.insert(squeeze_dim);
- }
-
- // Remove squeeze dimensions only
- for (uint32_t axis = 0; axis < input_rank; ++axis)
- {
- assert(input_tensor_shape.dim(axis).known());
- auto dim = input_tensor_shape.dim(axis).value();
- if (resolved_squeeze_dims.find((int64_t)axis) == resolved_squeeze_dims.cend())
- {
- // Not squeeze dim
- node_shape.rank(++node_rank);
- node_shape.dim(node_rank - 1) = dim;
- }
- else
- {
- // Is squeeze dim
- assert(dim == 1);
- // DO NOTHING
- }
- }
- }
-
- assert(node_shape.rank() > 0);
-
- auto shape_annot = stdex::make_unique<ShapeInferenceData>();
- shape_annot->tensor_shape(node_shape);
- node->annot(std::move(shape_annot));
-
- LOGGER(l);
- INFO(l) << "Fix TFSqueeze shape = " << node_shape;
-
- return true;
-}
-
-bool fix_shape(moco::tf::TFStopGradient *node)
-{
- // Output shape is same as the input
- auto input = node->input();
- return copy_shapedata(input, node);
-}
-
-bool fix_shape(moco::tf::TFSub *node)
-{
- auto x = node->x();
- auto y = node->y();
- loco::NodeShape x_shape;
- loco::NodeShape y_shape;
-
- if (!node_shape(x, x_shape))
- return false;
- if (!node_shape(y, y_shape))
- return false;
-
- // Output shape is same as the input
- return copy_shapedata(x, y, node);
-}
-
-bool fix_shape(moco::tf::TFTanh *node)
-{
- // Output shape is same as the input
- auto x = node->x();
- return copy_shapedata(x, node);
-}
-
-bool fix_shape(locoex::COpCall *node)
-{
- if (shape_inference_done(node))
- return false;
-
- auto shape_data = make_shape_inference_data(node);
- node->annot(std::move(shape_data));
-
- return true;
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-bool FixShapeTransform::run(loco::Graph *graph)
-{
- bool changed = false;
- for (auto node : loco::active_nodes(loco::output_nodes(graph)))
- {
-// clang-format off
-// TODO remove this block after Pull, Push is not used in import
-#define CANONICAL_NODE(TYPE_NAME) \
- if (as<loco::TYPE_NAME>(node)) \
- { \
- if (fix_shape(as<loco::TYPE_NAME>(node))) \
- changed = true; \
- } \
- else
-CANONICAL_NODE(Pull)
-CANONICAL_NODE(Push)
-#undef CANONICAL_NODE
-
-#define TENSORFLOW_NODE(OPCODE,CLASS) \
- if (as<moco::tf::CLASS>(node)) \
- { \
- if (fix_shape(as<moco::tf::CLASS>(node))) \
- changed = true; \
- } \
- else
-#include "Dialect/TFNodes.lst"
-#undef TENSORFLOW_NODE
- // clang-format on
-
- if (as<locoex::COpCall>(node))
- {
- if (fix_shape(as<locoex::COpCall>(node)))
- changed = true;
- }
- else
- {
- // Skip nodes that are not interested
- }
- }
-
- return changed;
-}
-
-} // namespace tf
-} // namespace moco
diff --git a/compiler/moco-tf/src/Transforms/FixShapeTransform.h b/compiler/moco-tf/src/Transforms/FixShapeTransform.h
deleted file mode 100644
index d790d0ec7..000000000
--- a/compiler/moco-tf/src/Transforms/FixShapeTransform.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_FIX_SHAPE_TRANSFORM_H__
-#define __MOCO_TF_FIX_SHAPE_TRANSFORM_H__
-
-#include "Transform.h"
-
-#include <loco.h>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Fix unknown shape to concrete shape for all nodes in the graph
-*/
-class FixShapeTransform : public Transform
-{
-public:
- const char *name(void) const final { return "FixShapeTransform"; }
-
-public:
- bool run(loco::Graph *graph) override;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_FIX_SHAPE_TRANSFORM_H__
diff --git a/compiler/moco-tf/src/Transforms/FixShapeTransform.test.cpp b/compiler/moco-tf/src/Transforms/FixShapeTransform.test.cpp
deleted file mode 100644
index bb346865f..000000000
--- a/compiler/moco-tf/src/Transforms/FixShapeTransform.test.cpp
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "FixShapeTransform.h"
-
-#include "TestHelper.h"
-
-#include "Annotations/PaddingData.h"
-#include "Annotations/ShapeInferenceData.h"
-
-#include "Dialect/TFNodes.h"
-
-#include <loco.h>
-
-#include <stdex/Memory.h>
-
-#include <gtest/gtest.h>
-
-using namespace moco::tf::test;
-
-TEST(FixShapeTransform, ctor)
-{
- moco::tf::FixShapeTransform fstransform;
- loco::Graph graph;
-
- ASSERT_FALSE(fstransform.run(&graph));
-}
-
-namespace
-{
-
-moco::tf::TFAvgPool *avgpool_network_simple1331(loco::Graph *graph)
-{
- auto avgpool_node = graph->nodes()->create<moco::tf::TFAvgPool>();
-
- avgpool_node->data_layout("NHWC");
- avgpool_node->ksize({1, 3, 3, 1});
- avgpool_node->strides({1, 1, 1, 1});
-
- // Dummy const node as ifm, just to fake FixShapeTransform for TFAvgPool.
- // FixShapeTransform only cares about ShapeInferenceData of ifm()
- auto const_node = graph->nodes()->create<moco::tf::TFConst>();
- {
- auto shapedata = stdex::make_unique<moco::tf::ShapeInferenceData>();
- loco::TensorShape tshape;
- tshape.rank(4);
- tshape.dim(0).set(1);
- tshape.dim(1).set(3);
- tshape.dim(2).set(3);
- tshape.dim(3).set(1);
- shapedata->tensor_shape(tshape);
- const_node->annot(std::move(shapedata));
- }
- avgpool_node->value(const_node);
-
- setup_output_node(graph, avgpool_node);
-
- return avgpool_node;
-}
-
-} // namespace
-
-TEST(FixShapeTransform, avgpool_same)
-{
- moco::tf::FixShapeTransform fstransform;
- loco::Graph graph;
-
- auto avgpool_node = avgpool_network_simple1331(&graph);
- avgpool_node->padding("SAME");
-
- moco::tf::FixShapeTransform transform;
- transform.run(&graph);
-
- auto shapedata = avgpool_node->annot<moco::tf::ShapeInferenceData>();
- ASSERT_NE(shapedata, nullptr);
- auto tshape = shapedata->tensor_shape();
- ASSERT_EQ(tshape.rank(), 4);
- ASSERT_EQ(tshape.dim(0).value(), 1);
- ASSERT_EQ(tshape.dim(1).value(), 3);
- ASSERT_EQ(tshape.dim(2).value(), 3);
- ASSERT_EQ(tshape.dim(3).value(), 1);
-}
-
-TEST(FixShapeTransform, avgpool_valid)
-{
- moco::tf::FixShapeTransform fstransform;
- loco::Graph graph;
-
- auto avgpool_node = avgpool_network_simple1331(&graph);
- avgpool_node->padding("VALID");
-
- moco::tf::FixShapeTransform transform;
- transform.run(&graph);
-
- auto shapedata = avgpool_node->annot<moco::tf::ShapeInferenceData>();
- ASSERT_NE(shapedata, nullptr);
- auto tshape = shapedata->tensor_shape();
- ASSERT_EQ(tshape.rank(), 4);
- ASSERT_EQ(tshape.dim(0).value(), 1);
- ASSERT_EQ(tshape.dim(1).value(), 1);
- ASSERT_EQ(tshape.dim(2).value(), 1);
- ASSERT_EQ(tshape.dim(3).value(), 1);
-}
-
-namespace
-{
-
-void conv2d_test(const std::array<uint32_t, 4> ifm_shape, const std::array<uint32_t, 4> ker_shape,
- const std::array<uint32_t, 2> stride_h_w, std::string padding,
- const std::array<uint32_t, 4> expected_shape)
-{
- moco::tf::FixShapeTransform fstransform;
- loco::Graph graph;
-
- auto conv2d_node = graph.nodes()->create<moco::tf::TFConv2D>();
- conv2d_node->data_layout("NHWC");
- conv2d_node->strides({1, stride_h_w[0], stride_h_w[1], 1});
- conv2d_node->padding(padding);
-
- auto ifm_node = graph.nodes()->create<moco::tf::TFConst>();
- {
- auto shapedata = stdex::make_unique<moco::tf::ShapeInferenceData>();
- loco::TensorShape tshape;
- tshape.rank(4);
- tshape.dim(0).set(ifm_shape[0]);
- tshape.dim(1).set(ifm_shape[1]);
- tshape.dim(2).set(ifm_shape[2]);
- tshape.dim(3).set(ifm_shape[3]);
- shapedata->tensor_shape(tshape);
- ifm_node->annot(std::move(shapedata));
- }
-
- auto ker_node = graph.nodes()->create<loco::ConstGen>();
- {
- auto shapedata = stdex::make_unique<moco::tf::ShapeInferenceData>();
- loco::TensorShape tshape;
- tshape.rank(4);
- tshape.dim(0).set(ker_shape[0]);
- tshape.dim(1).set(ker_shape[1]);
- tshape.dim(2).set(ker_shape[2]);
- tshape.dim(3).set(ker_shape[3]);
- shapedata->tensor_shape(tshape);
- ker_node->annot(std::move(shapedata));
- }
-
- conv2d_node->input(ifm_node);
- conv2d_node->filter(ker_node);
-
- setup_output_node(&graph, conv2d_node);
-
- moco::tf::FixShapeTransform transform;
- transform.run(&graph);
-
- auto shapedata = conv2d_node->annot<moco::tf::ShapeInferenceData>();
- ASSERT_NE(shapedata, nullptr);
- auto tshape = shapedata->tensor_shape();
- ASSERT_EQ(tshape.rank(), 4);
- ASSERT_EQ(tshape.dim(0).value(), expected_shape[0]);
- ASSERT_EQ(tshape.dim(1).value(), expected_shape[1]);
- ASSERT_EQ(tshape.dim(2).value(), expected_shape[2]);
- ASSERT_EQ(tshape.dim(3).value(), expected_shape[3]);
-}
-
-} // namespace
-
-/*
- Testing "InceptionV3/InceptionV3/Conv2d_1a_3x3/Conv2D" Conv2D node in Inception_v3:
- The result shape of this test is generated with the code below:
-
- ifm = tf.constant(value=1.1, shape=[1, 299, 299, 3])
- ker = tf.constant(value=1.1, shape=[3, 3, 3, 32])
-
- out = tf.nn.conv2d(ifm, ker, strides = [1, 2, 2, 1], padding= 'VALID')
-
- with tf.Session() as sess:
- res = sess.run(out)
- print(res.shape)
- */
-TEST(FixShapeTransform, conv2d_VALID)
-{
- moco::tf::FixShapeTransform fstransform;
- loco::Graph graph;
-
- conv2d_test({1, 299, 299, 3}, // ifm
- {3, 3, 3, 32}, // ker
- {2, 2}, // strides
- "VALID", // padding
- {1, 149, 149, 32}); // expected shape after FixShape
-}
-
-/*
- Testing "InceptionV3/InceptionV3/Conv2d_2b_3x3/Conv2D" Conv2D node in Inception_v3:
- The result shape of this test is generated with the code below:
-
- ifm = tf.constant(value=1.1, shape=[1, 147, 147, 32])
- ker = tf.constant(value=1.1, shape=[3, 3, 32, 64])
-
- out = tf.nn.conv2d(ifm, ker, strides = [1, 1, 1, 1], padding= 'SAME')
-
- with tf.Session() as sess:
- res = sess.run(out)
- print(res.shape)
- */
-TEST(FixShapeTransform, conv2d_SAME)
-{
- moco::tf::FixShapeTransform fstransform;
- loco::Graph graph;
-
- conv2d_test({1, 147, 147, 32}, // ifm
- {3, 3, 32, 64}, // ker
- {1, 1}, // strides
- "SAME", // padding
- {1, 147, 147, 64}); // expected shape after FixShape
-}
diff --git a/compiler/moco-tf/src/Transforms/FuseBinaryIntoPreceding.cpp b/compiler/moco-tf/src/Transforms/FuseBinaryIntoPreceding.cpp
deleted file mode 100644
index 2edcae72e..000000000
--- a/compiler/moco-tf/src/Transforms/FuseBinaryIntoPreceding.cpp
+++ /dev/null
@@ -1,547 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "FuseBinaryIntoPreceding.h"
-
-#include "Annotations/ShapeInferenceData.h"
-#include "Dialect/TFDialect.h"
-
-#include "IR/TFAdd.h"
-#include "IR/TFBiasAdd.h"
-#include "IR/TFConst.h"
-#include "IR/TFConv2D.h"
-#include "IR/TFDepthwiseConv2dNative.h"
-#include "IR/TFMul.h"
-
-#include <loco.h>
-#include <moco/Log.h>
-
-#include <cassert>
-#include <memory>
-
-namespace
-{
-
-/**
- * @brief Fusable operation type
- */
-enum class FuseType
-{
- Conv2D,
- DepthwiseConv2D,
- // TODO Support FullyConnected
-};
-
-// TODO rename this method when there is a better name
-bool is_only_one_valid(moco::tf::TFConst *xc, moco::tf::TFConst *yc)
-{
- if (xc == nullptr && yc == nullptr)
- return false;
- if (xc != nullptr && yc != nullptr)
- return false;
-
- return true;
-}
-
-// TODO Put this in some common place
-void copy_shape(const moco::tf::TFConst *src, moco::tf::TFConst *dst)
-{
- assert(src != nullptr);
- assert(dst != nullptr);
-
- uint32_t rank = src->rank();
- dst->rank(rank);
- for (uint32_t index = 0; index < rank; ++index)
- {
- if (src->dim(index).known())
- dst->dim(index) = src->dim(index);
- else
- dst->dim(index).unset();
- }
-}
-
-/**
- * @brief return true if shape is identical
- */
-bool shape_match(const moco::tf::TFConst *c1, const moco::tf::TFConst *c2)
-{
- assert(c1 != nullptr);
- assert(c2 != nullptr);
-
- uint32_t rank = c1->rank();
- if (rank != c2->rank())
- return false;
-
- for (uint32_t index = 0; index < rank; ++index)
- {
- if (!c1->dim(index).known() || !c2->dim(index).known())
- return false;
-
- if (c1->dim(index).value() != c2->dim(index).value())
- return false;
- }
- return true;
-}
-
-template <FuseType FT>
-moco::tf::TFConst *create_kernel_from_fuse_mulparam(loco::Graph *graph, moco::tf::TFConst *ker,
- moco::tf::TFConst *mulparam);
-
-template <>
-moco::tf::TFConst *create_kernel_from_fuse_mulparam<FuseType::Conv2D>(loco::Graph *graph,
- moco::tf::TFConst *ker,
- moco::tf::TFConst *mulparam)
-{
- auto ker_shape_inf = ker->annot<moco::tf::ShapeInferenceData>();
- assert(ker_shape_inf);
- auto ker_shape = ker_shape_inf->tensor_shape();
-
- auto mulparam_shape_inf = mulparam->annot<moco::tf::ShapeInferenceData>();
- assert(mulparam_shape_inf != nullptr);
- auto mulparam_shape = mulparam_shape_inf->tensor_shape();
-
- // create new ker_fused with same size of ker
- auto ker_fused = graph->nodes()->create<moco::tf::TFConst>();
-
- assert(ker_shape.rank() == 4);
- assert(mulparam_shape.rank() == 1);
- assert(ker_shape.dim(3).value() == mulparam_shape.dim(0).value());
-
- ker_fused->dtype(loco::DataType::FLOAT32);
- copy_shape(ker, ker_fused);
- auto ker_num_elements = ker->size<loco::DataType::FLOAT32>();
- ker_fused->size<loco::DataType::FLOAT32>(ker_num_elements);
-
- // TensorFlow Conv2D Kernel has HWIO format
- // Broadcast Mul vector to Kernel tensor by the Output
- const uint32_t ker_height = ker_shape.dim(0).value();
- const uint32_t ker_width = ker_shape.dim(1).value();
- const uint32_t ker_input = ker_shape.dim(2).value();
- const uint32_t ker_output = ker_shape.dim(3).value();
-
- for (uint32_t ker_y = 0; ker_y < ker_height; ++ker_y)
- {
- for (uint32_t ker_x = 0; ker_x < ker_width; ++ker_x)
- {
- for (uint32_t in_ch = 0; in_ch < ker_input; ++in_ch)
- {
- uint32_t num_items = ((ker_y * ker_width + ker_x) * ker_input + in_ch) * ker_output;
- for (uint32_t out_ch = 0; out_ch < ker_output; ++out_ch)
- {
- auto mulparam_v = mulparam->at<loco::DataType::FLOAT32>(out_ch);
- auto ker_v = ker->at<loco::DataType::FLOAT32>(num_items + out_ch);
- ker_fused->at<loco::DataType::FLOAT32>(num_items + out_ch) = ker_v * mulparam_v;
- }
- }
- }
- }
-
- return ker_fused;
-}
-
-/**
- * @brief Create a kernel from fuse mulparam<FuseType::DepthwiseConv2D> object
- * @return Kernel of fused mulparam
- */
-template <>
-moco::tf::TFConst *create_kernel_from_fuse_mulparam<FuseType::DepthwiseConv2D>(
- loco::Graph *graph, moco::tf::TFConst *ker, moco::tf::TFConst *mulparam)
-{
- auto ker_shape_inf = ker->annot<moco::tf::ShapeInferenceData>();
- assert(ker_shape_inf);
- auto ker_shape = ker_shape_inf->tensor_shape();
-
- auto mulparam_shape_inf = mulparam->annot<moco::tf::ShapeInferenceData>();
- assert(mulparam_shape_inf != nullptr);
- auto mulparam_shape = mulparam_shape_inf->tensor_shape();
-
- // create new ker_fused with same size of ker
- auto ker_fused = graph->nodes()->create<moco::tf::TFConst>();
-
- assert(ker_shape.rank() == 4);
- assert(mulparam_shape.rank() == 1);
- assert(ker_shape.dim(2).value() * ker_shape.dim(3).value() == mulparam_shape.dim(0).value());
-
- ker_fused->dtype(loco::DataType::FLOAT32);
- copy_shape(ker, ker_fused);
- auto ker_num_elements = ker->size<loco::DataType::FLOAT32>();
- ker_fused->size<loco::DataType::FLOAT32>(ker_num_elements);
-
- // TensorFlow DepthwiseConv2DNative Kernel has HWIM format
- // Broadcast Mul vector to Kernel tensor by the Output
- const uint32_t ker_height = ker_shape.dim(0).value();
- const uint32_t ker_width = ker_shape.dim(1).value();
- const uint32_t ker_input = ker_shape.dim(2).value();
- const uint32_t ker_multiplier = ker_shape.dim(3).value();
-
- for (uint32_t ker_y = 0; ker_y < ker_height; ++ker_y)
- {
- for (uint32_t ker_x = 0; ker_x < ker_width; ++ker_x)
- {
- for (uint32_t in_ch = 0; in_ch < ker_input; ++in_ch)
- {
- uint32_t num_items = ((ker_y * ker_width + ker_x) * ker_input + in_ch) * ker_multiplier;
- for (uint32_t ker_ch = 0; ker_ch < ker_multiplier; ++ker_ch)
- {
- auto mulparam_v = mulparam->at<loco::DataType::FLOAT32>(in_ch + ker_ch * ker_input);
- auto ker_v = ker->at<loco::DataType::FLOAT32>(num_items + ker_ch);
- ker_fused->at<loco::DataType::FLOAT32>(num_items + ker_ch) = ker_v * mulparam_v;
- }
- }
- }
- }
-
- return ker_fused;
-}
-
-/**
- * @brief Create a fused convolution opertion from kernel of fused mulparam
- * @return Fused convolution operation
- */
-template <FuseType FT, class T>
-T *fused_conv_node(loco::Graph *graph, moco::tf::TFConst *mulparam, T *conv_node)
-{
- LOGGER(l);
-
- // ker should be constant
- auto ker = dynamic_cast<moco::tf::TFConst *>(conv_node->filter());
- if (ker == nullptr)
- {
- // Wait until ker is becomes TFConst: there are cases when it's Identity.
- INFO(l) << "Mul fuse_to_preceding: precedingOp ker is not TFConst";
- return nullptr;
- }
- auto ifm = conv_node->input();
- assert(ifm != nullptr);
-
- // we need shape information, if not wait till it's ready
- if (ker->annot<moco::tf::ShapeInferenceData>() == nullptr)
- {
- INFO(l) << "Mul fuse_to_preceding: precedingOp ker has no shape";
- return nullptr;
- }
-
- auto mulparam_shape_inf = mulparam->annot<moco::tf::ShapeInferenceData>();
- if (mulparam_shape_inf == nullptr)
- {
- INFO(l) << "Mul fuse_to_preceding: precedingOp mulparam has no shape";
- return nullptr;
- }
- // if MulParam rank is not 1 we cannot fuse, just skip
- auto mulparam_shape = mulparam_shape_inf->tensor_shape();
- if (mulparam_shape.rank() != 1)
- {
- INFO(l) << "Mul fuse_to_preceding: Mul rank is not 1";
- return nullptr;
- }
-
- auto ker_fused = create_kernel_from_fuse_mulparam<FT>(graph, ker, mulparam);
- auto conv_fused = graph->nodes()->create<T>();
-
- conv_fused->input(ifm);
- conv_fused->filter(ker_fused);
- conv_fused->padding(conv_node->padding());
- conv_fused->data_layout(conv_node->data_layout());
- conv_fused->strides(conv_node->strides());
-
- return conv_fused;
-}
-
-/**
- * @note This creates fused ker:2 from ker:1, 'mulparam' and
- * new precedingOp:2 that uses ker:2 as the kernel.
- * Then make C to use precedingOp:2 as new input.
- *
- * <Before>
- * mulparam-\
- * ker:1 --\ \
- * ifm ----- precedingOp:1 ----------- Mul --- C
- *
- *
- * <After>
- * mulparam-\
- * ker:1 --\ \
- * - precedingOp:1 ----------- Mul ---
- * /
- * ifm ----- precedingOp:2 ------------------- C
- * ker:2 ---/
- *
- *
- * [Where]
- * - precedingOp:1 can be one of TFConv2D, TFDepthwiseConv2dNative, FullyConnected
- * - 'mulparam' and Mul will be disconnected from the Output.
- * - ker:2 is added with fused values of ker:1 and mulparam
- * - precedingOp:2 is added using ifm and ker:2 and other parameters
- * same as precedingOp:1.
- * - ker:1, precedingOp:1, 'mulparam' and Mul should be removed in
- * RemoveDeadNodeTransform if not used.
- */
-bool fuse_to_preceding(loco::Graph *graph, moco::tf::TFMul *node)
-{
- LOGGER(l);
-
- auto xc = dynamic_cast<moco::tf::TFConst *>(node->x());
- auto yc = dynamic_cast<moco::tf::TFConst *>(node->y());
-
- // Note: if both are constants, it should be done by constant-folding
- if (!(is_only_one_valid(xc, yc)))
- return false;
-
- moco::tf::TFConst *mulparam = nullptr;
- moco::tf::TFNode *precedingOp = nullptr;
-
- if (xc != nullptr)
- {
- mulparam = xc;
- precedingOp = dynamic_cast<moco::tf::TFNode *>(node->y());
- }
- else // yc != nullptr
- {
- mulparam = yc;
- precedingOp = dynamic_cast<moco::tf::TFNode *>(node->x());
- }
-
- assert(mulparam->dtype() == loco::DataType::FLOAT32);
-
- // TODO support FullyConnected
- moco::tf::TFNode *fused_node = nullptr;
- if (auto conv2d = dynamic_cast<moco::tf::TFConv2D *>(precedingOp))
- fused_node = fused_conv_node<FuseType::Conv2D, moco::tf::TFConv2D>(graph, mulparam, conv2d);
- else if (auto dw_conv2d = dynamic_cast<moco::tf::TFDepthwiseConv2dNative *>(precedingOp))
- fused_node = fused_conv_node<FuseType::DepthwiseConv2D, moco::tf::TFDepthwiseConv2dNative>(
- graph, mulparam, dw_conv2d);
-
- // Not ready yet
- if (fused_node == nullptr)
- return false;
-
- // Replace TFMul node with new precedingOp with fused kernel
- // This will leave existing precedingOp as-is but can be removed if not used
- // from other transformations
- replace(node).with(fused_node);
- // TODO check if need to disconnect
- // node->x(nullptr);
- // node->y(nullptr);
- // fused_node->ifm(nullptr);
- // fused_node->ker(nullptr);
-
- return true;
-}
-
-/**
- * @brief Create zero-filled BiasAdd opertion and insert after precedingOp
- * The plan is to fuse 'addparam' to TFBiasAdd bias
- * @return Zero-filled BiasAdd operation
- */
-template <class T>
-moco::tf::TFBiasAdd *create_biasadd_node(loco::Graph *graph, moco::tf::TFConst *addparam,
- T *precedingOp)
-{
- auto dtype = addparam->dtype();
- assert(dtype == loco::DataType::FLOAT32);
-
- // Create TFConst(bias of TFBiasAdd) with same shape and dtype of 'addparam' but
- // with values 0.0
- auto biasadd_param = graph->nodes()->create<moco::tf::TFConst>();
- biasadd_param->dtype(dtype);
- copy_shape(addparam, biasadd_param);
- auto biasadd_num_elements = addparam->size<loco::DataType::FLOAT32>();
- biasadd_param->size<loco::DataType::FLOAT32>(biasadd_num_elements);
- for (int32_t i = 0; i < biasadd_num_elements; i++)
- {
- biasadd_param->at<loco::DataType::FLOAT32>(i) = 0.0f;
- }
-
- // Create TFBiasAdd with same shape as TFAdd
- auto data_layout = precedingOp->data_layout();
- auto tf_biasadd = graph->nodes()->create<moco::tf::TFBiasAdd>();
- tf_biasadd->data_layout(data_layout);
-
- loco::replace(precedingOp).with(tf_biasadd);
- tf_biasadd->value(precedingOp);
- tf_biasadd->bias(biasadd_param);
-
- return tf_biasadd;
-}
-
-/**
- * @note TFAdd will be fused to TFBiasAdd
- *
- * <Before>
- * If precedingOp is not TFBiasAdd, then insert TFConst:1 + TFBiasAdd that
- * TFConst:1 has zero values.
- *
- * addparam --\
- * \
- * precedingOp ---------------------------- TFAdd ----- C
- *
- *
- * <Intermediate>
- * If it's TFBiasAdd and one of the input is TFConst type,
- * then we can fuse 'addparam' to the input TFConst:2 value of TFBiasAdd, where
- * TFConst:2 has added values from 'addparam'
- *
- * addparam --\
- * TFConst:1 --------\ \
- * precedingOp ------- TFBiasAdd ---------- TFAdd ----- C
- *
- *
- * <After>
- * addparam --\
- * TFConst:2 --------\ \
- * precedingOp ------- TFBiasAdd ---------- TFAdd -----
- * \--------------------- C
- *
- *
- * [Where]
- * - precedingOp can be TFConv2D, TFDepthwiseConv2dNative, FullyConnected,
- * TFBiasAdd.
- * - Intermediate is to insert TFBiasAdd + TFConst:1
- * - After is to fuse 'addparam' of TFAdd into TFConst:1 + TFBiasAdd
- * that becomes TFConst:2 + TFBiasAdd
- */
-bool fuse_to_preceding(loco::Graph *graph, moco::tf::TFAdd *node)
-{
- LOGGER(l);
-
- auto xc = dynamic_cast<moco::tf::TFConst *>(node->x());
- auto yc = dynamic_cast<moco::tf::TFConst *>(node->y());
-
- // Note: if both are constants, it should be done by constant-folding
- if (!(is_only_one_valid(xc, yc)))
- return false;
-
- moco::tf::TFConst *addparam = nullptr;
- moco::tf::TFNode *precedingOp = nullptr;
-
- if (xc != nullptr)
- {
- addparam = xc;
- precedingOp = dynamic_cast<moco::tf::TFNode *>(node->y());
- }
- else // yc != nullptr
- {
- addparam = yc;
- precedingOp = dynamic_cast<moco::tf::TFNode *>(node->x());
- }
-
- auto addparam_shape_inf = addparam->annot<moco::tf::ShapeInferenceData>();
- if (addparam_shape_inf == nullptr)
- {
- INFO(l) << "Add fuse_to_preceding: addparam has no shape";
- return false;
- }
- // if AddParam rank is not 0 or 1 we cannot fuse, just skip
- auto addparam_shape = addparam_shape_inf->tensor_shape();
- if (addparam_shape.rank() > 1)
- {
- INFO(l) << "Add fuse_to_preceding: Add rank is not 0 or 1";
- return false;
- }
-
- // TODO do something when rank() is 0
- if (addparam_shape.rank() == 0)
- {
- // Not supported yet
- return false;
- }
- assert(addparam_shape.rank() != 0);
-
- // TODO support FullyConnected
- moco::tf::TFBiasAdd *biasadd = nullptr;
- if (auto conv2d = dynamic_cast<moco::tf::TFConv2D *>(precedingOp))
- biasadd = create_biasadd_node<moco::tf::TFConv2D>(graph, addparam, conv2d);
- else if (auto dw_conv2d = dynamic_cast<moco::tf::TFDepthwiseConv2dNative *>(precedingOp))
- biasadd = create_biasadd_node<moco::tf::TFDepthwiseConv2dNative>(graph, addparam, dw_conv2d);
- else if (auto old_bias_add = dynamic_cast<moco::tf::TFBiasAdd *>(precedingOp))
- biasadd = old_bias_add;
-
- if (biasadd == nullptr)
- {
- // try next turn
- return false;
- }
-
- // Let's fuse addparam into biasadd bias
- auto biasadd_bias = dynamic_cast<moco::tf::TFConst *>(biasadd->bias());
- assert(biasadd_bias != nullptr);
- if (!shape_match(biasadd_bias, addparam))
- {
- INFO(l) << "TFBiasAdd bias and TFAdd input shape mismatch";
- return false;
- }
- auto add_num_elements = addparam->size<loco::DataType::FLOAT32>();
- assert(add_num_elements == biasadd_bias->size<loco::DataType::FLOAT32>());
- for (int32_t i = 0; i < add_num_elements; i++)
- {
- biasadd_bias->at<loco::DataType::FLOAT32>(i) += addparam->at<loco::DataType::FLOAT32>(i);
- }
-
- replace(node).with(biasadd);
- // TODO check if need to disconnect
- // node->x(nullptr);
- // node->y(nullptr);
-
- return true;
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-bool FuseBinaryIntoPreceding::run(loco::Graph *graph)
-{
- bool changed = false;
- auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
-
- for (auto node : active_nodes)
- {
- if (node->dialect() == TFDialect::get())
- {
- {
- auto tf_node = dynamic_cast<moco::tf::TFMul *>(node);
- if (tf_node != nullptr)
- {
- if (fuse_to_preceding(graph, tf_node))
- changed = true;
- }
- }
- {
- // TODO support Div
- }
-
- {
- auto tf_node = dynamic_cast<moco::tf::TFAdd *>(node);
- if (tf_node != nullptr)
- {
- if (fuse_to_preceding(graph, tf_node))
- changed = true;
- }
- }
- {
- // TODO support Sub
- }
- }
- }
-
- return changed;
-}
-
-} // namespace tf
-} // namespace moco
diff --git a/compiler/moco-tf/src/Transforms/FuseBinaryIntoPreceding.h b/compiler/moco-tf/src/Transforms/FuseBinaryIntoPreceding.h
deleted file mode 100644
index 33d4af14a..000000000
--- a/compiler/moco-tf/src/Transforms/FuseBinaryIntoPreceding.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_FUSE_BINARY_INTO_PRECEDING_H__
-#define __MOCO_TF_FUSE_BINARY_INTO_PRECEDING_H__
-
-#include "Transform.h"
-
-#include <loco.h>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Fuse TFAdd, TFMul to preceding TFConv2D or TFDepthWiseConv2D
-*/
-class FuseBinaryIntoPreceding : public Transform
-{
-public:
- const char *name(void) const final { return "FuseBinaryIntoPreceding"; }
-
-public:
- bool run(loco::Graph *graph) override;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_FUSE_BINARY_INTO_PRECEDING_H__
diff --git a/compiler/moco-tf/src/Transforms/RemoveTFIdentityNodeTransform.cpp b/compiler/moco-tf/src/Transforms/RemoveTFIdentityNodeTransform.cpp
deleted file mode 100644
index 50f7ab92f..000000000
--- a/compiler/moco-tf/src/Transforms/RemoveTFIdentityNodeTransform.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "RemoveTFIdentityNodeTransform.h"
-
-#include "Dialect/TFDialect.h"
-#include "Dialect/TFNode.h"
-
-#include <set>
-
-namespace moco
-{
-namespace tf
-{
-
-bool RemoveTFIdentityNodeTransform::run(loco::Graph *g)
-{
- struct Collector final : public moco::tf::TFNodeMutableVisitor<void>
- {
- void visit(moco::tf::TFIdentity *node) final
- {
- if (node->input() != nullptr)
- {
- candidates.insert(node);
- }
- }
-
- void visit(moco::tf::TFNode *) final { return; }
-
- std::set<moco::tf::TFIdentity *> candidates;
- };
-
- Collector collector;
-
- for (auto node : loco::all_nodes(g))
- {
- if (node->dialect() == moco::tf::TFDialect::get())
- {
- auto tf_node = dynamic_cast<moco::tf::TFNode *>(node);
- tf_node->accept(&collector);
- }
- }
-
- for (auto node : collector.candidates)
- {
- replace(node).with(node->input());
- node->input(nullptr);
- }
-
- return collector.candidates.size() > 0;
-}
-
-} // namespace tf
-} // namespace moco
diff --git a/compiler/moco-tf/src/Transforms/RemoveTFIdentityNodeTransform.h b/compiler/moco-tf/src/Transforms/RemoveTFIdentityNodeTransform.h
deleted file mode 100644
index 534061ee3..000000000
--- a/compiler/moco-tf/src/Transforms/RemoveTFIdentityNodeTransform.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_REMOVE_TFIDENTITY_NODE_TRANSFORM_H__
-#define __MOCO_TF_REMOVE_TFIDENTITY_NODE_TRANSFORM_H__
-
-#include "Transform.h"
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Use the input of "TFIdentity" node instead
- *
- * BEFORE:
- * [X] -> [TFIdentity] -> [Y]
- *
- * AFTER:
- * [X] -> [Y]
- * [TFIdentity]
- *
- * NOTE This transform does not remove "TFIdentity" node
- * This transform is identical to RemoveForwardNodeTransform
- */
-struct RemoveTFIdentityNodeTransform final : public Transform
-{
- const char *name(void) const final { return "RemoveTFIdentityNodeTransform"; }
-
- bool run(loco::Graph *g) final;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_REMOVE_TFIDENTITY_NODE_TRANSFORM_H__
diff --git a/compiler/moco-tf/src/Transforms/ResolveConstantShape.cpp b/compiler/moco-tf/src/Transforms/ResolveConstantShape.cpp
deleted file mode 100644
index 017aa666f..000000000
--- a/compiler/moco-tf/src/Transforms/ResolveConstantShape.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ResolveConstantShape.h"
-
-#include "IR/TFShape.h"
-#include "IR/TFConst.h"
-#include "Annotations/ShapeInferenceData.h"
-
-#include <loco.h>
-
-#include <cassert>
-
-namespace
-{
-
-/**
- * WHEN:
- * - TFShape's input shape is determined
- * DO:
- * - Replace TFShape into TFConst
- *
- *
- * <Before>
- * in ---- TFShape ---- out(s)
- *
- * <After>
- * in ---- TFShape
- *
- * TFConst ---- out(s)
- */
-bool resolve_constant_shape(loco::Graph *graph, moco::tf::TFShape *shape_node)
-{
- using moco::tf::ShapeInferenceData;
-
- auto input_shape = shape_node->input()->annot<ShapeInferenceData>();
-
- // Check condition
- if (!input_shape)
- {
- // Cannot resolve without known input_shape
- return false;
- }
- auto shape_rank = input_shape->rank();
- for (uint32_t axis = 0; axis < shape_rank; ++axis)
- {
- if (!input_shape->dim(axis).known())
- {
- // Cannot resolve with unknown dimension
- return false;
- }
- }
-
- auto input_tensor_shape = input_shape->tensor_shape();
-
- // Make TFConst to replace TFShape
- auto const_node = graph->nodes()->create<moco::tf::TFConst>();
-
- // set dtype
- auto dtype = shape_node->dtype();
- const_node->dtype(dtype);
-
- // set shape
- const_node->rank(1);
- const_node->dim(0) = shape_rank;
-
- // set data
- if (dtype == loco::DataType::S32)
- {
- // TODO Better to make template for this when support new dtype
- const_node->size<loco::DataType::S32>(shape_rank);
- for (uint32_t axis = 0; axis < shape_rank; ++axis)
- {
- int32_t dim = (int32_t)input_tensor_shape.dim(axis).value();
- assert(dim > 0);
- const_node->at<loco::DataType::S32>(axis) = dim;
- }
- }
- else
- {
- throw std::runtime_error("ResolveConstantShape: Not supported output data type");
- }
-
- // replace
- loco::replace(shape_node).with(const_node);
-
- return true;
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-bool ResolveConstantShape::run(loco::Graph *graph)
-{
- bool changed = false;
- for (auto node : loco::active_nodes(loco::output_nodes(graph)))
- {
- if (auto shape_node = as<moco::tf::TFShape>(node))
- {
- if (resolve_constant_shape(graph, shape_node))
- changed = true;
- }
- }
-
- return changed;
-}
-
-} // namespace tf
-} // namespace moco
diff --git a/compiler/moco-tf/src/Transforms/ResolveConstantShape.h b/compiler/moco-tf/src/Transforms/ResolveConstantShape.h
deleted file mode 100644
index 069418b7b..000000000
--- a/compiler/moco-tf/src/Transforms/ResolveConstantShape.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_RESOLVE_CONSTANT_SHAPE_H__
-#define __MOCO_TF_RESOLVE_CONSTANT_SHAPE_H__
-
-#include "Transform.h"
-
-#include <loco.h>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Replace fully determined TFShape node into TFConst
- */
-class ResolveConstantShape : public Transform
-{
-public:
- const char *name(void) const final { return "ResolveConstantShape"; }
-
-public:
- bool run(loco::Graph *graph) override;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_RESOLVE_CONSTANT_SHAPE_H__
diff --git a/compiler/moco-tf/src/Transforms/ResolveFusedBatchNorm.cpp b/compiler/moco-tf/src/Transforms/ResolveFusedBatchNorm.cpp
deleted file mode 100644
index 1eeb31f53..000000000
--- a/compiler/moco-tf/src/Transforms/ResolveFusedBatchNorm.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ResolveFusedBatchNorm.h"
-
-#include "IR/TFAdd.h"
-#include "IR/TFConst.h"
-#include "IR/TFMul.h"
-
-#include "IR/TFFusedBatchNorm.h"
-
-#include <loco.h>
-#include <moco/Log.h>
-
-#include <cassert>
-#include <cmath>
-#include <memory>
-
-namespace
-{
-
-bool is_same_shape(moco::tf::TFConst *lc, moco::tf::TFConst *rc)
-{
- if (lc->rank() != rc->rank())
- return false;
-
- for (auto r = 0; r < lc->rank(); ++r)
- {
- if (lc->dim(r).value() != rc->dim(r).value())
- return false;
- }
- return true;
-}
-
-void copy_shape(const moco::tf::TFConst *src, moco::tf::TFConst *dst)
-{
- assert(src != nullptr);
- assert(dst != nullptr);
-
- uint32_t rank = src->rank();
- dst->rank(rank);
- for (uint32_t index = 0; index < rank; ++index)
- {
- if (src->dim(index).known())
- dst->dim(index) = src->dim(index).value();
- else
- dst->dim(index).unset();
- }
-}
-
-/**
- * @note resolve_to_muladd() will transform TFFusedBatchNorm to TFMul, TFAdd and two ConstGen
- *
- * <arguments>
- * %0:input
- * %1:gamma : const
- * %2:beta : const
- * %3:mean : const
- * %4:variance : const
- * %5:epsilon : const
- *
- * <constant operations>
- * fbn_epsilon_array = make_array(%5:epsilon)
- * fbn_epsilon = %4:variance + fbn_epsilon_array
- * fbn_rsqrt = 1.0 / math::sqrt(fbn_epsilon)
- *
- * fbn_mean = %3:mean
- * fbn_mul = fbn_rsqrt * %1:gamma
- * fbn_offset = %2:beta
- *
- * fbn_mul_0_param = fbn_mul
- * fbn_add_param = fbn_offset - fbn_mean * fbn_mul
- *
- * <new replace nodes>
- * %11:fbn_mul_0_param = ConstGen(fbn_mul_0_param)
- * %12:fbn_mul_0 = TFMul(%0:input, %11:fbn_mul_0_param)
- * %21:fbn_add_param = ConstGen(fbn_add_param)
- * %22:fbn = TFAdd(%12:fbn_mul_0,%21:fbn_add_param)
- */
-bool resolve_to_muladd(loco::Graph *graph, moco::tf::TFFusedBatchNorm *node)
-{
- LOGGER(lfbn);
-
- auto tffbn_input = node->input();
- if (tffbn_input == nullptr)
- {
- // This node is already converted
- return false;
- }
-
- auto tffbn_gamma = dynamic_cast<moco::tf::TFConst *>(node->gamma());
- auto tffbn_beta = dynamic_cast<moco::tf::TFConst *>(node->beta());
- auto tffbn_mean = dynamic_cast<moco::tf::TFConst *>(node->mean());
- auto tffbn_variance = dynamic_cast<moco::tf::TFConst *>(node->variance());
-
- // all should be const
- if (tffbn_gamma == nullptr || tffbn_beta == nullptr || tffbn_mean == nullptr ||
- tffbn_variance == nullptr)
- {
- INFO(lfbn) << "TFFBN resolve_to_muladd: One of constant input node is not a constant"
- << std::endl;
- return false;
- }
- assert(tffbn_gamma->dtype() == loco::DataType::FLOAT32);
- assert(tffbn_beta->dtype() == loco::DataType::FLOAT32);
- assert(tffbn_mean->dtype() == loco::DataType::FLOAT32);
- assert(tffbn_variance->dtype() == loco::DataType::FLOAT32);
-
- // check all const shape are the same
- if (!is_same_shape(tffbn_gamma, tffbn_beta) || !is_same_shape(tffbn_gamma, tffbn_mean) ||
- !is_same_shape(tffbn_gamma, tffbn_variance))
- {
- INFO(lfbn) << "TFFBN resolve_to_muladd: Shape of constant are not same" << std::endl;
- return false;
- }
-
- auto tffbn_epsilon = node->epsilon();
- INFO(lfbn) << "TFFBN tffbn_epsilon = " << tffbn_epsilon << std::endl;
- auto const_num_elements = tffbn_gamma->size<loco::DataType::FLOAT32>();
- INFO(lfbn) << "TFFBN const_num_elements = " << const_num_elements << std::endl;
-
- // fbn_epsilon = %4:variance + fbn_epsilon_array
- std::unique_ptr<float[]> fbn_epsilon{new float[const_num_elements]};
- for (int32_t i = 0; i < const_num_elements; i++)
- {
- auto variance = tffbn_variance->at<loco::DataType::FLOAT32>(i);
- fbn_epsilon.get()[i] = variance + tffbn_epsilon;
- }
-
- // fbn_rsqrt = 1.0 / math::sqrt(fbn_epsilon)
- std::unique_ptr<float[]> fbn_rsqrt{new float[const_num_elements]};
- for (int32_t i = 0; i < const_num_elements; i++)
- {
- fbn_rsqrt.get()[i] = 1.0 / sqrt(fbn_epsilon.get()[i]);
- }
-
- // fbn_mean = %3:mean : TODO remove this block and use %3:mean
- std::unique_ptr<float[]> fbn_mean{new float[const_num_elements]};
- for (int32_t i = 0; i < const_num_elements; i++)
- {
- fbn_mean.get()[i] = tffbn_mean->at<loco::DataType::FLOAT32>(i);
- }
-
- // fbn_mul = fbn_rsqrt * %1:gamma
- std::unique_ptr<float[]> fbn_mul{new float[const_num_elements]};
- for (int32_t i = 0; i < const_num_elements; i++)
- {
- fbn_mul.get()[i] = fbn_rsqrt.get()[i] * tffbn_gamma->at<loco::DataType::FLOAT32>(i);
- }
-
- // fbn_offset = %2:beta : TODO remove this block and use %2:beta
- std::unique_ptr<float[]> fbn_offset{new float[const_num_elements]};
- for (int32_t i = 0; i < const_num_elements; i++)
- {
- fbn_offset.get()[i] = tffbn_beta->at<loco::DataType::FLOAT32>(i);
- }
-
- // fbn_mul_0_param = fbn_mul : remove this and use fbn_mul
- std::unique_ptr<float[]> fbn_mul_0_param{new float[const_num_elements]};
- for (int32_t i = 0; i < const_num_elements; i++)
- {
- fbn_mul_0_param.get()[i] = fbn_mul.get()[i];
- }
-
- // fbn_add_param = fbn_offset - fbn_mean * fbn_mul
- std::unique_ptr<float[]> fbn_add_param{new float[const_num_elements]};
- for (int32_t i = 0; i < const_num_elements; i++)
- {
- fbn_add_param.get()[i] = fbn_offset.get()[i] - fbn_mean.get()[i] * fbn_mul.get()[i];
- }
-
- INFO(lfbn) << "TFFBN create ConstGen" << std::endl;
-
- /*
- * %11:fbn_mul_0_param = ConstGen(fbn_mul_0_param)
- * %21:fbn_add_param = ConstGen(fbn_add_param)
- */
- auto const_fbn_mul_0_param = graph->nodes()->create<moco::tf::TFConst>();
- const_fbn_mul_0_param->dtype(loco::DataType::FLOAT32);
- copy_shape(tffbn_gamma, const_fbn_mul_0_param);
- const_fbn_mul_0_param->size<loco::DataType::FLOAT32>(const_num_elements);
- for (int32_t i = 0; i < const_num_elements; i++)
- {
- const_fbn_mul_0_param->at<loco::DataType::FLOAT32>(i) = fbn_mul_0_param.get()[i];
- }
- auto const_fbn_add_param = graph->nodes()->create<moco::tf::TFConst>();
- const_fbn_add_param->dtype(loco::DataType::FLOAT32);
- copy_shape(tffbn_gamma, const_fbn_add_param);
- const_fbn_add_param->size<loco::DataType::FLOAT32>(const_num_elements);
- for (int32_t i = 0; i < const_num_elements; i++)
- {
- const_fbn_add_param->at<loco::DataType::FLOAT32>(i) = fbn_add_param.get()[i];
- }
-
- INFO(lfbn) << "TFFBN create TFMul, TFAdd" << std::endl;
- /*
- * %12:fbn_mul_0 = TFMul(%0:input, %11:fbn_mul_0_param)
- * %22:fbn = TFAdd(%12:fbn_mul_0,%21:fbn_add_param)
- */
- auto fbn_mul_0 = graph->nodes()->create<moco::tf::TFMul>();
- fbn_mul_0->x(tffbn_input);
- fbn_mul_0->y(const_fbn_mul_0_param);
-
- auto fbn = graph->nodes()->create<moco::tf::TFAdd>();
- fbn->x(fbn_mul_0);
- fbn->y(const_fbn_add_param);
-
- // replace old node with new fbn
- replace(node).with(fbn);
- // unlink from graph
- node->input(nullptr);
- node->gamma(nullptr);
- node->beta(nullptr);
- node->mean(nullptr);
- node->variance(nullptr);
-
- return true;
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-bool ResolveFusedBatchNorm::run(loco::Graph *graph)
-{
- for (auto node : loco::active_nodes(loco::output_nodes(graph)))
- {
- if (as<moco::tf::TFFusedBatchNorm>(node))
- {
- if (resolve_to_muladd(graph, as<moco::tf::TFFusedBatchNorm>(node)))
- {
- // tree has been changed. let's return so that we don't need to
- // considier about following node is correct or not.
- return true;
- }
- }
- }
-
- return false;
-}
-
-} // namespace tf
-} // namespace moco
diff --git a/compiler/moco-tf/src/Transforms/ResolveFusedBatchNorm.h b/compiler/moco-tf/src/Transforms/ResolveFusedBatchNorm.h
deleted file mode 100644
index 9243951f5..000000000
--- a/compiler/moco-tf/src/Transforms/ResolveFusedBatchNorm.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_RESOLVE_FUSEDBATCHNORM_H__
-#define __MOCO_TF_RESOLVE_FUSEDBATCHNORM_H__
-
-#include "Transform.h"
-
-#include <loco.h>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Trasform TFFusedBatchNorm into TFAdd + TFRsqrt + TFMul + TFBatchNorm
-*/
-class ResolveFusedBatchNorm : public Transform
-{
-public:
- const char *name(void) const final { return "ResolveFusedBatchNorm"; }
-
-public:
- bool run(loco::Graph *graph) override;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_RESOLVE_FUSEDBATCHNORM_H__
diff --git a/compiler/moco-tf/src/Transforms/ResolveFusedBatchNorm.test.cpp b/compiler/moco-tf/src/Transforms/ResolveFusedBatchNorm.test.cpp
deleted file mode 100644
index de4e1051d..000000000
--- a/compiler/moco-tf/src/Transforms/ResolveFusedBatchNorm.test.cpp
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ResolveFusedBatchNorm.h"
-
-#include "LogHelper.h"
-#include "TestHelper.h"
-#include "IR/TFFusedBatchNorm.h"
-#include "Importer.h"
-
-#include <loco.h>
-#include <moco/Log.h>
-#include <stdex/Memory.h>
-#include <plier/tf/TestHelper.h>
-
-#include <google/protobuf/io/coded_stream.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
-#include <google/protobuf/text_format.h>
-
-#include <gtest/gtest.h>
-
-using namespace moco::tf::test;
-
-namespace
-{
-// clang-format off
-const char *fbn_basic_pbtxt = STRING_CONTENT(
-node {
- name: "input"
- op: "Const"
- attr {
- key: "dtype"
- value { type: DT_FLOAT }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim { size: 1 }
- dim { size: 4 }
- dim { size: 4 }
- dim { size: 1 }
- }
- float_val: 1.0
- }
- }
- }
-}
-node {
- name: "gamma"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 1
- }
- }
- float_val: 1.0
- }
- }
- }
-}
-node {
- name: "beta"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 1
- }
- }
- float_val: 1.0
- }
- }
- }
-}
-node {
- name: "FBN_01/mean"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 1
- }
- }
- float_val: 1.0
- }
- }
- }
-}
-node {
- name: "FBN_01/variance"
- op: "Const"
- attr {
- key: "dtype"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape {
- dim {
- size: 1
- }
- }
- float_val: 1.0
- }
- }
- }
-}
-node {
- name: "FBN_01"
- op: "FusedBatchNorm"
- input: "input"
- input: "gamma"
- input: "beta"
- input: "FBN_01/mean"
- input: "FBN_01/variance"
- attr {
- key: "T"
- value {
- type: DT_FLOAT
- }
- }
- attr {
- key: "data_format"
- value {
- s: "NHWC"
- }
- }
- attr {
- key: "epsilon"
- value {
- f: 0.001
- }
- }
- attr {
- key: "is_training"
- value {
- b: false
- }
- }
-}
-);
-// clang-format on
-
-} // namespace
-
-namespace
-{
-
-char to_char(bool b) { return b ? 'Y' : 'N'; }
-
-} // namespace
-
-TEST(ResolveFusedBatchNorm, fbn_resolve_basic)
-{
- LOGGER(l);
-
- // load graph
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
- signature.add_output(moco::tf::TensorName("FBN_01", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(fbn_basic_pbtxt, graph_def));
- auto graph = importer.import(signature, graph_def);
-
- INFO(l) << "Before ResolveFusedBatchNorm";
- INFO(l) << moco::tf::fmt(graph);
-
- moco::tf::ResolveFusedBatchNorm transform;
- bool changed = transform.run(graph.get());
-
- INFO(l) << "After ResolveFusedBatchNorm " << to_char(changed);
- INFO(l) << moco::tf::fmt(graph);
-
- // Output value test will be done with mocotest-tf
- // Network structure of transformation is not important and may be changed
- // in the future so it will not be checked here.
-
- SUCCEED();
-}
diff --git a/compiler/moco-tf/src/Transforms/ResolveReshapeWildcardDim.cpp b/compiler/moco-tf/src/Transforms/ResolveReshapeWildcardDim.cpp
deleted file mode 100644
index 80242521a..000000000
--- a/compiler/moco-tf/src/Transforms/ResolveReshapeWildcardDim.cpp
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ResolveReshapeWildcardDim.h"
-
-#include "IR/TFReshape.h"
-#include "IR/TFConst.h"
-#include "Annotations/ShapeInferenceData.h"
-
-#include <loco.h>
-
-#include <cassert>
-#include <limits>
-
-namespace
-{
-
-/**
- * @return true when 'node' has one and only one wildcard dimension
- * @return false when 'node' has no wildcard dimension, i.e. fixed reshape case
- *
- * @note Assertions in this function are sanity check for 'node', Reshape's
- * Const shape input
- */
-bool has_one_wildcard_dim(const moco::tf::TFConst *node)
-{
- assert(node->dtype() == loco::DataType::S32);
- assert(node->rank() == 1);
-
- auto len = node->dim(0).value();
- assert(len > 0);
-
- // Must have one and only wildcard dimension(-1)
- uint32_t count_wildcard_dim = 0;
- for (uint32_t i = 0; i < len; ++i)
- {
- auto dim = node->at<loco::DataType::S32>(i);
- if (dim == -1)
- count_wildcard_dim++;
- else
- assert(dim >= 1);
- }
-
- assert(count_wildcard_dim <= 1 &&
- "Invalid Reshape: there should be none or only one wildcard dimension");
- return count_wildcard_dim;
-}
-
-uint32_t volume(const loco::TensorShape &shape)
-{
- uint32_t ret = 1;
- auto rank = shape.rank();
- for (uint32_t axis = 0; axis < rank; ++axis)
- {
- ret *= shape.dim(axis).value();
- }
- return ret;
-}
-
-void deduce_and_fix_wildcard_dim(moco::tf::TFConst *node,
- const moco::tf::ShapeInferenceData *shape_data)
-{
- assert(has_one_wildcard_dim(node));
-
- assert(shape_data->domain() == loco::Domain::Tensor);
- auto shape = shape_data->tensor_shape();
-
- auto len = node->dim(0).value();
- uint32_t wildcard_index = std::numeric_limits<uint32_t>::max();
- uint32_t product_of_non_wildcard_dims = 1;
-
- // Deduce
- for (uint32_t i = 0; i < len; ++i)
- {
- auto dim = node->at<loco::DataType::S32>(i);
- if (dim == -1)
- {
- wildcard_index = i;
- }
- else
- {
- product_of_non_wildcard_dims *= dim;
- }
- }
- assert(wildcard_index != std::numeric_limits<uint32_t>::max());
-
- // Fix
- assert(volume(shape) % product_of_non_wildcard_dims == 0);
- node->at<loco::DataType::S32>(wildcard_index) = volume(shape) / product_of_non_wildcard_dims;
-}
-
-/**
- * WHEN:
- * - TFReshape's shape input is TFConst
- * - The TFConst is valid shape input for dynamic reshape, i.e. it has one and
- * only wildcard dimension(-1)
- * - TFReshape's tensor input has complete shape inference data
- * DO:
- * - Deduce what the wildcard dimension is and fix it
- */
-bool resolve_wildcard_dim(moco::tf::TFReshape *reshape)
-{
- // Check conditions (WHEN)
- auto const_shape_input = dynamic_cast<moco::tf::TFConst *>(reshape->shape());
- if (!const_shape_input)
- return false;
-
- if (!has_one_wildcard_dim(const_shape_input))
- return false;
-
- auto tensor_input_shape = reshape->tensor()->annot<moco::tf::ShapeInferenceData>();
- if (!tensor_input_shape)
- return false;
-
- // Deduce (DO)
- deduce_and_fix_wildcard_dim(const_shape_input, tensor_input_shape);
-
- return true;
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-bool ResolveReshapeWildcardDim::run(loco::Graph *graph)
-{
- bool changed = false;
- for (auto node : loco::active_nodes(loco::output_nodes(graph)))
- {
- if (auto reshape = as<moco::tf::TFReshape>(node))
- {
- if (resolve_wildcard_dim(reshape))
- changed = true;
- }
- }
-
- return changed;
-}
-
-} // namespace tf
-} // namespace moco
diff --git a/compiler/moco-tf/src/Transforms/ResolveReshapeWildcardDim.h b/compiler/moco-tf/src/Transforms/ResolveReshapeWildcardDim.h
deleted file mode 100644
index c165c9027..000000000
--- a/compiler/moco-tf/src/Transforms/ResolveReshapeWildcardDim.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __MOCO_TF_RESOLVE_RESHAPE_WILDCARD_DIM_H__
-#define __MOCO_TF_RESOLVE_RESHAPE_WILDCARD_DIM_H__
-
-#include "Transform.h"
-
-#include <loco.h>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Determine wildcard dimension (denoted as -1) of Reshape's shape input
- * if possible
- */
-class ResolveReshapeWildcardDim : public Transform
-{
-public:
- const char *name(void) const final { return "ResolveReshapeWildcardDim"; }
-
-public:
- bool run(loco::Graph *graph) override;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_RESOLVE_RESHAPE_WILDCARD_DIM_H__
diff --git a/compiler/moco-tf/src/Transforms/ShapeInferencePass.cpp b/compiler/moco-tf/src/Transforms/ShapeInferencePass.cpp
index e47b06964..64ba9dfb1 100644
--- a/compiler/moco-tf/src/Transforms/ShapeInferencePass.cpp
+++ b/compiler/moco-tf/src/Transforms/ShapeInferencePass.cpp
@@ -16,8 +16,9 @@
#include "ShapeInferencePass.h"
-#include "Dialect/TFShapeInferenceRule.h"
-#include "Dialect/TFDialect.h"
+#include <moco/IR/TFDialect.h>
+
+#include <moco/Service/TFShapeInferenceRule.h>
#include <loco.h>
@@ -39,7 +40,7 @@ namespace tf
bool ShapeInferencePass::run(loco::Graph *graph)
{
loco::CanonicalShapeInferenceRule canonical_rule;
- TFShapeInferenceRule tf_rule;
+ moco::TFShapeInferenceRule tf_rule;
locoex::COpShapeInferenceRule cop_rule; // rule for custop op
loco::MultiDialectShapeInferenceRule rules;
diff --git a/compiler/moco-tf/src/Transforms/TypeInferencePass.cpp b/compiler/moco-tf/src/Transforms/TypeInferencePass.cpp
index efdacc5a0..db6cf7521 100644
--- a/compiler/moco-tf/src/Transforms/TypeInferencePass.cpp
+++ b/compiler/moco-tf/src/Transforms/TypeInferencePass.cpp
@@ -16,8 +16,9 @@
#include "TypeInferencePass.h"
-#include "Dialect/TFTypeInferenceRule.h"
-#include "Dialect/TFDialect.h"
+#include <moco/IR/TFDialect.h>
+
+#include <moco/Service/TFTypeInferenceRule.h>
#include <loco.h>
@@ -35,7 +36,7 @@ namespace tf
bool TypeInferencePass::run(loco::Graph *graph)
{
loco::CanonicalTypeInferenceRule canonical_rule;
- TFTypeInferenceRule tf_rule; // rule for TF dialect
+ moco::TFTypeInferenceRule tf_rule; // rule for TF dialect
locoex::COpTypeInferenceRule cop_rule; // rule for custop op
loco::MultiDialectTypeInferenceRule rules;
diff --git a/compiler/moco-value-pbtxt-test/.gitignore b/compiler/moco-value-pbtxt-test/.gitignore
new file mode 100644
index 000000000..8dbfa9012
--- /dev/null
+++ b/compiler/moco-value-pbtxt-test/.gitignore
@@ -0,0 +1 @@
+/test.local.lst
diff --git a/compiler/moco-value-pbtxt-test/CMakeLists.txt b/compiler/moco-value-pbtxt-test/CMakeLists.txt
new file mode 100644
index 000000000..50513f0a1
--- /dev/null
+++ b/compiler/moco-value-pbtxt-test/CMakeLists.txt
@@ -0,0 +1,135 @@
+option(MOCO_VALUE_PBTXT_TEST "Enable moco value test for pbtxt input model" ON)
+
+if(NOT MOCO_VALUE_PBTXT_TEST)
+ return()
+endif(NOT MOCO_VALUE_PBTXT_TEST)
+
+if(NOT TARGET tfkit)
+ message(STATUS "moco: Skip test material preparation as tfkit is not defined")
+ return()
+endif(NOT TARGET tfkit)
+
+nncc_find_resource(TensorFlowTests)
+
+#
+# Copy [Testcase]/test.pbtxt to Testcase.pbtxt in binary folder
+# Copy [Testcase]/test.info to Testcase.info in binary folder
+# Encode Testcase.pbtxt to Testcase.pb
+#
+set(TEST_PBTXT_FILE "test.pbtxt")
+set(TEST_INFO_FILE "test.info")
+set(TEST_REPO "${TensorFlowTests_DIR}") # Where to find tests
+set(TEST_SPACE "${CMAKE_CURRENT_BINARY_DIR}") # Where to run tests
+
+unset(TESTCASES)
+
+macro(add NAME)
+ list(APPEND TESTCASES ${NAME})
+endmacro(add)
+
+# Read "test.lst"
+include("test.lst")
+# Read "test.local.lst" if exists
+include("test.local.lst" OPTIONAL)
+
+unset(MOCO_VALUE_PBTXT_DEPS)
+
+foreach(PREFIX IN ITEMS ${TESTCASES})
+ set(PBTXTFILE "${PREFIX}/${TEST_PBTXT_FILE}")
+ get_filename_component(DIR_NAME ${PBTXTFILE} DIRECTORY)
+
+ set(PBTXT_SOURCE_FILE "${DIR_NAME}.pbtxt")
+ set(PBTXT_SOURCE_PATH "${TEST_SPACE}/${DIR_NAME}.pbtxt")
+
+ set(PBTXT_INFO_FILE "${DIR_NAME}.info")
+ set(PBTXT_INFO_PATH "${TEST_SPACE}/${PBTXT_INFO_FILE}")
+
+ set(PB_OUTPUT_FILE "${DIR_NAME}.pb")
+ set(PB_PATH "${TEST_SPACE}/${PB_OUTPUT_FILE}")
+
+ # Copy files
+ add_custom_command(
+ OUTPUT ${PBTXT_SOURCE_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TEST_REPO}/${DIR_NAME}/${TEST_PBTXT_FILE}"
+ "${PBTXT_SOURCE_PATH}"
+ DEPENDS "${TEST_REPO}/${DIR_NAME}/${TEST_PBTXT_FILE}"
+ COMMENT "Copy ${PBTXT_SOURCE_FILE}"
+ )
+
+ add_custom_command(
+ OUTPUT ${PBTXT_INFO_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TEST_REPO}/${DIR_NAME}/${TEST_INFO_FILE}"
+ "${PBTXT_INFO_PATH}"
+ DEPENDS "${TEST_REPO}/${DIR_NAME}/${TEST_INFO_FILE}"
+ COMMENT "Copy ${PBTXT_INFO_FILE}"
+ )
+
+ # Use tfkit to encode
+ add_custom_command(
+ OUTPUT ${PB_OUTPUT_FILE}
+ COMMAND $<TARGET_FILE:tfkit> encode ${PBTXT_SOURCE_PATH} ${PB_OUTPUT_FILE}
+ DEPENDS tfkit ${PBTXT_SOURCE_PATH}
+ COMMENT "Generate ${PB_OUTPUT_FILE}"
+ )
+
+ list(APPEND MOCO_VALUE_PBTXT_TESTS ${DIR_NAME})
+ list(APPEND MOCO_VALUE_PBTXT_DEPS ${PBTXT_INFO_FILE} ${PB_OUTPUT_FILE})
+
+endforeach(PREFIX)
+
+if(NOT TARGET nnkit_tf_backend)
+ message(STATUS "moco: Skip adding test as nnkit_tf_backend is not defined")
+ return()
+endif(NOT TARGET nnkit_tf_backend)
+
+##
+## Copy runall.sh
+##
+set(TEST_RUNNER_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/runall.sh")
+set(TEST_RUNNER "${CMAKE_CURRENT_BINARY_DIR}/run-tests")
+
+add_custom_command(
+ OUTPUT ${TEST_RUNNER}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TEST_RUNNER_SOURCE}" "${TEST_RUNNER}"
+ DEPENDS ${TEST_RUNNER_SOURCE}
+ COMMENT "Generate test runner"
+)
+
+list(APPEND MOCO_VALUE_PBTXT_DEPS "${TEST_RUNNER}")
+
+###
+### Generate test.config
+###
+set(TOOLCHIAN_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/toolchain.config")
+
+add_custom_command(
+ OUTPUT ${TOOLCHIAN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TOOLCHIAN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'NNKIT_RUN_PATH=\"$<TARGET_FILE:nnkit-run>\"' >> ${TOOLCHIAN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TF_BACKEND_PATH=\"$<TARGET_FILE:nnkit_tf_backend>\"' >> ${TOOLCHIAN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'MOCO_TF_BACKEND_PATH=\"$<TARGET_FILE:nnkit_moco_tf_backend>\"' >> ${TOOLCHIAN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TF2TFLITE_PATH=\"$<TARGET_FILE:tf2tflite>\"' >> ${TOOLCHIAN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'RANDOMIZE_ACTION_PATH=\"$<TARGET_FILE:nnkit_randomize_action>\"' >> ${TOOLCHIAN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'HDF5_EXPORT_ACTION_PATH=\"$<TARGET_FILE:nnkit_HDF5_export_action>\"' >> ${TOOLCHIAN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'HDF5_IMPORT_ACTION_PATH=\"$<TARGET_FILE:nnkit_HDF5_import_action>\"' >> ${TOOLCHIAN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'I5DIFF_PATH=\"$<TARGET_FILE:i5diff>\"' >> ${TOOLCHIAN_CONFIG}
+ DEPENDS
+ nnkit-run
+ nnkit_tf_backend
+ nnkit_moco_tf_backend
+ tf2tflite
+ nnkit_randomize_action
+ nnkit_HDF5_export_action
+ nnkit_HDF5_import_action
+ i5diff
+ COMMENT "Generate test configuration"
+)
+
+list(APPEND MOCO_VALUE_PBTXT_DEPS "${TOOLCHIAN_CONFIG}")
+
+# This target enforces CMake to generate all the dependencies during "build" phase
+add_custom_target(moco_value_pbtxt_test_deps ALL DEPENDS ${MOCO_VALUE_PBTXT_DEPS})
+
+# Run tests
+add_test(NAME moco_value_pbtxt_test
+ COMMAND "${TEST_RUNNER}" "${TOOLCHIAN_CONFIG}" "${TEST_SPACE}" ${MOCO_VALUE_PBTXT_TESTS})
diff --git a/compiler/moco-value-pbtxt-test/README.md b/compiler/moco-value-pbtxt-test/README.md
new file mode 100644
index 000000000..f5d1ac225
--- /dev/null
+++ b/compiler/moco-value-pbtxt-test/README.md
@@ -0,0 +1 @@
+# moco-value-pbtxt-test
diff --git a/compiler/mocotest-tf/requires.cmake b/compiler/moco-value-pbtxt-test/requires.cmake
index 771418fed..771418fed 100644
--- a/compiler/mocotest-tf/requires.cmake
+++ b/compiler/moco-value-pbtxt-test/requires.cmake
diff --git a/compiler/moco-value-pbtxt-test/runall.sh b/compiler/moco-value-pbtxt-test/runall.sh
new file mode 100755
index 000000000..ee43f1ad6
--- /dev/null
+++ b/compiler/moco-value-pbtxt-test/runall.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+
+# Need at least 2 arguments
+if [[ $# -lt 2 ]]; then
+ echo "USAGE: $0 ..."
+ echo
+ echo "ARGUMENTS:"
+ echo " [test.config path]"
+ echo " [WORKDIR]"
+ echo " [Prefix1]"
+ echo " [Prefix2]"
+ echo " ..."
+ exit 255
+fi
+
+CONFIG_PATH="$1"; shift
+WORKDIR="$1"; shift
+
+source "${CONFIG_PATH}"
+
+echo "-- starting moco test tf"
+echo "-- Found nnkit-run: ${NNKIT_RUN_PATH}"
+echo "-- Found TF backend: ${TF_BACKEND_PATH}"
+echo "-- Found moco TF backend: ${MOCO_TF_BACKEND_PATH}"
+echo "-- Found randomize action: ${RANDOMIZE_ACTION_PATH}"
+echo "-- Found HDF5 export action: ${HDF5_EXPORT_ACTION_PATH}"
+echo "-- Found HDF5 import action: ${HDF5_IMPORT_ACTION_PATH}"
+echo "-- Found i5diff: ${I5DIFF_PATH}"
+echo "-- Found workdir: ${WORKDIR}"
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+pushd "${WORKDIR}"
+while [[ $# -ne 0 ]]; do
+ PREFIX="$1"; shift
+
+ TESTED+=("${PREFIX}")
+
+ PASSED_TAG="${PREFIX}.passed"
+
+ rm -f "${PASSED_TAG}"
+
+ cat > "${PREFIX}.log" <(
+ exec 2>&1
+
+ echo "-- Found pb: ${PREFIX}.pb"
+
+ # Show commands
+ set -x
+ "${NNKIT_RUN_PATH}" \
+ --backend "${TF_BACKEND_PATH}" \
+ --backend-arg "${WORKDIR}/${PREFIX}.pb" \
+ --backend-arg "${WORKDIR}/${PREFIX}.info" \
+ --pre "${RANDOMIZE_ACTION_PATH}" \
+ --pre "${HDF5_EXPORT_ACTION_PATH}" \
+ --pre-arg "${WORKDIR}/${PREFIX}.input.h5" \
+ --post "${HDF5_EXPORT_ACTION_PATH}" \
+ --post-arg "${WORKDIR}/${PREFIX}.expected.h5"
+
+ "${NNKIT_RUN_PATH}" \
+ --backend "${MOCO_TF_BACKEND_PATH}" \
+ --backend-arg "${WORKDIR}/${PREFIX}.pb" \
+ --backend-arg "${WORKDIR}/${PREFIX}.info" \
+ --pre "${HDF5_IMPORT_ACTION_PATH}" \
+ --pre-arg "${WORKDIR}/${PREFIX}.input.h5" \
+ --post "${HDF5_EXPORT_ACTION_PATH}" \
+ --post-arg "${WORKDIR}/${PREFIX}.obtained.h5"
+
+ "${I5DIFF_PATH}" -d 0.001 "${PREFIX}.expected.h5" "${PREFIX}.obtained.h5"
+
+ if [[ $? -eq 0 ]]; then
+ touch "${PASSED_TAG}"
+ fi
+ )
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ PASSED+=("$PREFIX")
+ else
+ FAILED+=("$PREFIX")
+ 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/moco-value-pbtxt-test/test.lst b/compiler/moco-value-pbtxt-test/test.lst
new file mode 100644
index 000000000..c2d8e42ff
--- /dev/null
+++ b/compiler/moco-value-pbtxt-test/test.lst
@@ -0,0 +1,103 @@
+add(NET_0000)
+add(NET_0001)
+add(NET_0002)
+add(NET_0003)
+add(NET_0004)
+add(NET_0005)
+add(NET_0006)
+add(NET_0007)
+add(NET_0008)
+add(NET_0009)
+add(NET_0010)
+add(NET_0011)
+add(NET_0012)
+add(NET_0013)
+add(NET_0014)
+add(NET_0015)
+add(NET_0016)
+add(NET_0017)
+add(NET_0018)
+add(NET_0019)
+add(NET_0020)
+add(NET_0021)
+add(NET_0022)
+add(NET_0023)
+add(NET_0024)
+add(NET_0025)
+add(NET_0026)
+add(NET_0028)
+add(NET_0029)
+add(NET_0030)
+add(NET_0031)
+add(NET_0032)
+add(NET_0033)
+add(NET_0034)
+add(NET_0035)
+# add(NET_0036)
+add(NET_0037)
+add(NET_0038)
+add(NET_0039)
+add(NET_0040)
+add(NET_0041)
+add(REGRESSION_0000)
+add(REGRESSION_0001)
+add(REGRESSION_0002)
+add(UNIT_Add_000)
+add(UNIT_Add_001)
+add(UNIT_Add_002)
+add(UNIT_Add_004)
+add(UNIT_Add_005)
+add(UNIT_AvgPool_000)
+add(UNIT_AvgPool_001)
+add(UNIT_BiasAdd_000)
+add(UNIT_BiasAdd_001)
+add(UNIT_BiasAdd_002)
+add(UNIT_ConcatV2_000)
+add(UNIT_ConcatV2_001)
+add(UNIT_ConcatV2_002)
+add(UNIT_Const_000)
+add(UNIT_Conv2D_000)
+add(UNIT_Conv2DBackpropInput_000)
+add(UNIT_Conv2DBackpropInput_001)
+add(UNIT_DepthwiseConv2dNative_000)
+add(UNIT_DepthwiseConv2dNative_001)
+add(UNIT_FusedBatchNorm_000)
+add(UNIT_FusedBatchNorm_001)
+add(UNIT_Maximum_000)
+add(UNIT_Maximum_001)
+add(UNIT_Maximum_002)
+add(UNIT_MaxPool_000)
+add(UNIT_MaxPool_001)
+add(UNIT_Mean_000)
+add(UNIT_Mean_001)
+add(UNIT_Mean_002)
+add(UNIT_Mean_003)
+add(UNIT_Mul_000)
+add(UNIT_Mul_001)
+add(UNIT_Mul_002)
+add(UNIT_Pad_000)
+add(UNIT_Placeholder_000)
+add(UNIT_Placeholder_001)
+add(UNIT_Placeholder_002)
+add(UNIT_Placeholder_003)
+add(UNIT_RealDiv_000)
+add(UNIT_Relu_000)
+add(UNIT_Relu6_000)
+add(UNIT_Reshape_000)
+add(UNIT_Rsqrt_000)
+add(UNIT_Softmax_000)
+add(UNIT_Softmax_001)
+add(UNIT_Softmax_002)
+add(UNIT_Softmax_003)
+add(UNIT_Sqrt_000)
+add(UNIT_SquaredDifference_000)
+add(UNIT_SquaredDifference_001)
+add(UNIT_Squeeze_000)
+add(UNIT_Squeeze_001)
+add(UNIT_Squeeze_002)
+add(UNIT_Squeeze_003)
+add(UNIT_StopGradient_000)
+add(UNIT_StopGradient_001)
+add(UNIT_Sub_000)
+add(UNIT_Sub_001)
+add(UNIT_Tanh_000)
diff --git a/compiler/moco/CMakeLists.txt b/compiler/moco/CMakeLists.txt
new file mode 100644
index 000000000..9fdd4398e
--- /dev/null
+++ b/compiler/moco/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_subdirectory(lang)
+add_subdirectory(support)
+add_subdirectory(service)
+add_subdirectory(import)
+add_subdirectory(pass)
diff --git a/compiler/moco/README.md b/compiler/moco/README.md
new file mode 100644
index 000000000..13c7aaae3
--- /dev/null
+++ b/compiler/moco/README.md
@@ -0,0 +1,3 @@
+# moco
+
+_moco_ provides building blocks to load and process TensorFlow models and to produce graph of loco canonical IR
diff --git a/compiler/moco/import/CMakeLists.txt b/compiler/moco/import/CMakeLists.txt
new file mode 100644
index 000000000..43107776e
--- /dev/null
+++ b/compiler/moco/import/CMakeLists.txt
@@ -0,0 +1,26 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(moco_import SHARED ${SOURCES})
+target_include_directories(moco_import PRIVATE src)
+target_include_directories(moco_import PUBLIC include)
+target_link_libraries(moco_import PUBLIC moco_lang)
+target_link_libraries(moco_import PUBLIC mio_tf)
+target_link_libraries(moco_import PUBLIC stdex)
+target_link_libraries(moco_import PRIVATE nncc_common)
+target_link_libraries(moco_import PRIVATE plier_tf)
+target_link_libraries(moco_import PRIVATE oops)
+install(TARGETS moco_import DESTINATION lib) # moco_tf_frontend requires moco_import
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(moco_import_test ${TESTS})
+target_include_directories(moco_import_test PRIVATE src)
+target_link_libraries(moco_import_test moco_import)
+target_link_libraries(moco_import_test plier_tf)
+target_link_libraries(moco_import_test oops)
diff --git a/compiler/moco/import/README.md b/compiler/moco/import/README.md
new file mode 100644
index 000000000..2704d35d6
--- /dev/null
+++ b/compiler/moco/import/README.md
@@ -0,0 +1,3 @@
+# moco-import
+
+_moco-import_ provides importing TensorFlow model file to _moco_ TensorFlow Dialect IR
diff --git a/compiler/moco/import/include/moco/GraphHelper.h b/compiler/moco/import/include/moco/GraphHelper.h
new file mode 100644
index 000000000..fad62af4e
--- /dev/null
+++ b/compiler/moco/import/include/moco/GraphHelper.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_GRAPH_HELPER_H__
+#define __MOCO_GRAPH_HELPER_H__
+
+#include <moco/IR/TFNode.h>
+
+#include <loco.h>
+
+namespace moco
+{
+
+/**
+ * @brief find_node_byname() will return a node with type T with given name
+ * in graph g
+ *
+ * @note this uses simple linear search, but can speed up with better
+ * algorithms when needed.
+ */
+template <typename T> T *find_node_byname(loco::Graph *g, const char *name)
+{
+ T *first_node = nullptr;
+ loco::Graph::NodeContext *nodes = g->nodes();
+ uint32_t count = nodes->size();
+
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ auto tfnode = dynamic_cast<TFNode *>(nodes->at(i));
+ if (tfnode != nullptr)
+ {
+ if (tfnode->name() == name)
+ {
+ // if tfnode is NOT type of T then return will be nullptr
+ // this is OK cause the user wanted to get type T but it isn't
+ return dynamic_cast<T *>(tfnode);
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+} // namespace moco
+
+#endif // __MOCO_GRAPH_HELPER_H__
diff --git a/compiler/moco/import/include/moco/Import/GraphBuilder.h b/compiler/moco/import/include/moco/Import/GraphBuilder.h
new file mode 100644
index 000000000..c19918def
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/GraphBuilder.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_GRAPH_BUILDER_H__
+#define __MOCO_IMPORT_GRAPH_BUILDER_H__
+
+#include "GraphBuilderContext.h"
+
+#include <tensorflow/core/framework/graph.pb.h>
+
+namespace moco
+{
+
+/**
+ * @brief Interface of convert TF NodeDef to loco::Node (e.g., Conv2DGraphBuilder)
+ */
+class GraphBuilder
+{
+public:
+ virtual bool validate(const tensorflow::NodeDef &) const = 0;
+ virtual void build(const tensorflow::NodeDef &, GraphBuilderContext *) const = 0;
+ virtual ~GraphBuilder() {}
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_GRAPH_BUILDER_H__
diff --git a/compiler/moco/import/include/moco/Import/GraphBuilderContext.h b/compiler/moco/import/include/moco/Import/GraphBuilderContext.h
new file mode 100644
index 000000000..ae4f02c2a
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/GraphBuilderContext.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_GRAPH_BUILDER_CONTEXT_H__
+#define __MOCO_IMPORT_GRAPH_BUILDER_CONTEXT_H__
+
+#include <moco/Names.h>
+
+#include <loco.h>
+
+#include <tensorflow/core/framework/graph.pb.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace moco
+{
+
+/**
+ * @brief Class to store and query tensorflow::NodeDef* with string name key
+ */
+class NodeDefTable
+{
+public:
+ /**
+ * @brief Registers a name with corresponding tensorflow::NodeDef*
+ */
+ void enroll(const std::string &node_name, const tensorflow::NodeDef *node);
+ /**
+ * @brief Queries enrolled(registered) with name and return node if found
+ * Will throw runtime_error if not found
+ */
+ const tensorflow::NodeDef *node(const std::string &node_name) const;
+
+private:
+ using MapNameNode_t = std::map<std::string, const tensorflow::NodeDef *>;
+
+ MapNameNode_t _table;
+};
+
+/**
+ * @brief Class to store and query loco::Node* with string name key
+ */
+class SymbolTable
+{
+public:
+ /**
+ * @brief Registers a name with corresponding loco::Node *
+ */
+ void enroll(const TensorName &tensor_name, loco::Node *node);
+ /**
+ * @brief Queries enrolled(registered) with name and return node if found
+ * Will throw runtime_error if not found
+ */
+ loco::Node *node(const TensorName &tensor_name) const;
+
+private:
+ using MapNameNode_t = std::map<TensorName, loco::Node *, TensorNameCompare>;
+
+ MapNameNode_t _table;
+};
+
+/**
+ * @brief Interface to connect the graph
+ */
+class GraphUpdate
+{
+public:
+ virtual ~GraphUpdate() = default;
+
+public:
+ /**
+ * @brief Do the graph input connections using the SymbolTable
+ */
+ virtual void input(const SymbolTable *) const = 0;
+};
+
+/**
+ * @brief Class to store GraphUpdate objects
+ */
+class UpdateQueue final
+{
+public:
+ /**
+ * @brief Registers GraphUpdate objects
+ */
+ void enroll(std::unique_ptr<GraphUpdate> &&update);
+
+public:
+ using Queue = std::vector<std::unique_ptr<GraphUpdate>>;
+
+ const Queue &queue() const { return _queue; }
+
+private:
+ Queue _queue;
+};
+
+/**
+ * @brief Class to store context to build loco graph IR from TensorFlow
+ */
+class GraphBuilderContext
+{
+public:
+ GraphBuilderContext(loco::Graph *g, NodeDefTable *nodedef, SymbolTable *tensor_names,
+ UpdateQueue *updates)
+ : _g(g), _nodedef(nodedef), _tensor_names(tensor_names), _updates(updates)
+ {
+ // DO NOTHING
+ }
+
+ GraphBuilderContext(const GraphBuilderContext &) = delete;
+ GraphBuilderContext(GraphBuilderContext &&) = delete;
+
+public:
+ loco::Graph *graph() { return _g; }
+ NodeDefTable *nodedef() { return _nodedef; }
+ SymbolTable *tensor_names() { return _tensor_names; }
+ UpdateQueue *updates() { return _updates; }
+
+private:
+ loco::Graph *_g;
+ NodeDefTable *_nodedef;
+ SymbolTable *_tensor_names;
+ UpdateQueue *_updates;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_GRAPH_BUILDER_CONTEXT_H__
diff --git a/compiler/moco/import/include/moco/Import/GraphBuilderRegistry.h b/compiler/moco/import/include/moco/Import/GraphBuilderRegistry.h
new file mode 100644
index 000000000..da65cffb8
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/GraphBuilderRegistry.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_GRAPH_BUILDER_REGISTRY_H__
+#define __MOCO_IMPORT_GRAPH_BUILDER_REGISTRY_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+#include <map>
+#include <memory>
+#include <string>
+
+namespace moco
+{
+
+struct GraphBuilderSource
+{
+ virtual ~GraphBuilderSource() = default;
+
+ /**
+ * @brief Returns registered GraphBuilder pointer for operator (nullptr if not present)
+ */
+ virtual const GraphBuilder *lookup(const std::string &op) const = 0;
+};
+
+/**
+ * @brief Class to return graph builder for TF nodes
+ */
+class GraphBuilderRegistry final : public GraphBuilderSource
+{
+public:
+ GraphBuilderRegistry();
+
+public:
+ GraphBuilderRegistry(const GraphBuilderSource *parent) : _parent{parent}
+ {
+ // DO NOTHING
+ }
+
+public:
+ /**
+ * @brief Returns registered GraphBuilder pointer for operator or
+ * nullptr if not registered
+ */
+ const GraphBuilder *lookup(const std::string &op) const final
+ {
+ if (_builder_map.find(op) == _builder_map.end())
+ return (_parent == nullptr) ? nullptr : _parent->lookup(op);
+
+ return _builder_map.at(op).get();
+ }
+
+ static GraphBuilderRegistry &get()
+ {
+ static GraphBuilderRegistry me;
+ return me;
+ }
+
+public:
+ void add(const std::string op, std::unique_ptr<GraphBuilder> &&builder)
+ {
+ _builder_map[op] = std::move(builder);
+ }
+
+private:
+ const GraphBuilderSource *_parent = nullptr;
+
+private:
+ std::map<const std::string, std::unique_ptr<GraphBuilder>> _builder_map;
+};
+
+} // namespace mono
+
+#endif // __MOCO_IMPORT_GRAPH_BUILDER_REGISTRY_H__
diff --git a/compiler/moco/import/include/moco/Import/ModelSignature.h b/compiler/moco/import/include/moco/Import/ModelSignature.h
new file mode 100644
index 000000000..0db7c2795
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/ModelSignature.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_MODELSIGNATURE_H__
+#define __MOCO_IMPORT_MODELSIGNATURE_H__
+
+#include <moco/Names.h>
+
+#include <loco.h>
+#include <angkor/TensorShape.h>
+
+#include <string>
+#include <vector>
+
+namespace moco
+{
+
+/**
+ * @brief Class to store information to run a model. Normally this info comes from users
+ * via CLI params or configuration file.
+ */
+struct ModelSignature
+{
+public:
+ void add_input(const TensorName &input) { _inputs.push_back(input); }
+ void add_input(const TensorName &&input) { _inputs.push_back(input); }
+ void add_output(const TensorName &output) { _outputs.push_back(output); }
+ void add_output(const TensorName &&output) { _outputs.push_back(output); }
+
+ const std::vector<TensorName> &inputs() const { return _inputs; }
+ const std::vector<TensorName> &outputs() const { return _outputs; }
+
+ /**
+ * @brief Adds customop op type (not name of node) provided from user
+ */
+ void add_customop(const std::string &op);
+ const std::vector<std::string> &customops() const { return _customops; }
+
+ /**
+ * @brief Adds node name and its shape provided from user
+ */
+ void shape(const std::string &node_name, const angkor::TensorShape &shape);
+ const angkor::TensorShape *shape(const std::string &node_name) const;
+
+ /**
+ * @brief Adds node name and its dtype provided from user
+ */
+ void dtype(const std::string &node_name, loco::DataType dtype);
+ loco::DataType dtype(const std::string &node_name) const;
+
+private:
+ std::vector<TensorName> _inputs; // graph inputs
+ std::vector<TensorName> _outputs; // graph outputs
+
+ // For custom op types passed from user (e.g., via CLI)
+ std::vector<std::string> _customops;
+
+ // For and node names and shapes passed from user (e.g., via CLI)
+ std::map<std::string, angkor::TensorShape> _shapes;
+
+ // For and node names and dtype passed from user (e.g., via CLI)
+ std::map<std::string, loco::DataType> _dtypes;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_MODELSIGNATURE_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes.h b/compiler/moco/import/include/moco/Import/Nodes.h
new file mode 100644
index 000000000..8c940a28c
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_NODES_H__
+#define __MOCO_IMPORT_NODES_H__
+
+#include "Nodes/Add.h"
+#include "Nodes/AvgPool.h"
+#include "Nodes/BiasAdd.h"
+#include "Nodes/Concat.h"
+#include "Nodes/Const.h"
+#include "Nodes/Conv2DBackpropInput.h"
+#include "Nodes/Conv2D.h"
+#include "Nodes/DepthwiseConv2dNative.h"
+#include "Nodes/FakeQuantWithMinMaxVars.h"
+#include "Nodes/FusedBatchNorm.h"
+#include "Nodes/Identity.h"
+#include "Nodes/Maximum.h"
+#include "Nodes/MaxPool.h"
+#include "Nodes/Mean.h"
+#include "Nodes/Mul.h"
+#include "Nodes/Pack.h"
+#include "Nodes/Pad.h"
+#include "Nodes/Placeholder.h"
+#include "Nodes/RealDiv.h"
+#include "Nodes/Relu6.h"
+#include "Nodes/Relu.h"
+#include "Nodes/Reshape.h"
+#include "Nodes/Rsqrt.h"
+#include "Nodes/Shape.h"
+#include "Nodes/Softmax.h"
+#include "Nodes/Sqrt.h"
+#include "Nodes/SquaredDifference.h"
+#include "Nodes/Squeeze.h"
+#include "Nodes/StopGradient.h"
+#include "Nodes/StridedSlice.h"
+#include "Nodes/Sub.h"
+#include "Nodes/Tanh.h"
+
+#endif // __MOCO_IMPORT_NODES_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Add.h b/compiler/moco/import/include/moco/Import/Nodes/Add.h
new file mode 100644
index 000000000..3d0d0f30f
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Add.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_ADD_H__
+#define __MOCO_IMPORT_OP_ADD_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for Add node
+ */
+class AddGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_ADD_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/AvgPool.h b/compiler/moco/import/include/moco/Import/Nodes/AvgPool.h
new file mode 100644
index 000000000..4c8087afe
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/AvgPool.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_AVG_POOL_H__
+#define __MOCO_IMPORT_OP_AVG_POOL_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+class AvgPoolGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const final;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_AVG_POOL_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/BiasAdd.h b/compiler/moco/import/include/moco/Import/Nodes/BiasAdd.h
new file mode 100644
index 000000000..214df03de
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/BiasAdd.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_BIAS_ADD_H__
+#define __MOCO_IMPORT_OP_BIAS_ADD_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+class BiasAddGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const final;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_BIAS_ADD_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Concat.h b/compiler/moco/import/include/moco/Import/Nodes/Concat.h
new file mode 100644
index 000000000..2341fb00c
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Concat.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_CONCAT_H__
+#define __MOCO_IMPORT_OP_CONCAT_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+class ConcatV2GraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const final;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_CONCAT_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Const.h b/compiler/moco/import/include/moco/Import/Nodes/Const.h
new file mode 100644
index 000000000..1ce378219
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Const.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_CONST_H__
+#define __MOCO_IMPORT_OP_CONST_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+class ConstGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const final;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_CONST_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Conv2D.h b/compiler/moco/import/include/moco/Import/Nodes/Conv2D.h
new file mode 100644
index 000000000..3bd3dc74a
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Conv2D.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_CONV_2D_H__
+#define __MOCO_IMPORT_OP_CONV_2D_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+class Conv2DGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const final;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_CONV_2D_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Conv2DBackpropInput.h b/compiler/moco/import/include/moco/Import/Nodes/Conv2DBackpropInput.h
new file mode 100644
index 000000000..262a443fe
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Conv2DBackpropInput.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_CONV_2D_BACKPROP_INPUT_H__
+#define __MOCO_IMPORT_OP_CONV_2D_BACKPROP_INPUT_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for Conv2DBackpropInput node
+ */
+class Conv2DBackpropInputGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_CONV_2D_BACKPROP_INPUT_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/DepthwiseConv2dNative.h b/compiler/moco/import/include/moco/Import/Nodes/DepthwiseConv2dNative.h
new file mode 100644
index 000000000..1dcbba1eb
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/DepthwiseConv2dNative.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_DEPTHWISE_CONV_2D_NATIVE_H__
+#define __MOCO_IMPORT_OP_DEPTHWISE_CONV_2D_NATIVE_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for DepthwiseConv2dNative node
+ */
+class DepthwiseConv2dNativeGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_DEPTHWISE_CONV_2D_NATIVE_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/FakeQuantWithMinMaxVars.h b/compiler/moco/import/include/moco/Import/Nodes/FakeQuantWithMinMaxVars.h
new file mode 100644
index 000000000..9e223c18e
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/FakeQuantWithMinMaxVars.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_FAKE_QUANT_WITH_MINMAX_VARS_H__
+#define __MOCO_IMPORT_OP_FAKE_QUANT_WITH_MINMAX_VARS_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for FakeQuantWithMinMaxVars node
+ */
+class FakeQuantWithMinMaxVarsGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_FAKE_QUANT_WITH_MINMAX_VARS_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/FusedBatchNorm.h b/compiler/moco/import/include/moco/Import/Nodes/FusedBatchNorm.h
new file mode 100644
index 000000000..38d1d5682
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/FusedBatchNorm.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_FUSED_BATCH_NORM_H__
+#define __MOCO_IMPORT_OP_FUSED_BATCH_NORM_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for FusedBatchNorm node
+ */
+class FusedBatchNormGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_FUSED_BATCH_NORM_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Identity.h b/compiler/moco/import/include/moco/Import/Nodes/Identity.h
new file mode 100644
index 000000000..29e04800f
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Identity.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_IDENTITY_H__
+#define __MOCO_IMPORT_OP_IDENTITY_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+class IdentityGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const final;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_IDENTITY_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/MaxPool.h b/compiler/moco/import/include/moco/Import/Nodes/MaxPool.h
new file mode 100644
index 000000000..696fa71e6
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/MaxPool.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_MAX_POOL_H__
+#define __MOCO_IMPORT_OP_MAX_POOL_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+class MaxPoolGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const final;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_MAX_POOL_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Maximum.h b/compiler/moco/import/include/moco/Import/Nodes/Maximum.h
new file mode 100644
index 000000000..69d897742
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Maximum.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_MAXIMUM_H__
+#define __MOCO_IMPORT_OP_MAXIMUM_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for Maximum node
+ */
+class MaximumGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_MAXIMUM_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Mean.h b/compiler/moco/import/include/moco/Import/Nodes/Mean.h
new file mode 100644
index 000000000..7bae1bb39
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Mean.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_MEAN_H__
+#define __MOCO_IMPORT_OP_MEAN_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for Mean node
+ */
+class MeanGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_MEAN_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Mul.h b/compiler/moco/import/include/moco/Import/Nodes/Mul.h
new file mode 100644
index 000000000..667c81954
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Mul.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_MUL_H__
+#define __MOCO_IMPORT_OP_MUL_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for Mul node
+ */
+class MulGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_MUL_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Pack.h b/compiler/moco/import/include/moco/Import/Nodes/Pack.h
new file mode 100644
index 000000000..94666ad51
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Pack.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_PACK_H__
+#define __MOCO_IMPORT_OP_PACK_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+class PackGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const final;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_PACK_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Pad.h b/compiler/moco/import/include/moco/Import/Nodes/Pad.h
new file mode 100644
index 000000000..22eab32ac
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Pad.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_PAD_H__
+#define __MOCO_IMPORT_OP_PAD_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for Pad node
+ */
+class PadGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_PAD_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Placeholder.h b/compiler/moco/import/include/moco/Import/Nodes/Placeholder.h
new file mode 100644
index 000000000..458600915
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Placeholder.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_PLACEHOLDER_H__
+#define __MOCO_IMPORT_OP_PLACEHOLDER_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for Placeholder node
+ */
+class PlaceholderGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_PLACEHOLDER_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/RealDiv.h b/compiler/moco/import/include/moco/Import/Nodes/RealDiv.h
new file mode 100644
index 000000000..142e8b5f8
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/RealDiv.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_REALDIV_H__
+#define __MOCO_IMPORT_OP_REALDIV_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for RealDiv node
+ */
+class RealDivGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_REALDIV_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Relu.h b/compiler/moco/import/include/moco/Import/Nodes/Relu.h
new file mode 100644
index 000000000..0bd9cff04
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Relu.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_RELU_H__
+#define __MOCO_IMPORT_OP_RELU_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for Relu node
+ */
+class ReluGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const final;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_RELU_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Relu6.h b/compiler/moco/import/include/moco/Import/Nodes/Relu6.h
new file mode 100644
index 000000000..d211b0543
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Relu6.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_RELU6_H__
+#define __MOCO_IMPORT_OP_RELU6_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for Relu6 node
+ */
+class Relu6GraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const final;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_RELU6_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Reshape.h b/compiler/moco/import/include/moco/Import/Nodes/Reshape.h
new file mode 100644
index 000000000..e8bfeee23
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Reshape.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_RESHAPE_H__
+#define __MOCO_IMPORT_OP_RESHAPE_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for Reshape node
+ */
+class ReshapeGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_RESHAPE_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Rsqrt.h b/compiler/moco/import/include/moco/Import/Nodes/Rsqrt.h
new file mode 100644
index 000000000..dedc52323
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Rsqrt.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_RSQRT_H__
+#define __MOCO_IMPORT_OP_RSQRT_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for Rsqrt node
+ */
+class RsqrtGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_RSQRT_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Shape.h b/compiler/moco/import/include/moco/Import/Nodes/Shape.h
new file mode 100644
index 000000000..e36e1d546
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Shape.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_SHAPE_H__
+#define __MOCO_IMPORT_OP_SHAPE_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for Shape node
+ */
+class ShapeGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_SHAPE_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Softmax.h b/compiler/moco/import/include/moco/Import/Nodes/Softmax.h
new file mode 100644
index 000000000..43fbb8852
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Softmax.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_SOFTMAX_H__
+#define __MOCO_IMPORT_OP_SOFTMAX_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+* @brief GraphBuilder for Softmax node
+*/
+class SoftmaxGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_SOFTMAX_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Sqrt.h b/compiler/moco/import/include/moco/Import/Nodes/Sqrt.h
new file mode 100644
index 000000000..d17dc3494
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Sqrt.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_SQRT_H__
+#define __MOCO_IMPORT_OP_SQRT_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for Sqrt node
+ */
+class SqrtGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_SQRT_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/SquaredDifference.h b/compiler/moco/import/include/moco/Import/Nodes/SquaredDifference.h
new file mode 100644
index 000000000..501464d65
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/SquaredDifference.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_SQUARED_DIFFERENCE_H__
+#define __MOCO_IMPORT_OP_SQUARED_DIFFERENCE_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for SquaredDifference node
+ */
+class SquaredDifferenceGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_SQUARED_DIFFERENCE_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Squeeze.h b/compiler/moco/import/include/moco/Import/Nodes/Squeeze.h
new file mode 100644
index 000000000..64ead074b
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Squeeze.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_SQUEEZE_H__
+#define __MOCO_IMPORT_OP_SQUEEZE_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for Squeeze node
+ */
+class SqueezeGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_SQUEEZE_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/StopGradient.h b/compiler/moco/import/include/moco/Import/Nodes/StopGradient.h
new file mode 100644
index 000000000..e547a8a8b
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/StopGradient.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_STOP_GRADIENT_H__
+#define __MOCO_IMPORT_OP_STOP_GRADIENT_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for StopGradient node
+ */
+class StopGradientGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_STOP_GRADIENT_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/StridedSlice.h b/compiler/moco/import/include/moco/Import/Nodes/StridedSlice.h
new file mode 100644
index 000000000..61170ebbf
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/StridedSlice.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_STRIDEDSLICE_H__
+#define __MOCO_IMPORT_OP_STRIDEDSLICE_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+class StridedSliceGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const final;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_STRIDEDSLICE_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Sub.h b/compiler/moco/import/include/moco/Import/Nodes/Sub.h
new file mode 100644
index 000000000..d6351e34a
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Sub.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_SUB_H__
+#define __MOCO_IMPORT_OP_SUB_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for Sub node
+ */
+class SubGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_SUB_H__
diff --git a/compiler/moco/import/include/moco/Import/Nodes/Tanh.h b/compiler/moco/import/include/moco/Import/Nodes/Tanh.h
new file mode 100644
index 000000000..183e117ef
--- /dev/null
+++ b/compiler/moco/import/include/moco/Import/Nodes/Tanh.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORT_OP_TANH_H__
+#define __MOCO_IMPORT_OP_TANH_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+namespace moco
+{
+
+/**
+ * @brief GraphBuilder for Tanh node
+ */
+class TanhGraphBuilder final : public GraphBuilder
+{
+public:
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORT_OP_TANH_H__
diff --git a/compiler/moco/import/include/moco/Importer.h b/compiler/moco/import/include/moco/Importer.h
new file mode 100644
index 000000000..ee0660c52
--- /dev/null
+++ b/compiler/moco/import/include/moco/Importer.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IMPORTER_H__
+#define __MOCO_IMPORTER_H__
+
+#include "moco/Import/ModelSignature.h"
+#include "moco/Import/GraphBuilderRegistry.h"
+
+#include <moco/Names.h>
+
+#include <loco.h>
+
+#include <tensorflow/core/framework/graph.pb.h>
+
+#include <memory>
+
+namespace moco
+{
+
+class Importer final
+{
+public:
+ Importer();
+
+public:
+ explicit Importer(const GraphBuilderSource *source) : _source{source}
+ {
+ // DO NOTHING
+ }
+
+public:
+ std::unique_ptr<loco::Graph> import(const ModelSignature &, tensorflow::GraphDef &) const;
+
+private:
+ const GraphBuilderSource *_source = nullptr;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IMPORTER_H__
diff --git a/compiler/moco/import/src/Convert.cpp b/compiler/moco/import/src/Convert.cpp
new file mode 100644
index 000000000..6285f5eab
--- /dev/null
+++ b/compiler/moco/import/src/Convert.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 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 "Convert.h"
+
+#include <algorithm>
+#include <cctype>
+
+// TODO move to some common file
+namespace moco
+{
+
+std::string str_toupper(std::string s)
+{
+ // from https://en.cppreference.com/w/cpp/string/byte/toupper
+ std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::toupper(c); });
+ return s;
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Convert.h b/compiler/moco/import/src/Convert.h
new file mode 100644
index 000000000..77dab3700
--- /dev/null
+++ b/compiler/moco/import/src/Convert.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 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 __CONVERT_H__
+#define __CONVERT_H__
+
+#include <string>
+
+// TODO move to some common file
+namespace moco
+{
+
+std::string str_toupper(std::string s);
+
+} // namespace moco
+
+#endif // __CONVERT_H__
diff --git a/compiler/moco/import/src/GraphBuilderContext.cpp b/compiler/moco/import/src/GraphBuilderContext.cpp
new file mode 100644
index 000000000..bbc1d8bd0
--- /dev/null
+++ b/compiler/moco/import/src/GraphBuilderContext.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/GraphBuilderContext.h"
+
+#include <oops/UserExn.h>
+
+#include <stdexcept>
+#include <string>
+
+namespace moco
+{
+
+void NodeDefTable::enroll(const std::string &node_name, const tensorflow::NodeDef *node)
+{
+ MapNameNode_t::iterator iter = _table.find(node_name);
+
+ if (iter != _table.end())
+ {
+ throw oops::UserExn("Duplicate node name in GraphDef", node_name);
+ }
+
+ _table[node_name] = node;
+}
+
+const tensorflow::NodeDef *NodeDefTable::node(const std::string &node_name) const
+{
+ MapNameNode_t::const_iterator iter = _table.find(node_name);
+
+ if (iter == _table.end())
+ {
+ throw oops::UserExn("Cannot find node with name in GraphDef", node_name);
+ }
+
+ return iter->second;
+}
+
+void SymbolTable::enroll(const TensorName &tensor_name, loco::Node *node)
+{
+ MapNameNode_t::iterator iter = _table.find(tensor_name);
+
+ if (iter != _table.end())
+ {
+ throw oops::UserExn("Duplicate node name in GraphDef", tensor_name.name());
+ }
+
+ _table[tensor_name] = node;
+}
+
+loco::Node *SymbolTable::node(const TensorName &tensor_name) const
+{
+ MapNameNode_t::const_iterator iter = _table.find(tensor_name);
+
+ if (iter == _table.end())
+ {
+ throw oops::UserExn("Cannot find node with name in GraphDef", tensor_name.name());
+ }
+
+ return iter->second;
+}
+
+void UpdateQueue::enroll(std::unique_ptr<GraphUpdate> &&update)
+{
+ _queue.push_back(std::move(update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/GraphBuilderContext.test.cpp b/compiler/moco/import/src/GraphBuilderContext.test.cpp
new file mode 100644
index 000000000..51f6db245
--- /dev/null
+++ b/compiler/moco/import/src/GraphBuilderContext.test.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/GraphBuilderContext.h"
+#include <moco/Names.h>
+
+#include <loco.h>
+
+#include <oops/UserExn.h>
+
+#include <gtest/gtest.h>
+
+TEST(GraphBuilderContext, ctor)
+{
+ auto graph = loco::make_graph();
+ moco::NodeDefTable nodedef;
+ moco::SymbolTable nodes;
+ moco::UpdateQueue updates;
+
+ moco::GraphBuilderContext context(graph.get(), &nodedef, &nodes, &updates);
+
+ ASSERT_EQ(context.graph(), graph.get());
+ ASSERT_EQ(context.nodedef(), &nodedef);
+ ASSERT_EQ(context.tensor_names(), &nodes);
+ ASSERT_EQ(context.updates(), &updates);
+}
+
+TEST(SymbolTable, node_name)
+{
+ moco::SymbolTable table;
+ loco::Pull pull_node;
+ moco::TensorName name("input", 0);
+ moco::TensorName invalid("invalid", 0);
+
+ table.enroll(name, &pull_node);
+ ASSERT_EQ(table.node(name), &pull_node);
+ // duplicate name should throw
+ EXPECT_THROW(table.enroll(name, &pull_node), oops::UserExn);
+ // unregistered name should throw
+ EXPECT_THROW(table.node(invalid), oops::UserExn);
+}
+
+namespace
+{
+
+class TestGraphUpdate final : public moco::GraphUpdate
+{
+public:
+ void input(const moco::SymbolTable *) const override;
+};
+
+void TestGraphUpdate::input(const moco::SymbolTable *) const {}
+
+} // namespace
+
+TEST(GraphUpdateQueue, queue)
+{
+ std::unique_ptr<TestGraphUpdate> update(new TestGraphUpdate());
+ moco::UpdateQueue updates;
+
+ updates.enroll(std::move(update));
+ auto &queue = updates.queue();
+ ASSERT_EQ(queue.size(), 1);
+}
diff --git a/compiler/moco/import/src/GraphBuilderRegistry.cpp b/compiler/moco/import/src/GraphBuilderRegistry.cpp
new file mode 100644
index 000000000..3a028513f
--- /dev/null
+++ b/compiler/moco/import/src/GraphBuilderRegistry.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/GraphBuilderRegistry.h"
+#include "moco/Import/Nodes.h"
+
+#include <stdex/Memory.h>
+
+namespace moco
+{
+
+GraphBuilderRegistry::GraphBuilderRegistry()
+{
+ add("Add", stdex::make_unique<AddGraphBuilder>());
+ add("AvgPool", stdex::make_unique<AvgPoolGraphBuilder>());
+ add("BiasAdd", stdex::make_unique<BiasAddGraphBuilder>());
+ add("ConcatV2", stdex::make_unique<ConcatV2GraphBuilder>());
+ add("Const", stdex::make_unique<ConstGraphBuilder>());
+ add("Conv2D", stdex::make_unique<Conv2DGraphBuilder>());
+ add("Conv2DBackpropInput", stdex::make_unique<Conv2DBackpropInputGraphBuilder>());
+ add("DepthwiseConv2dNative", stdex::make_unique<DepthwiseConv2dNativeGraphBuilder>());
+ add("FakeQuantWithMinMaxVars", stdex::make_unique<FakeQuantWithMinMaxVarsGraphBuilder>());
+ add("FusedBatchNorm", stdex::make_unique<FusedBatchNormGraphBuilder>());
+ add("Identity", stdex::make_unique<IdentityGraphBuilder>());
+ add("Maximum", stdex::make_unique<MaximumGraphBuilder>());
+ add("MaxPool", stdex::make_unique<MaxPoolGraphBuilder>());
+ add("Mean", stdex::make_unique<MeanGraphBuilder>());
+ add("Mul", stdex::make_unique<MulGraphBuilder>());
+ add("Pack", stdex::make_unique<PackGraphBuilder>());
+ add("Pad", stdex::make_unique<PadGraphBuilder>());
+ add("Placeholder", stdex::make_unique<PlaceholderGraphBuilder>());
+ add("RealDiv", stdex::make_unique<RealDivGraphBuilder>());
+ add("Relu", stdex::make_unique<ReluGraphBuilder>());
+ add("Relu6", stdex::make_unique<Relu6GraphBuilder>());
+ add("Reshape", stdex::make_unique<ReshapeGraphBuilder>());
+ add("Rsqrt", stdex::make_unique<RsqrtGraphBuilder>());
+ add("Shape", stdex::make_unique<ShapeGraphBuilder>());
+ add("Softmax", stdex::make_unique<SoftmaxGraphBuilder>());
+ add("Sqrt", stdex::make_unique<SqrtGraphBuilder>());
+ add("SquaredDifference", stdex::make_unique<SquaredDifferenceGraphBuilder>());
+ add("Squeeze", stdex::make_unique<SqueezeGraphBuilder>());
+ add("StopGradient", stdex::make_unique<StopGradientGraphBuilder>());
+ add("StridedSlice", stdex::make_unique<StridedSliceGraphBuilder>());
+ add("Sub", stdex::make_unique<SubGraphBuilder>());
+ add("Tanh", stdex::make_unique<TanhGraphBuilder>());
+
+ // Virtual node like `TFPush` need not to be added here
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Importer.cpp b/compiler/moco/import/src/Importer.cpp
new file mode 100644
index 000000000..3813affce
--- /dev/null
+++ b/compiler/moco/import/src/Importer.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Importer.h"
+#include "moco/Import/GraphBuilder.h"
+#include "moco/Import/GraphBuilderContext.h"
+
+#include "moco/Import/GraphBuilderRegistry.h"
+
+#include <moco/IR/Nodes/TFPlaceholder.h>
+#include <moco/IR/TFNode.h>
+
+#include <stdex/Memory.h>
+#include <oops/UserExn.h>
+
+#include <cassert>
+#include <sstream>
+#include <stdexcept>
+
+namespace
+{
+
+void convert_graph(const moco::GraphBuilderSource &source, const moco::ModelSignature &signature,
+ tensorflow::GraphDef &tf_graph_def, loco::Graph *graph)
+{
+ auto nodedef = stdex::make_unique<moco::NodeDefTable>();
+ auto tensor_names = stdex::make_unique<moco::SymbolTable>();
+ auto updates = stdex::make_unique<moco::UpdateQueue>();
+
+ moco::GraphBuilderContext gb_context(graph, nodedef.get(), tensor_names.get(), updates.get());
+
+ // Building a loco graph
+ // 1. Convert all the nodes to moco::TFNode
+ // 2. Connect inputs: set all node input(from a string) to actual node object
+ // 3. Set graph input
+ // 4. Create moco::TFPush node and set graph output
+
+ /**
+ * @brief Prepare tensorflow::NodeDef search table from name
+ */
+ for (const auto &n : tf_graph_def.node())
+ {
+ nodedef->enroll(n.name(), &n);
+ }
+
+ /**
+ * @brief 1. Convert all the nodes to moco::TFNode
+ *
+ * @note In each build for a TF node, four things happen
+ * 1) create corresponding moco::TFNode(s)
+ * 2) read and set the attributes to created moco::TFNode(s)
+ * 3) register name-moco::TFNode(last one of Nodes) that will be used as the output
+ * 4) queue a task to set the input of the moco::TFNode(first one of the Nodes)
+ * this is done only for required nodes depending on the operator
+ *
+ * @example Placeholder("in") - Identity("out")
+ * %1 = Placeholder --> 0x1001 (moco::TFNode* object address)
+ * (symboltable: register %1, after the registeration table will contain as below;
+ * "in" : 0x1001
+ * )
+ * (queue: this will be empty as Pull does not queue a task to set input;
+ * )
+ *
+ * %2 = Forward --> 0x1002
+ * (symboltable: register %2 and table will look like below;
+ * "in" : 0x1001
+ * "out" : 0x1002
+ * )
+ * (queue: Forward will queue a task with input "in";
+ * 0x1002: {"in"}
+ * )
+ */
+ for (const auto &n : tf_graph_def.node())
+ {
+ if (const auto *graph_builder = source.lookup(n.op()))
+ {
+ if (!graph_builder->validate(n))
+ {
+ throw oops::UserExn("Invalid operator", n.op());
+ }
+
+ graph_builder->build(n, &gb_context);
+ }
+ else
+ {
+ throw oops::UserExn("Not supported", n.op());
+ }
+ }
+
+ /**
+ * @brief 2. Connect inputs: Iterate updates and call each update input method
+ *
+ * @note Continue from above example graph, connecting inputs is done in following steps
+ * a) iterate queue
+ * b) call the input method for each update
+ * c) each update has the moco::TFNode *node and names of the input to connect
+ * node = 0x1002 and names = {"in"}
+ * d) from symbol table, "in" will return 0x1001
+ * e) set input of 0x1002 with 0x1001
+ */
+ for (auto &update : updates->queue())
+ {
+ update->input(tensor_names.get());
+ }
+
+ /**
+ * @brief 3. Set graph input
+ */
+ for (auto input : signature.inputs())
+ {
+ auto node = tensor_names->node(input);
+ assert(node != nullptr);
+
+ auto graph_input = graph->inputs()->create();
+
+ auto placeholder_node = loco::must_cast<moco::TFPlaceholder *>(node);
+
+ graph_input->name(input.nodeName());
+
+ // annotate index that should be passed to loco::Pull
+ moco::index(placeholder_node, graph_input->index());
+
+ // This implementation works as "PlaceholderGraphBuilder in Nodes/Placeholder.cpp"
+ // accepts only TF_FLOAT32 as of now.
+ //
+ // TODO Support other types
+ graph_input->dtype(loco::DataType::FLOAT32);
+ }
+
+ /**
+ * @brief 4. Create moco::TFPush node and set graph output
+ */
+ for (auto output : signature.outputs())
+ {
+ auto output_node = tensor_names->node(output);
+ assert(output_node);
+
+ // create moco::TFPush for output of graph
+ auto push_node = graph->nodes()->create<moco::TFPush>();
+ push_node->from(output_node); // set input of TFPush to output node
+
+ // set the graph output name and node object
+ auto graph_output = graph->outputs()->create();
+ graph_output->name(output.nodeName());
+ push_node->index(graph_output->index());
+
+ // TODO Support other types
+ graph_output->dtype(loco::DataType::FLOAT32);
+ }
+
+ // validate graph
+ assert(loco::valid(graph));
+}
+
+} // namespace
+
+namespace moco
+{
+
+Importer::Importer()
+{
+ // DO NOTHING
+}
+
+std::unique_ptr<loco::Graph> Importer::import(const ModelSignature &signature,
+ tensorflow::GraphDef &tf_graph_def) const
+{
+ auto graph = loco::make_graph();
+
+ const GraphBuilderSource *source_ptr = &moco::GraphBuilderRegistry::get();
+
+ if (_source != nullptr)
+ {
+ // Use user-defined GraphBuilderSource
+ source_ptr = _source;
+ }
+
+ convert_graph(*source_ptr, signature, tf_graph_def, graph.get());
+
+ return std::move(graph);
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Importer.test.cpp b/compiler/moco/import/src/Importer.test.cpp
new file mode 100644
index 000000000..23873390c
--- /dev/null
+++ b/compiler/moco/import/src/Importer.test.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Importer.h"
+#include "moco/GraphHelper.h"
+
+#include <moco/IR/Nodes/TFIdentity.h>
+
+#include "TestHelper.h"
+#include <loco.h>
+#include <plier/tf/TestHelper.h>
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+TEST(TensorFlowImport, Dummy) { moco::Importer import; }
+
+namespace
+{
+
+// clang-format off
+const char *basic_pbtxtdata = STRING_CONTENT(
+node {
+ name: "Placeholder"
+ op: "Placeholder"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "shape"
+ value {
+ shape {
+ dim {
+ size: 1
+ }
+ dim {
+ size: 2
+ }
+ dim {
+ size: 1
+ }
+ dim {
+ size: 2
+ }
+ }
+ }
+ }
+}
+node {
+ name: "output/identity"
+ op: "Identity"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, load_model_withio_tf)
+{
+ moco::ModelSignature signature;
+
+ signature.add_input(moco::TensorName("Placeholder", 0));
+ signature.add_output(moco::TensorName("output/identity", 0));
+
+ tensorflow::GraphDef graph_def;
+ EXPECT_TRUE(plier::tf::parse_graphdef(basic_pbtxtdata, graph_def));
+
+ moco::Importer importer;
+
+ std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
+
+ // what to test:
+ // - import reads Placeholder
+ // - import reads Identity
+ // - attribute values should match
+
+ auto tfidentity = find_first_node_bytype<moco::TFIdentity>(graph.get());
+ ASSERT_NE(tfidentity, nullptr);
+ ASSERT_NE(tfidentity->input(), nullptr);
+}
+
+namespace
+{
+
+// clang-format off
+const char *query_pbtxtdata = STRING_CONTENT(
+node {
+ name: "Placeholder"
+ op: "Placeholder"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "shape"
+ value {
+ shape {
+ dim {
+ size: 1
+ }
+ dim {
+ size: 2
+ }
+ dim {
+ size: 1
+ }
+ dim {
+ size: 2
+ }
+ }
+ }
+ }
+}
+node {
+ name: "Foo/w_min"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value { type: DT_FLOAT }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape { }
+ float_val: -1.0
+ }
+ }
+ }
+}
+node {
+ name: "output/identity"
+ op: "Identity"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+node {
+ name: "Foo/w_max"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value { type: DT_FLOAT }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape { }
+ float_val: -1.0
+ }
+ }
+ }
+}
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, find_node_by_name)
+{
+ moco::ModelSignature signature;
+
+ signature.add_input(moco::TensorName("Placeholder", 0));
+ signature.add_output(moco::TensorName("output/identity", 0));
+
+ tensorflow::GraphDef graph_def;
+ EXPECT_TRUE(plier::tf::parse_graphdef(query_pbtxtdata, graph_def));
+
+ moco::Importer importer;
+
+ std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
+
+ // what to test:
+ // - get name of first Identity node
+ // - find node by name `Foo/w_min`
+ // - find node by name `Foo/w_max`
+
+ auto tfidentity = find_first_node_bytype<moco::TFIdentity>(graph.get());
+ ASSERT_NE(tfidentity, nullptr);
+ ASSERT_NE(tfidentity->input(), nullptr);
+ ASSERT_STREQ(tfidentity->name().c_str(), "output/identity");
+
+ auto query_node = moco::find_node_byname<moco::TFConst>(graph.get(), "Foo/w_min");
+ ASSERT_NE(query_node, nullptr);
+ ASSERT_STREQ(query_node->name().c_str(), "Foo/w_min");
+
+ auto query_node2 = moco::find_node_byname<moco::TFConst>(graph.get(), "Foo/w_max");
+ ASSERT_NE(query_node2, nullptr);
+ ASSERT_STREQ(query_node2->name().c_str(), "Foo/w_max");
+}
diff --git a/compiler/moco/import/src/ModelSignature.cpp b/compiler/moco/import/src/ModelSignature.cpp
new file mode 100644
index 000000000..d4c7e5085
--- /dev/null
+++ b/compiler/moco/import/src/ModelSignature.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/ModelSignature.h"
+
+#include <oops/UserExn.h>
+
+namespace moco
+{
+
+void ModelSignature::add_customop(const std::string &op)
+{
+ if (std::find(_customops.begin(), _customops.end(), op) == _customops.end())
+ _customops.emplace_back(op);
+ else
+ throw oops::UserExn("Duplicate custom operator", op);
+}
+
+void ModelSignature::shape(const std::string &node_name, const angkor::TensorShape &shape)
+{
+ if (_shapes.find(node_name) != _shapes.end())
+ throw oops::UserExn("Duplicate node name", node_name);
+
+ _shapes[node_name] = shape;
+}
+
+const angkor::TensorShape *ModelSignature::shape(const std::string &node_name) const
+{
+ auto res = _shapes.find(node_name);
+ if (res == _shapes.end())
+ return nullptr;
+ else
+ return &res->second;
+}
+
+void ModelSignature::dtype(const std::string &node_name, loco::DataType dtype)
+{
+ if (_dtypes.find(node_name) != _dtypes.end())
+ throw oops::UserExn("Duplicate node name", node_name);
+
+ _dtypes[node_name] = dtype;
+}
+
+loco::DataType ModelSignature::dtype(const std::string &node_name) const
+{
+ auto res = _dtypes.find(node_name);
+ if (res == _dtypes.end())
+ return loco::DataType::Unknown;
+ else
+ return res->second;
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Add.cpp b/compiler/moco/import/src/Nodes/Add.cpp
new file mode 100644
index 000000000..6981a55e1
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Add.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Add.h"
+
+#include <moco/IR/Nodes/TFAdd.h>
+
+#include <loco.h>
+#include <stdex/Memory.h>
+
+namespace
+{
+
+using namespace moco;
+
+/**
+ * @brief GraphUpdate for TF Add node
+ */
+class TFAddGraphUpdate final : public GraphUpdate
+{
+public:
+ TFAddGraphUpdate(TFAdd *node, std::vector<TensorName> names) : _node(node), _names(names) {}
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFAdd *_node;
+ std::vector<TensorName> _names;
+};
+
+void TFAddGraphUpdate::input(const SymbolTable *tensor_names) const
+{
+ assert(_names.size() == 2);
+
+ _node->x(tensor_names->node(_names[0]));
+ _node->y(tensor_names->node(_names[1]));
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool AddGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ return node.input_size() == 2;
+}
+
+void AddGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // creating TF dialect Add node
+ auto tf_add = graph->nodes()->create<TFAdd>();
+ tf_add->name(node.name());
+
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, tf_add);
+
+ std::vector<TensorName> add_input_names;
+ add_input_names.push_back(TensorName(node.input(0))); // x
+ add_input_names.push_back(TensorName(node.input(1))); // y
+
+ auto tf_add_update = stdex::make_unique<TFAddGraphUpdate>(tf_add, add_input_names);
+ updates->enroll(std::move(tf_add_update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Add.test.cpp b/compiler/moco/import/src/Nodes/Add.test.cpp
new file mode 100644
index 000000000..ace2b0801
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Add.test.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Add.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *add_basic_pbtxt = STRING_CONTENT(
+ name: "ADD_01"
+ op: "Add"
+ input: "input_01"
+ input: "input_02"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, tf_add_basic)
+{
+ TFNodeBuildTester tester;
+ moco::AddGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(add_basic_pbtxt, nodedef));
+
+ // what to test:
+ // - TFAdd node should exist
+ // - both inputs x() and y() should not be null
+
+ tester.inputs({"input_01", "input_02"});
+ tester.output("ADD_01");
+ tester.run(nodedef, graphbuilder);
+}
diff --git a/compiler/moco/import/src/Nodes/AvgPool.cpp b/compiler/moco/import/src/Nodes/AvgPool.cpp
new file mode 100644
index 000000000..6d7fd36bb
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/AvgPool.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/AvgPool.h"
+
+#include <moco/IR/Nodes/TFAvgPool.h>
+
+#include <moco/Names.h>
+
+#include "Convert.h"
+#include <loco/IR/PermutingCodec.h>
+#include <stdex/Memory.h>
+#include <plier/tf/Convert.h>
+#include <oops/UserExn.h>
+
+#include <cassert>
+#include <stdexcept>
+
+using namespace plier::tf;
+
+namespace
+{
+
+using namespace moco;
+
+class TFAvgPoolGraphUpdate final : public GraphUpdate
+{
+public:
+ TFAvgPoolGraphUpdate(TFAvgPool *node, const TensorName &name)
+ : _avgpool_node(node), _value_name(name)
+ {
+ }
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFAvgPool *_avgpool_node;
+ const TensorName _value_name;
+};
+
+void TFAvgPoolGraphUpdate::input(const SymbolTable *node_table) const
+{
+ loco::Node *value_node = node_table->node(_value_name);
+ _avgpool_node->value(value_node);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool AvgPoolGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ if (node.input_size() != 1)
+ return false;
+
+ // note: even though "data_format" is not entered when a model is written,
+ // TF seems to generate "data_format" field into a pb file
+ if (!plier::tf::has_attrs(node, {"T", "data_format", "ksize", "padding", "strides"}))
+ return false;
+
+ auto tf_ksize = get_list_attr(node, "ksize");
+ auto ksize = as_int64_list(tf_ksize);
+ if (ksize.size() != 4)
+ {
+ // TODO support ksize length for 1 and 2
+ throw oops::UserExn("AvgPool only supports ksize length 4", node.name());
+ }
+
+ auto tf_strides = get_list_attr(node, "strides");
+ auto strides = as_int64_list(tf_strides);
+ if (strides.size() != 4)
+ {
+ // TODO support strides length for 1 and 2
+ throw oops::UserExn("AvgPool only supports strides length 4", node.name());
+ }
+
+ return true;
+}
+
+void AvgPoolGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // name of loco nodes
+ ::std::string avgPool2d_name = node.name();
+
+ // tensorflow data_format: one of NHWC or NCHW.
+ auto data_layout = get_string_attr(node, "data_format");
+ auto avgPool_node = graph->nodes()->create<TFAvgPool>();
+ avgPool_node->name(node.name());
+ avgPool_node->data_layout(data_layout);
+
+ // padding
+ auto padding = moco::str_toupper(get_string_attr(node, "padding"));
+ avgPool_node->padding(padding);
+
+ // ksize
+ auto tf_ksize = get_list_attr(node, "ksize");
+ auto ksize = as_int64_list(tf_ksize);
+ avgPool_node->ksize(ksize);
+
+ // strides
+ auto tf_strides = get_list_attr(node, "strides");
+ auto strides = as_int64_list(tf_strides);
+ avgPool_node->strides(strides);
+
+ // To set the input node of encode_node with avgPool2d_name
+ TensorName output_name(avgPool2d_name, 0);
+ tensor_names->enroll(output_name, avgPool_node);
+
+ // Record ifm inputs to featureEncode_node
+ auto update = stdex::make_unique<TFAvgPoolGraphUpdate>(avgPool_node, TensorName(node.input(0)));
+
+ updates->enroll(std::move(update));
+}
+
+} // namespace moco
+
+// TODO Consider a case when TF AvgPool is for 3D.
+// AvgPool works for 2D and other Dimensions, such as 3D
+// So, in future, some other GraphBuilder decide if AvgPoolGraphBuilder is used or
+// other GraphBuilder is used for TF AvgPool
diff --git a/compiler/moco/import/src/Nodes/AvgPool.test.cpp b/compiler/moco/import/src/Nodes/AvgPool.test.cpp
new file mode 100644
index 000000000..7d62f0eaa
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/AvgPool.test.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/AvgPool.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *avgpool_01_pbtxtdata = STRING_CONTENT(
+ name: "avgpool"
+ op: "AvgPool"
+ input: "const/float"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "data_format"
+ value {
+ s: "NHWC"
+ }
+ }
+ attr {
+ key: "ksize"
+ value {
+ list {
+ i: 1
+ i: 2
+ i: 3
+ i: 1
+ }
+ }
+ }
+ attr {
+ key: "padding"
+ value {
+ s: "VALID"
+ }
+ }
+ attr {
+ key: "strides"
+ value {
+ list {
+ i: 1
+ i: 3
+ i: 2
+ i: 1
+ }
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, AvgPool_01)
+{
+ TFNodeBuildTester tester;
+ moco::AvgPoolGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(avgpool_01_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFAvgPool
+ // - input should exist
+ // - attributes value should match
+
+ tester.inputs({"const/float"});
+ tester.output("avgpool");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = dynamic_cast<moco::TFAvgPool *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->data_layout(), "NHWC");
+ ASSERT_EQ(test_node->padding(), "VALID");
+ ASSERT_EQ(test_node->ksize(), std::vector<int64_t>({1, 2, 3, 1}));
+ ASSERT_EQ(test_node->strides(), std::vector<int64_t>({1, 3, 2, 1}));
+}
diff --git a/compiler/moco/import/src/Nodes/BiasAdd.cpp b/compiler/moco/import/src/Nodes/BiasAdd.cpp
new file mode 100644
index 000000000..a3eb91116
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/BiasAdd.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/BiasAdd.h"
+
+#include <moco/IR/Nodes/TFBiasAdd.h>
+
+#include <moco/Names.h>
+
+#include <loco.h>
+#include <loco/IR/PermutingCodec.h>
+#include <stdex/Memory.h>
+#include <plier/tf/Convert.h>
+#include <oops/UserExn.h>
+
+#include <cassert>
+#include <vector>
+
+namespace
+{
+using namespace moco;
+
+class TFBiasAddGraphUpdate final : public GraphUpdate
+{
+public:
+ TFBiasAddGraphUpdate(TFBiasAdd *biasadd, std::vector<TensorName> &names)
+ : _biasadd(biasadd), _names(names)
+ {
+ }
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFBiasAdd *_biasadd;
+ std::vector<TensorName> _names;
+};
+
+void TFBiasAddGraphUpdate::input(const SymbolTable *node_table) const
+{
+ assert(_names.size() == 2);
+
+ auto value_node = node_table->node(_names[0]);
+ auto bias_node = node_table->node(_names[1]);
+ assert(value_node != nullptr);
+ assert(bias_node != nullptr);
+
+ _biasadd->value(value_node);
+ _biasadd->bias(bias_node);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool BiasAddGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ if (node.input_size() != 2)
+ return false;
+
+ // note: even though "data_format" is not entered when a model is written,
+ // TF seems to generate "data_format" field into a pb file
+ if (!plier::tf::has_attrs(node, {"T", "data_format"}))
+ return false;
+
+ // TODO add type check
+ // type of input and bias should be same (except using quantization)
+
+ // Note In case of TF.nn.bias_add,
+ // "value may have any number of dimensions." ...
+ // but "data_format: A string. 'NHWC' and 'NCHW' are supported."
+ // Not sure if value should be 4-D tensor. Let's skip this check for now.
+
+ auto data_layout = plier::tf::get_string_attr(node, "data_format");
+ if (!(data_layout == "NHWC" || data_layout == "NCHW"))
+ {
+ throw oops::UserExn("BiasAdd Unsupported data_format", node.name());
+ }
+
+ return true;
+}
+
+void BiasAddGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // tensorflow data_format: one of NHWC or NCHW.
+ auto data_layout = plier::tf::get_string_attr(node, "data_format");
+ auto tf_bias_add = graph->nodes()->create<TFBiasAdd>();
+ tf_bias_add->name(node.name());
+ tf_bias_add->data_layout(data_layout);
+
+ // To set the input node of encode_node with biasAdd_name
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, tf_bias_add);
+
+ std::vector<TensorName> input_names;
+ input_names.push_back(TensorName(node.input(0)));
+ input_names.push_back(TensorName(node.input(1)));
+
+ auto update = stdex::make_unique<TFBiasAddGraphUpdate>(tf_bias_add, input_names);
+ updates->enroll(std::move(update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/BiasAdd.test.cpp b/compiler/moco/import/src/Nodes/BiasAdd.test.cpp
new file mode 100644
index 000000000..207045a6f
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/BiasAdd.test.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/BiasAdd.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+
+// clang-format off
+const char *bias_add_01_pbtxtdata = STRING_CONTENT(
+ name: "out"
+ op: "BiasAdd"
+ input: "val"
+ input: "bias"
+ attr {
+ key: "T"
+ value { type: DT_FLOAT }
+ }
+ attr {
+ key: "data_format"
+ value { s: "NHWC" }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, bias_add_01)
+{
+ TFNodeBuildTester tester;
+ moco::BiasAddGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(bias_add_01_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFBiasAdd
+ // - value() should not be nullptr
+ // - bias() should not be nullptr
+ // - data_layout should match
+
+ tester.inputs({"val", "bias"});
+ tester.output("out");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = loco::must_cast<moco::TFBiasAdd *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_TRUE(test_node->data_layout() == "NHWC");
+}
+
+namespace
+{
+
+// clang-format off
+const char *bias_add_NCHW_pbtxtdata = STRING_CONTENT(
+ name: "out"
+ op: "BiasAdd"
+ input: "val"
+ input: "bias"
+ attr {
+ key: "T"
+ value { type: DT_FLOAT }
+ }
+ attr {
+ key: "data_format"
+ value { s: "NCHW" }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, bias_add_NCHW_axis)
+{
+ TFNodeBuildTester tester;
+ moco::BiasAddGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(bias_add_NCHW_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFBiasAdd
+ // - value() should not be nullptr
+ // - bias() should not be nullptr
+ // - data_layout should match
+
+ tester.inputs({"val", "bias"});
+ tester.output("out");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = loco::must_cast<moco::TFBiasAdd *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_TRUE(test_node->data_layout() == "NCHW");
+}
diff --git a/compiler/moco/import/src/Nodes/Concat.cpp b/compiler/moco/import/src/Nodes/Concat.cpp
new file mode 100644
index 000000000..8bf8a84b5
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Concat.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Concat.h"
+
+#include <moco/IR/Nodes/TFConcatV2.h>
+
+#include <moco/Names.h>
+
+#include <loco.h>
+#include <stdex/Memory.h>
+#include <plier/tf/Convert.h>
+
+#include <cassert>
+
+namespace
+{
+
+using namespace moco;
+
+class TFConcatV2GraphUpdate final : public GraphUpdate
+{
+public:
+ TFConcatV2GraphUpdate(TFConcatV2 *node, std::vector<TensorName> names)
+ : _node(node), _names(names)
+ {
+ }
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFConcatV2 *_node;
+ std::vector<TensorName> _names;
+};
+
+void TFConcatV2GraphUpdate::input(const SymbolTable *tensor_names) const
+{
+ uint32_t num_values = _names.size() - 1; // exclude axis
+ assert(num_values >= 1);
+
+ for (uint32_t i = 0; i < num_values; ++i)
+ {
+ auto input_node = tensor_names->node(_names[i]);
+ assert(input_node != nullptr);
+ _node->values(i, input_node);
+ }
+ auto axis_node = tensor_names->node(_names[num_values]);
+ assert(axis_node != nullptr);
+ _node->axis(axis_node);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool ConcatV2GraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ if (!plier::tf::has_attrs(node, {"T", "N", "Tidx"}))
+ return false;
+
+ // Concat node SHOULD have 3 or more inputs, that is 2 + axis
+ const int num_inputs = node.input_size() - 1;
+ return (num_inputs >= 2) && (num_inputs == plier::tf::get_int_attr(node, "N"));
+}
+
+void ConcatV2GraphBuilder::build(const tensorflow::NodeDef &node,
+ GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ auto graph = context->graph();
+ auto tensor_names = context->tensor_names();
+ auto updates = context->updates();
+
+ const int num_inputs = node.input_size() - 1;
+ std::vector<TensorName> input_names;
+ auto concat_node = graph->nodes()->create<TFConcatV2>(num_inputs);
+ concat_node->name(node.name());
+
+ for (int ni = 0; ni < num_inputs; ++ni)
+ {
+ input_names.push_back(TensorName(node.input(ni)));
+ }
+ // last one is the axis
+ input_names.push_back(TensorName(node.input(num_inputs)));
+
+ // register string-name to the last node as output of concat(s)
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, concat_node);
+
+ auto update = stdex::make_unique<TFConcatV2GraphUpdate>(concat_node, input_names);
+ updates->enroll(std::move(update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Concat.test.cpp b/compiler/moco/import/src/Nodes/Concat.test.cpp
new file mode 100644
index 000000000..30a7db792
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Concat.test.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Concat.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+
+// clang-format off
+const char *concat_01_pbtxtdata = STRING_CONTENT(
+ name: "Concat"
+ op: "ConcatV2"
+ input: "Input01"
+ input: "Input02"
+ input: "Axis"
+ attr {
+ key: "N"
+ value {
+ i: 2
+ }
+ }
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tidx"
+ value {
+ type: DT_INT32
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, concat_01)
+{
+ TFNodeBuildTester tester;
+ moco::ConcatV2GraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(concat_01_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFConcatV2
+ // - there should be two values
+ // - values(idx) should not be nullptr
+ // - axis() should not be nullptr
+
+ tester.inputs({"Input01", "Input02", "Axis"});
+ tester.output("Concat");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = loco::must_cast<moco::TFConcatV2 *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->num_values(), 2);
+}
+
+namespace
+{
+
+// clang-format off
+const char *concat_02_pbtxtdata = STRING_CONTENT(
+ name: "Concat"
+ op: "ConcatV2"
+ input: "Input01"
+ input: "Input02"
+ input: "Input03"
+ input: "Axis"
+ attr {
+ key: "N"
+ value {
+ i: 3
+ }
+ }
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tidx"
+ value {
+ type: DT_INT32
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, concat_02)
+{
+ TFNodeBuildTester tester;
+ moco::ConcatV2GraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(concat_02_pbtxtdata, nodedef));
+
+ // what to test: TFConcatV2 has 3 inputs
+ // - there should exist TFConcatV2
+ // - values(idx) should not be nullptr
+ // - axis() should not be nullptr
+
+ tester.inputs({"Input01", "Input02", "Input03", "Axis"});
+ tester.output("Concat");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = loco::must_cast<moco::TFConcatV2 *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->num_values(), 3);
+}
diff --git a/compiler/moco/import/src/Nodes/Const.cpp b/compiler/moco/import/src/Nodes/Const.cpp
new file mode 100644
index 000000000..15ea717db
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Const.cpp
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Const.h"
+
+#include <moco/Names.h>
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+#include <plier/tf/Convert.h>
+#include <oops/UserExn.h>
+
+#include <cassert>
+#include <stdexcept>
+#include <string>
+
+namespace
+{
+
+using namespace moco;
+
+void read_value_int8(TFConst *const_node, int num_elements,
+ const tensorflow::TensorProto &input_tensor)
+{
+ const_node->size<loco::DataType::S8>(num_elements);
+
+ int32_t input_elements = input_tensor.int_val_size();
+
+ if (input_tensor.tensor_content().size() == num_elements * sizeof(int8_t))
+ {
+ const std::string &str_content = input_tensor.tensor_content();
+ const int8_t *s8_ptr = reinterpret_cast<const int8_t *>(str_content.c_str());
+ for (int32_t i = 0; i < num_elements; i++)
+ {
+ const_node->at<loco::DataType::S8>(i) = *(s8_ptr + i);
+ }
+ }
+ else if (0 < input_elements && input_elements <= num_elements)
+ {
+ for (int32_t i = 0; i < input_elements; i++)
+ {
+ const_node->at<loco::DataType::S8>(i) = input_tensor.int_val(i);
+ }
+
+ for (int32_t i = input_elements; i < num_elements; i++)
+ {
+ const_node->at<loco::DataType::S8>(i) = input_tensor.int_val(input_elements - 1);
+ }
+ }
+ else
+ {
+ throw oops::UserExn("Invalid Const values", const_node->name());
+ }
+}
+
+void read_value_int32(TFConst *const_node, int num_elements,
+ const tensorflow::TensorProto &input_tensor)
+{
+ const_node->size<loco::DataType::S32>(num_elements);
+
+ int32_t input_elements = input_tensor.int_val_size();
+
+ if (input_tensor.tensor_content().size() == num_elements * sizeof(int32_t))
+ {
+ const std::string &str_content = input_tensor.tensor_content();
+ const int32_t *s32_ptr = reinterpret_cast<const int32_t *>(str_content.c_str());
+ for (int32_t i = 0; i < num_elements; i++)
+ {
+ const_node->at<loco::DataType::S32>(i) = *(s32_ptr + i);
+ }
+ }
+ else if (0 < input_elements && input_elements <= num_elements)
+ {
+ for (int32_t i = 0; i < input_elements; i++)
+ {
+ const_node->at<loco::DataType::S32>(i) = input_tensor.int_val(i);
+ }
+
+ for (int32_t i = input_elements; i < num_elements; i++)
+ {
+ const_node->at<loco::DataType::S32>(i) = input_tensor.int_val(input_elements - 1);
+ }
+ }
+ else
+ {
+ throw oops::UserExn("Invalid Const values", const_node->name());
+ }
+}
+
+void read_value_float32(TFConst *const_node, int num_elements,
+ const tensorflow::TensorProto &input_tensor)
+{
+ const_node->size<loco::DataType::FLOAT32>(num_elements);
+
+ int32_t input_elements = input_tensor.float_val_size();
+
+ if (input_tensor.tensor_content().size() == num_elements * sizeof(float))
+ {
+ const std::string &str_content = input_tensor.tensor_content();
+ const float *float_ptr = reinterpret_cast<const float *>(str_content.c_str());
+ for (int32_t i = 0; i < num_elements; i++)
+ {
+ const_node->at<loco::DataType::FLOAT32>(i) = *(float_ptr + i);
+ }
+ }
+ else if (0 < input_elements && input_elements <= num_elements)
+ {
+ for (int32_t i = 0; i < input_elements; i++)
+ {
+ const_node->at<loco::DataType::FLOAT32>(i) = input_tensor.float_val(i);
+ }
+
+ for (int32_t i = input_elements; i < num_elements; i++)
+ {
+ const_node->at<loco::DataType::FLOAT32>(i) = input_tensor.float_val(input_elements - 1);
+ }
+ }
+ else
+ {
+ throw oops::UserExn("Invalid Const values", const_node->name());
+ }
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool ConstGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ if (!plier::tf::has_attrs(node, {"dtype", "value"}))
+ return false;
+
+ const auto &input_tensor = plier::tf::get_tensor_attr(node, "value");
+ const auto &input_shape = input_tensor.tensor_shape();
+ const auto &input_dims = input_shape.dim();
+
+ if (!(input_shape.dim_size() <= 6))
+ return false;
+
+ for (auto &d : input_dims)
+ {
+ if (d.size() > std::numeric_limits<int>::max())
+ throw oops::UserExn("Const Shape element overflows", node.name());
+
+ if (d.size() < 0)
+ throw oops::UserExn("Unknown dim size", node.name());
+ }
+
+ auto dtype = plier::tf::as_loco_datatype(plier::tf::get_datatype_attr(node, "dtype"));
+ if (!(dtype == loco::DataType::S32 || dtype == loco::DataType::FLOAT32 ||
+ dtype == loco::DataType::S8))
+ return false;
+ // TODO support other dtype
+
+ return true;
+}
+
+void ConstGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+
+ // Create a "TFConstant" node for Const
+ auto const_node = graph->nodes()->create<TFConst>();
+ const_node->name(node.name());
+
+ // set dtype
+ auto dtype = plier::tf::as_loco_datatype(plier::tf::get_datatype_attr(node, "dtype"));
+ const_node->dtype(dtype);
+
+ // import shape and value
+ const auto &input_tensor = plier::tf::get_tensor_attr(node, "value");
+ const auto &input_shape = input_tensor.tensor_shape();
+ const auto &input_dims = input_shape.dim();
+ assert(input_shape.dim_size() <= 6);
+ const_node->rank(input_shape.dim_size());
+ int index = 0;
+ bool zero_sized_shape = false;
+ for (auto &d : input_dims)
+ {
+ assert(d.size() <= std::numeric_limits<int>::max());
+ if (d.size() == 0)
+ zero_sized_shape = true;
+
+ assert(d.size() >= 0);
+ const_node->dim(index++) = d.size();
+ }
+
+ int num_elements = 1;
+ if (zero_sized_shape)
+ {
+ const_node->rank(0);
+ num_elements = 0;
+ }
+ else
+ {
+ for (uint32_t d = 0; d < const_node->rank(); d++)
+ {
+ num_elements *= const_node->dim(d).value();
+ }
+ }
+
+ switch (dtype)
+ {
+ case loco::DataType::S8:
+ read_value_int8(const_node, num_elements, input_tensor);
+ break;
+
+ case loco::DataType::S32:
+ read_value_int32(const_node, num_elements, input_tensor);
+ break;
+
+ case loco::DataType::FLOAT32:
+ read_value_float32(const_node, num_elements, input_tensor);
+ break;
+
+ // TODO support other types
+
+ default:
+ assert(false);
+ }
+
+ // register string-name to node
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, const_node);
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Const.test.cpp b/compiler/moco/import/src/Nodes/Const.test.cpp
new file mode 100644
index 000000000..5a9390ba9
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Const.test.cpp
@@ -0,0 +1,465 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Const.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+
+// Test case for "input_tensor.float_val_size() == num_elements"
+
+// clang-format off
+const char *const_float_01_pbtxtdata = STRING_CONTENT(
+ name: "const/float"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: 2
+ }
+ dim {
+ size: 3
+ }
+ }
+ float_val: 1.1
+ float_val: 2.2
+ float_val: 3.3
+ float_val: 4.4
+ float_val: 5.5
+ float_val: 6.6
+ }
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, const_float_01)
+{
+ TFNodeBuildTester tester;
+ moco::ConstGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(const_float_01_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFConst
+ // - values should match
+
+ tester.inputs({});
+ tester.output("const/float");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = loco::must_cast<moco::TFConst *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->size<loco::DataType::FLOAT32>(), 6);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(0), 1.1f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(1), 2.2f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(2), 3.3f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(3), 4.4f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(4), 5.5f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(5), 6.6f);
+}
+
+namespace
+{
+// Test case for "input_tensor.float_val_size() == 1"
+
+// clang-format off
+const char *const_float_02_pbtxtdata = STRING_CONTENT(
+ name: "const/float"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: 2
+ }
+ dim {
+ size: 3
+ }
+ }
+ float_val: 1.1
+ }
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, const_float_02)
+{
+ TFNodeBuildTester tester;
+ moco::ConstGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(const_float_02_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFConst
+ // - values should match
+
+ tester.inputs({});
+ tester.output("const/float");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = loco::must_cast<moco::TFConst *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->size<loco::DataType::FLOAT32>(), 6);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(0), 1.1f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(1), 1.1f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(2), 1.1f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(3), 1.1f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(4), 1.1f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(5), 1.1f);
+}
+
+namespace
+{
+// Test case for "input_tensor.tensor_content().size() == num_elements * sizeof(float)"
+// Generated with tfkit tool: "cat ./test.pbtxt | ./tfkit pack"
+
+// clang-format off
+const char *const_float_03_pbtxtdata = STRING_CONTENT(
+ name: "const/float"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: 2
+ }
+ dim {
+ size: 3
+ }
+ }
+ tensor_content: "\315\314\214?\315\314\014@33S@\315\314\214@\000\000\260@33\323@"
+ }
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, const_float_03)
+{
+ TFNodeBuildTester tester;
+ moco::ConstGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(const_float_03_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFConst
+ // - values should match
+
+ tester.inputs({});
+ tester.output("const/float");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = loco::must_cast<moco::TFConst *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->size<loco::DataType::FLOAT32>(), 6);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(0), 1.1f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(1), 2.2f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(2), 3.3f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(3), 4.4f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(4), 5.5f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(5), 6.6f);
+}
+
+namespace
+{
+// Test case for "input_tensor.float_val_size() < num_elements"
+
+// clang-format off
+const char *const_float_04_pbtxtdata = STRING_CONTENT(
+ name: "const/float"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim {
+ size: 2
+ }
+ dim {
+ size: 3
+ }
+ }
+ float_val: 1.1
+ float_val: 2.2
+ }
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, const_float_04)
+{
+ TFNodeBuildTester tester;
+ moco::ConstGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(const_float_04_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFConst
+ // - values should match
+
+ tester.inputs({});
+ tester.output("const/float");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = loco::must_cast<moco::TFConst *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->size<loco::DataType::FLOAT32>(), 6);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(0), 1.1f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(1), 2.2f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(2), 2.2f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(3), 2.2f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(4), 2.2f);
+ ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(5), 2.2f);
+}
+
+namespace
+{
+// Test case for "input_tensor.int_val_size() < num_elements"
+
+// clang-format off
+const char *const_int32_04_pbtxtdata = STRING_CONTENT(
+ name: "const/int"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ dim {
+ size: 2
+ }
+ dim {
+ size: 3
+ }
+ }
+ int_val: 1
+ int_val: 2
+ }
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, const_int32_04)
+{
+ TFNodeBuildTester tester;
+ moco::ConstGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(const_int32_04_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFConst
+ // - values should match
+
+ tester.inputs({});
+ tester.output("const/int");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = loco::must_cast<moco::TFConst *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->size<loco::DataType::S32>(), 6);
+ ASSERT_EQ(test_node->at<loco::DataType::S32>(0), 1);
+ ASSERT_EQ(test_node->at<loco::DataType::S32>(1), 2);
+ ASSERT_EQ(test_node->at<loco::DataType::S32>(2), 2);
+ ASSERT_EQ(test_node->at<loco::DataType::S32>(3), 2);
+ ASSERT_EQ(test_node->at<loco::DataType::S32>(4), 2);
+ ASSERT_EQ(test_node->at<loco::DataType::S32>(5), 2);
+}
+
+namespace
+{
+// Test case for "scalar"
+
+// clang-format off
+const char *const_int32_scalar_pbtxtdata = STRING_CONTENT(
+ name: "const/int"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT32
+ tensor_shape {
+ }
+ int_val: 3
+ }
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, const_int32_scalar)
+{
+ TFNodeBuildTester tester;
+ moco::ConstGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(const_int32_scalar_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFConst
+ // - there should be one element and value should be 3
+
+ tester.inputs({});
+ tester.output("const/int");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = loco::must_cast<moco::TFConst *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->size<loco::DataType::S32>(), 1);
+ ASSERT_EQ(test_node->at<loco::DataType::S32>(0), 3);
+}
+
+namespace
+{
+
+// clang-format off
+const char *const_int8_01_pbtxtdata = STRING_CONTENT(
+ name: "const/int8"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_INT8
+ }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_INT8
+ tensor_shape {
+ dim {
+ size: 2
+ }
+ dim {
+ size: 3
+ }
+ }
+ int_val: 0
+ int_val: -1
+ int_val: 1
+ int_val: 2
+ int_val: 3
+ int_val: 4
+ }
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, const_int8_01)
+{
+ TFNodeBuildTester tester;
+ moco::ConstGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(const_int8_01_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFConst
+ // - values should match
+
+ tester.inputs({});
+ tester.output("const/int8");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = loco::must_cast<moco::TFConst *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->size<loco::DataType::S8>(), 6);
+ ASSERT_EQ(test_node->at<loco::DataType::S8>(0), 0);
+ ASSERT_EQ(test_node->at<loco::DataType::S8>(1), -1);
+ ASSERT_EQ(test_node->at<loco::DataType::S8>(2), 1);
+ ASSERT_EQ(test_node->at<loco::DataType::S8>(3), 2);
+ ASSERT_EQ(test_node->at<loco::DataType::S8>(4), 3);
+ ASSERT_EQ(test_node->at<loco::DataType::S8>(5), 4);
+}
diff --git a/compiler/moco/import/src/Nodes/Conv2D.cpp b/compiler/moco/import/src/Nodes/Conv2D.cpp
new file mode 100644
index 000000000..e6b98dcd1
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Conv2D.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Conv2D.h"
+
+#include <moco/IR/Nodes/TFConv2D.h>
+
+#include <moco/Names.h>
+
+#include "Convert.h"
+
+#include <loco.h>
+#include <loco/IR/PermutingCodec.h>
+#include <stdex/Memory.h>
+#include <plier/tf/Convert.h>
+#include <oops/UserExn.h>
+
+#include <cassert>
+#include <stdexcept>
+#include <algorithm>
+
+namespace
+{
+using namespace moco;
+
+class TFConv2DGraphUpdate final : public GraphUpdate
+{
+public:
+ TFConv2DGraphUpdate(TFConv2D *node, std::vector<TensorName> names) : _node(node), _names(names) {}
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFConv2D *_node;
+ std::vector<TensorName> _names;
+};
+
+void TFConv2DGraphUpdate::input(const SymbolTable *node_table) const
+{
+ assert(_names.size() == 2);
+
+ auto input_node = node_table->node(_names[0]);
+ auto filter_node = node_table->node(_names[1]);
+ assert(input_node != nullptr);
+ assert(filter_node != nullptr);
+
+ _node->input(input_node);
+ _node->filter(filter_node);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool Conv2DGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ if (node.input_size() != 2)
+ return false;
+
+ // note: even though "data_format" is not entered when a model is written,
+ // TF seems to generate "data_format" field into a pb file
+ if (!plier::tf::has_attrs(node, {"T", "data_format", "padding", "strides"}))
+ return false;
+
+ auto data_layout = plier::tf::get_string_attr(node, "data_format");
+ if (!(data_layout == "NHWC" || data_layout == "NCHW"))
+ {
+ throw oops::UserExn("Conv2D Unsupported data_format", node.name());
+ }
+
+ // dilation attribute is not fully supported
+ if (plier::tf::has_attr(node, "dilations"))
+ {
+ // TODO Support non-default dilations
+ auto dilation = plier::tf::get_list_attr(node, "dilations").i();
+ if (!std::all_of(dilation.begin(), dilation.end(), [](std::int64_t dil) { return dil == 1; }))
+ return false;
+ }
+ // Else, dilations are automatically set to default [1,1,1,1] which we assumes now
+
+ return true;
+}
+
+void Conv2DGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // name of loco nodes
+ std::string conv2d_name = node.name();
+
+ auto conv2d = graph->nodes()->create<TFConv2D>();
+ conv2d->name(node.name());
+
+ // read attributes
+ auto data_layout = plier::tf::get_string_attr(node, "data_format");
+ assert(data_layout == "NHWC" || data_layout == "NCHW");
+ conv2d->data_layout(data_layout);
+
+ auto tf_strides = plier::tf::get_list_attr(node, "strides");
+ auto strides = plier::tf::as_int64_list(tf_strides);
+ conv2d->strides(strides);
+
+ auto padding = moco::str_toupper(plier::tf::get_string_attr(node, "padding"));
+ assert(padding == "VALID" || padding == "SAME");
+ conv2d->padding(padding);
+
+ // save the name for graph link updates
+ TensorName output_name(conv2d_name, 0);
+ tensor_names->enroll(output_name, conv2d);
+
+ std::vector<TensorName> input_names;
+ input_names.push_back(TensorName(node.input(0))); // input
+ input_names.push_back(TensorName(node.input(1))); // kernel
+
+ // Record ifm inputs to featureEncode_node
+ auto tfconv2d_update = stdex::make_unique<TFConv2DGraphUpdate>(conv2d, input_names);
+
+ updates->enroll(std::move(tfconv2d_update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Conv2D.test.cpp b/compiler/moco/import/src/Nodes/Conv2D.test.cpp
new file mode 100644
index 000000000..ba006f489
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Conv2D.test.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Conv2D.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *conv2d_01_pbtxtdata = STRING_CONTENT(
+ name: "conv2d"
+ op: "Conv2D"
+ input: "ifm"
+ input: "ker"
+ attr { key: "T" value { type: DT_FLOAT } }
+ attr { key: "data_format" value { s: "NHWC" } }
+ attr { key: "dilations" value { list { i: 1 i: 1 i: 1 i: 1 } } }
+ attr { key: "padding" value { s: "VALID" } }
+ attr { key: "strides" value { list { i: 1 i: 2 i: 3 i: 1 } } }
+);
+// clang-format on
+} // namespace
+
+TEST(TensorFlowImport, Conv2D_01)
+{
+ TFNodeBuildTester tester;
+ moco::Conv2DGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(conv2d_01_pbtxtdata, nodedef));
+
+ // what to test:
+ // - Conv2D node should exist
+ // - ifm() should not be nullptr
+ // - ker() should not be nullptr
+ // - attribute values should match
+
+ tester.inputs({"ifm", "ker"});
+ tester.output("conv2d");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = dynamic_cast<moco::TFConv2D *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->padding(), "VALID");
+ ASSERT_EQ(test_node->data_layout(), "NHWC");
+ auto strides = test_node->strides();
+ ASSERT_EQ(strides.size(), 4);
+ // TODO add verify dilation
+}
+
+namespace
+{
+// clang-format off
+const char *conv2d_inception_pbtxtdata = STRING_CONTENT(
+ name: "InceptionV3/InceptionV3/Conv2d_1a_3x3/Conv2D"
+ op: "Conv2D"
+ input: "input:0"
+ input: "InceptionV3/Conv2d_1a_3x3/weights/read/_3__cf__3"
+ attr {
+ key: "T"
+ value { type: DT_FLOAT }
+ }
+ attr {
+ key: "data_format"
+ value { s: "NHWC" }
+ }
+ attr {
+ key: "dilations"
+ value {
+ list { i: 1 i: 1 i: 1 i: 1 }
+ }
+ }
+ attr {
+ key: "padding"
+ value { s: "VALID" }
+ }
+ attr {
+ key: "strides"
+ value {
+ list { i: 1 i: 2 i: 2 i: 1 }
+ }
+ }
+);
+} // namespace
+
+TEST(TensorFlowImport, Conv2D_inception_indexed_tensor_name)
+{
+ TFNodeBuildTester tester;
+ moco::Conv2DGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(conv2d_inception_pbtxtdata, nodedef));
+
+ // what to test: name with ':0' should be treated correctly
+ // - Conv2D node should exist
+ // - ifm() should not be nullptr
+ // - ker() should not be nullptr
+
+ tester.inputs({"input", "InceptionV3/Conv2d_1a_3x3/weights/read/_3__cf__3"});
+ tester.output("InceptionV3/InceptionV3/Conv2d_1a_3x3/Conv2D");
+ tester.run(nodedef, graphbuilder);
+}
diff --git a/compiler/moco/import/src/Nodes/Conv2DBackpropInput.cpp b/compiler/moco/import/src/Nodes/Conv2DBackpropInput.cpp
new file mode 100644
index 000000000..74c6605ab
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Conv2DBackpropInput.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Conv2DBackpropInput.h"
+
+#include <moco/IR/Nodes/TFConv2DBackpropInput.h>
+
+#include "Convert.h"
+
+#include <loco.h>
+#include <stdex/Memory.h>
+#include <plier/tf/Convert.h>
+#include <oops/UserExn.h>
+
+namespace
+{
+using namespace moco;
+
+/// @brief GraphUpdate for Conv2DBackpropInput node
+class Conv2DBackpropInputGraphUpdate final : public GraphUpdate
+{
+public:
+ Conv2DBackpropInputGraphUpdate(TFConv2DBackpropInput *node, std::vector<TensorName> names)
+ : _node(node), _input_names(names)
+ {
+ // DO NOTHING
+ }
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFConv2DBackpropInput *_node;
+ std::vector<TensorName> _input_names;
+};
+
+void Conv2DBackpropInputGraphUpdate::input(const SymbolTable *table) const
+{
+ assert(_input_names.size() == 3);
+
+ auto input_sizes_node = table->node(_input_names[0]);
+ auto filter_node = table->node(_input_names[1]);
+ auto out_backprop_node = table->node(_input_names[2]);
+
+ assert(input_sizes_node != nullptr);
+ assert(filter_node != nullptr);
+ assert(out_backprop_node != nullptr);
+
+ _node->input_sizes(input_sizes_node);
+ _node->filter(filter_node);
+ _node->out_backprop(out_backprop_node);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool Conv2DBackpropInputGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ if (node.input_size() != 3)
+ return false;
+
+ if (!plier::tf::has_attrs(node, {"T", "data_format", "padding", "strides"}))
+ return false;
+
+ auto data_layout = plier::tf::get_string_attr(node, "data_format");
+ if (!(data_layout == "NHWC" || data_layout == "NCHW"))
+ {
+ throw oops::UserExn("Conv2DBackprop Unsupported data_format", node.name());
+ }
+
+ // dilation attribute is not fully supported
+ if (plier::tf::has_attr(node, "dilations"))
+ {
+ // TODO Support non-default dilations
+ auto dilation = plier::tf::get_list_attr(node, "dilations").i();
+ if (!std::all_of(dilation.begin(), dilation.end(), [](std::int64_t dil) { return dil == 1; }))
+ return false;
+ }
+ // Else, dilations are automatically set to default [1,1,1,1] which we assumes now
+
+ return true;
+}
+
+void Conv2DBackpropInputGraphBuilder::build(const tensorflow::NodeDef &node,
+ GraphBuilderContext *context) const
+{
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // name of loco nodes
+ std::string conv2d_backprop_name = node.name();
+
+ auto conv2d_backprop = graph->nodes()->create<TFConv2DBackpropInput>();
+ conv2d_backprop->name(node.name());
+
+ // read attributes
+ auto data_layout = plier::tf::get_string_attr(node, "data_format");
+ assert(data_layout == "NHWC" || data_layout == "NCHW");
+ conv2d_backprop->data_layout(data_layout);
+
+ auto tf_strides = plier::tf::get_list_attr(node, "strides");
+ auto strides = plier::tf::as_int64_list(tf_strides);
+ conv2d_backprop->strides(strides);
+
+ auto padding = moco::str_toupper(plier::tf::get_string_attr(node, "padding"));
+ assert(padding == "VALID" || padding == "SAME");
+ conv2d_backprop->padding(padding);
+
+ // save the name for graph link updates
+ TensorName output_name(conv2d_backprop_name, 0);
+ tensor_names->enroll(output_name, conv2d_backprop);
+
+ std::vector<TensorName> input_names;
+ input_names.push_back(TensorName(node.input(0))); // input_sizes
+ input_names.push_back(TensorName(node.input(1))); // filter
+ input_names.push_back(TensorName(node.input(2))); // out_backprop
+
+ // update
+ auto conv2d_backprop_update =
+ stdex::make_unique<Conv2DBackpropInputGraphUpdate>(conv2d_backprop, input_names);
+
+ updates->enroll(std::move(conv2d_backprop_update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Conv2DBackpropInput.test.cpp b/compiler/moco/import/src/Nodes/Conv2DBackpropInput.test.cpp
new file mode 100644
index 000000000..8c462bc3b
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Conv2DBackpropInput.test.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Conv2DBackpropInput.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *conv2d_backprop_input_01_pbtxtdata = STRING_CONTENT(
+ name: "ofm"
+ op: "Conv2DBackpropInput"
+ input: "outshape"
+ input: "weights"
+ input: "ifm"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "data_format"
+ value {
+ s: "NHWC"
+ }
+ }
+ attr {
+ key: "dilations"
+ value {
+ list {
+ i: 1
+ i: 1
+ i: 1
+ i: 1
+ }
+ }
+ }
+ attr {
+ key: "padding"
+ value {
+ s: "SAME"
+ }
+ }
+ attr {
+ key: "strides"
+ value {
+ list {
+ i: 1
+ i: 2
+ i: 2
+ i: 1
+ }
+ }
+ }
+);
+// clang-format on
+} // namespace
+
+TEST(TensorFlowImport, conv2d_backprop_input_01)
+{
+ TFNodeBuildTester tester;
+ moco::Conv2DBackpropInputGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(conv2d_backprop_input_01_pbtxtdata, nodedef));
+
+ // what to test:
+ // - All node inputs are valid
+ // - All attributes are as expected
+
+ tester.inputs({"outshape", "weights", "ifm"});
+ tester.output("ofm");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = dynamic_cast<moco::TFConv2DBackpropInput *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->padding(), "SAME");
+ ASSERT_EQ(test_node->data_layout(), "NHWC");
+ ASSERT_EQ(test_node->strides().size(), 4);
+}
diff --git a/compiler/moco/import/src/Nodes/DepthwiseConv2dNative.cpp b/compiler/moco/import/src/Nodes/DepthwiseConv2dNative.cpp
new file mode 100644
index 000000000..3991a4d51
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/DepthwiseConv2dNative.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/DepthwiseConv2dNative.h"
+
+#include <moco/IR/Nodes/TFDepthwiseConv2dNative.h>
+
+#include <moco/Names.h>
+
+#include "Convert.h"
+
+#include <plier/tf/Convert.h>
+#include <loco/IR/PermutingCodec.h>
+#include <stdex/Memory.h>
+#include <oops/UserExn.h>
+
+#include <cassert>
+
+using namespace plier::tf;
+
+namespace
+{
+using namespace moco;
+
+class TFDepthwiseConv2dNativeGraphUpdate final : public GraphUpdate
+{
+public:
+ TFDepthwiseConv2dNativeGraphUpdate(TFDepthwiseConv2dNative *node, std::vector<TensorName> names)
+ : _node(node), _names(names)
+ {
+ }
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFDepthwiseConv2dNative *_node;
+ std::vector<TensorName> _names;
+};
+
+void TFDepthwiseConv2dNativeGraphUpdate::input(const SymbolTable *node_table) const
+{
+ assert(_names.size() == 2);
+
+ auto input_node = node_table->node(_names[0]);
+ auto filter_node = node_table->node(_names[1]);
+ assert(input_node != nullptr);
+ assert(filter_node != nullptr);
+
+ _node->input(input_node);
+ _node->filter(filter_node);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool DepthwiseConv2dNativeGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ if (node.input_size() != 2)
+ return false;
+
+ // note: even though "data_format" and "dilations" are not entered when a model is written,
+ // TF seems to generate those field into a pb file.
+ if (!has_attrs(node, {"T", "data_format", "dilations", "padding", "strides"}))
+ return false;
+
+ auto data_layout = plier::tf::get_string_attr(node, "data_format");
+ if (!(data_layout == "NHWC" || data_layout == "NCHW"))
+ {
+ throw oops::UserExn("DepthwiseConv2dNative Unsupported data_format", node.name());
+ }
+
+ auto padding = moco::str_toupper(get_string_attr(node, "padding"));
+ if (!(padding == "VALID" || padding == "SAME"))
+ return false;
+
+ auto tf_strides = get_list_attr(node, "strides");
+ auto strides = as_int64_list(tf_strides);
+ if (!(strides.size() == 4))
+ {
+ throw oops::UserExn("DepthwiseConv2dNative strides requires rank 4", node.name());
+ }
+ auto stride_n = strides.at(0);
+ auto stride_h = strides.at(1);
+ auto stride_w = strides.at(2);
+ auto stride_c = strides.at(3);
+ if (!(stride_n == 1 && stride_c == 1) || !(stride_h == stride_w))
+ {
+ // TODO this message may need to be refined
+ throw oops::UserExn("DepthwiseConv2dNative strides requires N=C=1, H=W", node.name());
+ }
+
+ return true;
+}
+
+void DepthwiseConv2dNativeGraphBuilder::build(const tensorflow::NodeDef &node,
+ GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ auto depthwiseconv2d_native_node = graph->nodes()->create<TFDepthwiseConv2dNative>();
+ depthwiseconv2d_native_node->name(node.name());
+
+ // read attributes
+ auto data_layout = get_string_attr(node, "data_format");
+ depthwiseconv2d_native_node->data_layout(data_layout);
+
+ auto tf_strides = get_list_attr(node, "strides");
+ auto strides = as_int64_list(tf_strides);
+ depthwiseconv2d_native_node->strides(strides);
+
+ auto padding = moco::str_toupper(get_string_attr(node, "padding"));
+ depthwiseconv2d_native_node->padding(padding);
+
+ // save the name for graph link updates
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, depthwiseconv2d_native_node);
+
+ std::vector<TensorName> input_names;
+ input_names.push_back(TensorName(node.input(0))); // input
+ input_names.push_back(TensorName(node.input(1))); // kernel
+
+ // Record ifm inputs to featureEncode_node
+ auto tfdepthwiseconv2dnative_update = stdex::make_unique<TFDepthwiseConv2dNativeGraphUpdate>(
+ depthwiseconv2d_native_node, input_names);
+
+ updates->enroll(std::move(tfdepthwiseconv2dnative_update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/DepthwiseConv2dNative.test.cpp b/compiler/moco/import/src/Nodes/DepthwiseConv2dNative.test.cpp
new file mode 100644
index 000000000..c65283c1b
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/DepthwiseConv2dNative.test.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/DepthwiseConv2dNative.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *depthwise_conv2d_native_01_pbtxtdata = STRING_CONTENT(
+ name: "depthwise"
+ op: "DepthwiseConv2dNative"
+ input: "input"
+ input: "filter"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "data_format"
+ value {
+ s: "NHWC"
+ }
+ }
+ attr {
+ key: "dilations"
+ value {
+ list {
+ i: 1
+ i: 1
+ i: 1
+ i: 1
+ }
+ }
+ }
+ attr {
+ key: "padding"
+ value {
+ s: "VALID"
+ }
+ }
+ attr {
+ key: "strides"
+ value {
+ list {
+ i: 1
+ i: 1
+ i: 1
+ i: 1
+ }
+ }
+ }
+);
+// clang-format on
+} // namespace
+
+TEST(TensorFlowImport, Depthwise_conv2d_native)
+{
+ TFNodeBuildTester tester;
+ moco::DepthwiseConv2dNativeGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(depthwise_conv2d_native_01_pbtxtdata, nodedef));
+
+ // what to test:
+ // - All node inputs are valid
+ // - All attributes are as expected
+
+ tester.inputs({"input", "filter"});
+ tester.output("depthwise");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = dynamic_cast<moco::TFDepthwiseConv2dNative *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->padding(), "VALID");
+ ASSERT_EQ(test_node->data_layout(), "NHWC");
+ ASSERT_EQ(test_node->strides().size(), 4);
+}
diff --git a/compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.cpp b/compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.cpp
new file mode 100644
index 000000000..d2fa3d1eb
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/FakeQuantWithMinMaxVars.h"
+
+#include <moco/IR/Nodes/TFFakeQuantWithMinMaxVars.h>
+
+#include <moco/Names.h>
+
+#include "Convert.h"
+
+#include <plier/tf/Convert.h>
+#include <loco/IR/PermutingCodec.h>
+#include <stdex/Memory.h>
+
+#include <cassert>
+
+using namespace plier::tf;
+
+namespace
+{
+using namespace moco;
+
+class TFFakeQuantWithMinMaxVarsGraphUpdate final : public GraphUpdate
+{
+public:
+ TFFakeQuantWithMinMaxVarsGraphUpdate(TFFakeQuantWithMinMaxVars *node,
+ std::vector<TensorName> names)
+ : _node(node), _names(names)
+ {
+ }
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFFakeQuantWithMinMaxVars *_node;
+ std::vector<TensorName> _names;
+};
+
+void TFFakeQuantWithMinMaxVarsGraphUpdate::input(const SymbolTable *node_table) const
+{
+ assert(_names.size() == 3);
+
+ auto inputs_node = node_table->node(_names[0]);
+ auto min_node = node_table->node(_names[1]);
+ auto max_node = node_table->node(_names[2]);
+ assert(inputs_node != nullptr);
+ assert(min_node != nullptr);
+ assert(max_node != nullptr);
+
+ _node->inputs(inputs_node);
+ _node->min(min_node);
+ _node->max(max_node);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool FakeQuantWithMinMaxVarsGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ if (node.input_size() != 3)
+ return false;
+
+ // attrs "narrow_range", "num_bits" are optional
+ return true;
+}
+
+void FakeQuantWithMinMaxVarsGraphBuilder::build(const tensorflow::NodeDef &node,
+ GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ auto fakequant_node = graph->nodes()->create<TFFakeQuantWithMinMaxVars>();
+ fakequant_node->name(node.name());
+
+ // read optional attributes
+ if (has_attr(node, "num_bits"))
+ {
+ auto num_bits = get_int_attr(node, "num_bits");
+ fakequant_node->num_bits(num_bits);
+ }
+ if (has_attr(node, "narrow_range"))
+ {
+ auto narrow_range = get_bool_attr(node, "narrow_range");
+ fakequant_node->narrow_range(narrow_range);
+ }
+
+ // save the name for graph link updates
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, fakequant_node);
+
+ std::vector<TensorName> input_names;
+ input_names.push_back(TensorName(node.input(0))); // inputs
+ input_names.push_back(TensorName(node.input(1))); // min
+ input_names.push_back(TensorName(node.input(2))); // max
+
+ // Record ifm inputs to featureEncode_node
+ auto tffakequant_update =
+ stdex::make_unique<TFFakeQuantWithMinMaxVarsGraphUpdate>(fakequant_node, input_names);
+
+ updates->enroll(std::move(tffakequant_update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.test.cpp b/compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.test.cpp
new file mode 100644
index 000000000..40c494bb0
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.test.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/FakeQuantWithMinMaxVars.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *fakequant_01_pbtxtdata = STRING_CONTENT(
+ name: "FakeQuant"
+ op: "FakeQuantWithMinMaxVars"
+ input: "Input"
+ input: "FakeMin"
+ input: "FakeMax"
+ attr {
+ key: "narrow_range"
+ value { b: true }
+ }
+ attr {
+ key: "num_bits"
+ value { i: 16 }
+ }
+);
+// clang-format on
+} // namespace
+
+TEST(TensorFlowImport, FakeQuantWithMinMaxVars)
+{
+ TFNodeBuildTester tester;
+ moco::FakeQuantWithMinMaxVarsGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(fakequant_01_pbtxtdata, nodedef));
+
+ // what to test:
+ // - All node inputs are valid
+ // - All attributes are as expected
+
+ tester.inputs({"Input", "FakeMin", "FakeMax"});
+ tester.output("FakeQuant");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = dynamic_cast<moco::TFFakeQuantWithMinMaxVars *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->narrow_range(), true);
+ ASSERT_EQ(test_node->num_bits(), 16);
+}
diff --git a/compiler/moco/import/src/Nodes/FusedBatchNorm.cpp b/compiler/moco/import/src/Nodes/FusedBatchNorm.cpp
new file mode 100644
index 000000000..59f98017c
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/FusedBatchNorm.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/FusedBatchNorm.h"
+
+#include <moco/IR/Nodes/TFFusedBatchNorm.h>
+
+#include <loco.h>
+#include <stdex/Memory.h>
+#include <plier/tf/Convert.h>
+
+namespace
+{
+
+using namespace moco;
+
+/**
+ * @brief GraphUpdate for FusedBatchNorm node
+ */
+class FusedBatchNormGraphUpdate final : public GraphUpdate
+{
+public:
+ FusedBatchNormGraphUpdate(TFFusedBatchNorm *node, std::vector<TensorName> names)
+ : _node(node), _names(names)
+ {
+ }
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFFusedBatchNorm *_node;
+ std::vector<TensorName> _names;
+};
+
+void FusedBatchNormGraphUpdate::input(const SymbolTable *tensor_names) const
+{
+ assert(_names.size() == 5);
+
+ _node->x(tensor_names->node(_names[0]));
+ _node->scale(tensor_names->node(_names[1]));
+ _node->offset(tensor_names->node(_names[2]));
+ _node->mean(tensor_names->node(_names[3]));
+ _node->variance(tensor_names->node(_names[4]));
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool FusedBatchNormGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ if (node.input_size() != 5)
+ return false;
+
+ return plier::tf::has_attrs(node, {"epsilon"});
+}
+
+void FusedBatchNormGraphBuilder::build(const tensorflow::NodeDef &node,
+ GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ float epsilon = plier::tf::get_float_attr(node, "epsilon");
+
+ // creating TF dialect FusedBatchNorm node
+ auto tf_fbn = graph->nodes()->create<TFFusedBatchNorm>();
+ tf_fbn->name(node.name());
+ tf_fbn->epsilon(epsilon);
+
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, tf_fbn);
+
+ std::vector<TensorName> fbn_input_names;
+ fbn_input_names.push_back(TensorName(node.input(0))); // input
+ fbn_input_names.push_back(TensorName(node.input(1))); // scale
+ fbn_input_names.push_back(TensorName(node.input(2))); // offset
+ fbn_input_names.push_back(TensorName(node.input(3))); // mean
+ fbn_input_names.push_back(TensorName(node.input(4))); // variance
+
+ auto tf_fbn_update = stdex::make_unique<FusedBatchNormGraphUpdate>(tf_fbn, fbn_input_names);
+ updates->enroll(std::move(tf_fbn_update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/FusedBatchNorm.test.cpp b/compiler/moco/import/src/Nodes/FusedBatchNorm.test.cpp
new file mode 100644
index 000000000..0f2e037b8
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/FusedBatchNorm.test.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/FusedBatchNorm.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *fbn_basic_pbtxt = STRING_CONTENT(
+ name: "FBN_01"
+ op: "FusedBatchNorm"
+ input: "input"
+ input: "gamma"
+ input: "beta"
+ input: "FBN_01/mean"
+ input: "FBN_01/variance"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "data_format"
+ value {
+ s: "NHWC"
+ }
+ }
+ attr {
+ key: "epsilon"
+ value {
+ f: 0.001
+ }
+ }
+ attr {
+ key: "is_training"
+ value {
+ b: false
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, tf_fbn_basic)
+{
+ TFNodeBuildTester tester;
+ moco::FusedBatchNormGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(fbn_basic_pbtxt, nodedef));
+
+ // what to test:
+ // - there should exist a TFFusedBatchNorm
+ // - input() should not be nullptr
+ // - gamma() should not be nullptr
+ // - beta() should not be nullptr
+ // - mean() should not be nullptr
+ // - variance() should not be nullptr
+ // - epsilon() value should match
+
+ tester.inputs({"input", "gamma", "beta", "FBN_01/mean", "FBN_01/variance"});
+ tester.output("FBN_01");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = dynamic_cast<moco::TFFusedBatchNorm *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->epsilon(), 0.001f);
+}
diff --git a/compiler/moco/import/src/Nodes/Identity.cpp b/compiler/moco/import/src/Nodes/Identity.cpp
new file mode 100644
index 000000000..8ca0e2d01
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Identity.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Identity.h"
+
+#include <moco/IR/Nodes/TFIdentity.h>
+
+#include <moco/Names.h>
+#include <loco.h>
+#include <stdex/Memory.h>
+
+#include <vector>
+
+namespace
+{
+
+using namespace moco;
+
+class TFIdentityGraphUpdate final : public GraphUpdate
+{
+public:
+ TFIdentityGraphUpdate(TFIdentity *node, const std::vector<TensorName> &names)
+ : _node(node), _names(names)
+ {
+ }
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFIdentity *_node;
+ const std::vector<TensorName> _names;
+};
+
+void TFIdentityGraphUpdate::input(const SymbolTable *tensor_names) const
+{
+ for (auto &name : _names)
+ {
+ loco::Node *target = tensor_names->node(name);
+ _node->input(target);
+ }
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool IdentityGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ if (node.input_size() < 1) // from TensorFlow lite toco
+ return false;
+
+ return true;
+}
+
+void IdentityGraphBuilder::build(const tensorflow::NodeDef &node,
+ GraphBuilderContext *context) const
+{
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // Create a Identity node
+ auto identity_node = graph->nodes()->create<TFIdentity>();
+ identity_node->name(node.name());
+
+ // register string-name to node
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, identity_node);
+
+ // Queue node input update
+ // TODO: Check if we really need multiple input handlings
+ std::vector<TensorName> names;
+ for (int i = 0; i < node.input_size(); ++i)
+ {
+ names.emplace_back(TensorName(node.input(i)));
+ }
+ auto update = stdex::make_unique<TFIdentityGraphUpdate>(identity_node, names);
+ updates->enroll(std::move(update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/MaxPool.cpp b/compiler/moco/import/src/Nodes/MaxPool.cpp
new file mode 100644
index 000000000..63275a3b8
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/MaxPool.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/MaxPool.h"
+
+#include <moco/IR/Nodes/TFMaxPool.h>
+
+#include <moco/Names.h>
+
+#include "Convert.h"
+
+#include <loco.h>
+#include <loco/IR/PermutingCodec.h>
+#include <stdex/Memory.h>
+#include <plier/tf/Convert.h>
+#include <oops/UserExn.h>
+
+#include <cassert>
+#include <stdexcept>
+
+namespace
+{
+
+using namespace moco;
+
+class TFMaxPoolGraphUpdate final : public GraphUpdate
+{
+public:
+ TFMaxPoolGraphUpdate(TFMaxPool *node, const TensorName &name)
+ : _maxpool_node(node), _input_name(name)
+ {
+ }
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFMaxPool *_maxpool_node;
+ const TensorName _input_name;
+};
+
+void TFMaxPoolGraphUpdate::input(const SymbolTable *node_table) const
+{
+ loco::Node *input_node = node_table->node(_input_name);
+ _maxpool_node->input(input_node);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool MaxPoolGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ // note: even though "data_format" is not entered when a model is written,
+ // TF seems to generate "data_format" field into a pb file
+ if (!plier::tf::has_attrs(node, {"T", "data_format", "ksize", "padding", "strides"}))
+ return false;
+
+ auto data_layout = plier::tf::get_string_attr(node, "data_format");
+ if (!(data_layout == "NHWC" || data_layout == "NCHW"))
+ {
+ throw oops::UserExn("MaxPool Unsupported data_format", node.name());
+ }
+
+ auto tf_ksize = plier::tf::get_list_attr(node, "ksize");
+ auto ksize = plier::tf::as_int64_list(tf_ksize);
+ if (ksize.size() != 4)
+ {
+ // TODO support ksize length for 1 and 2
+ throw oops::UserExn("MaxPool ksize requires rank 4", node.name());
+ }
+
+ auto tf_strides = plier::tf::get_list_attr(node, "strides");
+ auto strides = plier::tf::as_int64_list(tf_strides);
+ if (strides.size() != 4)
+ {
+ // TODO support strides length for 1 and 2
+ throw oops::UserExn("MaxPool strides requires rank 4", node.name());
+ }
+
+ return true;
+}
+
+void MaxPoolGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // name of loco nodes
+ ::std::string node_name = node.name();
+
+ // tensorflow data_format: one of NHWC or NCHW.
+ auto data_layout = plier::tf::get_string_attr(node, "data_format");
+ auto maxPool_node = graph->nodes()->create<TFMaxPool>();
+ maxPool_node->name(node.name());
+ maxPool_node->data_layout(data_layout);
+
+ // padding
+ auto padding = moco::str_toupper(plier::tf::get_string_attr(node, "padding"));
+ maxPool_node->padding(padding);
+
+ // ksize
+ auto tf_ksize = plier::tf::get_list_attr(node, "ksize");
+ auto ksize = plier::tf::as_int64_list(tf_ksize);
+ assert(ksize.size() == 4);
+ maxPool_node->ksize(ksize);
+
+ // strides
+ auto tf_strides = plier::tf::get_list_attr(node, "strides");
+ auto strides = plier::tf::as_int64_list(tf_strides);
+ assert(strides.size() == 4);
+ maxPool_node->strides(strides);
+
+ // To set the input node of encode_node with node_name
+ TensorName output_name(node_name, 0);
+ tensor_names->enroll(output_name, maxPool_node);
+
+ // Record ifm inputs to featureEncode_node
+ auto update = stdex::make_unique<TFMaxPoolGraphUpdate>(maxPool_node, TensorName(node.input(0)));
+
+ updates->enroll(std::move(update));
+}
+
+} // namespace moco
+
+// TODO Consider a case when TF MaxPool is for 3D.
+// MaxPool works for 2D and other Dimensions, such as 3D
+// So, in future, some other GraphBuilder decide if MaxPoolGraphBuilder is used or
+// other GraphBuilder is used for TF MaxPool
diff --git a/compiler/moco/import/src/Nodes/MaxPool.test.cpp b/compiler/moco/import/src/Nodes/MaxPool.test.cpp
new file mode 100644
index 000000000..a85e2027b
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/MaxPool.test.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/MaxPool.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *maxpool_01_pbtxtdata = STRING_CONTENT(
+ name: "maxpool"
+ op: "MaxPool"
+ input: "const/float"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "data_format"
+ value {
+ s: "NHWC"
+ }
+ }
+ attr {
+ key: "ksize"
+ value {
+ list {
+ i: 1
+ i: 2
+ i: 3
+ i: 1
+ }
+ }
+ }
+ attr {
+ key: "padding"
+ value {
+ s: "VALID"
+ }
+ }
+ attr {
+ key: "strides"
+ value {
+ list {
+ i: 1
+ i: 3
+ i: 2
+ i: 1
+ }
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, MaxPool_01)
+{
+ TFNodeBuildTester tester;
+ moco::MaxPoolGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(maxpool_01_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFMaxPool
+ // - attributes value should match
+
+ tester.inputs({"const/float"});
+ tester.output("maxpool");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = dynamic_cast<moco::TFMaxPool *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->data_layout(), "NHWC");
+ ASSERT_EQ(test_node->padding(), "VALID");
+ ASSERT_EQ(test_node->ksize(), std::vector<int64_t>({1, 2, 3, 1}));
+ ASSERT_EQ(test_node->strides(), std::vector<int64_t>({1, 3, 2, 1}));
+}
diff --git a/compiler/moco/import/src/Nodes/Maximum.cpp b/compiler/moco/import/src/Nodes/Maximum.cpp
new file mode 100644
index 000000000..43bbbabe6
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Maximum.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 "moco/Import/Nodes/Maximum.h"
+
+#include <moco/IR/Nodes/TFMaximum.h>
+
+#include <loco.h>
+#include <stdex/Memory.h>
+
+namespace
+{
+
+using namespace moco;
+
+/**
+ * @brief GraphUpdate for TF Maximum node
+ */
+class TFMaximumGraphUpdate final : public GraphUpdate
+{
+public:
+ TFMaximumGraphUpdate(TFMaximum *node, std::vector<TensorName> names) : _node(node), _names(names)
+ {
+ }
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFMaximum *_node;
+ std::vector<TensorName> _names;
+};
+
+void TFMaximumGraphUpdate::input(const SymbolTable *tensor_names) const
+{
+ assert(_names.size() == 2);
+
+ _node->x(tensor_names->node(_names[0]));
+ _node->y(tensor_names->node(_names[1]));
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool MaximumGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ return node.input_size() == 2;
+}
+
+void MaximumGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // creating TF dialect Maximum node
+ auto tf_maximum = graph->nodes()->create<TFMaximum>();
+ tf_maximum->name(node.name());
+
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, tf_maximum);
+
+ std::vector<TensorName> add_input_names;
+ add_input_names.push_back(TensorName(node.input(0))); // x
+ add_input_names.push_back(TensorName(node.input(1))); // y
+
+ auto tf_maximum_update = stdex::make_unique<TFMaximumGraphUpdate>(tf_maximum, add_input_names);
+ updates->enroll(std::move(tf_maximum_update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Maximum.test.cpp b/compiler/moco/import/src/Nodes/Maximum.test.cpp
new file mode 100644
index 000000000..2a8b63622
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Maximum.test.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Maximum.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *maximum_basic_pbtxt = STRING_CONTENT(
+ name: "MAXIMUM_01"
+ op: "Maximum"
+ input: "input_01"
+ input: "input_02"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, tf_maximum_basic)
+{
+ TFNodeBuildTester tester;
+ moco::MaximumGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(maximum_basic_pbtxt, nodedef));
+
+ // what to test:
+ // - TFMaximum node should exist
+ // - both inputs x() and y() should not be null
+
+ tester.inputs({"input_01", "input_02"});
+ tester.output("MAXIMUM_01");
+ tester.run(nodedef, graphbuilder);
+}
diff --git a/compiler/moco/import/src/Nodes/Mean.cpp b/compiler/moco/import/src/Nodes/Mean.cpp
new file mode 100644
index 000000000..30fb0f1f7
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Mean.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Mean.h"
+
+#include <moco/IR/Nodes/TFMean.h>
+
+#include <loco.h>
+#include <stdex/Memory.h>
+#include <plier/tf/Convert.h>
+
+namespace
+{
+using namespace moco;
+
+/**
+ * @brief GraphUpdate for Mean node
+ */
+class MeanGraphUpdate final : public GraphUpdate
+{
+public:
+ MeanGraphUpdate(TFMean *node, const TensorName &&input_name,
+ const TensorName &&reduction_indices_name)
+ : _node(node), _input_name(input_name), _reduction_indices_name(reduction_indices_name)
+ {
+ // DO NOTHING
+ }
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFMean *_node;
+ const TensorName _input_name;
+ const TensorName _reduction_indices_name;
+};
+
+void MeanGraphUpdate::input(const SymbolTable *table) const
+{
+ loco::Node *input_node = table->node(_input_name);
+ loco::Node *reduction_indices_node = table->node(_reduction_indices_name);
+ _node->input(input_node);
+ _node->reduction_indices(reduction_indices_node);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool MeanGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ if (node.input_size() != 2)
+ return false;
+
+ if (!plier::tf::has_attrs(node, {"T", "Tidx", "keep_dims"}))
+ return false;
+
+ auto dtype = plier::tf::get_datatype_attr(node, "Tidx");
+ if (dtype != tensorflow::DataType::DT_INT32 && dtype != tensorflow::DataType::DT_INT64)
+ return false;
+
+ return true;
+}
+
+void MeanGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // creating TF dialect Mean node
+ auto tf_mean = graph->nodes()->create<TFMean>();
+ tf_mean->name(node.name());
+ tf_mean->keep_dims(plier::tf::get_bool_attr(node, "keep_dims"));
+
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, tf_mean);
+
+ auto update = stdex::make_unique<MeanGraphUpdate>(tf_mean, TensorName(node.input(0)),
+ TensorName(node.input(1)));
+ updates->enroll(std::move(update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Mean.test.cpp b/compiler/moco/import/src/Nodes/Mean.test.cpp
new file mode 100644
index 000000000..4666c32d0
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Mean.test.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Mean.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+
+// clang-format off
+const char *mean_true_pbtxtdata = STRING_CONTENT(
+ name: "Mean"
+ op: "Mean"
+ input: "Placeholder"
+ input: "Const"
+ attr {
+ key: "T"
+ value { type: DT_FLOAT }
+ }
+ attr {
+ key: "Tidx"
+ value { type: DT_INT32 }
+ }
+ attr {
+ key: "keep_dims"
+ value { b: true }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, mean_true)
+{
+ TFNodeBuildTester tester;
+ moco::MeanGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(mean_true_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFMean
+ // - input node should not be nullptr
+ // - reduction_indeces node should not be nullptr
+ // - keep_dims attribute is set same as pbtxt
+
+ tester.inputs({"Placeholder", "Const"});
+ tester.output("Mean");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = loco::must_cast<moco::TFMean *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->keep_dims(), true);
+}
+
+namespace
+{
+
+// clang-format off
+const char *mean_false_pbtxtdata = STRING_CONTENT(
+ name: "Mean"
+ op: "Mean"
+ input: "Placeholder"
+ input: "Const"
+ attr {
+ key: "T"
+ value { type: DT_FLOAT }
+ }
+ attr {
+ key: "Tidx"
+ value { type: DT_INT32 }
+ }
+ attr {
+ key: "keep_dims"
+ value { b: false }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, mean_false)
+{
+ TFNodeBuildTester tester;
+ moco::MeanGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(mean_false_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFMean
+ // - input node should not be nullptr
+ // - reduction_indeces node should not be nullptr
+ // - keep_dims attribute is set same as pbtxt
+
+ tester.inputs({"Placeholder", "Const"});
+ tester.output("Mean");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = loco::must_cast<moco::TFMean *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->keep_dims(), false);
+}
diff --git a/compiler/moco/import/src/Nodes/Mul.cpp b/compiler/moco/import/src/Nodes/Mul.cpp
new file mode 100644
index 000000000..ab926b59e
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Mul.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Mul.h"
+
+#include <moco/IR/Nodes/TFMul.h>
+
+#include <loco.h>
+#include <stdex/Memory.h>
+
+namespace
+{
+
+using namespace moco;
+
+/**
+ * @brief GraphUpdate for TF Mul node
+ */
+class TFMulGraphUpdate final : public GraphUpdate
+{
+public:
+ TFMulGraphUpdate(TFMul *node, std::vector<TensorName> names) : _node(node), _names(names) {}
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFMul *_node;
+ std::vector<TensorName> _names;
+};
+
+void TFMulGraphUpdate::input(const SymbolTable *tensor_names) const
+{
+ assert(_names.size() == 2);
+
+ _node->x(tensor_names->node(_names[0]));
+ _node->y(tensor_names->node(_names[1]));
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool MulGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ return node.input_size() == 2;
+}
+
+void MulGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // creating TF dialect Mul node
+ auto tf_mul = graph->nodes()->create<TFMul>();
+ tf_mul->name(node.name());
+
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, tf_mul);
+
+ std::vector<TensorName> add_input_names;
+ add_input_names.push_back(TensorName(node.input(0))); // x
+ add_input_names.push_back(TensorName(node.input(1))); // y
+
+ auto tf_mul_update = stdex::make_unique<TFMulGraphUpdate>(tf_mul, add_input_names);
+ updates->enroll(std::move(tf_mul_update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Mul.test.cpp b/compiler/moco/import/src/Nodes/Mul.test.cpp
new file mode 100644
index 000000000..92730b377
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Mul.test.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Mul.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *mul_basic_pbtxt = STRING_CONTENT(
+ name: "MUL_01"
+ op: "Mul"
+ input: "input_01"
+ input: "input_02"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, tf_mul_basic)
+{
+ TFNodeBuildTester tester;
+ moco::MulGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(mul_basic_pbtxt, nodedef));
+
+ // what to test:
+ // - TFMul node should exist
+ // - both inputs x() and y() should not be null
+
+ tester.inputs({"input_01", "input_02"});
+ tester.output("MUL_01");
+ tester.run(nodedef, graphbuilder);
+}
diff --git a/compiler/moco/import/src/Nodes/Pack.cpp b/compiler/moco/import/src/Nodes/Pack.cpp
new file mode 100644
index 000000000..45815a30e
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Pack.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Pack.h"
+
+#include <moco/IR/Nodes/TFPack.h>
+#include <moco/IR/Nodes/TFConst.h>
+
+#include <moco/Names.h>
+
+#include <loco.h>
+#include <loco/IR/NodeShape.h>
+#include <stdex/Memory.h>
+#include <plier/tf/Convert.h>
+
+#include <cassert>
+
+namespace
+{
+
+using namespace moco;
+
+class TFPackGraphUpdate final : public GraphUpdate
+{
+public:
+ TFPackGraphUpdate(TFPack *node, std::vector<TensorName> names) : _node(node), _names(names) {}
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFPack *_node;
+ std::vector<TensorName> _names;
+};
+
+void TFPackGraphUpdate::input(const SymbolTable *tensor_names) const
+{
+ uint32_t num_values = _names.size();
+ assert(num_values >= 1);
+
+ for (uint32_t i = 0; i < num_values; ++i)
+ {
+ auto input_node = tensor_names->node(_names[i]);
+ assert(input_node != nullptr);
+ _node->values(i, input_node);
+ }
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool PackGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ if (!plier::tf::has_attrs(node, {"T", "N", "axis"}))
+ return false;
+
+ const int num_inputs = node.input_size();
+ return (num_inputs >= 1) && (num_inputs == plier::tf::get_int_attr(node, "N"));
+}
+
+void PackGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ auto graph = context->graph();
+ auto tensor_names = context->tensor_names();
+ auto updates = context->updates();
+
+ const int num_inputs = node.input_size();
+ std::vector<TensorName> input_names;
+ auto pack_node = graph->nodes()->create<TFPack>(num_inputs);
+ pack_node->name(node.name());
+
+ for (int ni = 0; ni < num_inputs; ++ni)
+ {
+ input_names.push_back(TensorName(node.input(ni)));
+ }
+
+ pack_node->axis(plier::tf::get_int_attr(node, "axis"));
+
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, pack_node);
+
+ auto update = stdex::make_unique<TFPackGraphUpdate>(pack_node, input_names);
+ updates->enroll(std::move(update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Pack.test.cpp b/compiler/moco/import/src/Nodes/Pack.test.cpp
new file mode 100644
index 000000000..3ee27453e
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Pack.test.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Pack.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+
+// clang-format off
+const char *pack_01_pbtxtdata = STRING_CONTENT(
+ name: "Pack"
+ op: "Pack"
+ input: "input_1"
+ input: "input_2"
+ input: "input_3"
+ input: "input_4"
+ attr {
+ key: "N"
+ value {
+ i: 4
+ }
+ }
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "axis"
+ value {
+ i: 0
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, tf_pack_basic)
+{
+ TFNodeBuildTester tester;
+ moco::PackGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(pack_01_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFPack
+ // - there should be four values
+ // - values(idx) should not be nullptr
+ // - axis() should be 0
+
+ tester.inputs({"input_1", "input_2", "input_3", "input_4"});
+ tester.output("Pack");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = loco::must_cast<moco::TFPack *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->N(), 4);
+ ASSERT_NE(test_node->values(0), nullptr);
+ ASSERT_NE(test_node->values(1), nullptr);
+ ASSERT_NE(test_node->values(2), nullptr);
+ ASSERT_NE(test_node->values(3), nullptr);
+ ASSERT_EQ(test_node->axis(), 0);
+}
diff --git a/compiler/moco/import/src/Nodes/Pad.cpp b/compiler/moco/import/src/Nodes/Pad.cpp
new file mode 100644
index 000000000..262a68fa0
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Pad.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Pad.h"
+
+#include <moco/IR/Nodes/TFPad.h>
+
+#include <loco.h>
+#include <stdex/Memory.h>
+#include <plier/tf/Convert.h>
+
+namespace
+{
+
+using namespace moco;
+
+/**
+ * @brief GraphUpdate for TF Pad node
+ */
+class TFPadGraphUpdate final : public GraphUpdate
+{
+public:
+ TFPadGraphUpdate(TFPad *node, std::vector<TensorName> names) : _node(node), _names(names) {}
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFPad *_node;
+ std::vector<TensorName> _names;
+};
+
+void TFPadGraphUpdate::input(const SymbolTable *table) const
+{
+ assert(_names.size() == 2);
+
+ _node->input(table->node(_names[0]));
+ _node->paddings(table->node(_names[1]));
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool PadGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ if (node.input_size() != 2)
+ return false;
+
+ return plier::tf::has_attrs(node, {"T", "Tpaddings"});
+}
+
+void PadGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // creating TF dialect Pad node
+ auto tf_pad = graph->nodes()->create<TFPad>();
+ tf_pad->name(node.name());
+
+ // register string-name to node
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, tf_pad);
+
+ std::vector<TensorName> add_input_names;
+ add_input_names.push_back(TensorName(node.input(0))); // input
+ add_input_names.push_back(TensorName(node.input(1))); // paddings
+
+ // Queue node input update
+ auto tf_pad_update = stdex::make_unique<TFPadGraphUpdate>(tf_pad, add_input_names);
+ updates->enroll(std::move(tf_pad_update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Pad.test.cpp b/compiler/moco/import/src/Nodes/Pad.test.cpp
new file mode 100644
index 000000000..19769cf6b
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Pad.test.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Pad.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *pad_basic_pbtxt = STRING_CONTENT(
+ name: "Pad"
+ op: "Pad"
+ input: "input"
+ input: "paddings"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tpaddings"
+ value {
+ type: DT_INT32
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, tf_pad_basic)
+{
+ TFNodeBuildTester tester;
+ moco::PadGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(pad_basic_pbtxt, nodedef));
+
+ // what to test:
+ // - TFPad node should exist
+ // - input input() should not be null
+ // - input paddings() should not be null
+
+ tester.inputs({"input", "paddings"});
+ tester.output("Pad");
+ tester.run(nodedef, graphbuilder);
+}
diff --git a/compiler/moco/import/src/Nodes/Placeholder.cpp b/compiler/moco/import/src/Nodes/Placeholder.cpp
new file mode 100644
index 000000000..0033f664b
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Placeholder.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Placeholder.h"
+
+#include <moco/IR/Nodes/TFPlaceholder.h>
+
+#include <moco/Names.h>
+#include <plier/tf/Convert.h>
+
+#include <cassert>
+#include <stdexcept>
+
+namespace moco
+{
+
+bool PlaceholderGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ if (!plier::tf::has_attrs(node, {"dtype", "shape"}))
+ return false;
+
+ loco::DataType dtype = plier::tf::as_loco_datatype(plier::tf::get_datatype_attr(node, "dtype"));
+ if (dtype != loco::DataType::FLOAT32)
+ return false;
+ // TODO support other types
+
+ return true;
+}
+
+void PlaceholderGraphBuilder::build(const tensorflow::NodeDef &node,
+ GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+
+ loco::DataType dtype = plier::tf::as_loco_datatype(plier::tf::get_datatype_attr(node, "dtype"));
+ const auto &shape = plier::tf::get_shape_attr(node, "shape");
+ // TODO handle for unknown rank
+ assert(!shape.unknown_rank());
+ int64_t num_dims = shape.dim_size();
+
+ // TODO support other types
+ assert(dtype == loco::DataType::FLOAT32);
+
+ // Create a "Placeholder" node as an input
+ auto placeholder_node = graph->nodes()->create<moco::TFPlaceholder>();
+ placeholder_node->name(node.name());
+ placeholder_node->dtype(dtype);
+
+ // Setting shape info.
+ placeholder_node->rank(num_dims);
+ for (int64_t d = 0; d < num_dims; d++)
+ {
+ assert(shape.dim(d).size() < std::numeric_limits<uint32_t>::max());
+ int64_t dim_value = shape.dim(d).size();
+ if (dim_value >= 0)
+ {
+ uint32_t dim_value32 = static_cast<uint32_t>(dim_value);
+ placeholder_node->dim(d) = dim_value32;
+ }
+ else
+ {
+ placeholder_node->dim(d).unset();
+ // TODO Remove assert() and do implement
+ // NOTE Current implementation assumes dim is all know
+ assert(false);
+ }
+ }
+
+ // register string-name to node
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, placeholder_node);
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Placeholder.test.cpp b/compiler/moco/import/src/Nodes/Placeholder.test.cpp
new file mode 100644
index 000000000..80488ce39
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Placeholder.test.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Placeholder.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *known_batch_pbtxt = STRING_CONTENT(
+ name: "placeholder"
+ op: "Placeholder"
+ attr {
+ key: "dtype" value { type: DT_FLOAT }
+ }
+ attr {
+ key: "shape"
+ value {
+ shape {
+ dim { size: 1024 }
+ dim { size: 2 }
+ dim { size: 3 }
+ dim { size: 4 }
+ }
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, placeholder_knwon_batch)
+{
+ TFNodeBuildTester tester;
+ moco::PlaceholderGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(known_batch_pbtxt, nodedef));
+
+ // what to test:
+ // - TFPlaceholder node should exist
+ // - shape attribute should match
+
+ tester.inputs({});
+ tester.output("placeholder");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = dynamic_cast<moco::TFPlaceholder *>(tester.output());
+ assert(test_node != nullptr);
+ ASSERT_TRUE(test_node->dim(0).known() && test_node->dim(0).value() == 1024);
+ ASSERT_TRUE(test_node->dim(1).known() && test_node->dim(1).value() == 2);
+ ASSERT_TRUE(test_node->dim(2).known() && test_node->dim(2).value() == 3);
+ ASSERT_TRUE(test_node->dim(3).known() && test_node->dim(3).value() == 4);
+}
diff --git a/compiler/moco/import/src/Nodes/RealDiv.cpp b/compiler/moco/import/src/Nodes/RealDiv.cpp
new file mode 100644
index 000000000..de3d57673
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/RealDiv.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/RealDiv.h"
+
+#include <moco/IR/Nodes/TFRealDiv.h>
+
+#include <loco.h>
+#include <stdex/Memory.h>
+
+namespace
+{
+
+using namespace moco;
+
+/**
+ * @brief GraphUpdate for TF RealDiv node
+ */
+class TFRealDivGraphUpdate final : public GraphUpdate
+{
+public:
+ TFRealDivGraphUpdate(TFRealDiv *node, std::vector<TensorName> names) : _node(node), _names(names)
+ {
+ }
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFRealDiv *_node;
+ std::vector<TensorName> _names;
+};
+
+void TFRealDivGraphUpdate::input(const SymbolTable *tensor_names) const
+{
+ assert(_names.size() == 2);
+
+ _node->x(tensor_names->node(_names[0]));
+ _node->y(tensor_names->node(_names[1]));
+}
+
+} // namespace
+
+namespace moco
+{
+bool RealDivGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ return node.input_size() == 2;
+}
+
+void RealDivGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // creating TF dialect RealDiv node
+ auto tf_div = graph->nodes()->create<TFRealDiv>();
+ tf_div->name(node.name());
+
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, tf_div);
+
+ std::vector<TensorName> div_input_names;
+ div_input_names.push_back(TensorName(node.input(0))); // x
+ div_input_names.push_back(TensorName(node.input(1))); // y
+
+ auto tf_div_update = stdex::make_unique<TFRealDivGraphUpdate>(tf_div, div_input_names);
+ updates->enroll(std::move(tf_div_update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/RealDiv.test.cpp b/compiler/moco/import/src/Nodes/RealDiv.test.cpp
new file mode 100644
index 000000000..cda2d3738
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/RealDiv.test.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/RealDiv.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *div_basic_pbtxt = STRING_CONTENT(
+ name: "DIV_01"
+ op: "RealDiv"
+ input: "input_01"
+ input: "input_02"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, tf_div_basic)
+{
+ TFNodeBuildTester tester;
+ moco::RealDivGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(div_basic_pbtxt, nodedef));
+
+ // what to test:
+ // - TFRealDiv node should exist
+ // - both inputs x() and y() should not be null
+
+ tester.inputs({"input_01", "input_02"});
+ tester.output("DIV_01");
+ tester.run(nodedef, graphbuilder);
+}
diff --git a/compiler/moco/import/src/Nodes/Relu.cpp b/compiler/moco/import/src/Nodes/Relu.cpp
new file mode 100644
index 000000000..eedc8155d
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Relu.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Relu.h"
+
+#include <moco/IR/Nodes/TFRelu.h>
+
+#include <moco/Names.h>
+#include <loco.h>
+#include <stdex/Memory.h>
+
+#include <cassert>
+#include <stdexcept>
+
+namespace
+{
+
+using namespace moco;
+
+class TFReluGraphUpdate final : public GraphUpdate
+{
+public:
+ TFReluGraphUpdate(TFRelu *node, const TensorName &&name) : _node(node), _name(name) {}
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFRelu *_node;
+ const TensorName _name;
+};
+
+void TFReluGraphUpdate::input(const SymbolTable *table) const
+{
+ loco::Node *target = table->node(_name);
+ _node->features(target);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool ReluGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ // ReLU node SHOULD have only one input
+ if (node.input_size() != 1)
+ return false;
+
+ return true;
+}
+
+void ReluGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // Create a "TFRelu" node for Relu
+ auto relu_node = graph->nodes()->create<TFRelu>();
+ relu_node->name(node.name());
+
+ // register string-name to node
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, relu_node);
+
+ // Queue node input update
+ auto update = stdex::make_unique<TFReluGraphUpdate>(relu_node, TensorName(node.input(0)));
+ updates->enroll(std::move(update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Relu.test.cpp b/compiler/moco/import/src/Nodes/Relu.test.cpp
new file mode 100644
index 000000000..a20ee081d
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Relu.test.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Relu.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+
+// clang-format off
+const char *relu_01_pbtxtdata = STRING_CONTENT(
+ name: "ReLU"
+ op: "Relu"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, relu_01)
+{
+ TFNodeBuildTester tester;
+ moco::ReluGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(relu_01_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFRelu
+ // - features node should not be nullptr
+
+ tester.inputs({"Placeholder"});
+ tester.output("ReLU");
+ tester.run(nodedef, graphbuilder);
+}
diff --git a/compiler/moco/import/src/Nodes/Relu6.cpp b/compiler/moco/import/src/Nodes/Relu6.cpp
new file mode 100644
index 000000000..4700ba408
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Relu6.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Relu6.h"
+
+#include <moco/IR/Nodes/TFRelu6.h>
+
+#include <stdex/Memory.h>
+
+namespace
+{
+
+using namespace moco;
+
+class TFRelu6GraphUpdate final : public GraphUpdate
+{
+public:
+ TFRelu6GraphUpdate(TFRelu6 *node, const TensorName &&name) : _node(node), _name(name) {}
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFRelu6 *_node;
+ const TensorName _name;
+};
+
+void TFRelu6GraphUpdate::input(const SymbolTable *table) const
+{
+ loco::Node *target = table->node(_name);
+ _node->features(target);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool Relu6GraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ // ReLU6 node SHOULD have only one input
+ if (node.input_size() != 1)
+ return false;
+ return true;
+}
+
+void Relu6GraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // Create a "TFRelu6" node for Relu
+ auto relu_node = graph->nodes()->create<TFRelu6>();
+ relu_node->name(node.name());
+
+ // register string-name to node
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, relu_node);
+
+ // Queue node input update
+ auto update = stdex::make_unique<TFRelu6GraphUpdate>(relu_node, TensorName(node.input(0)));
+ updates->enroll(std::move(update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Relu6.test.cpp b/compiler/moco/import/src/Nodes/Relu6.test.cpp
new file mode 100644
index 000000000..26beb6c17
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Relu6.test.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Relu6.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+
+// clang-format off
+const char *relu6_01_pbtxtdata = STRING_CONTENT(
+ name: "ReLU6"
+ op: "Relu6"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, relu6_01)
+{
+ TFNodeBuildTester tester;
+ moco::Relu6GraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(relu6_01_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFRelu6
+ // - features node should not be null
+
+ tester.inputs({"Placeholder"});
+ tester.output("ReLU6");
+ tester.run(nodedef, graphbuilder);
+}
diff --git a/compiler/moco/import/src/Nodes/Reshape.cpp b/compiler/moco/import/src/Nodes/Reshape.cpp
new file mode 100644
index 000000000..26e22513f
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Reshape.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Reshape.h"
+
+#include <moco/IR/Nodes/TFReshape.h>
+
+#include <moco/Names.h>
+#include <plier/tf/Convert.h>
+#include <loco.h>
+#include <stdex/Memory.h>
+
+#include <cassert>
+#include <stdexcept>
+
+namespace
+{
+using namespace moco;
+
+class ReshapeGraphUpdate final : public GraphUpdate
+{
+public:
+ ReshapeGraphUpdate(TFReshape *node, std::vector<TensorName> names) : _node(node), _names(names) {}
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFReshape *_node;
+ std::vector<TensorName> _names;
+};
+
+void ReshapeGraphUpdate::input(const SymbolTable *node_table) const
+{
+ assert(_names.size() == 2);
+
+ auto tensor_node = node_table->node(_names[0]);
+ auto shape_node = node_table->node(_names[1]);
+
+ assert(tensor_node != nullptr);
+ assert(shape_node != nullptr);
+
+ _node->tensor(tensor_node);
+ _node->shape(shape_node);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool ReshapeGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ // Tensorflow Reshape has 2 inputs: tensor & shape
+ if (node.input_size() != 2)
+ return false;
+
+ // TODO Assert Tshape value is DT_INT32?
+ return plier::tf::has_attrs(node, {"T", "Tshape"});
+}
+
+void ReshapeGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // name of loco nodes
+ std::string reshape_name = node.name();
+
+ auto reshape = graph->nodes()->create<TFReshape>();
+ reshape->name(node.name());
+
+ // save the name for graph link updates
+ TensorName output_name(reshape_name, 0);
+ tensor_names->enroll(output_name, reshape);
+
+ std::vector<TensorName> input_names;
+ input_names.push_back(TensorName(node.input(0))); // tensor
+ input_names.push_back(TensorName(node.input(1))); // shape
+
+ // Queue node input update
+ auto update = stdex::make_unique<ReshapeGraphUpdate>(reshape, input_names);
+
+ updates->enroll(std::move(update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Reshape.test.cpp b/compiler/moco/import/src/Nodes/Reshape.test.cpp
new file mode 100644
index 000000000..c406bf47b
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Reshape.test.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Reshape.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+
+// clang-format off
+const char *reshape_01_pbtxtdata = STRING_CONTENT(
+ name: "reshape"
+ op: "Reshape"
+ input: "placeholder"
+ input: "shape"
+ attr {
+ key: "T"
+ value { type: DT_FLOAT }
+ }
+ attr {
+ key: "Tshape"
+ value { type: DT_INT32 }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, reshape_01)
+{
+ TFNodeBuildTester tester;
+ moco::ReshapeGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(reshape_01_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFReshape
+ // - input nodes should not be null
+
+ tester.inputs({"placeholder", "shape"});
+ tester.output("reshape");
+ tester.run(nodedef, graphbuilder);
+}
diff --git a/compiler/moco/import/src/Nodes/Rsqrt.cpp b/compiler/moco/import/src/Nodes/Rsqrt.cpp
new file mode 100644
index 000000000..979ac90c9
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Rsqrt.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Rsqrt.h"
+
+#include <moco/IR/Nodes/TFRsqrt.h>
+
+#include <loco.h>
+#include <stdex/Memory.h>
+
+namespace
+{
+
+using namespace moco;
+
+/**
+ * @brief GraphUpdate for TF Rsqrt node
+ */
+class TFRsqrtGraphUpdate final : public GraphUpdate
+{
+public:
+ TFRsqrtGraphUpdate(TFRsqrt *node, TensorName &&name) : _node(node), _name(name) {}
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFRsqrt *_node;
+ TensorName _name;
+};
+
+void TFRsqrtGraphUpdate::input(const SymbolTable *table) const
+{
+ loco::Node *target = table->node(_name);
+ _node->x(target);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool RsqrtGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ return node.input_size() == 1;
+}
+
+void RsqrtGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // creating TF dialect Rsqrt node
+ auto tf_rsqrt = graph->nodes()->create<TFRsqrt>();
+ tf_rsqrt->name(node.name());
+
+ // register string-name to node
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, tf_rsqrt);
+
+ // Queue node input update
+ auto tf_rsqrt_update =
+ stdex::make_unique<TFRsqrtGraphUpdate>(tf_rsqrt, TensorName(node.input(0)));
+ updates->enroll(std::move(tf_rsqrt_update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Rsqrt.test.cpp b/compiler/moco/import/src/Nodes/Rsqrt.test.cpp
new file mode 100644
index 000000000..2750725bc
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Rsqrt.test.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Rsqrt.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *rsqrt_basic_pbtxt = STRING_CONTENT(
+ name: "RSQRT_01"
+ op: "Rsqrt"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, tf_rsqrt_basic)
+{
+ TFNodeBuildTester tester;
+ moco::RsqrtGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(rsqrt_basic_pbtxt, nodedef));
+
+ // what to test:
+ // - TFRsqrt node should exist
+ // - input x() should not be null
+
+ tester.inputs({"Placeholder"});
+ tester.output("RSQRT_01");
+ tester.run(nodedef, graphbuilder);
+}
diff --git a/compiler/moco/import/src/Nodes/Shape.cpp b/compiler/moco/import/src/Nodes/Shape.cpp
new file mode 100644
index 000000000..1e112ebb0
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Shape.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Shape.h"
+
+#include <moco/IR/Nodes/TFShape.h>
+
+#include <loco.h>
+#include <stdex/Memory.h>
+#include <plier/tf/Convert.h>
+
+namespace
+{
+using namespace moco;
+
+/**
+ * @brief GraphUpdate for Shape node
+ */
+class ShapeGraphUpdate final : public GraphUpdate
+{
+public:
+ ShapeGraphUpdate(TFShape *node, const TensorName &&input_name)
+ : _node(node), _input_name(input_name)
+ {
+ // DO NOTHING
+ }
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFShape *_node;
+ const TensorName _input_name;
+};
+
+void ShapeGraphUpdate::input(const SymbolTable *table) const
+{
+ loco::Node *input_node = table->node(_input_name);
+ _node->input(input_node);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool ShapeGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ if (node.input_size() != 1)
+ return false;
+
+ return plier::tf::has_attrs(node, {"T"});
+}
+
+void ShapeGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // create TF dialect Shape node
+ auto tf_shape = graph->nodes()->create<TFShape>();
+ tf_shape->name(node.name());
+
+ if (plier::tf::has_attrs(node, {"out_type"}))
+ {
+ auto dtype = plier::tf::as_loco_datatype(plier::tf::get_datatype_attr(node, "out_type"));
+ // TODO Support other dtype like S64
+ assert(dtype == loco::DataType::S32);
+
+ tf_shape->dtype(dtype);
+ }
+ else
+ {
+ // Set to S32, TF-documented default value for 'out_type'
+ tf_shape->dtype(loco::DataType::S32);
+ }
+
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, tf_shape);
+
+ auto update = stdex::make_unique<ShapeGraphUpdate>(tf_shape, TensorName(node.input(0)));
+ updates->enroll(std::move(update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Shape.test.cpp b/compiler/moco/import/src/Nodes/Shape.test.cpp
new file mode 100644
index 000000000..4aaf66c6f
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Shape.test.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Shape.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+
+// clang-format off
+const char *shape_000_pbtxtdata = STRING_CONTENT(
+ name: "Shape"
+ op: "Shape"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value { type: DT_FLOAT }
+ }
+ attr {
+ key: "out_type"
+ value { type: DT_INT32 }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, shape_000)
+{
+ TFNodeBuildTester tester;
+ moco::ShapeGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(shape_000_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFShape
+ // - input node should not be null
+ // - dtype attribute is set same as out_type attribute of pbtxt
+
+ tester.inputs({"Placeholder"});
+ tester.output("Shape");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = dynamic_cast<moco::TFShape *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->dtype(), loco::DataType::S32);
+}
diff --git a/compiler/moco/import/src/Nodes/Softmax.cpp b/compiler/moco/import/src/Nodes/Softmax.cpp
new file mode 100644
index 000000000..6f2c609ff
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Softmax.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Softmax.h"
+
+#include <moco/IR/Nodes/TFSoftmax.h>
+
+#include <loco.h>
+#include <stdex/Memory.h>
+#include <plier/tf/Convert.h>
+
+namespace
+{
+using namespace moco;
+
+/**
+* @brief GraphUpdate for Softmax node
+*/
+class SoftmaxGraphUpdate final : public GraphUpdate
+{
+public:
+ SoftmaxGraphUpdate(TFSoftmax *node, const TensorName &&input_name)
+ : _node(node), _input_name(input_name)
+ {
+ // DO NOTHING
+ }
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFSoftmax *_node;
+ const TensorName _input_name;
+};
+
+void SoftmaxGraphUpdate::input(const SymbolTable *table) const
+{
+ loco::Node *input_node = table->node(_input_name);
+ _node->logits(input_node);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool SoftmaxGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ if (node.input_size() != 1)
+ return false;
+
+ return plier::tf::has_attrs(node, {"T"});
+}
+
+void SoftmaxGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // creating TF dialect Softmax node
+ auto tf_softmax = graph->nodes()->create<TFSoftmax>();
+ tf_softmax->name(node.name());
+
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, tf_softmax);
+
+ auto update = stdex::make_unique<SoftmaxGraphUpdate>(tf_softmax, TensorName(node.input(0)));
+ updates->enroll(std::move(update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Softmax.test.cpp b/compiler/moco/import/src/Nodes/Softmax.test.cpp
new file mode 100644
index 000000000..b7c0797bb
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Softmax.test.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Softmax.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+
+// clang-format off
+const char *softmax_2d_pbtxtdata = STRING_CONTENT(
+ name: "Softmax"
+ op: "Softmax"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, softmax_2d)
+{
+ TFNodeBuildTester tester;
+ moco::SoftmaxGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(softmax_2d_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFSoftmax
+ // - logits node should not be null
+
+ tester.inputs({"Placeholder"});
+ tester.output("Softmax");
+ tester.run(nodedef, graphbuilder);
+}
diff --git a/compiler/moco/import/src/Nodes/Sqrt.cpp b/compiler/moco/import/src/Nodes/Sqrt.cpp
new file mode 100644
index 000000000..f891e48f6
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Sqrt.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Sqrt.h"
+
+#include <moco/IR/Nodes/TFSqrt.h>
+
+#include <loco.h>
+#include <stdex/Memory.h>
+
+namespace
+{
+
+using namespace moco;
+
+/**
+ * @brief GraphUpdate for TF Sqrt node
+ */
+class TFSqrtGraphUpdate final : public GraphUpdate
+{
+public:
+ TFSqrtGraphUpdate(TFSqrt *node, TensorName &&name) : _node(node), _name(name) {}
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFSqrt *_node;
+ TensorName _name;
+};
+
+void TFSqrtGraphUpdate::input(const SymbolTable *table) const
+{
+ loco::Node *target = table->node(_name);
+ _node->x(target);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool SqrtGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ return node.input_size() == 1;
+}
+
+void SqrtGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // creating TF dialect Sqrt node
+ auto tf_sqrt = graph->nodes()->create<TFSqrt>();
+ tf_sqrt->name(node.name());
+
+ // register string-name to node
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, tf_sqrt);
+
+ // Queue node input update
+ auto tf_sqrt_update = stdex::make_unique<TFSqrtGraphUpdate>(tf_sqrt, TensorName(node.input(0)));
+ updates->enroll(std::move(tf_sqrt_update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Sqrt.test.cpp b/compiler/moco/import/src/Nodes/Sqrt.test.cpp
new file mode 100644
index 000000000..427d4df0f
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Sqrt.test.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Sqrt.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *sqrt_basic_pbtxt = STRING_CONTENT(
+ name: "SQRT_01"
+ op: "Sqrt"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, tf_sqrt_basic)
+{
+ TFNodeBuildTester tester;
+ moco::SqrtGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(sqrt_basic_pbtxt, nodedef));
+
+ // what to test:
+ // - TFSqrt node should exist
+ // - input x() should not be null
+
+ tester.inputs({"Placeholder"});
+ tester.output("SQRT_01");
+ tester.run(nodedef, graphbuilder);
+}
diff --git a/compiler/moco/import/src/Nodes/SquaredDifference.cpp b/compiler/moco/import/src/Nodes/SquaredDifference.cpp
new file mode 100644
index 000000000..17a1fe93d
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/SquaredDifference.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/SquaredDifference.h"
+
+#include <moco/IR/Nodes/TFSquaredDifference.h>
+
+#include <loco.h>
+#include <stdex/Memory.h>
+
+namespace
+{
+
+using namespace moco;
+
+/**
+ * @brief GraphUpdate for TF SquaredDifference node
+ */
+class TFSquaredDifferenceGraphUpdate final : public GraphUpdate
+{
+public:
+ TFSquaredDifferenceGraphUpdate(TFSquaredDifference *node, std::vector<TensorName> names)
+ : _node(node), _names(names)
+ {
+ }
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFSquaredDifference *_node;
+ std::vector<TensorName> _names;
+};
+
+void TFSquaredDifferenceGraphUpdate::input(const SymbolTable *table) const
+{
+ assert(_names.size() == 2);
+
+ _node->x(table->node(_names[0]));
+ _node->y(table->node(_names[1]));
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool SquaredDifferenceGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ return node.input_size() == 2;
+}
+
+void SquaredDifferenceGraphBuilder::build(const tensorflow::NodeDef &node,
+ GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // creating TF dialect SquaredDifference node
+ auto tf_sqdiff = graph->nodes()->create<TFSquaredDifference>();
+ tf_sqdiff->name(node.name());
+
+ // register string-name to node
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, tf_sqdiff);
+
+ std::vector<TensorName> add_input_names;
+ add_input_names.push_back(TensorName(node.input(0))); // x
+ add_input_names.push_back(TensorName(node.input(1))); // y
+
+ // Queue node input update
+ auto tf_sqrt_update =
+ stdex::make_unique<TFSquaredDifferenceGraphUpdate>(tf_sqdiff, add_input_names);
+ updates->enroll(std::move(tf_sqrt_update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/SquaredDifference.test.cpp b/compiler/moco/import/src/Nodes/SquaredDifference.test.cpp
new file mode 100644
index 000000000..336ab1358
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/SquaredDifference.test.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/SquaredDifference.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *sqdiff_basic_pbtxt = STRING_CONTENT(
+ name: "squared_difference"
+ op: "SquaredDifference"
+ input: "input_01"
+ input: "input_02"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, tf_squdiff_basic)
+{
+ TFNodeBuildTester tester;
+ moco::SquaredDifferenceGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(sqdiff_basic_pbtxt, nodedef));
+
+ // what to test:
+ // - TFSquaredDifference node should exist
+ // - input x() should not be null
+ // - input y() should not be null
+
+ tester.inputs({"input_01", "input_02"});
+ tester.output("squared_difference");
+ tester.run(nodedef, graphbuilder);
+}
diff --git a/compiler/moco/import/src/Nodes/Squeeze.cpp b/compiler/moco/import/src/Nodes/Squeeze.cpp
new file mode 100644
index 000000000..1b4ebae6f
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Squeeze.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Squeeze.h"
+
+#include <moco/IR/Nodes/TFSqueeze.h>
+
+#include <moco/Names.h>
+
+#include <loco.h>
+#include <stdex/Memory.h>
+#include <plier/tf/Convert.h>
+#include <oops/UserExn.h>
+
+namespace
+{
+using namespace moco;
+
+/**
+ * @brief GraphUpdate for Squeeze node
+ */
+class SqueezeGraphUpdate final : public GraphUpdate
+{
+public:
+ SqueezeGraphUpdate(TFSqueeze *node, const TensorName &&input_name)
+ : _node(node), _input_name(input_name)
+ {
+ // DO NOTHING
+ }
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFSqueeze *_node;
+ const TensorName _input_name;
+};
+
+void SqueezeGraphUpdate::input(const SymbolTable *table) const
+{
+ loco::Node *input_node = table->node(_input_name);
+ _node->input(input_node);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool SqueezeGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ if (node.input_size() != 1)
+ return false;
+
+ if (!plier::tf::has_attrs(node, {"T"}))
+ return false;
+
+ if (plier::tf::has_attrs(node, {"axis"}))
+ {
+ // TODO support 'axis' attribute
+ oops::UserExn("Squeeze: Unsupported 'axis' attribute", node.name());
+ }
+
+ return true;
+}
+
+void SqueezeGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // TODO support 'axis' attribute
+ assert(!plier::tf::has_attrs(node, {"axis"}));
+
+ std::vector<int64_t> squeeze_dims;
+ if (plier::tf::has_attrs(node, {"squeeze_dims"}))
+ {
+ auto squeeze_dim_list = plier::tf::get_list_attr(node, {"squeeze_dims"});
+ // TODO assert squeeze_dims are mutually different?
+ squeeze_dims = plier::tf::as_int64_list(squeeze_dim_list);
+ }
+ // Note that it is possible that NodeDef does not have squeeze_dims attribute.
+ // In that case, TFSqueeze also has empty squeeze_dims,
+
+ // creating TF dialect Squeeze node
+ auto tf_squeeze = graph->nodes()->create<TFSqueeze>();
+ tf_squeeze->name(node.name());
+ tf_squeeze->squeeze_dims(squeeze_dims);
+
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, tf_squeeze);
+
+ auto update = stdex::make_unique<SqueezeGraphUpdate>(tf_squeeze, TensorName(node.input(0)));
+ updates->enroll(std::move(update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Squeeze.test.cpp b/compiler/moco/import/src/Nodes/Squeeze.test.cpp
new file mode 100644
index 000000000..e8188f98b
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Squeeze.test.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Squeeze.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+
+// clang-format off
+const char *squeeze_all_pbtxtdata = STRING_CONTENT(
+ name: "Squeeze"
+ op: "Squeeze"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value { type: DT_FLOAT }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, squeeze_all)
+{
+ TFNodeBuildTester tester;
+ moco::SqueezeGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(squeeze_all_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFSqueeze
+ // - input node should not be null
+ // - squeeze_dims attribute is set same as pbtxt
+
+ tester.inputs({"Placeholder"});
+ tester.output("Squeeze");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = dynamic_cast<moco::TFSqueeze *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->squeeze_dims().size(), 0);
+}
+
+namespace
+{
+
+// clang-format off
+const char *squeeze_some_pbtxtdata = STRING_CONTENT(
+ name: "Squeeze"
+ op: "Squeeze"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value { type: DT_FLOAT }
+ }
+ attr {
+ key: "squeeze_dims"
+ value {
+ list { i: 1 }
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, squeeze_some)
+{
+ TFNodeBuildTester tester;
+ moco::SqueezeGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(squeeze_some_pbtxtdata, nodedef));
+
+ // what to test:
+ // - there should exist TFSqueeze
+ // - input node should not be null
+ // - squeeze_dims attribute is set same as pbtxt
+
+ tester.inputs({"Placeholder"});
+ tester.output("Squeeze");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = dynamic_cast<moco::TFSqueeze *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->squeeze_dims().size(), 1);
+ ASSERT_EQ(test_node->squeeze_dims().at(0), 1);
+}
+
+// TODO Add test case for negative squeeze dim
diff --git a/compiler/moco/import/src/Nodes/StopGradient.cpp b/compiler/moco/import/src/Nodes/StopGradient.cpp
new file mode 100644
index 000000000..9caec6943
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/StopGradient.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/StopGradient.h"
+
+#include <moco/IR/Nodes/TFStopGradient.h>
+
+#include <loco.h>
+#include <plier/tf/Convert.h>
+#include <stdex/Memory.h>
+
+namespace
+{
+
+using namespace moco;
+
+/**
+ * @brief GraphUpdate for TF StopGradient node
+ */
+class TFStopGradientGraphUpdate final : public GraphUpdate
+{
+public:
+ TFStopGradientGraphUpdate(TFStopGradient *node, TensorName &&name) : _node(node), _name(name) {}
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFStopGradient *_node;
+ TensorName _name;
+};
+
+void TFStopGradientGraphUpdate::input(const SymbolTable *table) const
+{
+ loco::Node *target = table->node(_name);
+ _node->input(target);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool StopGradientGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ if (node.input_size() != 1)
+ return false;
+
+ return plier::tf::has_attrs(node, {"T"});
+}
+
+void StopGradientGraphBuilder::build(const tensorflow::NodeDef &node,
+ GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // creating TF dialect StopGradient node
+ auto tf_stopgradient = graph->nodes()->create<TFStopGradient>();
+ tf_stopgradient->name(node.name());
+
+ // register string-name to node
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, tf_stopgradient);
+
+ // Queue node input update
+ auto tf_stopgradient_update =
+ stdex::make_unique<TFStopGradientGraphUpdate>(tf_stopgradient, TensorName(node.input(0)));
+ updates->enroll(std::move(tf_stopgradient_update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/StopGradient.test.cpp b/compiler/moco/import/src/Nodes/StopGradient.test.cpp
new file mode 100644
index 000000000..0bf70ebcc
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/StopGradient.test.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/StopGradient.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *stopgradient_basic_pbtxt = STRING_CONTENT(
+ name: "StopGradient_01"
+ op: "StopGradient"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, tf_stopgradient_basic)
+{
+ TFNodeBuildTester tester;
+ moco::StopGradientGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(stopgradient_basic_pbtxt, nodedef));
+
+ // what to test:
+ // - TFStopGradient node should exist
+ // - input() should not be null
+
+ tester.inputs({"Placeholder"});
+ tester.output("StopGradient_01");
+ tester.run(nodedef, graphbuilder);
+}
diff --git a/compiler/moco/import/src/Nodes/StridedSlice.cpp b/compiler/moco/import/src/Nodes/StridedSlice.cpp
new file mode 100644
index 000000000..06d388be0
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/StridedSlice.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/StridedSlice.h"
+
+#include <moco/IR/Nodes/TFStridedSlice.h>
+#include <moco/IR/Nodes/TFConst.h>
+
+#include <moco/Names.h>
+
+#include "Convert.h"
+
+#include <loco.h>
+#include <stdex/Memory.h>
+#include <plier/tf/Convert.h>
+#include <oops/UserExn.h>
+
+namespace
+{
+using namespace moco;
+
+class TFStridedSliceGraphUpdate final : public GraphUpdate
+{
+public:
+ TFStridedSliceGraphUpdate(TFStridedSlice *node, std::vector<TensorName> names)
+ : _node(node), _names(names)
+ {
+ }
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFStridedSlice *_node;
+ std::vector<TensorName> _names;
+};
+
+void TFStridedSliceGraphUpdate::input(const SymbolTable *node_table) const
+{
+ // TODO support size 3 where strides is None
+ assert(_names.size() == 4);
+
+ auto input_node = node_table->node(_names[0]);
+ auto begin_node = node_table->node(_names[1]);
+ auto end_node = node_table->node(_names[2]);
+ auto strides_node = node_table->node(_names[3]);
+ assert(input_node != nullptr);
+ assert(begin_node != nullptr);
+ assert(end_node != nullptr);
+ assert(strides_node != nullptr);
+
+ _node->input(input_node);
+ _node->begin(begin_node);
+ _node->end(end_node);
+ _node->strides(strides_node);
+
+ // TODO move validation codes to some suitable place
+ // Run basic validation
+
+ // TODO support full mask features
+ if (_node->begin_mask() != 0 || _node->end_mask() != 0 || _node->ellipsis_mask() != 0 ||
+ _node->new_axis_mask() != 0 || _node->shrink_axis_mask() != 1)
+ {
+ throw oops::UserExn("Mask attributes are not supported for now: ", _node->name());
+ }
+
+ // Only Const are supported for now
+ auto const_input = dynamic_cast<moco::TFConst *>(_node->input());
+ auto const_begin = dynamic_cast<moco::TFConst *>(_node->begin());
+ auto const_end = dynamic_cast<moco::TFConst *>(_node->end());
+ auto const_strides = dynamic_cast<moco::TFConst *>(_node->strides());
+ if (const_input == nullptr || const_begin == nullptr || const_end == nullptr ||
+ const_strides == nullptr)
+ {
+ throw oops::UserExn("Only Const inputs are supported for now: ", _node->name());
+ }
+
+ // TODO support S64
+ if (const_begin->dtype() != loco::DataType::S32 || const_end->dtype() != loco::DataType::S32 ||
+ const_strides->dtype() != loco::DataType::S32)
+ {
+ throw oops::UserExn("Only Const types of INT32 are supported for begin/end/strides for now: ",
+ _node->name());
+ }
+
+ // Input Rank should match number of elements of the begin/end/strides
+ auto rin = const_input->rank();
+ if (rin != const_begin->size<loco::DataType::S32>() ||
+ rin != const_end->size<loco::DataType::S32>() ||
+ rin != const_strides->size<loco::DataType::S32>())
+ {
+ throw oops::UserExn("Ranks for inputs should be same: ", _node->name());
+ }
+
+ // TODO support strides type of S64
+ // TODO support other strides value
+ // Only support stride 1 for now
+ uint32_t elements = const_strides->size<loco::DataType::S32>();
+ for (uint32_t e = 0; e < elements; ++e)
+ {
+ if (const_strides->at<loco::DataType::S32>(e) != 1)
+ {
+ throw oops::UserExn("Only stride 1 is supported for now: ", _node->name());
+ }
+ }
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool StridedSliceGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ // TODO support node.input_size() == 3 where strides is None
+ if (node.input_size() != 4)
+ return false;
+
+ if (!plier::tf::has_attrs(node, {"T", "Index", "begin_mask", "end_mask", "ellipsis_mask",
+ "new_axis_mask", "shrink_axis_mask"}))
+ return false;
+
+ return true;
+}
+
+void StridedSliceGraphBuilder::build(const tensorflow::NodeDef &node,
+ GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ std::string node_name = node.name();
+
+ auto stridedslice = graph->nodes()->create<TFStridedSlice>();
+ stridedslice->name(node_name);
+
+ // read attributes
+ auto begin_mask = plier::tf::get_int_attr(node, "begin_mask");
+ auto end_mask = plier::tf::get_int_attr(node, "end_mask");
+ auto ellipsis_mask = plier::tf::get_int_attr(node, "ellipsis_mask");
+ auto new_axis_mask = plier::tf::get_int_attr(node, "new_axis_mask");
+ auto shrink_axis_mask = plier::tf::get_int_attr(node, "shrink_axis_mask");
+
+ stridedslice->begin_mask(begin_mask);
+ stridedslice->end_mask(end_mask);
+ stridedslice->ellipsis_mask(ellipsis_mask);
+ stridedslice->new_axis_mask(new_axis_mask);
+ stridedslice->shrink_axis_mask(shrink_axis_mask);
+
+ // TODO support general mask values: we support only this limited case for now
+ assert(begin_mask == 0);
+ assert(end_mask == 0);
+ assert(ellipsis_mask == 0);
+ assert(new_axis_mask == 0);
+ assert(shrink_axis_mask == 1);
+
+ // save the name for graph link updates
+ TensorName output_name(node_name, 0);
+ tensor_names->enroll(output_name, stridedslice);
+
+ std::vector<TensorName> input_names;
+ input_names.push_back(TensorName(node.input(0))); // input
+ input_names.push_back(TensorName(node.input(1))); // begin
+ input_names.push_back(TensorName(node.input(2))); // end
+ input_names.push_back(TensorName(node.input(3))); // strides
+
+ auto tfconv2d_update = stdex::make_unique<TFStridedSliceGraphUpdate>(stridedslice, input_names);
+
+ updates->enroll(std::move(tfconv2d_update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/StridedSlice.test.cpp b/compiler/moco/import/src/Nodes/StridedSlice.test.cpp
new file mode 100644
index 000000000..dd3c13314
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/StridedSlice.test.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/StridedSlice.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *stridedslice_basic_pbtxt = STRING_CONTENT(
+ name: "StridedSlice"
+ op: "StridedSlice"
+ input: "input"
+ input: "begin"
+ input: "end"
+ input: "strides"
+ attr {
+ key: "Index"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "begin_mask"
+ value {
+ i: 0
+ }
+ }
+ attr {
+ key: "ellipsis_mask"
+ value {
+ i: 0
+ }
+ }
+ attr {
+ key: "end_mask"
+ value {
+ i: 0
+ }
+ }
+ attr {
+ key: "new_axis_mask"
+ value {
+ i: 0
+ }
+ }
+ attr {
+ key: "shrink_axis_mask"
+ value {
+ i: 1
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, tf_stridedslice_basic)
+{
+ TFNodeBuildTester tester;
+ moco::StridedSliceGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(stridedslice_basic_pbtxt, nodedef));
+
+ // what to test:
+ // - TFStridedSlice node should exist
+ // - inputs should not be nullptr
+ // - attributes should match the values
+
+ tester.inputs({"input", "begin", "end", "strides"}, loco::DataType::S32);
+ tester.output("StridedSlice");
+ tester.run(nodedef, graphbuilder);
+
+ auto test_node = loco::must_cast<moco::TFStridedSlice *>(tester.output());
+ ASSERT_NE(test_node, nullptr);
+ ASSERT_EQ(test_node->begin_mask(), 0);
+ ASSERT_EQ(test_node->end_mask(), 0);
+ ASSERT_EQ(test_node->ellipsis_mask(), 0);
+ ASSERT_EQ(test_node->new_axis_mask(), 0);
+ ASSERT_EQ(test_node->shrink_axis_mask(), 1);
+}
+
+// TODO add test where strides is None
diff --git a/compiler/moco/import/src/Nodes/Sub.cpp b/compiler/moco/import/src/Nodes/Sub.cpp
new file mode 100644
index 000000000..bdad81d67
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Sub.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Sub.h"
+
+#include <moco/IR/Nodes/TFSub.h>
+
+#include <loco.h>
+#include <stdex/Memory.h>
+
+namespace
+{
+
+using namespace moco;
+
+/**
+ * @brief GraphUpdate for TF Sub node
+ */
+class TFSubGraphUpdate final : public GraphUpdate
+{
+public:
+ TFSubGraphUpdate(TFSub *node, std::vector<TensorName> names) : _node(node), _names(names) {}
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFSub *_node;
+ std::vector<TensorName> _names;
+};
+
+void TFSubGraphUpdate::input(const SymbolTable *tensor_names) const
+{
+ assert(_names.size() == 2);
+
+ _node->x(tensor_names->node(_names[0]));
+ _node->y(tensor_names->node(_names[1]));
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool SubGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ return node.input_size() == 2;
+}
+
+void SubGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // creating TF dialect Sub node
+ auto tf_sub = graph->nodes()->create<TFSub>();
+ tf_sub->name(node.name());
+
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, tf_sub);
+
+ std::vector<TensorName> sub_input_names;
+ sub_input_names.push_back(TensorName(node.input(0))); // x
+ sub_input_names.push_back(TensorName(node.input(1))); // y
+
+ auto tf_sub_update = stdex::make_unique<TFSubGraphUpdate>(tf_sub, sub_input_names);
+ updates->enroll(std::move(tf_sub_update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Sub.test.cpp b/compiler/moco/import/src/Nodes/Sub.test.cpp
new file mode 100644
index 000000000..05f1fb0d6
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Sub.test.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Sub.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *sub_basic_pbtxt = STRING_CONTENT(
+ name: "SUB_01"
+ op: "Sub"
+ input: "input_01"
+ input: "input_02"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, tf_sub_basic)
+{
+ TFNodeBuildTester tester;
+ moco::SubGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(sub_basic_pbtxt, nodedef));
+
+ // what to test:
+ // - TFSub node should exist
+ // - both inputs x() and y() should not be null
+
+ tester.inputs({"input_01", "input_02"});
+ tester.output("SUB_01");
+ tester.run(nodedef, graphbuilder);
+}
diff --git a/compiler/moco/import/src/Nodes/Tanh.cpp b/compiler/moco/import/src/Nodes/Tanh.cpp
new file mode 100644
index 000000000..c89fa862a
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Tanh.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Tanh.h"
+
+#include <moco/IR/Nodes/TFTanh.h>
+
+#include <loco.h>
+#include <stdex/Memory.h>
+
+namespace
+{
+
+using namespace moco;
+
+/**
+ * @brief GraphUpdate for TF Tanh node
+ */
+class TFTanhGraphUpdate final : public GraphUpdate
+{
+public:
+ TFTanhGraphUpdate(TFTanh *node, TensorName &&name) : _node(node), _name(name) {}
+
+ void input(const SymbolTable *) const override;
+
+private:
+ TFTanh *_node;
+ TensorName _name;
+};
+
+void TFTanhGraphUpdate::input(const SymbolTable *table) const
+{
+ loco::Node *target = table->node(_name);
+ _node->x(target);
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool TanhGraphBuilder::validate(const tensorflow::NodeDef &node) const
+{
+ return node.input_size() == 1;
+}
+
+void TanhGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // creating TF dialect Tanh node
+ auto tf_tanh = graph->nodes()->create<TFTanh>();
+ tf_tanh->name(node.name());
+
+ // register string-name to node
+ TensorName output_name(node.name(), 0);
+ tensor_names->enroll(output_name, tf_tanh);
+
+ // Queue node input update
+ auto tf_tanh_update = stdex::make_unique<TFTanhGraphUpdate>(tf_tanh, TensorName(node.input(0)));
+ updates->enroll(std::move(tf_tanh_update));
+}
+
+} // namespace moco
diff --git a/compiler/moco/import/src/Nodes/Tanh.test.cpp b/compiler/moco/import/src/Nodes/Tanh.test.cpp
new file mode 100644
index 000000000..20ebd15b2
--- /dev/null
+++ b/compiler/moco/import/src/Nodes/Tanh.test.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Import/Nodes/Tanh.h"
+#include "TestHelper.h"
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+// clang-format off
+const char *tanh_basic_pbtxt = STRING_CONTENT(
+ name: "output/tanh"
+ op: "Tanh"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+);
+// clang-format on
+
+} // namespace
+
+TEST(TensorFlowImport, tf_tanh_basic)
+{
+ TFNodeBuildTester tester;
+ moco::TanhGraphBuilder graphbuilder;
+ tensorflow::NodeDef nodedef;
+
+ EXPECT_TRUE(plier::tf::parse_nodedef(tanh_basic_pbtxt, nodedef));
+
+ // what to test:
+ // - TFTanh node should exist
+ // - input x() should not be null
+
+ tester.inputs({"Placeholder"});
+ tester.output("output/tanh");
+ tester.run(nodedef, graphbuilder);
+}
diff --git a/compiler/moco/import/src/TestHelper.h b/compiler/moco/import/src/TestHelper.h
new file mode 100644
index 000000000..54ca45b4a
--- /dev/null
+++ b/compiler/moco/import/src/TestHelper.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TEST_HELPER_H__
+#define __TEST_HELPER_H__
+
+#include "moco/Import/GraphBuilder.h"
+
+#include <moco/IR/TFNode.h>
+#include <loco.h>
+#include <plier/tf/TestHelper.h>
+
+#include <tensorflow/core/framework/graph.pb.h>
+
+#define STRING_CONTENT(content) #content
+
+namespace moco
+{
+namespace test
+{
+
+template <typename T> T *find_first_node_bytype(loco::Graph *g)
+{
+ T *first_node = nullptr;
+ loco::Graph::NodeContext *nodes = g->nodes();
+ uint32_t count = nodes->size();
+
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ first_node = dynamic_cast<T *>(nodes->at(i));
+ if (first_node != nullptr)
+ break;
+ }
+
+ return first_node;
+}
+
+} // namespace test
+} // namespace moco
+
+namespace moco
+{
+namespace test
+{
+
+class TFNodeBuildTester
+{
+public:
+ TFNodeBuildTester();
+
+public:
+ void inputs(const std::vector<std::string> &names);
+ void inputs(const std::vector<std::string> &names, const loco::DataType dtype);
+ void output(const char *name);
+ moco::TFNode *output(void);
+
+ void run(tensorflow::NodeDef &node_def, moco::GraphBuilder &graph_builder);
+
+private:
+ std::unique_ptr<moco::SymbolTable> _tensor_names;
+ std::unique_ptr<loco::Graph> _graph;
+
+ std::vector<moco::TFNode *> _inputs;
+ const char *_output{nullptr};
+};
+
+} // namespace test
+} // namespace moco
+
+#endif // __TEST_HELPER_H__
diff --git a/compiler/moco/import/src/TestHelper.test.cpp b/compiler/moco/import/src/TestHelper.test.cpp
new file mode 100644
index 000000000..06c3dd372
--- /dev/null
+++ b/compiler/moco/import/src/TestHelper.test.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestHelper.h"
+
+#include <moco/IR/Nodes/TFConst.h>
+#include <stdex/Memory.h>
+
+#include <gtest/gtest.h>
+
+namespace moco
+{
+namespace test
+{
+
+TFNodeBuildTester::TFNodeBuildTester()
+{
+ _graph = loco::make_graph();
+ _tensor_names = stdex::make_unique<moco::SymbolTable>();
+}
+
+void TFNodeBuildTester::inputs(const std::vector<std::string> &names)
+{
+ for (auto name : names)
+ {
+ auto input = _graph->nodes()->create<moco::TFConst>();
+ moco::TensorName name_01(name, 0);
+ _tensor_names->enroll(name_01, input);
+
+ _inputs.push_back(input);
+ }
+}
+
+void TFNodeBuildTester::inputs(const std::vector<std::string> &names, const loco::DataType dtype)
+{
+ for (auto name : names)
+ {
+ auto input = _graph->nodes()->create<moco::TFConst>();
+ input->dtype(dtype);
+ moco::TensorName name_01(name, 0);
+ _tensor_names->enroll(name_01, input);
+
+ _inputs.push_back(input);
+ }
+}
+
+void TFNodeBuildTester::output(const char *name) { _output = name; }
+
+moco::TFNode *TFNodeBuildTester::output(void)
+{
+ assert(_output != nullptr);
+
+ moco::TensorName tname(_output, 0);
+ return static_cast<moco::TFNode *>(_tensor_names->node(tname));
+}
+
+void TFNodeBuildTester::run(tensorflow::NodeDef &nodedef, moco::GraphBuilder &graphbuilder)
+{
+ assert(_output != nullptr);
+
+ auto node_defs = stdex::make_unique<moco::NodeDefTable>();
+ auto updates = stdex::make_unique<moco::UpdateQueue>();
+
+ moco::GraphBuilderContext gb_context(_graph.get(), node_defs.get(), _tensor_names.get(),
+ updates.get());
+
+ EXPECT_TRUE(graphbuilder.validate(nodedef));
+ graphbuilder.build(nodedef, &gb_context);
+
+ for (auto &update : updates->queue())
+ {
+ update->input(_tensor_names.get());
+ }
+
+ auto tfnode = output();
+ ASSERT_NE(tfnode, nullptr);
+ ASSERT_STREQ(tfnode->name().c_str(), _output);
+
+ int idx = 0;
+ ASSERT_EQ(tfnode->arity(), _inputs.size());
+ for (auto input : _inputs)
+ {
+ ASSERT_EQ(tfnode->arg(idx++), input);
+ }
+}
+
+} // namespace test
+} // namespace moco
diff --git a/compiler/moco/lang/CMakeLists.txt b/compiler/moco/lang/CMakeLists.txt
new file mode 100644
index 000000000..a64fdf92a
--- /dev/null
+++ b/compiler/moco/lang/CMakeLists.txt
@@ -0,0 +1,21 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(moco_lang SHARED ${SOURCES})
+target_include_directories(moco_lang PRIVATE src)
+target_include_directories(moco_lang PUBLIC include)
+target_link_libraries(moco_lang PUBLIC loco)
+target_link_libraries(moco_lang PRIVATE nncc_common)
+target_link_libraries(moco_lang PRIVATE stdex)
+install(TARGETS moco_lang DESTINATION lib) # moco_tf_frontend requires moco_lang
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(moco_lang_test ${TESTS})
+target_include_directories(moco_lang_test PRIVATE src)
+target_link_libraries(moco_lang_test moco_lang)
diff --git a/compiler/moco/lang/README.md b/compiler/moco/lang/README.md
new file mode 100644
index 000000000..6ee3fc660
--- /dev/null
+++ b/compiler/moco/lang/README.md
@@ -0,0 +1,3 @@
+# lang
+
+`lang` provides TensorFlow Dialect IR
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFAdd.h b/compiler/moco/lang/include/moco/IR/Nodes/TFAdd.h
new file mode 100644
index 000000000..13b064fba
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFAdd.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFADD_H__
+#define __MOCO_IR_TFADD_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+
+/// @note TFAdd corresponds to the following GraphDef
+/*
+node {
+ name: "add"
+ op: "Add"
+ input: "x"
+ input: "y"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+*/
+
+class TFAdd final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Add>>
+{
+public:
+ TFAdd() = default;
+
+public:
+ Node *x(void) const { return at(0)->node(); }
+ void x(Node *node) { at(0)->node(node); }
+
+ Node *y(void) const { return at(1)->node(); }
+ void y(Node *node) { at(1)->node(node); }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFADD_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFAvgPool.h b/compiler/moco/lang/include/moco/IR/Nodes/TFAvgPool.h
new file mode 100644
index 000000000..74c91b5fb
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFAvgPool.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFAVGPOOL_H__
+#define __MOCO_IR_TFAVGPOOL_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+#include <vector>
+
+namespace moco
+{
+
+/// @note TFAvgPool corresponds to the following GraphDef
+/*
+node {
+ name: "avgpool"
+ op: "AvgPool"
+ input: "placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "data_format"
+ value {
+ s: "NHWC"
+ }
+ }
+ attr {
+ key: "ksize"
+ value {
+ list {
+ i: 1 i: 3 i: 3 i: 1
+ }
+ }
+ }
+ attr {
+ key: "padding"
+ value {
+ s: "SAME"
+ }
+ }
+ attr {
+ key: "strides"
+ value {
+ list {
+ i: 1 i: 1 i: 1 i: 1
+ }
+ }
+ }
+}
+*/
+
+class TFAvgPool final : public FixedArityNode<1, TFNodeImpl<TFOpcode::AvgPool>>
+{
+public:
+ TFAvgPool() = default;
+
+public:
+ Node *value(void) const { return at(0)->node(); }
+ void value(Node *node) { return at(0)->node(node); }
+
+public:
+ const TFDataLayout &data_layout(void) const { return _data_layout; }
+ void data_layout(const TFDataLayout &data_layout) { _data_layout = data_layout; }
+
+ const TFPadding &padding(void) const { return _padding; }
+ void padding(const TFPadding &padding) { _padding = padding; }
+
+ const std::vector<int64_t> &ksize(void) const { return _ksize; }
+ void ksize(const std::vector<int64_t> &ksize) { _ksize = ksize; }
+
+ const std::vector<int64_t> &strides(void) const { return _strides; }
+ void strides(const std::vector<int64_t> &strides) { _strides = strides; }
+
+private:
+ TFDataLayout _data_layout;
+ TFPadding _padding;
+ std::vector<int64_t> _ksize;
+ std::vector<int64_t> _strides;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFAVGPOOL_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFBiasAdd.h b/compiler/moco/lang/include/moco/IR/Nodes/TFBiasAdd.h
new file mode 100644
index 000000000..11e309caa
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFBiasAdd.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFBIASADD_H__
+#define __MOCO_IR_TFBIASADD_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+
+/// @note TFBiasAdd corresponds to the following GraphDef
+/*
+node {
+ name: "bias_add_01"
+ op: "BiasAdd"
+ input: "input_01"
+ input: "bias_add_01/bias"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "data_format"
+ value {
+ s: "NHWC"
+ }
+ }
+}
+*/
+
+class TFBiasAdd final : public FixedArityNode<2, TFNodeImpl<TFOpcode::BiasAdd>>
+{
+public:
+ TFBiasAdd() = default;
+
+public:
+ Node *value(void) const { return at(0)->node(); }
+ void value(Node *node) { return at(0)->node(node); }
+
+ Node *bias(void) const { return at(1)->node(); }
+ void bias(Node *node) { return at(1)->node(node); }
+
+ const TFDataLayout data_layout(void) const { return _data_layout; }
+ void data_layout(const TFDataLayout &data_layout) { _data_layout = data_layout; }
+
+private:
+ TFDataLayout _data_layout;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFBIASADD_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFConcatV2.h b/compiler/moco/lang/include/moco/IR/Nodes/TFConcatV2.h
new file mode 100644
index 000000000..7f0d32697
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFConcatV2.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFCONCATV2_H__
+#define __MOCO_IR_TFCONCATV2_H__
+
+#include "moco/IR/TFNodeDecl.h"
+#include "moco/IR/VariadicArityNode.h"
+
+namespace moco
+{
+
+/// @note TFConcatV2 corresponds to the following GraphDef
+/*
+node {
+ name: "Concat"
+ op: "ConcatV2"
+ input: "Input01"
+ input: "Input02"
+ input: "Axis"
+ attr {
+ key: "N"
+ value {
+ i: 2
+ }
+ }
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "Tidx"
+ value {
+ type: DT_INT32
+ }
+ }
+}
+*/
+
+class TFConcatV2 final : public VariadicArityNode<TFNodeImpl<TFOpcode::ConcatV2>>
+{
+public:
+ TFConcatV2(uint32_t arity) : VariadicArityNode<TFNodeImpl<TFOpcode::ConcatV2>>(arity + 1)
+ {
+ // we add +1 for axis of VariadicArityNode ctor
+ // at least one value is required
+ assert(arity >= 1);
+ }
+
+public:
+ uint32_t num_values(void) const
+ {
+ // last one is for axis
+ return arity() - 1;
+ }
+
+public:
+ Node *values(uint32_t index) const
+ {
+ assert(index < num_values());
+ return at(index)->node();
+ }
+ void values(uint32_t index, Node *node)
+ {
+ assert(index < num_values());
+ at(index)->node(node);
+ }
+
+ Node *axis(void) const { return at(num_values())->node(); }
+ void axis(Node *node) { at(num_values())->node(node); }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFCONCATV2_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFConst.h b/compiler/moco/lang/include/moco/IR/Nodes/TFConst.h
new file mode 100644
index 000000000..7c2595fcb
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFConst.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFCONSTANT_H__
+#define __MOCO_IR_TFCONSTANT_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+#include <loco/IR/DataTypeTraits.h>
+#include <loco/IR/NodeMixins.h>
+#include <loco/IR/TensorShape.h>
+
+#include <vector>
+
+namespace moco
+{
+
+/// @note TFConst corresponds to the following GraphDef
+/*
+node {
+ name: "val"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value { type: DT_FLOAT }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim { size: 1 }
+ dim { size: 3 }
+ dim { size: 4 }
+ dim { size: 4 }
+ }
+ float_val: 2.1
+ }
+ }
+ }
+}
+*/
+
+/**
+ * @brief IR for tf.constant
+ *
+ * @note Implementation for this class came from Canonical ConstGen
+ * Read comments in loco::ConstGen for details
+ */
+class TFConst final : public FixedArityNode<0, TFNodeImpl<TFOpcode::Const>>,
+ public loco::NodeMixin<loco::NodeTrait::DataType>,
+ public loco::NodeMixin<loco::NodeTrait::TensorShape>
+{
+public:
+ TFConst() = default;
+
+public:
+ template <loco::DataType DT> uint32_t size(void) const;
+ template <loco::DataType DT> void size(uint32_t size);
+
+ template <loco::DataType DT> const typename loco::DataTypeImpl<DT>::Type &at(uint32_t n) const;
+ template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &at(uint32_t n);
+
+private:
+ std::vector<uint8_t> _data;
+};
+
+} // namespace moco
+
+namespace moco
+{
+
+loco::TensorShape tensor_shape(const TFConst *node);
+
+uint32_t num_elements(const TFConst *tfconst);
+bool same_shape(const TFConst *lhs, const TFConst *rhs);
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFCONSTANT_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFConv2D.h b/compiler/moco/lang/include/moco/IR/Nodes/TFConv2D.h
new file mode 100644
index 000000000..0d5a17879
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFConv2D.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFCONV2D_H__
+#define __MOCO_IR_TFCONV2D_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+#include <vector>
+
+namespace moco
+{
+
+class TFConv2D final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Conv2D>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(Node *node) { at(0)->node(node); }
+
+ loco::Node *filter(void) const { return at(1)->node(); }
+ void filter(Node *node) { at(1)->node(node); }
+
+public:
+ const TFPadding &padding(void) const { return _padding; }
+ void padding(const TFPadding &padding) { _padding = padding; }
+
+ const TFDataLayout &data_layout(void) const { return _data_layout; }
+ void data_layout(const TFDataLayout &data_layout) { _data_layout = data_layout; }
+
+ const std::vector<int64_t> &strides(void) const { return _strides; }
+ void strides(const std::vector<int64_t> &strides) { _strides = strides; }
+
+private:
+ TFPadding _padding;
+ TFDataLayout _data_layout;
+ std::vector<int64_t> _strides;
+ // TODO Support "Dilation"
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFCONV2D_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFConv2DBackpropInput.h b/compiler/moco/lang/include/moco/IR/Nodes/TFConv2DBackpropInput.h
new file mode 100644
index 000000000..43e620d24
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFConv2DBackpropInput.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFCONV2DBACKPROPINPUT_H__
+#define __MOCO_IR_TFCONV2DBACKPROPINPUT_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+#include <vector>
+
+namespace moco
+{
+
+/// @note TFConv2DBackpropInput corresponds to the following GraphDef
+/*
+node {
+ name: "conv2d_backprop_input"
+ op: "Conv2DBackpropInput"
+ input: "input_sizes"
+ input: "filter"
+ input: "out_backprop"
+ attr {
+ key: "T"
+ value { type: DT_FLOAT }
+ }
+ attr {
+ key: "data_format"
+ value { s: "NHWC" }
+ }
+ attr {
+ key: "dilations"
+ value {
+ list { i: 1 i: 1 i: 1 i: 1 }
+ }
+ }
+ attr {
+ key: "padding"
+ value { s: "SAME" }
+ }
+ attr {
+ key: "strides"
+ value {
+ list { i: 1 i: 2 i: 2 i: 1 }
+ }
+ }
+}
+*/
+
+/**
+ * @note For Tensorflow Conv2DBackpropInput, 'input' refers actual output of the
+ * node, and 'input' refers actual input. The reasone of this is, as name
+ * suggests, because it is inspired from backpropagation of convolution.
+ * For example, 'out_backprop' of Conv2DBackpropInput is its actual input
+ * feature map, and 'input_sizes' means desired output node's size.
+ * Note that this convention is against loco canonical's convention.
+ */
+class TFConv2DBackpropInput final
+ : public FixedArityNode<3, TFNodeImpl<TFOpcode::Conv2DBackpropInput>>
+{
+public:
+ loco::Node *input_sizes(void) const { return at(0)->node(); }
+ void input_sizes(Node *node) { at(0)->node(node); }
+
+ loco::Node *filter(void) const { return at(1)->node(); }
+ void filter(Node *node) { at(1)->node(node); }
+
+ loco::Node *out_backprop(void) const { return at(2)->node(); }
+ void out_backprop(Node *node) { at(2)->node(node); }
+
+public:
+ const TFPadding &padding(void) const { return _padding; }
+ void padding(const TFPadding &padding) { _padding = padding; }
+
+ const TFDataLayout &data_layout(void) const { return _data_layout; }
+ void data_layout(const TFDataLayout &data_layout) { _data_layout = data_layout; }
+
+ const std::vector<int64_t> &strides(void) const { return _strides; }
+ void strides(const std::vector<int64_t> &strides) { _strides = strides; }
+
+private:
+ TFPadding _padding;
+ TFDataLayout _data_layout;
+ std::vector<int64_t> _strides;
+ // TODO Support "Dilation"
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFCONV2DBACKPROPINPUT_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFDepthwiseConv2dNative.h b/compiler/moco/lang/include/moco/IR/Nodes/TFDepthwiseConv2dNative.h
new file mode 100644
index 000000000..aefc0b5d9
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFDepthwiseConv2dNative.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFDEPTHWISECONV2DNATIVE_H__
+#define __MOCO_IR_TFDEPTHWISECONV2DNATIVE_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+#include <vector>
+
+namespace moco
+{
+
+class TFDepthwiseConv2dNative final
+ : public FixedArityNode<2, TFNodeImpl<TFOpcode::DepthwiseConv2dNative>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(Node *node) { at(0)->node(node); }
+
+ loco::Node *filter(void) const { return at(1)->node(); }
+ void filter(Node *node) { at(1)->node(node); }
+
+public:
+ const TFPadding &padding(void) const { return _padding; }
+ void padding(const TFPadding &padding) { _padding = padding; }
+
+ const TFDataLayout &data_layout(void) const { return _data_layout; }
+ void data_layout(const TFDataLayout &data_layout) { _data_layout = data_layout; }
+
+ const std::vector<int64_t> &strides(void) const { return _strides; }
+ void strides(const std::vector<int64_t> &strides) { _strides = strides; }
+
+private:
+ TFPadding _padding;
+ TFDataLayout _data_layout;
+ std::vector<int64_t> _strides;
+ // TODO Support "Dilation"
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFDEPTHWISECONV2DNATIVE_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFFakeQuantWithMinMaxVars.h b/compiler/moco/lang/include/moco/IR/Nodes/TFFakeQuantWithMinMaxVars.h
new file mode 100644
index 000000000..ec54da596
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFFakeQuantWithMinMaxVars.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFFAKEQUANTWITHMINMAXVARS_H__
+#define __MOCO_IR_TFFAKEQUANTWITHMINMAXVARS_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+#include <vector>
+
+namespace moco
+{
+
+class TFFakeQuantWithMinMaxVars final
+ : public FixedArityNode<3, TFNodeImpl<TFOpcode::FakeQuantWithMinMaxVars>>
+{
+public:
+ loco::Node *inputs(void) const { return at(0)->node(); }
+ void inputs(Node *node) { at(0)->node(node); }
+
+ loco::Node *min(void) const { return at(1)->node(); }
+ void min(Node *node) { at(1)->node(node); }
+
+ loco::Node *max(void) const { return at(2)->node(); }
+ void max(Node *node) { at(2)->node(node); }
+
+public:
+ const int64_t &num_bits(void) const { return _num_bits; }
+ void num_bits(const int64_t &num_bits) { _num_bits = num_bits; }
+
+ const bool &narrow_range(void) const { return _narrow_range; }
+ void narrow_range(const bool &narrow_range) { _narrow_range = narrow_range; }
+
+private:
+ int64_t _num_bits{8};
+ bool _narrow_range{false};
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFFAKEQUANTWITHMINMAXVARS_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFFusedBatchNorm.h b/compiler/moco/lang/include/moco/IR/Nodes/TFFusedBatchNorm.h
new file mode 100644
index 000000000..5b980e3b2
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFFusedBatchNorm.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFFUSEDBATCHNORM_H__
+#define __MOCO_IR_TFFUSEDBATCHNORM_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+
+class TFFusedBatchNorm final : public FixedArityNode<5, TFNodeImpl<TFOpcode::FusedBatchNorm>>
+{
+public:
+ TFFusedBatchNorm() = default;
+
+public:
+ Node *x(void) const { return at(0)->node(); }
+ void x(Node *node) { at(0)->node(node); }
+
+ Node *scale(void) const { return at(1)->node(); } // gamma
+ void scale(Node *node) { at(1)->node(node); }
+
+ Node *offset(void) const { return at(2)->node(); } // beta
+ void offset(Node *node) { at(2)->node(node); }
+
+ Node *mean(void) const { return at(3)->node(); }
+ void mean(Node *node) { at(3)->node(node); }
+
+ Node *variance(void) const { return at(4)->node(); }
+ void variance(Node *node) { at(4)->node(node); }
+
+ float epsilon(void) const { return _epsilon; }
+ void epsilon(float epsilon) { _epsilon = epsilon; }
+
+private:
+ float _epsilon = 0.001f;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFFUSEDBATCHNORM_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFIdentity.h b/compiler/moco/lang/include/moco/IR/Nodes/TFIdentity.h
new file mode 100644
index 000000000..26a1a36bf
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFIdentity.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFIDENTITY_H__
+#define __MOCO_IR_TFIDENTITY_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+
+/// @note TFIdentity corresponds to the following GraphDef
+/*
+node {
+ name: "identity"
+ op: "Identity"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+*/
+
+class TFIdentity final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Identity>>
+{
+public:
+ TFIdentity() = default;
+
+public:
+ Node *input(void) const { return at(0)->node(); }
+ void input(Node *node) { at(0)->node(node); }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFIDENTITY_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFMaxPool.h b/compiler/moco/lang/include/moco/IR/Nodes/TFMaxPool.h
new file mode 100644
index 000000000..a66b4044e
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFMaxPool.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFMAXPOOL_H__
+#define __MOCO_IR_TFMAXPOOL_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+#include <vector>
+
+namespace moco
+{
+
+/// @note TFMaxPool corresponds to the following GraphDef
+/*
+node {
+ name: "maxpool2d"
+ op: "MaxPool"
+ input: "placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "data_format"
+ value {
+ s: "NHWC"
+ }
+ }
+ attr {
+ key: "ksize"
+ value {
+ list {
+ i: 1 i: 2 i: 2 i: 1
+ }
+ }
+ }
+ attr {
+ key: "padding"
+ value {
+ s: "VALID"
+ }
+ }
+ attr {
+ key: "strides"
+ value {
+ list {
+ i: 1 i: 1 i: 1 i: 1
+ }
+ }
+ }
+}
+*/
+
+class TFMaxPool final : public FixedArityNode<1, TFNodeImpl<TFOpcode::MaxPool>>
+{
+public:
+ TFMaxPool() = default;
+
+public:
+ Node *input(void) const { return at(0)->node(); }
+ void input(Node *node) { return at(0)->node(node); }
+
+public:
+ const TFDataLayout &data_layout(void) const { return _data_layout; }
+ void data_layout(const TFDataLayout &data_layout) { _data_layout = data_layout; }
+
+ const TFPadding &padding(void) const { return _padding; }
+ void padding(const TFPadding &padding) { _padding = padding; }
+
+ const std::vector<int64_t> &ksize(void) const { return _ksize; }
+ void ksize(const std::vector<int64_t> &ksize) { _ksize = ksize; }
+
+ const std::vector<int64_t> &strides(void) const { return _strides; }
+ void strides(const std::vector<int64_t> &strides) { _strides = strides; }
+
+private:
+ TFDataLayout _data_layout;
+ TFPadding _padding;
+ std::vector<int64_t> _ksize;
+ std::vector<int64_t> _strides;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFMAXPOOL_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFMaximum.h b/compiler/moco/lang/include/moco/IR/Nodes/TFMaximum.h
new file mode 100644
index 000000000..346dbebe8
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFMaximum.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFMAXIMUM_H__
+#define __MOCO_IR_TFMAXIMUM_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+
+/// @note TFMaximum corresponds to the following GraphDef
+/*
+node {
+ name: "maximum"
+ op: "Maximum"
+ input: "x"
+ input: "y"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+*/
+
+class TFMaximum final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Maximum>>
+{
+public:
+ TFMaximum() = default;
+
+public:
+ Node *x(void) const { return at(0)->node(); }
+ void x(Node *node) { at(0)->node(node); }
+
+ Node *y(void) const { return at(1)->node(); }
+ void y(Node *node) { at(1)->node(node); }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFMAXIMUM_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFMean.h b/compiler/moco/lang/include/moco/IR/Nodes/TFMean.h
new file mode 100644
index 000000000..abcd21c49
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFMean.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFMEAN_H__
+#define __MOCO_IR_TFMEAN_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+#include <vector>
+
+namespace moco
+{
+
+/// @note TFMean corresponds to the following GraphDef
+/*
+node {
+ name: "Mean"
+ op: "Mean"
+ input: "Placeholder"
+ input: "Mean/reduction_indices"
+ attr {
+ key: "T"
+ value { type: DT_FLOAT }
+ }
+ attr {
+ key: "Tidx"
+ value { type: DT_INT32 }
+ }
+ attr {
+ key: "keep_dims"
+ value { b: true }
+ }
+}
+*/
+
+class TFMean final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Mean>>
+{
+public:
+ TFMean() = default;
+
+public:
+ Node *input(void) const { return at(0)->node(); }
+ void input(Node *node) { at(0)->node(node); }
+
+ Node *reduction_indices(void) const { return at(1)->node(); }
+ void reduction_indices(Node *node) { at(1)->node(node); }
+
+public:
+ bool keep_dims(void) const { return _keep_dims; }
+ void keep_dims(bool keep_dims) { _keep_dims = keep_dims; }
+
+private:
+ bool _keep_dims = false;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFMEAN_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFMul.h b/compiler/moco/lang/include/moco/IR/Nodes/TFMul.h
new file mode 100644
index 000000000..4692838cb
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFMul.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFMUL_H__
+#define __MOCO_IR_TFMUL_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+
+/// @note TFMul corresponds to the following GraphDef
+/*
+node {
+ name: "mul"
+ op: "Mul"
+ input: "x"
+ input: "y"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+*/
+
+class TFMul final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Mul>>
+{
+public:
+ TFMul() = default;
+
+public:
+ Node *x(void) const { return at(0)->node(); }
+ void x(Node *node) { at(0)->node(node); }
+
+ Node *y(void) const { return at(1)->node(); }
+ void y(Node *node) { at(1)->node(node); }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFMUL_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFPack.h b/compiler/moco/lang/include/moco/IR/Nodes/TFPack.h
new file mode 100644
index 000000000..1046a18ed
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFPack.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFPACK_H__
+#define __MOCO_IR_TFPACK_H__
+
+#include "moco/IR/TFNodeDecl.h"
+#include "moco/IR/VariadicArityNode.h"
+
+namespace moco
+{
+/// @note TFPack corresponds to the following GraphDef
+/*
+node {
+ name: "Pack"
+ op: "Pack"
+ input: "input_1"
+ input: "input_2"
+ attr {
+ key: "N"
+ value {
+ i: 2
+ }
+ }
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "axis"
+ value {
+ i: 0
+ }
+ }
+}
+*/
+
+class TFPack final : public VariadicArityNode<TFNodeImpl<TFOpcode::Pack>>
+{
+public:
+ TFPack(uint32_t arity) : VariadicArityNode<TFNodeImpl<TFOpcode::Pack>>(arity)
+ {
+ // at least one item should exist
+ assert(arity >= 1);
+ }
+
+public:
+ Node *values(uint32_t index) const
+ {
+ assert(index < arity());
+ return at(index)->node();
+ }
+ void values(uint32_t index, Node *node)
+ {
+ assert(index < arity());
+ at(index)->node(node);
+ }
+
+public:
+ uint32_t N(void) const { return arity(); }
+
+ int32_t axis(void) const { return _axis; }
+ void axis(int32_t axis) { _axis = axis; }
+
+private:
+ int32_t _axis{0};
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFPACK_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFPad.h b/compiler/moco/lang/include/moco/IR/Nodes/TFPad.h
new file mode 100644
index 000000000..dae4741d6
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFPad.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFPAD_H__
+#define __MOCO_IR_TFPAD_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+/// @note TFPad corresponds to the following GraphDef
+/*
+node {
+ name: "Pad"
+ op: "Pad"
+ input: "Const_tensor"
+ input: "Const_paddings"
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "Tpaddings"
+ value {
+ type: DT_INT32
+ }
+ }
+}
+*/
+
+class TFPad final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Pad>>
+{
+public:
+ TFPad() = default;
+
+public:
+ Node *input(void) const { return at(0)->node(); }
+ void input(Node *node) { at(0)->node(node); }
+
+ Node *paddings(void) const { return at(1)->node(); }
+ void paddings(Node *node) { at(1)->node(node); }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFPAD_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFPlaceholder.h b/compiler/moco/lang/include/moco/IR/Nodes/TFPlaceholder.h
new file mode 100644
index 000000000..65a78e665
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFPlaceholder.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFPLACEHOLDER_H__
+#define __MOCO_IR_TFPLACEHOLDER_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+#include <loco/IR/DataTypeTraits.h>
+#include <loco/IR/NodeMixins.h>
+#include <loco/IR/GraphInputIndex.h>
+#include <loco/IR/TensorShape.h>
+
+namespace moco
+{
+
+/// @note TFPlaceholder corresponds to the following GraphDef
+/*
+node {
+ name: "placeholder"
+ op: "Placeholder"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "shape"
+ value {
+ shape {
+ dim {
+ size: 1
+ }
+ dim {
+ size: 3
+ }
+ dim {
+ size: 3
+ }
+ dim {
+ size: 1
+ }
+ }
+ }
+ }
+}
+*/
+
+/**
+ * @brief IR for tf.placeholder
+ */
+class TFPlaceholder final : public FixedArityNode<0, TFNodeImpl<TFOpcode::Placeholder>>,
+ public loco::NodeMixin<loco::NodeTrait::DataType>,
+ public loco::NodeMixin<loco::NodeTrait::TensorShape>
+{
+public:
+ TFPlaceholder() = default;
+
+ // TODO Update unkown shape information. tensorflow::NodeDef may not have "shape" attr.
+};
+
+} // namespace moco
+
+namespace moco
+{
+
+bool indexed(const TFPlaceholder *node);
+loco::GraphInputIndex index(const TFPlaceholder *node);
+void index(TFPlaceholder *node, const loco::GraphInputIndex index);
+loco::TensorShape tensor_shape(const TFPlaceholder *node);
+
+TFPlaceholder *placeholder_node(loco::Graph *g, const loco::GraphInputIndex &idx);
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFPLACEHOLDER_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFPush.h b/compiler/moco/lang/include/moco/IR/Nodes/TFPush.h
new file mode 100644
index 000000000..e45804252
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFPush.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFPUSH_H__
+#define __MOCO_IR_TFPUSH_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+#include <loco.h>
+
+namespace moco
+{
+
+/**
+ * @brief Make a value visible to user
+ *
+ * @note TFPush is a virtual node that does not corresponds to real TensorFlow node
+ * Why this node is introduced:
+ * - Any TensorFlow Nodes can be an output.
+ * - So let any TFNode type can provide OutputIndex using Annotation.
+ * - Problem comes when in transformation, output node can be replaced.
+ * - This causes that OutputIndex Annotation should be copied to new node.
+ * - This makes every transformation in any Dialect code change.
+ * - And even worse, this makes every new transformation follow this rule.
+ * - Which is not good.
+ * - Thus, like loco Canonical does, follow loco::Push.
+ */
+class TFPush /* to user */ final : public FixedArityNode<1, TFNodeImpl<TFOpcode::TFPush>>
+{
+public:
+ TFPush() = default;
+
+public:
+ loco::Node *from(void) const { return at(0)->node(); }
+ void from(loco::Node *node) { at(0)->node(node); }
+
+public:
+ void index(const loco::GraphOutputIndex &index);
+
+ /**
+ * @brief Get associated output index
+ *
+ * The behavior of this method is undefined when "index" is not set before.
+ *
+ * NOTE This method intentionally returns "GraphOutputIndex" instead of "const GraphOutputIndex &"
+ * not to expose the internal implementation details.
+ */
+ loco::GraphOutputIndex index(void) const;
+
+ /**
+ * @brief Check whether index is initialized
+ *
+ * NOTE "indexed" method does not validate whether index is in a valid range
+ */
+ bool indexed(void) const { return _index != -1; }
+
+ /**
+ * @brief Reset output index
+ */
+ void index_reset(void) { _index = -1; }
+
+private:
+ int64_t _index = -1; // Uninitialized
+};
+
+/// @brief Find a TFPush node with a given output index
+TFPush *push_node(loco::Graph *g, const loco::GraphOutputIndex &index);
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFPUSH_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFRealDiv.h b/compiler/moco/lang/include/moco/IR/Nodes/TFRealDiv.h
new file mode 100644
index 000000000..8d61b3d13
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFRealDiv.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFREALDIV_H__
+#define __MOCO_IR_TFREALDIV_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+
+/// @note TFRealDiv corresponds to the following GraphDef
+/*
+node {
+ name: "div"
+ op: "RealDiv"
+ input: "x"
+ input: "y"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+*/
+
+class TFRealDiv final : public FixedArityNode<2, TFNodeImpl<TFOpcode::RealDiv>>
+{
+public:
+ TFRealDiv() = default;
+
+public:
+ Node *x(void) const { return at(0)->node(); }
+ void x(Node *node) { at(0)->node(node); }
+
+ Node *y(void) const { return at(1)->node(); }
+ void y(Node *node) { at(1)->node(node); }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFREALDIV_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFRelu.h b/compiler/moco/lang/include/moco/IR/Nodes/TFRelu.h
new file mode 100644
index 000000000..90e121e5e
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFRelu.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFRELU_H__
+#define __MOCO_IR_TFRELU_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+
+/// @note TFRelu corresponds to the following GraphDef
+/*
+node {
+ name: "output/relu"
+ op: "Relu"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+*/
+
+class TFRelu final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Relu>>
+{
+public:
+ TFRelu() = default;
+
+public:
+ Node *features(void) const { return at(0)->node(); }
+ void features(Node *node) { at(0)->node(node); }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFRELU_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFRelu6.h b/compiler/moco/lang/include/moco/IR/Nodes/TFRelu6.h
new file mode 100644
index 000000000..bb705b782
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFRelu6.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFRELU6_H__
+#define __MOCO_IR_TFRELU6_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+
+/// @note TFRelu6 corresponds to the following GraphDef
+/*
+node {
+ name: "Relu6"
+ op: "Relu6"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value { type: DT_FLOAT }
+ }
+}
+*/
+
+class TFRelu6 final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Relu6>>
+{
+public:
+ TFRelu6() = default;
+
+public:
+ Node *features(void) const { return at(0)->node(); }
+ void features(Node *node) { at(0)->node(node); }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFRELU6_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFReshape.h b/compiler/moco/lang/include/moco/IR/Nodes/TFReshape.h
new file mode 100644
index 000000000..1f743565d
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFReshape.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFRESHAPE_H__
+#define __MOCO_IR_TFRESHAPE_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+
+/// @note TFReshape corresponds to the following GraphDef
+/*
+node {
+ name: "reshape"
+ op: "Reshape"
+ input: "tensor"
+ input: "shape"
+ attr {
+ key: "T"
+ value { type: DT_FLOAT }
+ }
+}
+*/
+
+class TFReshape final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Reshape>>
+{
+public:
+ TFReshape() = default;
+
+public:
+ Node *tensor(void) const { return at(0)->node(); }
+ void tensor(Node *node) { at(0)->node(node); }
+
+ Node *shape(void) const { return at(1)->node(); }
+ void shape(Node *node) { at(1)->node(node); }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFRESHAPE_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFRsqrt.h b/compiler/moco/lang/include/moco/IR/Nodes/TFRsqrt.h
new file mode 100644
index 000000000..c71a5b98c
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFRsqrt.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFRSQRT_H__
+#define __MOCO_IR_TFRSQRT_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+
+/// @note TFRsqrt corresponds to the following GraphDef
+/*
+node {
+ name: "Rsqrt"
+ op: "Rsqrt"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+*/
+
+class TFRsqrt final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Rsqrt>>
+{
+public:
+ TFRsqrt() = default;
+
+public:
+ Node *x(void) const { return at(0)->node(); }
+ void x(Node *node) { at(0)->node(node); }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFRSQRT_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFShape.h b/compiler/moco/lang/include/moco/IR/Nodes/TFShape.h
new file mode 100644
index 000000000..36f0f1e69
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFShape.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFSHAPE_H__
+#define __MOCO_IR_TFSHAPE_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+#include <loco/IR/NodeMixins.h>
+
+#include <vector>
+
+namespace moco
+{
+
+/// @note TFShape corresponds to the following GraphDef
+/*
+node {
+ name: "Shape"
+ op: "Shape"
+ input: "some_input"
+ attr {
+ key: "T"
+ value { type: DT_FLOAT }
+ }
+ attr {
+ key: "out_type"
+ value { type: DT_INT32 }
+ }
+}
+*/
+
+/// @note Mixed in dtype() is for 'out_type' attribute
+class TFShape final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Shape>>,
+ public loco::NodeMixin<loco::NodeTrait::DataType>
+{
+public:
+ TFShape() = default;
+
+public:
+ Node *input(void) const { return at(0)->node(); }
+ void input(Node *node) { at(0)->node(node); }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFSHAPE_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFSoftmax.h b/compiler/moco/lang/include/moco/IR/Nodes/TFSoftmax.h
new file mode 100644
index 000000000..c98df1d82
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFSoftmax.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFSOFTMAX_H__
+#define __MOCO_IR_TFSOFTMAX_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+
+class TFSoftmax final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Softmax>>
+{
+public:
+ TFSoftmax() = default;
+
+public:
+ Node *logits(void) const { return at(0)->node(); }
+ void logits(Node *node) { at(0)->node(node); }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFSOFTMAX_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFSqrt.h b/compiler/moco/lang/include/moco/IR/Nodes/TFSqrt.h
new file mode 100644
index 000000000..273b5d49b
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFSqrt.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFSQRT_H__
+#define __MOCO_IR_TFSQRT_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+
+/// @note TFSqrt corresponds to the following GraphDef
+/*
+node {
+ name: "Sqrt"
+ op: "Sqrt"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+*/
+
+class TFSqrt final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Sqrt>>
+{
+public:
+ TFSqrt() = default;
+
+public:
+ Node *x(void) const { return at(0)->node(); }
+ void x(Node *node) { at(0)->node(node); }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFSQRT_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFSquaredDifference.h b/compiler/moco/lang/include/moco/IR/Nodes/TFSquaredDifference.h
new file mode 100644
index 000000000..4e0a929d3
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFSquaredDifference.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFSQUAREDDIFFERENCE_H__
+#define __MOCO_IR_TFSQUAREDDIFFERENCE_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+
+/// @note TFSquaredDifference corresponds to the following GraphDef
+/*
+node {
+ name: "SquaredDifference"
+ op: "SquaredDifference"
+ input: "input_x"
+ input: "input_y"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+*/
+
+class TFSquaredDifference final : public FixedArityNode<2, TFNodeImpl<TFOpcode::SquaredDifference>>
+{
+public:
+ TFSquaredDifference() = default;
+
+public:
+ Node *x(void) const { return at(0)->node(); }
+ void x(Node *node) { at(0)->node(node); }
+
+ Node *y(void) const { return at(1)->node(); }
+ void y(Node *node) { at(1)->node(node); }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFSQUAREDDIFFERENCE_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFSqueeze.h b/compiler/moco/lang/include/moco/IR/Nodes/TFSqueeze.h
new file mode 100644
index 000000000..612497ee7
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFSqueeze.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFSQUEEZE_H__
+#define __MOCO_IR_TFSQUEEZE_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+#include <vector>
+
+namespace moco
+{
+
+/// @note TFSqueeze corresponds to the following GraphDef
+/*
+node {
+ name: "squeeze"
+ op: "Squeeze"
+ input: "x"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "squeeze_dims"
+ value {
+ list {
+ i: a
+ i: b
+ ..
+ }
+ }
+ }
+}
+*/
+
+class TFSqueeze final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Squeeze>>
+{
+public:
+ TFSqueeze() = default;
+
+public:
+ Node *input(void) const { return at(0)->node(); }
+ void input(Node *node) { at(0)->node(node); }
+
+public:
+ const std::vector<int64_t> &squeeze_dims(void) const { return _squeeze_dims; }
+ void squeeze_dims(const std::vector<int64_t> &squeeze_dims) { _squeeze_dims = squeeze_dims; }
+
+private:
+ std::vector<int64_t> _squeeze_dims;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFSQUEEZE_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFStopGradient.h b/compiler/moco/lang/include/moco/IR/Nodes/TFStopGradient.h
new file mode 100644
index 000000000..cfebd92a9
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFStopGradient.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFSTOPGRADIENT_H__
+#define __MOCO_IR_TFSTOPGRADIENT_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+
+/// @note TFStopGradient corresponds to the following GraphDef
+/*
+node {
+ name: "StopGradient"
+ op: "StopGradient"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+*/
+
+class TFStopGradient final : public FixedArityNode<1, TFNodeImpl<TFOpcode::StopGradient>>
+{
+public:
+ TFStopGradient() = default;
+
+public:
+ Node *input(void) const { return at(0)->node(); }
+ void input(Node *node) { at(0)->node(node); }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFSTOPGRADIENT_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFStridedSlice.h b/compiler/moco/lang/include/moco/IR/Nodes/TFStridedSlice.h
new file mode 100644
index 000000000..75012b219
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFStridedSlice.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFSTRIDEDSLICE_H__
+#define __MOCO_IR_TFSTRIDEDSLICE_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+
+/// @note TFStridedSlice corresponds to the following GraphDef
+/*
+node {
+ name: "StridedSlice"
+ op: "StridedSlice"
+ input: "input"
+ input: "begin"
+ input: "end"
+ input: "stride"
+ attr {
+ key: "Index"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "T"
+ value {
+ type: DT_INT32
+ }
+ }
+ attr {
+ key: "begin_mask"
+ value {
+ i: 0
+ }
+ }
+ attr {
+ key: "ellipsis_mask"
+ value {
+ i: 0
+ }
+ }
+ attr {
+ key: "end_mask"
+ value {
+ i: 0
+ }
+ }
+ attr {
+ key: "new_axis_mask"
+ value {
+ i: 0
+ }
+ }
+ attr {
+ key: "shrink_axis_mask"
+ value {
+ i: 0
+ }
+ }
+}
+*/
+
+class TFStridedSlice final : public FixedArityNode<4, TFNodeImpl<TFOpcode::StridedSlice>>
+{
+public:
+ TFStridedSlice() = default;
+
+public:
+ Node *input(void) const { return at(0)->node(); }
+ void input(Node *node) { at(0)->node(node); }
+
+ Node *begin(void) const { return at(1)->node(); }
+ void begin(Node *node) { at(1)->node(node); }
+
+ Node *end(void) const { return at(2)->node(); }
+ void end(Node *node) { at(2)->node(node); }
+
+ Node *strides(void) const { return at(3)->node(); }
+ void strides(Node *node) { at(3)->node(node); }
+
+public:
+ int32_t begin_mask(void) const { return _begin_mask; }
+ void begin_mask(int32_t begin_mask) { _begin_mask = begin_mask; }
+
+ int32_t end_mask(void) const { return _end_mask; }
+ void end_mask(int32_t end_mask) { _end_mask = end_mask; }
+
+ int32_t ellipsis_mask(void) const { return _ellipsis_mask; }
+ void ellipsis_mask(int32_t ellipsis_mask) { _ellipsis_mask = ellipsis_mask; }
+
+ int32_t new_axis_mask(void) const { return _new_axis_mask; }
+ void new_axis_mask(int32_t new_axis_mask) { _new_axis_mask = new_axis_mask; }
+
+ int32_t shrink_axis_mask(void) const { return _shrink_axis_mask; }
+ void shrink_axis_mask(int32_t shrink_axis_mask) { _shrink_axis_mask = shrink_axis_mask; }
+
+private:
+ 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};
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFSTRIDEDSLICE_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFSub.h b/compiler/moco/lang/include/moco/IR/Nodes/TFSub.h
new file mode 100644
index 000000000..27905cbdb
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFSub.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFSUB_H__
+#define __MOCO_IR_TFSUB_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+
+/// @note TFSub corresponds to the following GraphDef
+/*
+node {
+ name: "sub"
+ op: "Sub"
+ input: "x"
+ input: "y"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+}
+*/
+
+class TFSub final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Sub>>
+{
+public:
+ TFSub() = default;
+
+public:
+ Node *x(void) const { return at(0)->node(); }
+ void x(Node *node) { at(0)->node(node); }
+
+ Node *y(void) const { return at(1)->node(); }
+ void y(Node *node) { at(1)->node(node); }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFSUB_H__
diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFTanh.h b/compiler/moco/lang/include/moco/IR/Nodes/TFTanh.h
new file mode 100644
index 000000000..4543c62f3
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/Nodes/TFTanh.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFTANH_H__
+#define __MOCO_IR_TFTANH_H__
+
+#include "moco/IR/TFNodeDecl.h"
+
+namespace moco
+{
+
+class TFTanh final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Tanh>>
+{
+public:
+ TFTanh() = default;
+
+public:
+ Node *x(void) const { return at(0)->node(); }
+ void x(Node *node) { at(0)->node(node); }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFTANH_H__
diff --git a/compiler/moco/lang/include/moco/IR/TFDataLayout.h b/compiler/moco/lang/include/moco/IR/TFDataLayout.h
new file mode 100644
index 000000000..f0edfacd5
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/TFDataLayout.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFDATALAYOUT_H__
+#define __MOCO_IR_TFDATALAYOUT_H__
+
+#include <string>
+
+namespace moco
+{
+
+using TFDataLayout = std::string;
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFDATALAYOUT_H__
diff --git a/compiler/moco/lang/include/moco/IR/TFDialect.h b/compiler/moco/lang/include/moco/IR/TFDialect.h
new file mode 100644
index 000000000..847bc527f
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/TFDialect.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFDIALECT_H__
+#define __MOCO_IR_TFDIALECT_H__
+
+#include <loco/IR/Dialect.h>
+
+namespace moco
+{
+
+/**
+ * @brief A singleton for TensorFlow Dialect
+ */
+class TFDialect final : public loco::Dialect
+{
+private:
+ TFDialect();
+
+public:
+ TFDialect(const TFDialect &) = delete;
+ TFDialect(TFDialect &&) = delete;
+
+public:
+ static loco::Dialect *get(void);
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFDIALECT_H__
diff --git a/compiler/moco/lang/include/moco/IR/TFNode.h b/compiler/moco/lang/include/moco/IR/TFNode.h
new file mode 100644
index 000000000..e3d900ba3
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/TFNode.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFNODE_H__
+#define __MOCO_IR_TFNODE_H__
+
+#include "moco/IR/TFNodeDecl.h"
+#include "moco/IR/TFNodeImpl.h"
+
+#endif // __MOCO_IR_TFNODE_H__
diff --git a/compiler/moco/lang/include/moco/IR/TFNodeDecl.h b/compiler/moco/lang/include/moco/IR/TFNodeDecl.h
new file mode 100644
index 000000000..68d7161b6
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/TFNodeDecl.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFNODE_DECL_H__
+#define __MOCO_IR_TFNODE_DECL_H__
+
+#include <loco/IR/Node.h>
+#include <loco/IR/Dialect.h>
+
+#include "moco/IR/TFOpcode.h"
+#include "moco/IR/TFNodeVisitor.forward.h"
+
+#include "moco/IR/TFDataLayout.h"
+#include "moco/IR/TFPadding.h"
+
+#include <array>
+#include <string>
+
+namespace moco
+{
+
+/**
+ * @note NodeName is string name of the Node without ':#' prefix like ':0' or ':1'
+ */
+using NodeName = std::string;
+
+struct TFNode : public loco::Node
+{
+ virtual ~TFNode() = default;
+
+ const loco::Dialect *dialect(void) const final;
+ virtual TFOpcode opcode(void) const = 0;
+
+ template <typename T> T accept(TFNodeVisitorBase<T> *) const;
+ template <typename T> T accept(TFNodeMutableVisitorBase<T> *);
+
+ NodeName name(void) const { return _name; }
+ void name(const NodeName &name) { _name = name; }
+
+private:
+ NodeName _name;
+};
+
+template <TFOpcode Code> struct TFNodeImpl : public TFNode
+{
+ virtual ~TFNodeImpl() = default;
+
+ uint32_t opnum(void) const final { return static_cast<uint32_t>(Code); }
+ TFOpcode opcode(void) const final { return Code; }
+};
+
+/**
+ * @brief Nodes with the fixed number of inputs
+ */
+template <unsigned N, typename Base> class FixedArityNode : public Base
+{
+public:
+ FixedArityNode()
+ {
+ for (uint32_t n = 0; n < N; ++n)
+ {
+ _args[n] = std::unique_ptr<loco::Use>{new loco::Use{this}};
+ }
+ }
+
+ virtual ~FixedArityNode() = default;
+
+public:
+ unsigned arity(void) const final { return N; }
+
+ loco::Node *arg(uint32_t n) const final { return _args.at(n)->node(); }
+
+ void drop(void) final
+ {
+ for (uint32_t n = 0; n < N; ++n)
+ {
+ _args.at(n)->node(nullptr);
+ }
+ }
+
+protected:
+ // This API allows inherited classes to access "_args" field.
+ loco::Use *at(unsigned n) const { return _args.at(n).get(); }
+
+private:
+ std::array<std::unique_ptr<loco::Use>, N> _args{};
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFNODE_DECL_H__
diff --git a/compiler/moco/lang/include/moco/IR/TFNodeImpl.h b/compiler/moco/lang/include/moco/IR/TFNodeImpl.h
new file mode 100644
index 000000000..afc306031
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/TFNodeImpl.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFNODE_IMPL_H__
+#define __MOCO_IR_TFNODE_IMPL_H__
+
+#include "moco/IR/TFNodes.h"
+#include "moco/IR/TFNodeVisitor.h"
+
+#include <stdexcept>
+
+namespace moco
+{
+
+template <typename T> T TFNode::accept(TFNodeVisitorBase<T> *v) const
+{
+ switch (this->opcode())
+ {
+#define TENSORFLOW_NODE(OPCODE, CLASS) \
+ case TFOpcode::OPCODE: \
+ return v->visit(dynamic_cast<const CLASS *>(this));
+
+#include "TFNodes.lst"
+#undef TENSORFLOW_NODE
+ default:
+ break;
+ }
+
+ // TODO including oops will make oops dependent to modules that include this
+ // postpone decision to this or not
+ throw std::runtime_error{"Unsupported Node"};
+}
+
+template <typename T> T TFNode::accept(TFNodeMutableVisitorBase<T> *v)
+{
+ switch (this->opcode())
+ {
+#define TENSORFLOW_NODE(OPCODE, CLASS) \
+ case TFOpcode::OPCODE: \
+ return v->visit(dynamic_cast<CLASS *>(this));
+
+#include "TFNodes.lst"
+#undef TENSORFLOW_NODE
+ default:
+ break;
+ }
+
+ // TODO including oops will make oops dependent to modules that include this
+ // postpone decision to this or not
+ throw std::runtime_error{"Unsupported Node"};
+}
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFNODE_IMPL_H__
diff --git a/compiler/moco/lang/include/moco/IR/TFNodeVisitor.forward.h b/compiler/moco/lang/include/moco/IR/TFNodeVisitor.forward.h
new file mode 100644
index 000000000..1eb86871c
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/TFNodeVisitor.forward.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFNODE_VISITOR_FORWARD_H__
+#define __MOCO_IR_TFNODE_VISITOR_FORWARD_H__
+
+namespace moco
+{
+
+// NOTE These forward declarations SHOULD BE aligned with Node delcarations in
+// "TFNodeVisitor.h"
+template <typename T> struct TFNodeVisitorBase;
+template <typename T> struct TFNodeMutableVisitorBase;
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFNODE_VISITOR_FORWARD_H__
diff --git a/compiler/moco/lang/include/moco/IR/TFNodeVisitor.h b/compiler/moco/lang/include/moco/IR/TFNodeVisitor.h
new file mode 100644
index 000000000..8d23e447d
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/TFNodeVisitor.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFNODE_VISITOR_H__
+#define __MOCO_IR_TFNODE_VISITOR_H__
+
+#include "moco/IR/TFNodes.h"
+
+#include <stdexcept>
+
+namespace moco
+{
+
+/**
+ * DO NOT use this class. Use TFNodeVisitor instead.
+ */
+template <typename T> struct TFNodeVisitorBase
+{
+ virtual ~TFNodeVisitorBase() = default;
+
+#define TENSORFLOW_NODE(OPCODE, CLASS) virtual T visit(const CLASS *) = 0;
+#include "TFNodes.lst"
+#undef TENSORFLOW_NODE
+};
+
+template <typename T> struct TFNodeVisitor : public TFNodeVisitorBase<T>
+{
+ virtual ~TFNodeVisitor() = default;
+
+#define TENSORFLOW_NODE(OPCODE, CLASS) \
+ virtual T visit(const CLASS *node) { return visit(static_cast<const TFNode *>(node)); }
+#include "TFNodes.lst"
+#undef TENSORFLOW_NODE
+
+ // TODO including oops will make oops dependent to modules that include this
+ // postpone decision to this or not
+ /// @brief Default fallback
+ virtual T visit(const TFNode *) { throw std::runtime_error{"Unsupported Node"}; }
+};
+
+/**
+ * DO NOT use this class. Use TFNodeMutableVisitor instead.
+ */
+template <typename T> struct TFNodeMutableVisitorBase
+{
+ virtual ~TFNodeMutableVisitorBase() = default;
+
+#define TENSORFLOW_NODE(OPCODE, CLASS) virtual T visit(CLASS *) = 0;
+#include "TFNodes.lst"
+#undef TENSORFLOW_NODE
+};
+
+template <typename T> struct TFNodeMutableVisitor : public TFNodeMutableVisitorBase<T>
+{
+ virtual ~TFNodeMutableVisitor() = default;
+
+#define TENSORFLOW_NODE(OPCODE, CLASS) \
+ virtual T visit(CLASS *node) { return visit(static_cast<TFNode *>(node)); }
+#include "TFNodes.lst"
+#undef TENSORFLOW_NODE
+
+ // TODO including oops will make oops dependent to modules that include this
+ // postpone decision to this or not
+ /// @brief Default fallback
+ virtual T visit(TFNode *) { throw std::runtime_error{"Unsupported Node"}; }
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFNODE_VISITOR_H__
diff --git a/compiler/moco/lang/include/moco/IR/TFNodes.h b/compiler/moco/lang/include/moco/IR/TFNodes.h
new file mode 100644
index 000000000..ad54dfdf3
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/TFNodes.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFNODES_H__
+#define __MOCO_IR_TFNODES_H__
+
+#include "moco/IR/Nodes/TFAdd.h"
+#include "moco/IR/Nodes/TFAvgPool.h"
+#include "moco/IR/Nodes/TFBiasAdd.h"
+#include "moco/IR/Nodes/TFConcatV2.h"
+#include "moco/IR/Nodes/TFConst.h"
+#include "moco/IR/Nodes/TFConv2D.h"
+#include "moco/IR/Nodes/TFConv2DBackpropInput.h"
+#include "moco/IR/Nodes/TFDepthwiseConv2dNative.h"
+#include "moco/IR/Nodes/TFFakeQuantWithMinMaxVars.h"
+#include "moco/IR/Nodes/TFFusedBatchNorm.h"
+#include "moco/IR/Nodes/TFIdentity.h"
+#include "moco/IR/Nodes/TFMaximum.h"
+#include "moco/IR/Nodes/TFMaxPool.h"
+#include "moco/IR/Nodes/TFMean.h"
+#include "moco/IR/Nodes/TFMul.h"
+#include "moco/IR/Nodes/TFPack.h"
+#include "moco/IR/Nodes/TFPad.h"
+#include "moco/IR/Nodes/TFPlaceholder.h"
+#include "moco/IR/Nodes/TFRealDiv.h"
+#include "moco/IR/Nodes/TFRelu.h"
+#include "moco/IR/Nodes/TFRelu6.h"
+#include "moco/IR/Nodes/TFReshape.h"
+#include "moco/IR/Nodes/TFRsqrt.h"
+#include "moco/IR/Nodes/TFShape.h"
+#include "moco/IR/Nodes/TFSoftmax.h"
+#include "moco/IR/Nodes/TFSqrt.h"
+#include "moco/IR/Nodes/TFSquaredDifference.h"
+#include "moco/IR/Nodes/TFSqueeze.h"
+#include "moco/IR/Nodes/TFStopGradient.h"
+#include "moco/IR/Nodes/TFStridedSlice.h"
+#include "moco/IR/Nodes/TFSub.h"
+#include "moco/IR/Nodes/TFTanh.h"
+// For virtual node(s)
+#include "moco/IR/Nodes/TFPush.h"
+
+#endif // __MOCO_IR_TFNODES_H__
diff --git a/compiler/moco/lang/include/moco/IR/TFNodes.lst b/compiler/moco/lang/include/moco/IR/TFNodes.lst
new file mode 100644
index 000000000..8373d2b8d
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/TFNodes.lst
@@ -0,0 +1,48 @@
+#ifndef TENSORFLOW_NODE
+#error "Define TENSORFLOW_NODE"
+#endif // TENSORFLOW_NODE
+
+//
+// PLEASE SORT NODE DECLS IN ALPHABETICAL ORDER
+//
+// Naming rule: Follow names in TensorFlow C++ source
+// ex) for AvgPool, tensorflow/core/ops/nn_ops.cc
+// REGISTER_OP("AvgPool") <-- OPCODE: AvgPool. Prefix `TF` for CLASS name
+// .Input("value: T") <-- Input name is 'value'
+//
+
+// TENSORFLOW_NODE(OPCODE, CLASS)
+TENSORFLOW_NODE(Add, TFAdd)
+TENSORFLOW_NODE(AvgPool, TFAvgPool)
+TENSORFLOW_NODE(BiasAdd, TFBiasAdd)
+TENSORFLOW_NODE(ConcatV2, TFConcatV2)
+TENSORFLOW_NODE(Const, TFConst)
+TENSORFLOW_NODE(Conv2D, TFConv2D)
+TENSORFLOW_NODE(Conv2DBackpropInput, TFConv2DBackpropInput)
+TENSORFLOW_NODE(DepthwiseConv2dNative, TFDepthwiseConv2dNative)
+TENSORFLOW_NODE(FakeQuantWithMinMaxVars, TFFakeQuantWithMinMaxVars)
+TENSORFLOW_NODE(FusedBatchNorm, TFFusedBatchNorm)
+TENSORFLOW_NODE(Identity, TFIdentity)
+TENSORFLOW_NODE(Maximum, TFMaximum)
+TENSORFLOW_NODE(MaxPool, TFMaxPool)
+TENSORFLOW_NODE(Mean, TFMean)
+TENSORFLOW_NODE(Mul, TFMul)
+TENSORFLOW_NODE(Pack, TFPack)
+TENSORFLOW_NODE(Pad, TFPad)
+TENSORFLOW_NODE(Placeholder, TFPlaceholder)
+TENSORFLOW_NODE(RealDiv, TFRealDiv)
+TENSORFLOW_NODE(Relu, TFRelu)
+TENSORFLOW_NODE(Relu6, TFRelu6)
+TENSORFLOW_NODE(Reshape, TFReshape)
+TENSORFLOW_NODE(Rsqrt, TFRsqrt)
+TENSORFLOW_NODE(Shape, TFShape)
+TENSORFLOW_NODE(Softmax, TFSoftmax)
+TENSORFLOW_NODE(Sqrt, TFSqrt)
+TENSORFLOW_NODE(SquaredDifference, TFSquaredDifference)
+TENSORFLOW_NODE(Squeeze, TFSqueeze)
+TENSORFLOW_NODE(StopGradient, TFStopGradient)
+TENSORFLOW_NODE(StridedSlice, TFStridedSlice)
+TENSORFLOW_NODE(Sub, TFSub)
+TENSORFLOW_NODE(Tanh, TFTanh)
+// For virtual node(s)
+TENSORFLOW_NODE(TFPush, TFPush)
diff --git a/compiler/moco/lang/include/moco/IR/TFOpcode.h b/compiler/moco/lang/include/moco/IR/TFOpcode.h
new file mode 100644
index 000000000..7524dcce4
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/TFOpcode.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFOPCODE_H__
+#define __MOCO_IR_TFOPCODE_H__
+
+namespace moco
+{
+
+/**
+ * @brief TensorFlow Node Opcode
+ */
+enum class TFOpcode
+{
+#define TENSORFLOW_NODE(OPCODE, CLASS) OPCODE,
+#include "TFNodes.lst"
+#undef TENSORFLOW_NODE
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFOPCODE_H__
diff --git a/compiler/moco/lang/include/moco/IR/TFPadding.h b/compiler/moco/lang/include/moco/IR/TFPadding.h
new file mode 100644
index 000000000..c75b3f2ce
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/TFPadding.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_TFPADDING_H__
+#define __MOCO_IR_TFPADDING_H__
+
+#include <string>
+
+namespace moco
+{
+
+using TFPadding = std::string;
+
+} // namespace moco
+
+#endif // __MOCO_IR_TFPADDING_H__
diff --git a/compiler/moco/lang/include/moco/IR/VariadicArityNode.h b/compiler/moco/lang/include/moco/IR/VariadicArityNode.h
new file mode 100644
index 000000000..7df0f7dec
--- /dev/null
+++ b/compiler/moco/lang/include/moco/IR/VariadicArityNode.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_IR_VARIADIC_ARITY_NODE_H__
+#define __MOCO_IR_VARIADIC_ARITY_NODE_H__
+
+#include <loco/IR/Node.h>
+#include <loco/IR/Use.h>
+
+#include <vector>
+#include <memory>
+#include <cassert>
+
+namespace moco
+{
+
+/**
+ * @brief Nodes with the variadic inputs
+ */
+template <typename Base> class VariadicArityNode : public Base
+{
+public:
+ VariadicArityNode(uint32_t arity)
+ {
+ for (uint32_t n = 0; n < arity; ++n)
+ {
+ _args.emplace_back(std::move(std::unique_ptr<loco::Use>{new loco::Use{this}}));
+ }
+ };
+
+ virtual ~VariadicArityNode() = default;
+
+public:
+ uint32_t arity(void) const final { return _args.size(); }
+
+ loco::Node *arg(uint32_t n) const final
+ {
+ assert(n < _args.size());
+ return _args.at(n)->node();
+ }
+
+ void drop(void) final
+ {
+ for (uint32_t n = 0; n < _args.size(); ++n)
+ {
+ _args.at(n)->node(nullptr);
+ }
+ }
+
+protected:
+ // This API allows inherited classes to access "_args" field.
+ loco::Use *at(uint32_t n) const
+ {
+ assert(n < _args.size());
+ return _args.at(n).get();
+ }
+
+private:
+ std::vector<std::unique_ptr<loco::Use>> _args;
+};
+
+} // namespace moco
+
+#endif // __MOCO_IR_VARIADIC_ARITY_NODE_H__
diff --git a/compiler/moco/lang/include/moco/Names.h b/compiler/moco/lang/include/moco/Names.h
new file mode 100644
index 000000000..1addc812b
--- /dev/null
+++ b/compiler/moco/lang/include/moco/Names.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_NAMES_H__
+#define __MOCO_NAMES_H__
+
+#include <string>
+#include <stdexcept>
+
+namespace moco
+{
+
+struct TensorName final
+{
+public:
+ /**
+ * @brief Constructor
+ *
+ * @note If tensor_name does not have ":index", this constructor adds ":0" by default
+ */
+ explicit TensorName(const std::string &tensor_name)
+ {
+ if (tensor_name.find(":") != std::string::npos) // tensor_name is a form of letter:0
+ {
+ _name.assign(tensor_name);
+ }
+ else
+ {
+ _name.assign(tensor_name + ":0"); // if it does not have ":index", adds ":0" by default
+ }
+ }
+
+ explicit TensorName(const std::string &node_name, const int tensor_index)
+ {
+ if (node_name.find(":") != std::string::npos) // tensor_name is already a form of name:0
+ {
+ // TODO including oops will make oops dependent to modules that include this
+ // postpone decision to this or not
+ throw std::runtime_error("Error: Node name has already tensor index:" + node_name);
+ }
+ else
+ {
+ _name.assign(node_name + ":" + std::to_string(tensor_index));
+ }
+ }
+
+ const std::string &name() const { return _name; }
+
+ /**
+ * @brief Returns node name from tensor name by removing, e.g., ":0"
+ */
+ const std::string nodeName() const
+ {
+ auto index = _name.find(":");
+
+ if (index != std::string::npos)
+ return _name.substr(0, index);
+ else
+ {
+ // TODO including oops will make oops dependent to modules that include this
+ // postpone decision to this or not
+ throw std::runtime_error{"Error: Tensor name should be a 'name:number' format: " + _name};
+ }
+ };
+
+private:
+ std::string _name;
+};
+
+/**
+ * @brief To use TensorName as a key in std::map, this struct defines how to compare two TensorNames
+ */
+struct TensorNameCompare
+{
+ bool operator()(const TensorName &lhs, const TensorName &rhs) const
+ {
+ return lhs.name() < rhs.name();
+ }
+};
+
+} // namespace moco
+
+#endif // __MOCO_NAMES_H__
diff --git a/compiler/moco/lang/src/IR/Nodes/TFAdd.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFAdd.test.cpp
new file mode 100644
index 000000000..d2cfb6ac4
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFAdd.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFAdd.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFAddTest, constructor)
+{
+ moco::TFAdd add_node;
+
+ ASSERT_EQ(add_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(add_node.opcode(), moco::TFOpcode::Add);
+
+ ASSERT_EQ(add_node.x(), nullptr);
+ ASSERT_EQ(add_node.y(), nullptr);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFAvgPool.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFAvgPool.test.cpp
new file mode 100644
index 000000000..32a27ffa0
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFAvgPool.test.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFAvgPool.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFAvgPoolTest, constructor)
+{
+ moco::TFAvgPool avgpool;
+
+ ASSERT_EQ(avgpool.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(avgpool.opcode(), moco::TFOpcode::AvgPool);
+
+ ASSERT_EQ(avgpool.value(), nullptr);
+ ASSERT_EQ(avgpool.data_layout(), "");
+ ASSERT_EQ(avgpool.padding(), "");
+ ASSERT_EQ(avgpool.ksize(), std::vector<int64_t>({}));
+ ASSERT_EQ(avgpool.strides(), std::vector<int64_t>({}));
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFBiasAdd.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFBiasAdd.test.cpp
new file mode 100644
index 000000000..4a15a4981
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFBiasAdd.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFBiasAdd.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFBiasAddTest, constructor)
+{
+ moco::TFBiasAdd bias_add;
+
+ ASSERT_EQ(bias_add.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(bias_add.opcode(), moco::TFOpcode::BiasAdd);
+
+ ASSERT_EQ(bias_add.value(), nullptr);
+ ASSERT_EQ(bias_add.bias(), nullptr);
+ ASSERT_EQ(bias_add.data_layout(), "");
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFConcatV2.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFConcatV2.test.cpp
new file mode 100644
index 000000000..8f7df92d0
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFConcatV2.test.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFConcatV2.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFConcatV2Test, constructor)
+{
+ moco::TFConcatV2 concatv2_node(3); // num of values
+
+ ASSERT_EQ(concatv2_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(concatv2_node.opcode(), moco::TFOpcode::ConcatV2);
+
+ ASSERT_EQ(concatv2_node.num_values(), 3);
+ ASSERT_EQ(concatv2_node.values(0), nullptr);
+ ASSERT_EQ(concatv2_node.values(1), nullptr);
+ ASSERT_EQ(concatv2_node.values(2), nullptr);
+ ASSERT_EQ(concatv2_node.axis(), nullptr);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFConst.cpp b/compiler/moco/lang/src/IR/Nodes/TFConst.cpp
new file mode 100644
index 000000000..5c8c08ec0
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFConst.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFConst.h"
+
+#include <cassert>
+
+namespace moco
+{
+
+template <loco::DataType DT> uint32_t TFConst::size(void) const
+{
+ assert(dtype() == DT);
+ assert(_data.size() % sizeof(typename loco::DataTypeImpl<DT>::Type) == 0);
+ return _data.size() / sizeof(typename loco::DataTypeImpl<DT>::Type);
+}
+
+template <loco::DataType DT> void TFConst::size(uint32_t l)
+{
+ assert(dtype() == DT);
+ _data.resize(l * sizeof(typename loco::DataTypeImpl<DT>::Type));
+}
+
+template <loco::DataType DT>
+const typename loco::DataTypeImpl<DT>::Type &TFConst::at(uint32_t n) const
+{
+ assert(dtype() == DT);
+ assert(n < size<DT>());
+ return *(reinterpret_cast<const typename loco::DataTypeImpl<DT>::Type *>(_data.data()) + n);
+}
+
+template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &TFConst::at(uint32_t n)
+{
+ assert(dtype() == DT);
+ assert(n < size<DT>());
+ return *(reinterpret_cast<typename loco::DataTypeImpl<DT>::Type *>(_data.data()) + n);
+}
+
+#define INSTANTIATE(DT) \
+ template uint32_t TFConst::size<DT>(void) const; \
+ template void TFConst::size<DT>(uint32_t); \
+ template const typename loco::DataTypeImpl<DT>::Type &TFConst::at<DT>(uint32_t) const; \
+ template typename loco::DataTypeImpl<DT>::Type &TFConst::at<DT>(uint32_t);
+
+INSTANTIATE(loco::DataType::S8);
+INSTANTIATE(loco::DataType::S32);
+INSTANTIATE(loco::DataType::FLOAT32);
+
+#undef INSTANTIATE
+
+loco::TensorShape tensor_shape(const TFConst *node)
+{
+ assert(node != nullptr);
+
+ loco::TensorShape shape;
+
+ uint32_t rank = node->rank();
+ shape.rank(rank);
+ for (uint32_t index = 0; index < rank; ++index)
+ {
+ assert(node->dim(index).known());
+ shape.dim(index) = node->dim(index).value();
+ }
+
+ return shape;
+}
+
+uint32_t num_elements(const TFConst *tfconst)
+{
+ assert(tfconst != nullptr);
+
+ uint32_t num_elements = 1;
+ for (uint32_t index = 0; index < tfconst->rank(); ++index)
+ {
+ assert(tfconst->dim(index).known());
+ uint32_t dim = tfconst->dim(index).value();
+ num_elements = num_elements * dim;
+ }
+ return num_elements;
+}
+
+bool same_shape(const TFConst *lhs, const TFConst *rhs)
+{
+ assert(lhs != nullptr);
+ assert(rhs != nullptr);
+
+ if (lhs->rank() != rhs->rank())
+ return false;
+
+ for (uint32_t index = 0; index < lhs->rank(); ++index)
+ {
+ assert(lhs->dim(index).known());
+ assert(rhs->dim(index).known());
+ if (lhs->dim(index).value() != rhs->dim(index).value())
+ return false;
+ }
+ return true;
+}
+
+} // namespace moco
diff --git a/compiler/moco/lang/src/IR/Nodes/TFConst.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFConst.test.cpp
new file mode 100644
index 000000000..259966e33
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFConst.test.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFConst.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFConstantTest, constructor)
+{
+ moco::TFConst constant;
+
+ ASSERT_EQ(constant.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(constant.opcode(), moco::TFOpcode::Const);
+
+ ASSERT_EQ(constant.dtype(), loco::DataType::Unknown);
+ ASSERT_EQ(constant.rank(), 0);
+
+ constant.dtype(loco::DataType::FLOAT32);
+ ASSERT_EQ(constant.dtype(), loco::DataType::FLOAT32);
+
+ constant.rank(2);
+ ASSERT_EQ(constant.rank(), 2);
+
+ constant.dim(0) = 2;
+ constant.dim(1) = 3;
+
+ ASSERT_TRUE(constant.dim(0).known());
+ ASSERT_TRUE(constant.dim(1).known());
+
+ ASSERT_EQ(constant.dim(0), 2);
+ ASSERT_EQ(constant.dim(1), 3);
+
+ constant.size<loco::DataType::FLOAT32>(6);
+
+ ASSERT_EQ(constant.size<loco::DataType::FLOAT32>(), 6);
+
+ constant.at<loco::DataType::FLOAT32>(0) = 0.0f; // Set 0,0
+ constant.at<loco::DataType::FLOAT32>(1) = 1.0f; // Set 0,1
+ constant.at<loco::DataType::FLOAT32>(2) = 2.0f; // Set 0,2
+ constant.at<loco::DataType::FLOAT32>(3) = 3.0f; // Set 1,0
+ constant.at<loco::DataType::FLOAT32>(4) = 4.0f; // Set 1,1
+ constant.at<loco::DataType::FLOAT32>(5) = 5.0f; // Set 1,2
+
+ ASSERT_EQ(constant.at<loco::DataType::FLOAT32>(0), 0.0f);
+ ASSERT_EQ(constant.at<loco::DataType::FLOAT32>(1), 1.0f);
+ ASSERT_EQ(constant.at<loco::DataType::FLOAT32>(2), 2.0f);
+ ASSERT_EQ(constant.at<loco::DataType::FLOAT32>(3), 3.0f);
+ ASSERT_EQ(constant.at<loco::DataType::FLOAT32>(4), 4.0f);
+ ASSERT_EQ(constant.at<loco::DataType::FLOAT32>(5), 5.0f);
+}
+
+TEST(TFConstantTest, datatype_s8)
+{
+ moco::TFConst constant;
+
+ ASSERT_EQ(constant.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(constant.opcode(), moco::TFOpcode::Const);
+
+ ASSERT_EQ(constant.dtype(), loco::DataType::Unknown);
+ ASSERT_EQ(constant.rank(), 0);
+
+ constant.dtype(loco::DataType::S8);
+ ASSERT_EQ(constant.dtype(), loco::DataType::S8);
+
+ constant.rank(1);
+ ASSERT_EQ(constant.rank(), 1);
+
+ constant.dim(0) = 3;
+ ASSERT_TRUE(constant.dim(0).known());
+ ASSERT_EQ(constant.dim(0), 3);
+ constant.size<loco::DataType::S8>(3);
+ ASSERT_EQ(constant.size<loco::DataType::S8>(), 3);
+
+ constant.at<loco::DataType::S8>(0) = -1;
+ constant.at<loco::DataType::S8>(1) = 1;
+ constant.at<loco::DataType::S8>(2) = 0;
+
+ ASSERT_EQ(constant.at<loco::DataType::S8>(0), -1);
+ ASSERT_EQ(constant.at<loco::DataType::S8>(1), 1);
+ ASSERT_EQ(constant.at<loco::DataType::S8>(2), 0);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFConv2D.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFConv2D.test.cpp
new file mode 100644
index 000000000..3e3453db0
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFConv2D.test.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFConv2D.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFConv2DTest, constructor)
+{
+ moco::TFConv2D conv2d_node;
+
+ ASSERT_EQ(conv2d_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(conv2d_node.opcode(), moco::TFOpcode::Conv2D);
+
+ ASSERT_EQ(conv2d_node.input(), nullptr);
+ ASSERT_EQ(conv2d_node.filter(), nullptr);
+ ASSERT_EQ(conv2d_node.padding(), "");
+ ASSERT_EQ(conv2d_node.data_layout(), "");
+ ASSERT_EQ(conv2d_node.strides().size(), 0);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFConv2DBackpropInput.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFConv2DBackpropInput.test.cpp
new file mode 100644
index 000000000..f7ad4ce67
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFConv2DBackpropInput.test.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFConv2DBackpropInput.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFConv2DBackpropInputTest, constructor)
+{
+ moco::TFConv2DBackpropInput conv2dbi_node;
+
+ ASSERT_EQ(conv2dbi_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(conv2dbi_node.opcode(), moco::TFOpcode::Conv2DBackpropInput);
+
+ ASSERT_EQ(conv2dbi_node.input_sizes(), nullptr);
+ ASSERT_EQ(conv2dbi_node.filter(), nullptr);
+ ASSERT_EQ(conv2dbi_node.out_backprop(), nullptr);
+ ASSERT_EQ(conv2dbi_node.padding(), "");
+ ASSERT_EQ(conv2dbi_node.data_layout(), "");
+ ASSERT_EQ(conv2dbi_node.strides().size(), 0);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFDepthwiseConv2dNative.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFDepthwiseConv2dNative.test.cpp
new file mode 100644
index 000000000..2562997c2
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFDepthwiseConv2dNative.test.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFDepthwiseConv2dNative.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFDepthwiseConv2dNativeTest, constructor)
+{
+ moco::TFDepthwiseConv2dNative depthwiseConv2dnative_node;
+
+ ASSERT_EQ(depthwiseConv2dnative_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(depthwiseConv2dnative_node.opcode(), moco::TFOpcode::DepthwiseConv2dNative);
+
+ ASSERT_EQ(depthwiseConv2dnative_node.input(), nullptr);
+ ASSERT_EQ(depthwiseConv2dnative_node.filter(), nullptr);
+ ASSERT_EQ(depthwiseConv2dnative_node.padding(), "");
+ ASSERT_EQ(depthwiseConv2dnative_node.data_layout(), "");
+ ASSERT_EQ(depthwiseConv2dnative_node.strides().size(), 0);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFFakeQuantWithMinMaxVars.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFFakeQuantWithMinMaxVars.test.cpp
new file mode 100644
index 000000000..be8fc3a70
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFFakeQuantWithMinMaxVars.test.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFFakeQuantWithMinMaxVars.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFFakeQuantWithMinMaxVarsTest, constructor)
+{
+ moco::TFFakeQuantWithMinMaxVars fakequant_node;
+
+ ASSERT_EQ(fakequant_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(fakequant_node.opcode(), moco::TFOpcode::FakeQuantWithMinMaxVars);
+
+ ASSERT_EQ(fakequant_node.inputs(), nullptr);
+ ASSERT_EQ(fakequant_node.min(), nullptr);
+ ASSERT_EQ(fakequant_node.max(), nullptr);
+ ASSERT_EQ(fakequant_node.num_bits(), 8);
+ ASSERT_EQ(fakequant_node.narrow_range(), false);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFFusedBatchNorm.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFFusedBatchNorm.test.cpp
new file mode 100644
index 000000000..265f8f9a4
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFFusedBatchNorm.test.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFFusedBatchNorm.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFFusedBatchNormTest, constructor)
+{
+ moco::TFFusedBatchNorm fbn_node;
+
+ ASSERT_EQ(fbn_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(fbn_node.opcode(), moco::TFOpcode::FusedBatchNorm);
+
+ ASSERT_EQ(fbn_node.x(), nullptr);
+ ASSERT_EQ(fbn_node.scale(), nullptr);
+ ASSERT_EQ(fbn_node.offset(), nullptr);
+ ASSERT_EQ(fbn_node.mean(), nullptr);
+ ASSERT_EQ(fbn_node.variance(), nullptr);
+ ASSERT_NE(fbn_node.epsilon(), 0.0f);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFIdentity.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFIdentity.test.cpp
new file mode 100644
index 000000000..deb17d502
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFIdentity.test.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFIdentity.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFIdentituTest, constructor)
+{
+ moco::TFIdentity identity_node;
+
+ ASSERT_EQ(identity_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(identity_node.opcode(), moco::TFOpcode::Identity);
+
+ ASSERT_EQ(identity_node.input(), nullptr);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFMaxPool.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFMaxPool.test.cpp
new file mode 100644
index 000000000..482ad889d
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFMaxPool.test.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFMaxPool.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFMaxPoolTest, constructor)
+{
+ moco::TFMaxPool maxpool;
+
+ ASSERT_EQ(maxpool.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(maxpool.opcode(), moco::TFOpcode::MaxPool);
+
+ ASSERT_EQ(maxpool.input(), nullptr);
+ ASSERT_EQ(maxpool.data_layout(), "");
+ ASSERT_EQ(maxpool.padding(), "");
+ ASSERT_EQ(maxpool.ksize(), std::vector<int64_t>({}));
+ ASSERT_EQ(maxpool.strides(), std::vector<int64_t>({}));
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFMaximum.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFMaximum.test.cpp
new file mode 100644
index 000000000..568bd7038
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFMaximum.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "moco/IR/Nodes/TFMaximum.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFMaximumTest, constructor)
+{
+ moco::TFMaximum max_node;
+
+ ASSERT_EQ(max_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(max_node.opcode(), moco::TFOpcode::Maximum);
+
+ ASSERT_EQ(max_node.x(), nullptr);
+ ASSERT_EQ(max_node.y(), nullptr);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFMean.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFMean.test.cpp
new file mode 100644
index 000000000..126b31783
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFMean.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFMean.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFMeanTest, constructor)
+{
+ moco::TFMean mean_node;
+
+ ASSERT_EQ(mean_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(mean_node.opcode(), moco::TFOpcode::Mean);
+
+ ASSERT_EQ(mean_node.input(), nullptr);
+ ASSERT_EQ(mean_node.reduction_indices(), nullptr);
+ ASSERT_EQ(mean_node.keep_dims(), false);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFMul.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFMul.test.cpp
new file mode 100644
index 000000000..a4a1ecfd7
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFMul.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFMul.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFMulTest, constructor)
+{
+ moco::TFMul mul_node;
+
+ ASSERT_EQ(mul_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(mul_node.opcode(), moco::TFOpcode::Mul);
+
+ ASSERT_EQ(mul_node.x(), nullptr);
+ ASSERT_EQ(mul_node.y(), nullptr);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFPack.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFPack.test.cpp
new file mode 100644
index 000000000..a62b39f3d
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFPack.test.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFPack.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFPackTest, constructor)
+{
+ moco::TFPack pack_node(3); // num of values
+
+ ASSERT_EQ(pack_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(pack_node.opcode(), moco::TFOpcode::Pack);
+
+ ASSERT_EQ(pack_node.N(), 3);
+ ASSERT_EQ(pack_node.values(0), nullptr);
+ ASSERT_EQ(pack_node.values(1), nullptr);
+ ASSERT_EQ(pack_node.values(2), nullptr);
+ ASSERT_EQ(pack_node.axis(), 0);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFPad.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFPad.test.cpp
new file mode 100644
index 000000000..f3f3dcc8c
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFPad.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFPad.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFPadTest, constructor)
+{
+ moco::TFPad pad;
+
+ ASSERT_EQ(pad.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(pad.opcode(), moco::TFOpcode::Pad);
+
+ ASSERT_EQ(pad.input(), nullptr);
+ ASSERT_EQ(pad.paddings(), nullptr);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFPlaceholder.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFPlaceholder.test.cpp
new file mode 100644
index 000000000..e082f0c3e
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFPlaceholder.test.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFPlaceholder.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFPlaceholderTest, constructor)
+{
+ moco::TFPlaceholder placeholder;
+
+ ASSERT_EQ(placeholder.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(placeholder.opcode(), moco::TFOpcode::Placeholder);
+
+ ASSERT_EQ(placeholder.dtype(), loco::DataType::Unknown);
+ ASSERT_EQ(placeholder.rank(), 0);
+
+ placeholder.dtype(loco::DataType::FLOAT32);
+ ASSERT_EQ(placeholder.dtype(), loco::DataType::FLOAT32);
+
+ placeholder.rank(2);
+ ASSERT_EQ(placeholder.rank(), 2);
+
+ placeholder.dim(0) = 2;
+ placeholder.dim(1) = 3;
+
+ ASSERT_TRUE(placeholder.dim(0).known());
+ ASSERT_TRUE(placeholder.dim(1).known());
+
+ ASSERT_EQ(placeholder.dim(0), 2);
+ ASSERT_EQ(placeholder.dim(1), 3);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFRealDiv.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFRealDiv.test.cpp
new file mode 100644
index 000000000..bfb8154a6
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFRealDiv.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFRealDiv.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFRealDivTest, constructor)
+{
+ moco::TFRealDiv div_node;
+
+ ASSERT_EQ(div_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(div_node.opcode(), moco::TFOpcode::RealDiv);
+
+ ASSERT_EQ(div_node.x(), nullptr);
+ ASSERT_EQ(div_node.y(), nullptr);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFRelu.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFRelu.test.cpp
new file mode 100644
index 000000000..650e2550d
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFRelu.test.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFRelu.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFReluTest, constructor)
+{
+ moco::TFRelu relu_node;
+
+ ASSERT_EQ(relu_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(relu_node.opcode(), moco::TFOpcode::Relu);
+
+ ASSERT_EQ(relu_node.features(), nullptr);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFRelu6.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFRelu6.test.cpp
new file mode 100644
index 000000000..9cce83df3
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFRelu6.test.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFRelu6.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFRelu6Test, constructor)
+{
+ moco::TFRelu6 relu6_node;
+
+ ASSERT_EQ(relu6_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(relu6_node.opcode(), moco::TFOpcode::Relu6);
+
+ ASSERT_EQ(relu6_node.features(), nullptr);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFReshape.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFReshape.test.cpp
new file mode 100644
index 000000000..514c691e9
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFReshape.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFReshape.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFReshapeTest, constructor)
+{
+ moco::TFReshape reshape_node;
+
+ ASSERT_EQ(reshape_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(reshape_node.opcode(), moco::TFOpcode::Reshape);
+
+ ASSERT_EQ(reshape_node.tensor(), nullptr);
+ ASSERT_EQ(reshape_node.shape(), nullptr);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFRsqrt.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFRsqrt.test.cpp
new file mode 100644
index 000000000..e94336dfe
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFRsqrt.test.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes//TFRsqrt.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFRsqrtTest, constructor)
+{
+ moco::TFRsqrt rsqrt_node;
+
+ ASSERT_EQ(rsqrt_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(rsqrt_node.opcode(), moco::TFOpcode::Rsqrt);
+
+ ASSERT_EQ(rsqrt_node.x(), nullptr);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFShape.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFShape.test.cpp
new file mode 100644
index 000000000..28110d790
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFShape.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes//TFShape.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFShapeTest, constructor)
+{
+ moco::TFShape shape_node;
+
+ ASSERT_EQ(shape_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(shape_node.opcode(), moco::TFOpcode::Shape);
+
+ ASSERT_EQ(shape_node.input(), nullptr);
+ ASSERT_EQ(shape_node.dtype(), loco::DataType::Unknown);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFSoftmax.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFSoftmax.test.cpp
new file mode 100644
index 000000000..67449feac
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFSoftmax.test.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFSoftmax.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFSoftmaxTest, constructor)
+{
+ moco::TFSoftmax softmax_node;
+
+ ASSERT_EQ(softmax_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(softmax_node.opcode(), moco::TFOpcode::Softmax);
+
+ ASSERT_EQ(softmax_node.logits(), nullptr);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFSqrt.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFSqrt.test.cpp
new file mode 100644
index 000000000..942769f6c
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFSqrt.test.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFSqrt.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFSqrtTest, constructor)
+{
+ moco::TFSqrt sqrt_node;
+
+ ASSERT_EQ(sqrt_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(sqrt_node.opcode(), moco::TFOpcode::Sqrt);
+
+ ASSERT_EQ(sqrt_node.x(), nullptr);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFSquaredDifference.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFSquaredDifference.test.cpp
new file mode 100644
index 000000000..c3ece9b70
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFSquaredDifference.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFSquaredDifference.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFSquaredDifferenceTest, constructor)
+{
+ moco::TFSquaredDifference sd_node;
+
+ ASSERT_EQ(sd_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(sd_node.opcode(), moco::TFOpcode::SquaredDifference);
+
+ ASSERT_EQ(sd_node.x(), nullptr);
+ ASSERT_EQ(sd_node.y(), nullptr);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFSqueeze.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFSqueeze.test.cpp
new file mode 100644
index 000000000..034ca70b2
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFSqueeze.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFSqueeze.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFSqueezeTest, constructor)
+{
+ moco::TFSqueeze squeeze_node;
+
+ ASSERT_EQ(squeeze_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(squeeze_node.opcode(), moco::TFOpcode::Squeeze);
+
+ ASSERT_EQ(squeeze_node.input(), nullptr);
+ ASSERT_EQ(squeeze_node.squeeze_dims().size(), 0);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFStopGradient.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFStopGradient.test.cpp
new file mode 100644
index 000000000..054ccda41
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFStopGradient.test.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFStopGradient.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFStopGradientTest, constructor)
+{
+ moco::TFStopGradient node;
+
+ ASSERT_EQ(node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(node.opcode(), moco::TFOpcode::StopGradient);
+
+ ASSERT_EQ(node.input(), nullptr);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFStridedSlice.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFStridedSlice.test.cpp
new file mode 100644
index 000000000..9e7e45543
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFStridedSlice.test.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFStridedSlice.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFStridedSliceTest, constructor)
+{
+ moco::TFStridedSlice node;
+
+ ASSERT_EQ(node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(node.opcode(), moco::TFOpcode::StridedSlice);
+
+ ASSERT_EQ(node.input(), nullptr);
+ ASSERT_EQ(node.begin(), nullptr);
+ ASSERT_EQ(node.end(), nullptr);
+ ASSERT_EQ(node.strides(), nullptr);
+ ASSERT_EQ(node.begin_mask(), 0);
+ ASSERT_EQ(node.end_mask(), 0);
+ ASSERT_EQ(node.ellipsis_mask(), 0);
+ ASSERT_EQ(node.new_axis_mask(), 0);
+ ASSERT_EQ(node.shrink_axis_mask(), 0);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFSub.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFSub.test.cpp
new file mode 100644
index 000000000..4b80713bd
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFSub.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFSub.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFSubTest, constructor)
+{
+ moco::TFSub sub_node;
+
+ ASSERT_EQ(sub_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(sub_node.opcode(), moco::TFOpcode::Sub);
+
+ ASSERT_EQ(sub_node.x(), nullptr);
+ ASSERT_EQ(sub_node.y(), nullptr);
+}
diff --git a/compiler/moco/lang/src/IR/Nodes/TFTanh.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFTanh.test.cpp
new file mode 100644
index 000000000..38458a694
--- /dev/null
+++ b/compiler/moco/lang/src/IR/Nodes/TFTanh.test.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFTanh.h"
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFTanhTest, constructor)
+{
+ moco::TFTanh tanh_node;
+
+ ASSERT_EQ(tanh_node.dialect(), moco::TFDialect::get());
+ ASSERT_EQ(tanh_node.opcode(), moco::TFOpcode::Tanh);
+
+ ASSERT_EQ(tanh_node.x(), nullptr);
+}
diff --git a/compiler/moco/lang/src/IR/TFDialect.cpp b/compiler/moco/lang/src/IR/TFDialect.cpp
new file mode 100644
index 000000000..35bbcc2c9
--- /dev/null
+++ b/compiler/moco/lang/src/IR/TFDialect.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/TFDialect.h"
+#include "moco/IR/TFNode.h"
+
+#include <loco/IR/Graph.h>
+#include <loco/IR/GraphInputIndex.h>
+#include <loco/IR/GraphOutputIndex.h>
+
+#include <stdex/Memory.h>
+
+#include <cassert>
+#include <stdexcept>
+
+namespace
+{
+
+struct GiiQueryServiceImpl final : public loco::GraphInputIndexQueryService
+{
+ bool associated(const loco::Node *node) const final
+ {
+ if (auto tfplaceholder = dynamic_cast<const moco::TFPlaceholder *>(node))
+ {
+ return moco::indexed(tfplaceholder);
+ }
+ return false;
+ }
+
+ loco::GraphOutputIndex index(const loco::Node *node) const final
+ {
+ assert(associated(node));
+ auto tfplaceholder = dynamic_cast<const moco::TFPlaceholder *>(node);
+ assert(tfplaceholder != nullptr);
+ return moco::index(tfplaceholder);
+ }
+};
+
+struct GoiQueryServiceImpl final : public loco::GraphOutputIndexQueryService
+{
+ bool associated(const loco::Node *node) const final
+ {
+ if (auto tfpush = dynamic_cast<const moco::TFPush *>(node))
+ {
+ return tfpush->indexed();
+ }
+ return false;
+ }
+
+ loco::GraphOutputIndex index(const loco::Node *node) const final
+ {
+ assert(associated(node));
+ if (auto tfpush = dynamic_cast<const moco::TFPush *>(node))
+ {
+ return tfpush->index();
+ }
+ throw std::invalid_argument("node");
+ }
+};
+
+} // namespace
+
+namespace moco
+{
+
+TFDialect::TFDialect()
+{
+ service<loco::GraphInputIndexQueryService>(stdex::make_unique<GiiQueryServiceImpl>());
+ service<loco::GraphOutputIndexQueryService>(stdex::make_unique<GoiQueryServiceImpl>());
+}
+
+loco::Dialect *TFDialect::get(void)
+{
+ static TFDialect d;
+ return &d;
+}
+
+} // namespace moco
diff --git a/compiler/moco/lang/src/IR/TFDialect.test.cpp b/compiler/moco/lang/src/IR/TFDialect.test.cpp
new file mode 100644
index 000000000..3c8b1a16b
--- /dev/null
+++ b/compiler/moco/lang/src/IR/TFDialect.test.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/TFDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(TFDialectTest, get)
+{
+ auto d = moco::TFDialect::get();
+
+ // get() SHOULD return a valid(non-null) pointer
+ ASSERT_NE(d, nullptr);
+ // The return value SHOULD be stable across multiple invocations
+ ASSERT_EQ(d, moco::TFDialect::get());
+}
diff --git a/compiler/moco/lang/src/IR/TFNode.cpp b/compiler/moco/lang/src/IR/TFNode.cpp
new file mode 100644
index 000000000..ab9356196
--- /dev/null
+++ b/compiler/moco/lang/src/IR/TFNode.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/TFNode.h"
+#include "moco/IR/TFDialect.h"
+
+#include <cassert>
+
+namespace moco
+{
+
+const loco::Dialect *TFNode::dialect(void) const { return TFDialect::get(); }
+
+} // namespace moco
+
+// TODO move this to appropriate place
+#include <stdex/Memory.h>
+
+namespace moco
+{
+
+struct GraphInputIndexAnnotation : public loco::NodeAnnotation
+{
+public:
+ GraphInputIndexAnnotation(const loco::GraphInputIndex &index) : _index{index}
+ {
+ // DO NOTHING
+ }
+
+public:
+ const loco::GraphInputIndex &index(void) const { return _index; }
+
+private:
+ loco::GraphInputIndex _index;
+};
+
+bool indexed(const TFPlaceholder *node)
+{
+ return (node->annot<GraphInputIndexAnnotation>() != nullptr);
+}
+
+loco::GraphInputIndex index(const TFPlaceholder *node)
+{
+ assert(indexed(node));
+ return node->annot<GraphInputIndexAnnotation>()->index();
+}
+
+void index(TFPlaceholder *node, const loco::GraphInputIndex index)
+{
+ node->annot(stdex::make_unique<GraphInputIndexAnnotation>(index));
+}
+
+loco::TensorShape tensor_shape(const TFPlaceholder *node)
+{
+ assert(node != nullptr);
+
+ loco::TensorShape shape;
+
+ uint32_t rank = node->rank();
+ shape.rank(rank);
+ for (uint32_t index = 0; index < rank; ++index)
+ {
+ if (node->dim(index).known())
+ shape.dim(index) = node->dim(index).value();
+ else
+ shape.dim(index).unset();
+ }
+
+ return shape;
+}
+
+TFPlaceholder *placeholder_node(loco::Graph *g, const loco::GraphInputIndex &idx)
+{
+ for (uint32_t n = 0; n < g->nodes()->size(); ++n)
+ {
+ if (auto tfplaceholder = dynamic_cast<TFPlaceholder *>(g->nodes()->at(n)))
+ {
+ if (indexed(tfplaceholder) && index(tfplaceholder) == idx)
+ {
+ return tfplaceholder;
+ }
+ }
+ }
+ return nullptr;
+}
+
+} // namespace moco
+
+namespace moco
+{
+
+/**
+ * TFPush
+ */
+
+void TFPush::index(const loco::GraphOutputIndex &index)
+{
+ // Push internally stores "GraphOutputIndex" as int64_t
+ _index = static_cast<int64_t>(index);
+}
+
+loco::GraphOutputIndex TFPush::index(void) const
+{
+ assert(_index >= std::numeric_limits<loco::GraphOutputIndex>::min());
+ assert(_index <= std::numeric_limits<loco::GraphOutputIndex>::max());
+ return static_cast<loco::GraphOutputIndex>(_index);
+}
+
+TFPush *push_node(loco::Graph *g, const loco::GraphOutputIndex &index)
+{
+ for (uint32_t n = 0; n < g->nodes()->size(); ++n)
+ {
+ if (auto tfpush = dynamic_cast<TFPush *>(g->nodes()->at(n)))
+ {
+ if (tfpush->indexed() && tfpush->index() == index)
+ {
+ return tfpush;
+ }
+ }
+ }
+ return nullptr;
+}
+
+} // namespace moco
diff --git a/compiler/moco/lang/src/IR/TFNode.test.cpp b/compiler/moco/lang/src/IR/TFNode.test.cpp
new file mode 100644
index 000000000..4df1211db
--- /dev/null
+++ b/compiler/moco/lang/src/IR/TFNode.test.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/Nodes/TFPlaceholder.h"
+
+#include <loco.h>
+
+#include <gtest/gtest.h>
+
+TEST(TFNodeTest_Placeholder, index)
+{
+ loco::Graph graph;
+
+ auto test_node = graph.nodes()->create<moco::TFPlaceholder>();
+
+ loco::GraphInputIndex index_set{100};
+ moco::index(test_node, index_set);
+
+ auto index_get = moco::index(test_node);
+ ASSERT_EQ(index_get, index_set);
+}
+
+TEST(TFNodeTest_Placeholder, name)
+{
+ loco::Graph graph;
+
+ auto test_node = graph.nodes()->create<moco::TFPlaceholder>();
+
+ test_node->name("PlaceholderName");
+ ASSERT_EQ(test_node->name(), "PlaceholderName");
+}
diff --git a/compiler/moco/lang/src/IR/VariadicArityNode.test.cpp b/compiler/moco/lang/src/IR/VariadicArityNode.test.cpp
new file mode 100644
index 000000000..57361af98
--- /dev/null
+++ b/compiler/moco/lang/src/IR/VariadicArityNode.test.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/IR/VariadicArityNode.h"
+
+#include <loco/IR/Nodes.h>
+
+#include <gtest/gtest.h>
+
+namespace
+{
+
+using namespace moco;
+
+class ArbitraryInputNode : public VariadicArityNode<loco::Node>
+{
+public:
+ ArbitraryInputNode(uint32_t arity) : VariadicArityNode<loco::Node>(arity) {}
+
+ void input(uint32_t idx, loco::Node *node) { at(idx)->node(node); }
+ loco::Node *input(uint32_t idx) const { return at(idx)->node(); }
+
+ const loco::Dialect *dialect(void) const { return nullptr; } // this won't be called for testing
+ uint32_t opnum(void) const { return -1; } // this won't be called for testing
+};
+
+} // namespace
+
+TEST(CustomOpTest, VariadicArityNode_arity_n)
+{
+ loco::ConstGen cg0, cg1, cg2;
+
+ ArbitraryInputNode a_node(3);
+ a_node.input(0, &cg0);
+ a_node.input(1, &cg1);
+ a_node.input(2, &cg2);
+
+ ASSERT_EQ(a_node.arity(), 3);
+ ASSERT_EQ(a_node.input(0), &cg0);
+ ASSERT_EQ(a_node.input(1), &cg1);
+ ASSERT_EQ(a_node.input(2), &cg2);
+}
diff --git a/compiler/moco/pass/CMakeLists.txt b/compiler/moco/pass/CMakeLists.txt
new file mode 100644
index 000000000..1eba86283
--- /dev/null
+++ b/compiler/moco/pass/CMakeLists.txt
@@ -0,0 +1,26 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(moco_pass SHARED ${SOURCES})
+target_include_directories(moco_pass PRIVATE src)
+target_include_directories(moco_pass PUBLIC include)
+target_link_libraries(moco_pass PUBLIC loco)
+target_link_libraries(moco_pass PUBLIC logo_core)
+target_link_libraries(moco_pass PUBLIC moco_lang)
+target_link_libraries(moco_pass PRIVATE moco_support)
+target_link_libraries(moco_pass PRIVATE stdex)
+target_link_libraries(moco_pass PRIVATE oops)
+install(TARGETS moco_pass DESTINATION lib)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(moco_pass_test ${TESTS})
+target_include_directories(moco_pass_test PRIVATE src)
+target_link_libraries(moco_pass_test moco_pass)
+target_link_libraries(moco_pass_test moco_support)
+target_link_libraries(moco_pass_test stdex)
diff --git a/compiler/moco/pass/README.md b/compiler/moco/pass/README.md
new file mode 100644
index 000000000..51921b8db
--- /dev/null
+++ b/compiler/moco/pass/README.md
@@ -0,0 +1,3 @@
+# pass
+
+_pass_ provides _moco_ General Graph Passes for Transformation and Optimization
diff --git a/compiler/moco/pass/include/moco/Pass/Passes.h b/compiler/moco/pass/include/moco/Pass/Passes.h
new file mode 100644
index 000000000..210f0acfc
--- /dev/null
+++ b/compiler/moco/pass/include/moco/Pass/Passes.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_PASS_PASSES_H__
+#define __MOCO_PASS_PASSES_H__
+
+#include "Passes/ConstantFoldAdd.h"
+#include "Passes/ConstantFoldMul.h"
+#include "Passes/ConstantFoldPack.h"
+#include "Passes/ConstantFoldStridedSlice.h"
+#include "Passes/FuseBinaryIntoPreceding.h"
+#include "Passes/RemoveTFIdentityNode.h"
+#include "Passes/ResolveConstantShape.h"
+#include "Passes/ResolveFusedBatchNorm.h"
+#include "Passes/ResolveReshapeWildcardDim.h"
+#include "Passes/ResolveSquaredDifference.h"
+#include "Passes/SqueezeReduceNode.h"
+
+#endif // __MOCO_PASS_PASSES_H__
diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldAdd.h b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldAdd.h
new file mode 100644
index 000000000..ed58d5ee3
--- /dev/null
+++ b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldAdd.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_PASS_CONSTANTFOLD_ADD_H__
+#define __MOCO_PASS_CONSTANTFOLD_ADD_H__
+
+#include <logo/Pass.h>
+
+#include <loco.h>
+
+namespace moco
+{
+
+/**
+ * @brief Constant folder for Const + Add -> Const
+ */
+class ConstantFoldAdd : public logo::Pass
+{
+public:
+ const char *name(void) const final { return "ConstantFoldAdd"; }
+
+public:
+ bool run(loco::Graph *graph) override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_PASS_CONSTANTFOLD_ADD_H__
diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldMul.h b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldMul.h
new file mode 100644
index 000000000..5528b8612
--- /dev/null
+++ b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldMul.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_PASS_CONSTANTFOLD_MUL_H__
+#define __MOCO_PASS_CONSTANTFOLD_MUL_H__
+
+#include <logo/Pass.h>
+
+#include <loco.h>
+
+namespace moco
+{
+
+/**
+ * @brief Constant folder for Const + Mul -> Const
+*/
+class ConstantFoldMul : public logo::Pass
+{
+public:
+ const char *name(void) const final { return "ConstantFoldMul"; }
+
+public:
+ bool run(loco::Graph *graph) override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_PASS_CONSTANTFOLD_MUL_H__
diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldPack.h b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldPack.h
new file mode 100644
index 000000000..fc6bc0ace
--- /dev/null
+++ b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldPack.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_PASS_CONSTANTFOLD_PACK_H__
+#define __MOCO_PASS_CONSTANTFOLD_PACK_H__
+
+#include <logo/Pass.h>
+
+#include <loco.h>
+
+namespace moco
+{
+
+// TODO Provide like ConstantFoldPass<TFPack> for ConstantFold extension
+
+/**
+ * @brief Constant folder for Const + Pack -> Const
+*/
+class ConstantFoldPack : public logo::Pass
+{
+public:
+ const char *name(void) const final { return "ConstantFoldPack"; }
+
+public:
+ bool run(loco::Graph *graph) override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_PASS_CONSTANTFOLD_PACK_H__
diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldStridedSlice.h b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldStridedSlice.h
new file mode 100644
index 000000000..1e3492c2c
--- /dev/null
+++ b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldStridedSlice.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_PASS_CONSTANTFOLD_STRIDEDSLICE_H__
+#define __MOCO_PASS_CONSTANTFOLD_STRIDEDSLICE_H__
+
+#include <logo/Pass.h>
+
+#include <loco.h>
+
+namespace moco
+{
+
+/**
+ * @brief Constant folder for Const + StridedSlice -> Const
+*/
+class ConstantFoldStridedSlice : public logo::Pass
+{
+public:
+ const char *name(void) const final { return "ConstantFoldStridedSlice"; }
+
+public:
+ bool run(loco::Graph *graph) override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_PASS_CONSTANTFOLD_STRIDEDSLICE_H__
diff --git a/compiler/moco/pass/include/moco/Pass/Passes/FuseBinaryIntoPreceding.h b/compiler/moco/pass/include/moco/Pass/Passes/FuseBinaryIntoPreceding.h
new file mode 100644
index 000000000..24e3567c0
--- /dev/null
+++ b/compiler/moco/pass/include/moco/Pass/Passes/FuseBinaryIntoPreceding.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_PASS_FUSE_BINARY_INTO_PRECEDING_H__
+#define __MOCO_PASS_FUSE_BINARY_INTO_PRECEDING_H__
+
+#include <logo/Pass.h>
+
+#include <loco.h>
+
+namespace moco
+{
+
+/**
+ * @brief Fuse TFAdd, TFMul to preceding TFConv2D or TFDepthWiseConv2D
+*/
+class FuseBinaryIntoPreceding : public logo::Pass
+{
+public:
+ const char *name(void) const final { return "FuseBinaryIntoPreceding"; }
+
+public:
+ bool run(loco::Graph *graph) override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_PASS_FUSE_BINARY_INTO_PRECEDING_H__
diff --git a/compiler/moco/pass/include/moco/Pass/Passes/RemoveTFIdentityNode.h b/compiler/moco/pass/include/moco/Pass/Passes/RemoveTFIdentityNode.h
new file mode 100644
index 000000000..388249b63
--- /dev/null
+++ b/compiler/moco/pass/include/moco/Pass/Passes/RemoveTFIdentityNode.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_PASS_REMOVE_TFIDENTITY_NODE_H__
+#define __MOCO_PASS_REMOVE_TFIDENTITY_NODE_H__
+
+#include <logo/Pass.h>
+
+#include <loco.h>
+
+namespace moco
+{
+
+/**
+ * @brief Use the input of "TFIdentity" node instead
+ *
+ * BEFORE:
+ * [X] -> [TFIdentity] -> [Y]
+ *
+ * AFTER:
+ * [X] -> [Y]
+ * [TFIdentity]
+ *
+ * NOTE This transform does not remove "TFIdentity" node
+ * This transform is identical to RemoveForwardNode
+ */
+struct RemoveTFIdentityNode final : public logo::Pass
+{
+ const char *name(void) const final { return "RemoveTFIdentityNode"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace moco
+
+#endif // __MOCO_PASS_REMOVE_TFIDENTITY_NODE_H__
diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ResolveConstantShape.h b/compiler/moco/pass/include/moco/Pass/Passes/ResolveConstantShape.h
new file mode 100644
index 000000000..16046a052
--- /dev/null
+++ b/compiler/moco/pass/include/moco/Pass/Passes/ResolveConstantShape.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_PASS_RESOLVE_CONSTANT_SHAPE_H__
+#define __MOCO_PASS_RESOLVE_CONSTANT_SHAPE_H__
+
+#include <logo/Pass.h>
+
+#include <loco.h>
+
+namespace moco
+{
+
+/**
+ * @brief Replace fully determined TFShape node into TFConst
+ */
+class ResolveConstantShape : public logo::Pass
+{
+public:
+ const char *name(void) const final { return "ResolveConstantShape"; }
+
+public:
+ bool run(loco::Graph *graph) override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_PASS_RESOLVE_CONSTANT_SHAPE_H__
diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ResolveFusedBatchNorm.h b/compiler/moco/pass/include/moco/Pass/Passes/ResolveFusedBatchNorm.h
new file mode 100644
index 000000000..ce5ea0bb0
--- /dev/null
+++ b/compiler/moco/pass/include/moco/Pass/Passes/ResolveFusedBatchNorm.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_PASS_RESOLVE_FUSEDBATCHNORM_H__
+#define __MOCO_PASS_RESOLVE_FUSEDBATCHNORM_H__
+
+#include <logo/Pass.h>
+
+#include <loco.h>
+
+namespace moco
+{
+
+/**
+ * @brief Trasform TFFusedBatchNorm into TFAdd + TFRsqrt + TFMul + TFBatchNorm
+*/
+class ResolveFusedBatchNorm : public logo::Pass
+{
+public:
+ const char *name(void) const final { return "ResolveFusedBatchNorm"; }
+
+public:
+ bool run(loco::Graph *graph) override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_PASS_RESOLVE_FUSEDBATCHNORM_H__
diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ResolveReshapeWildcardDim.h b/compiler/moco/pass/include/moco/Pass/Passes/ResolveReshapeWildcardDim.h
new file mode 100644
index 000000000..137c97379
--- /dev/null
+++ b/compiler/moco/pass/include/moco/Pass/Passes/ResolveReshapeWildcardDim.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_PASS_RESOLVE_RESHAPE_WILDCARD_DIM_H__
+#define __MOCO_PASS_RESOLVE_RESHAPE_WILDCARD_DIM_H__
+
+#include <logo/Pass.h>
+
+#include <loco.h>
+
+namespace moco
+{
+
+/**
+ * @brief Determine wildcard dimension (denoted as -1) of Reshape's shape input
+ * if possible
+ */
+class ResolveReshapeWildcardDim : public logo::Pass
+{
+public:
+ const char *name(void) const final { return "ResolveReshapeWildcardDim"; }
+
+public:
+ bool run(loco::Graph *graph) override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_PASS_RESOLVE_RESHAPE_WILDCARD_DIM_H__
diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ResolveSquaredDifference.h b/compiler/moco/pass/include/moco/Pass/Passes/ResolveSquaredDifference.h
new file mode 100644
index 000000000..1aa78655e
--- /dev/null
+++ b/compiler/moco/pass/include/moco/Pass/Passes/ResolveSquaredDifference.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_PASS_RESOLVE_SQUARED_DIFFERENCE_H__
+#define __MOCO_PASS_RESOLVE_SQUARED_DIFFERENCE_H__
+
+#include <logo/Pass.h>
+
+#include <loco.h>
+
+namespace moco
+{
+
+/**
+ * @brief Decompose TFSquaredDifference to TFSub, TFMul
+ */
+class ResolveSquaredDifference : public logo::Pass
+{
+public:
+ const char *name(void) const final { return "ResolveSquaredDifference"; }
+
+public:
+ bool run(loco::Graph *graph) override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_PASS_RESOLVE_SQUARED_DIFFERENCE_H__
diff --git a/compiler/moco/pass/include/moco/Pass/Passes/SqueezeReduceNode.h b/compiler/moco/pass/include/moco/Pass/Passes/SqueezeReduceNode.h
new file mode 100644
index 000000000..d4a3e65c6
--- /dev/null
+++ b/compiler/moco/pass/include/moco/Pass/Passes/SqueezeReduceNode.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_PASS_SQUEEZE_REDUCE_NODE_H__
+#define __MOCO_PASS_SQUEEZE_REDUCE_NODE_H__
+
+#include <logo/Pass.h>
+
+#include <loco.h>
+
+namespace moco
+{
+
+/**
+ * @brief If ReduceTypeOP don't keep dimensions, replace the ReduceTypeOp
+ * as new one to keep dimensions and insert TFSqueeze
+ */
+class SqueezeReduceNode : public logo::Pass
+{
+public:
+ const char *name(void) const final { return "SqueezeReduceNode"; }
+
+public:
+ bool run(loco::Graph *graph) override;
+};
+
+} // namespace moco
+
+#endif // __MOCO_PASS_SQUEEZE_REDUCE_NODE_H__
diff --git a/compiler/moco/pass/src/ConstantFoldAdd.test.cpp b/compiler/moco/pass/src/ConstantFoldAdd.test.cpp
new file mode 100644
index 000000000..bc9489fbd
--- /dev/null
+++ b/compiler/moco/pass/src/ConstantFoldAdd.test.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Pass/Passes/ConstantFoldAdd.h"
+#include "TestHelper.h"
+
+#include <moco/IR/TFNodes.h>
+#include <loco.h>
+#include <stdex/Memory.h>
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+
+moco::TFConst *const_vector_init(loco::Graph *graph, std::vector<int32_t> values)
+{
+ auto const_node = graph->nodes()->create<moco::TFConst>();
+ auto dim = values.size();
+
+ const_node->dtype(loco::DataType::S32);
+ const_node->rank(1);
+ const_node->dim(0).set(dim);
+
+ const_node->size<loco::DataType::S32>(dim);
+ for (int32_t i = 0; i < dim; ++i)
+ const_node->at<loco::DataType::S32>(i) = values[i];
+
+ return const_node;
+}
+
+} // namespace
+
+TEST(ConstantFoldAdd, basic_vector)
+{
+ loco::Graph graph;
+
+ auto add_node = graph.nodes()->create<moco::TFAdd>();
+ {
+ auto const_from_ss = const_vector_init(&graph, {1, 3, 5});
+ add_node->x(const_from_ss);
+
+ auto const_y = const_vector_init(&graph, {2});
+ add_node->y(const_y);
+ }
+ setup_output_node(&graph, add_node);
+
+ auto pass = stdex::make_unique<moco::ConstantFoldAdd>();
+ bool cont = true;
+ while (cont)
+ {
+ cont = pass->run(&graph);
+ }
+
+ auto ssnode = find_first_node_bytype<moco::TFAdd>(&graph);
+ ASSERT_EQ(ssnode, nullptr);
+
+ auto ssconst = find_first_node_bytype<moco::TFConst>(&graph);
+ ASSERT_NE(ssconst, nullptr);
+ ASSERT_EQ(ssconst->size<loco::DataType::S32>(), 3);
+ ASSERT_EQ(ssconst->at<loco::DataType::S32>(0), 3);
+ ASSERT_EQ(ssconst->at<loco::DataType::S32>(1), 5);
+ ASSERT_EQ(ssconst->at<loco::DataType::S32>(2), 7);
+}
+
+TEST(ConstantFoldAdd, basic_refinedet_1)
+{
+ loco::Graph graph;
+
+ auto add_node = graph.nodes()->create<moco::TFAdd>();
+ {
+ auto const_from_ss = const_vector_init(&graph, {10});
+ add_node->x(const_from_ss);
+
+ auto const_y = const_vector_init(&graph, {0});
+ add_node->y(const_y);
+ }
+ setup_output_node(&graph, add_node);
+
+ auto pass = stdex::make_unique<moco::ConstantFoldAdd>();
+ bool cont = true;
+ while (cont)
+ {
+ cont = pass->run(&graph);
+ }
+
+ auto ssnode = find_first_node_bytype<moco::TFAdd>(&graph);
+ ASSERT_EQ(ssnode, nullptr);
+
+ auto ssconst = find_first_node_bytype<moco::TFConst>(&graph);
+ ASSERT_NE(ssconst, nullptr);
+ ASSERT_EQ(ssconst->size<loco::DataType::S32>(), 1);
+ ASSERT_EQ(ssconst->at<loco::DataType::S32>(0), 10);
+}
diff --git a/compiler/moco/pass/src/ConstantFoldHelper.cpp b/compiler/moco/pass/src/ConstantFoldHelper.cpp
new file mode 100644
index 000000000..79b04863c
--- /dev/null
+++ b/compiler/moco/pass/src/ConstantFoldHelper.cpp
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ConstantFoldHelper.h"
+
+#include <cassert>
+#include <sstream>
+#include <string>
+
+namespace
+{
+
+// TODO this may need to be moved to loco
+bool same_shape(const loco::TensorShape *lhs, const loco::TensorShape *rhs)
+{
+ if (lhs->rank() != rhs->rank())
+ return false;
+
+ for (uint32_t r = 0; r < lhs->rank(); r++)
+ {
+ if (lhs->dim(r).value() != rhs->dim(r).value())
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+
+TFConst *new_const(loco::Graph *graph, loco::TensorShape &tensor_shape, const loco::DataType &dtype)
+{
+ assert(dtype == loco::DataType::S32 || dtype == loco::DataType::FLOAT32);
+
+ auto const_node = graph->nodes()->create<TFConst>();
+ const_node->dtype(dtype);
+ const_node->rank(tensor_shape.rank());
+
+ // Calc number of elements for target node and set shape
+ uint32_t num_elements = 1;
+ for (uint32_t r = 0; r < tensor_shape.rank(); r++)
+ {
+ const_node->dim(r) = tensor_shape.dim(r);
+ assert(const_node->dim(r).known());
+ num_elements = num_elements * const_node->dim(r).value();
+ }
+ if (dtype == loco::DataType::S32)
+ const_node->size<loco::DataType::S32>(num_elements);
+ else if (dtype == loco::DataType::FLOAT32)
+ const_node->size<loco::DataType::FLOAT32>(num_elements);
+
+ // give name for this node from address to be unique
+ std::ostringstream oss;
+ oss << "Const_" << (void *)const_node;
+ const_node->name(oss.str());
+
+ return const_node;
+}
+
+} // namespace moco
+
+namespace moco
+{
+
+template <> int32_t scalar_from_const<int32_t>(const TFConst *tfconst)
+{
+ assert(tfconst->rank() == 0 || tfconst->rank() == 1);
+ assert(tfconst->dtype() == loco::DataType::S32);
+ return tfconst->at<loco::DataType::S32>(0);
+}
+
+template <> float scalar_from_const<float>(const TFConst *tfconst)
+{
+ assert(tfconst->rank() == 0 || tfconst->rank() == 1);
+ assert(tfconst->dtype() == loco::DataType::FLOAT32);
+ return tfconst->at<loco::DataType::FLOAT32>(0);
+}
+
+bool valid_shape_for_constfold_binary_op(const loco::TensorShape &lhs, const loco::TensorShape &rhs)
+{
+ // scalar
+ if (lhs.rank() == 0 || rhs.rank() == 0)
+ return true;
+
+ // same as scalar
+ if (lhs.rank() == 1 && lhs.dim(0).value() == 1)
+ return true;
+ if (rhs.rank() == 1 && rhs.dim(0).value() == 1)
+ return true;
+
+ // for elementwise binary operation
+ return ::same_shape(&lhs, &rhs);
+}
+
+} // namespace moco
+
+namespace moco
+{
+
+float BinaryFunc::apply(float, float) const
+{
+ throw std::runtime_error{"F32 is not supported yet"};
+}
+
+int32_t BinaryFunc::apply(int32_t, int32_t) const
+{
+ throw std::runtime_error{"S32 is not supported yet"};
+}
+
+} // namespace moco
+
+namespace
+{
+
+void apply_binary_s32(const moco::TFConst *lhs, int32_t rhs, moco::TFConst *output,
+ const moco::BinaryFunc &f)
+{
+ assert(lhs->dtype() == loco::DataType::S32);
+ assert(same_shape(lhs, output));
+
+ uint32_t nume = num_elements(lhs);
+ for (uint32_t e = 0; e < nume; e++)
+ {
+ output->at<loco::DataType::S32>(e) = f.apply(lhs->at<loco::DataType::S32>(e), rhs);
+ }
+}
+
+void apply_binary_f32(const moco::TFConst *lhs, float rhs, moco::TFConst *output,
+ const moco::BinaryFunc &f)
+{
+ assert(lhs->dtype() == loco::DataType::FLOAT32);
+ assert(same_shape(lhs, output));
+
+ uint32_t nume = num_elements(lhs);
+ for (uint32_t e = 0; e < nume; e++)
+ {
+ output->at<loco::DataType::FLOAT32>(e) = f.apply(lhs->at<loco::DataType::FLOAT32>(e), rhs);
+ }
+}
+
+void apply_binary_s32(const moco::TFConst *lhs, const moco::TFConst *rhs, moco::TFConst *output,
+ const moco::BinaryFunc &f)
+{
+ assert(same_shape(output, lhs));
+ assert(same_shape(output, rhs));
+ assert(output->dtype() == lhs->dtype());
+ assert(output->dtype() == rhs->dtype());
+
+ uint32_t nume = num_elements(lhs);
+ for (uint32_t e = 0; e < nume; e++)
+ {
+ output->at<loco::DataType::S32>(e) =
+ f.apply(lhs->at<loco::DataType::S32>(e), rhs->at<loco::DataType::S32>(e));
+ }
+}
+
+void apply_binary_f32(const moco::TFConst *lhs, const moco::TFConst *rhs, moco::TFConst *output,
+ const moco::BinaryFunc &f)
+{
+ assert(same_shape(output, lhs));
+ assert(same_shape(output, rhs));
+ assert(output->dtype() == lhs->dtype());
+ assert(output->dtype() == rhs->dtype());
+
+ uint32_t nume = num_elements(lhs);
+ for (uint32_t e = 0; e < nume; e++)
+ {
+ output->at<loco::DataType::FLOAT32>(e) =
+ f.apply(lhs->at<loco::DataType::FLOAT32>(e), rhs->at<loco::DataType::FLOAT32>(e));
+ }
+}
+
+} // namespace
+
+namespace moco
+{
+
+template <>
+void apply_binary<int32_t>(const moco::TFConst *x_const, const moco::TFConst *y_const,
+ moco::TFConst *output_const, const moco::BinaryFunc &f)
+{
+ auto x_shape = moco::tensor_shape(x_const);
+ auto y_shape = moco::tensor_shape(y_const);
+
+ if (y_shape.rank() == 0 || y_shape.rank() == 1)
+ {
+ auto rhs = scalar_from_const<int32_t>(y_const);
+ apply_binary_s32(x_const, rhs, output_const, f);
+ }
+ else if (x_shape.rank() == 0 || x_shape.rank() == 1)
+ {
+ auto rhs = scalar_from_const<int32_t>(x_const);
+ apply_binary_s32(y_const, rhs, output_const, f);
+ }
+ else
+ {
+ apply_binary_f32(x_const, y_const, output_const, f);
+ }
+}
+
+template <>
+void apply_binary<float>(const moco::TFConst *x_const, const moco::TFConst *y_const,
+ moco::TFConst *output_const, const moco::BinaryFunc &f)
+{
+ auto x_shape = moco::tensor_shape(x_const);
+ auto y_shape = moco::tensor_shape(y_const);
+
+ if (y_shape.rank() == 0 || y_shape.rank() == 1)
+ {
+ auto rhs = scalar_from_const<float>(y_const);
+ apply_binary_f32(x_const, rhs, output_const, f);
+ }
+ else if (x_shape.rank() == 0 || x_shape.rank() == 1)
+ {
+ auto rhs = scalar_from_const<float>(x_const);
+ apply_binary_f32(y_const, rhs, output_const, f);
+ }
+ else
+ {
+ apply_binary_f32(x_const, y_const, output_const, f);
+ }
+}
+
+} // namespace moco
diff --git a/compiler/moco/pass/src/ConstantFoldHelper.h b/compiler/moco/pass/src/ConstantFoldHelper.h
new file mode 100644
index 000000000..393b083f2
--- /dev/null
+++ b/compiler/moco/pass/src/ConstantFoldHelper.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_CONSTANT_FOLD_HELPER_H__
+#define __MOCO_CONSTANT_FOLD_HELPER_H__
+
+#include <moco/IR/Nodes/TFConst.h>
+
+#include <loco.h>
+#include <loco/IR/TensorShape.h>
+
+namespace moco
+{
+
+TFConst *new_const(loco::Graph *graph, loco::TensorShape &tensor_shape,
+ const loco::DataType &dtype);
+
+template <typename T> T scalar_from_const(const TFConst *tfconst);
+template <> int32_t scalar_from_const<int32_t>(const TFConst *tfconst);
+template <> float scalar_from_const<float>(const TFConst *tfconst);
+
+/**
+ * @note Check if it is valid to run Constant folding for binary operations
+ * as-of current implementation. That is currently we support for
+ * element-wise or one of the input is scalar.
+ * TODO Support other shapes of binary operation
+ */
+bool valid_shape_for_constfold_binary_op(const loco::TensorShape &lhs,
+ const loco::TensorShape &rhs);
+
+struct BinaryFunc
+{
+ virtual ~BinaryFunc() = default;
+
+ virtual float apply(float, float) const;
+ virtual int32_t apply(int32_t, int32_t) const;
+};
+
+template <typename T>
+void apply_binary(const moco::TFConst *x_const, const moco::TFConst *y_const,
+ moco::TFConst *output_const, const moco::BinaryFunc &f);
+template <>
+void apply_binary<int32_t>(const moco::TFConst *x_const, const moco::TFConst *y_const,
+ moco::TFConst *output_const, const moco::BinaryFunc &f);
+template <>
+void apply_binary<float>(const moco::TFConst *x_const, const moco::TFConst *y_const,
+ moco::TFConst *output_const, const moco::BinaryFunc &f);
+
+} // namespace moco
+
+#endif // __MOCO_CONSTANT_FOLD_HELPER_H__
diff --git a/compiler/moco/pass/src/ConstantFoldMul.test.cpp b/compiler/moco/pass/src/ConstantFoldMul.test.cpp
new file mode 100644
index 000000000..4e9b78fd4
--- /dev/null
+++ b/compiler/moco/pass/src/ConstantFoldMul.test.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Pass/Passes/ConstantFoldMul.h"
+#include "TestHelper.h"
+
+#include <moco/IR/TFNodes.h>
+#include <loco.h>
+#include <stdex/Memory.h>
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+
+moco::TFConst *const_vector_init(loco::Graph *graph, std::vector<int32_t> values)
+{
+ auto const_node = graph->nodes()->create<moco::TFConst>();
+ auto dim = values.size();
+
+ const_node->dtype(loco::DataType::S32);
+ const_node->rank(1);
+ const_node->dim(0).set(dim);
+
+ const_node->size<loco::DataType::S32>(dim);
+ for (int32_t i = 0; i < dim; ++i)
+ const_node->at<loco::DataType::S32>(i) = values[i];
+
+ return const_node;
+}
+
+} // namespace
+
+TEST(ConstantFoldMul, basic_vector)
+{
+ loco::Graph graph;
+
+ auto mul_node = graph.nodes()->create<moco::TFMul>();
+ {
+ auto const_from_ss = const_vector_init(&graph, {1, 3, 5});
+ mul_node->x(const_from_ss);
+
+ auto const_y = const_vector_init(&graph, {2});
+ mul_node->y(const_y);
+ }
+ setup_output_node(&graph, mul_node);
+
+ auto pass = stdex::make_unique<moco::ConstantFoldMul>();
+ bool cont = true;
+ while (cont)
+ {
+ cont = pass->run(&graph);
+ }
+
+ auto ssnode = find_first_node_bytype<moco::TFMul>(&graph);
+ ASSERT_EQ(ssnode, nullptr);
+
+ auto ssconst = find_first_node_bytype<moco::TFConst>(&graph);
+ ASSERT_NE(ssconst, nullptr);
+ ASSERT_EQ(ssconst->size<loco::DataType::S32>(), 3);
+ ASSERT_EQ(ssconst->at<loco::DataType::S32>(0), 2);
+ ASSERT_EQ(ssconst->at<loco::DataType::S32>(1), 6);
+ ASSERT_EQ(ssconst->at<loco::DataType::S32>(2), 10);
+}
+
+TEST(ConstantFoldMul, basic_refinedet_1)
+{
+ loco::Graph graph;
+
+ auto mul_node = graph.nodes()->create<moco::TFMul>();
+ {
+ auto const_from_ss = const_vector_init(&graph, {5});
+ mul_node->x(const_from_ss);
+
+ auto const_y = const_vector_init(&graph, {2});
+ mul_node->y(const_y);
+ }
+ setup_output_node(&graph, mul_node);
+
+ auto pass = stdex::make_unique<moco::ConstantFoldMul>();
+ bool cont = true;
+ while (cont)
+ {
+ cont = pass->run(&graph);
+ }
+
+ auto ssnode = find_first_node_bytype<moco::TFMul>(&graph);
+ ASSERT_EQ(ssnode, nullptr);
+
+ auto ssconst = find_first_node_bytype<moco::TFConst>(&graph);
+ ASSERT_NE(ssconst, nullptr);
+ ASSERT_EQ(ssconst->size<loco::DataType::S32>(), 1);
+ ASSERT_EQ(ssconst->at<loco::DataType::S32>(0), 10);
+}
diff --git a/compiler/moco/pass/src/ConstantFoldPack.test.cpp b/compiler/moco/pass/src/ConstantFoldPack.test.cpp
new file mode 100644
index 000000000..cb6eff0c8
--- /dev/null
+++ b/compiler/moco/pass/src/ConstantFoldPack.test.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Pass/Passes/ConstantFoldPack.h"
+#include "TestHelper.h"
+
+#include <moco/IR/TFNodes.h>
+#include <loco.h>
+#include <stdex/Memory.h>
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+
+moco::TFConst *const_vector_init(loco::Graph *graph, std::vector<int32_t> values)
+{
+ auto const_node = graph->nodes()->create<moco::TFConst>();
+ auto dim = values.size();
+
+ const_node->dtype(loco::DataType::S32);
+ const_node->rank(1);
+ const_node->dim(0).set(dim);
+
+ const_node->size<loco::DataType::S32>(dim);
+ for (int32_t i = 0; i < dim; ++i)
+ const_node->at<loco::DataType::S32>(i) = values[i];
+
+ return const_node;
+}
+
+} // namespace
+
+TEST(ConstantFoldPack, basic_scalar4_vector)
+{
+ loco::Graph graph;
+
+ auto pack_node = graph.nodes()->create<moco::TFPack>(4);
+ {
+ auto input_0 = const_vector_init(&graph, {1});
+ pack_node->values(0, input_0);
+
+ auto input_1 = const_vector_init(&graph, {10});
+ pack_node->values(1, input_1);
+
+ auto input_2 = const_vector_init(&graph, {10});
+ pack_node->values(2, input_2);
+
+ auto input_3 = const_vector_init(&graph, {64});
+ pack_node->values(3, input_3);
+ }
+ // add Identity node as the output Pack will be replaced
+ auto identity = graph.nodes()->create<moco::TFIdentity>();
+ identity->input(pack_node);
+ setup_output_node(&graph, identity);
+
+ auto pass = stdex::make_unique<moco::ConstantFoldPack>();
+ bool cont = true;
+ while (cont)
+ {
+ cont = pass->run(&graph);
+ }
+
+ auto pnode = find_first_node_bytype<moco::TFPack>(&graph);
+ ASSERT_EQ(pnode, nullptr);
+
+ auto pconst = find_first_node_bytype<moco::TFConst>(&graph);
+ ASSERT_NE(pconst, nullptr);
+ ASSERT_EQ(pconst->rank(), 2);
+ ASSERT_EQ(pconst->size<loco::DataType::S32>(), 4);
+ ASSERT_EQ(pconst->at<loco::DataType::S32>(0), 1);
+ ASSERT_EQ(pconst->at<loco::DataType::S32>(1), 10);
+ ASSERT_EQ(pconst->at<loco::DataType::S32>(2), 10);
+ ASSERT_EQ(pconst->at<loco::DataType::S32>(3), 64);
+}
diff --git a/compiler/moco/pass/src/ConstantFoldStridedSlice.test.cpp b/compiler/moco/pass/src/ConstantFoldStridedSlice.test.cpp
new file mode 100644
index 000000000..b5bada221
--- /dev/null
+++ b/compiler/moco/pass/src/ConstantFoldStridedSlice.test.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Pass/Passes/ConstantFoldStridedSlice.h"
+#include "TestHelper.h"
+
+#include <moco/IR/TFNodes.h>
+#include <loco.h>
+#include <stdex/Memory.h>
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+
+moco::TFConst *const_vector_init(loco::Graph *graph, std::vector<int32_t> values)
+{
+ auto const_node = graph->nodes()->create<moco::TFConst>();
+ auto dim = values.size();
+
+ const_node->dtype(loco::DataType::S32);
+ const_node->rank(1);
+ const_node->dim(0).set(dim);
+
+ const_node->size<loco::DataType::S32>(dim);
+ for (int32_t i = 0; i < dim; ++i)
+ const_node->at<loco::DataType::S32>(i) = values[i];
+
+ return const_node;
+}
+
+moco::TFConst *const_matrix(loco::Graph *graph, int32_t dimh, int32_t dimw)
+{
+ auto const_node = graph->nodes()->create<moco::TFConst>();
+
+ const_node->dtype(loco::DataType::S32);
+ const_node->rank(2);
+ const_node->dim(0).set(dimh);
+ const_node->dim(1).set(dimw);
+
+ auto elements = dimh * dimw;
+ const_node->size<loco::DataType::S32>(elements);
+ for (int32_t i = 0; i < elements; ++i)
+ const_node->at<loco::DataType::S32>(i) = i;
+
+ return const_node;
+}
+
+} // namespace
+
+TEST(ConstantFoldStridedSlice, basic_matrix55_11)
+{
+ loco::Graph graph;
+
+ auto sslice_node = graph.nodes()->create<moco::TFStridedSlice>();
+ {
+ auto const_input = const_matrix(&graph, 5, 5);
+ sslice_node->input(const_input);
+
+ auto const_begin = const_vector_init(&graph, {1, 1});
+ sslice_node->begin(const_begin);
+ auto const_end = const_vector_init(&graph, {2, 4});
+ sslice_node->end(const_end);
+ auto const_strides = const_vector_init(&graph, {1, 1});
+ sslice_node->strides(const_strides);
+
+ sslice_node->shrink_axis_mask(1);
+ }
+ setup_output_node(&graph, sslice_node);
+
+ auto pass = stdex::make_unique<moco::ConstantFoldStridedSlice>();
+ bool cont = true;
+ while (cont)
+ {
+ cont = pass->run(&graph);
+ }
+
+ auto ssnode = find_first_node_bytype<moco::TFStridedSlice>(&graph);
+ ASSERT_EQ(ssnode, nullptr);
+
+ auto ssconst = find_first_node_bytype<moco::TFConst>(&graph);
+ ASSERT_NE(ssconst, nullptr);
+ ASSERT_EQ(ssconst->size<loco::DataType::S32>(), 3);
+ ASSERT_EQ(ssconst->at<loco::DataType::S32>(0), 6);
+ ASSERT_EQ(ssconst->at<loco::DataType::S32>(1), 7);
+ ASSERT_EQ(ssconst->at<loco::DataType::S32>(2), 8);
+}
+
+TEST(ConstantFoldStridedSlice, basic_vector4_0)
+{
+ loco::Graph graph;
+
+ auto sslice_node = graph.nodes()->create<moco::TFStridedSlice>();
+ {
+ auto const_input = const_vector_init(&graph, {1, 5, 5, 64});
+ sslice_node->input(const_input);
+
+ auto const_begin = const_vector_init(&graph, {0});
+ sslice_node->begin(const_begin);
+ auto const_end = const_vector_init(&graph, {1});
+ sslice_node->end(const_end);
+ auto const_strides = const_vector_init(&graph, {1});
+ sslice_node->strides(const_strides);
+
+ sslice_node->shrink_axis_mask(1);
+ }
+ setup_output_node(&graph, sslice_node);
+
+ auto pass = stdex::make_unique<moco::ConstantFoldStridedSlice>();
+ bool cont = true;
+ while (cont)
+ {
+ cont = pass->run(&graph);
+ }
+
+ auto ssnode = find_first_node_bytype<moco::TFStridedSlice>(&graph);
+ ASSERT_EQ(ssnode, nullptr);
+
+ auto ssconst = find_first_node_bytype<moco::TFConst>(&graph);
+ ASSERT_NE(ssconst, nullptr);
+ ASSERT_EQ(ssconst->size<loco::DataType::S32>(), 1);
+ ASSERT_EQ(ssconst->at<loco::DataType::S32>(0), 1);
+}
+
+TEST(ConstantFoldStridedSlice, basic_vector4_1)
+{
+ loco::Graph graph;
+
+ auto sslice_node = graph.nodes()->create<moco::TFStridedSlice>();
+ {
+ auto const_input = const_vector_init(&graph, {1, 5, 5, 64});
+ sslice_node->input(const_input);
+
+ auto const_begin = const_vector_init(&graph, {1});
+ sslice_node->begin(const_begin);
+ auto const_end = const_vector_init(&graph, {2});
+ sslice_node->end(const_end);
+ auto const_strides = const_vector_init(&graph, {1});
+ sslice_node->strides(const_strides);
+
+ sslice_node->shrink_axis_mask(1);
+ }
+ setup_output_node(&graph, sslice_node);
+
+ auto pass = stdex::make_unique<moco::ConstantFoldStridedSlice>();
+ bool cont = true;
+ while (cont)
+ {
+ cont = pass->run(&graph);
+ }
+
+ auto ssnode = find_first_node_bytype<moco::TFStridedSlice>(&graph);
+ ASSERT_EQ(ssnode, nullptr);
+
+ auto ssconst = find_first_node_bytype<moco::TFConst>(&graph);
+ ASSERT_NE(ssconst, nullptr);
+ ASSERT_EQ(ssconst->size<loco::DataType::S32>(), 1);
+ ASSERT_EQ(ssconst->at<loco::DataType::S32>(0), 5);
+}
+
+TEST(ConstantFoldStridedSlice, basic_vector4_2)
+{
+ loco::Graph graph;
+
+ auto sslice_node = graph.nodes()->create<moco::TFStridedSlice>();
+ {
+ auto const_input = const_vector_init(&graph, {1, 5, 5, 64});
+ sslice_node->input(const_input);
+
+ auto const_begin = const_vector_init(&graph, {2});
+ sslice_node->begin(const_begin);
+ auto const_end = const_vector_init(&graph, {3});
+ sslice_node->end(const_end);
+ auto const_strides = const_vector_init(&graph, {1});
+ sslice_node->strides(const_strides);
+
+ sslice_node->shrink_axis_mask(1);
+ }
+ setup_output_node(&graph, sslice_node);
+
+ auto pass = stdex::make_unique<moco::ConstantFoldStridedSlice>();
+ bool cont = true;
+ while (cont)
+ {
+ cont = pass->run(&graph);
+ }
+
+ auto ssnode = find_first_node_bytype<moco::TFStridedSlice>(&graph);
+ ASSERT_EQ(ssnode, nullptr);
+
+ auto ssconst = find_first_node_bytype<moco::TFConst>(&graph);
+ ASSERT_NE(ssconst, nullptr);
+ ASSERT_EQ(ssconst->size<loco::DataType::S32>(), 1);
+ ASSERT_EQ(ssconst->at<loco::DataType::S32>(0), 5);
+}
+
+namespace
+{
+
+/**
+ * @note tfconst_at() implementation should be same as that of inside
+ * ConstantFoldStridedSlice.cpp for valid testing
+ */
+int32_t tfconst_at(const moco::TFConst *tfconst, const std::vector<uint32_t> &pos)
+{
+ uint32_t rank = tfconst->rank();
+ assert(rank == pos.size());
+
+ uint32_t element = 0;
+ for (uint32_t r = 0; r < rank; ++r)
+ {
+ uint32_t dim = tfconst->dim(r).value();
+ element = element * dim + pos.at(r);
+ }
+ return tfconst->at<loco::DataType::S32>(element);
+}
+
+} // namespace
+
+TEST(ConstantFoldStridedSlice, tfconst_at)
+{
+ loco::Graph graph;
+
+ auto const_node = graph.nodes()->create<moco::TFConst>();
+
+ const_node->dtype(loco::DataType::S32);
+ const_node->rank(3);
+ const_node->dim(0).set(2);
+ const_node->dim(1).set(3);
+ const_node->dim(2).set(4);
+
+ auto elements = 2 * 3 * 4;
+ const_node->size<loco::DataType::S32>(elements);
+ for (int32_t i = 0; i < elements; ++i)
+ const_node->at<loco::DataType::S32>(i) = i;
+ /*
+ [
+ [ 0, 1, 2, 3] <- [0,0,0]
+ [ 4, 5, 6, 7] <- [0,1,0] [0,1,1] [0,1,2]
+ [ 8, 9,10,11]
+ ]
+ [
+ [12,13,14,15]
+ [16,17,18,19] <- [1,1,0] [1,1,1]
+ [20,21,22,23] <- [1,2,0] [1,2,1] [1,2,2] [1,2,3]
+ ]
+ */
+
+ ASSERT_EQ(tfconst_at(const_node, {0, 0, 0}), 0);
+ ASSERT_EQ(tfconst_at(const_node, {1, 1, 1}), 17);
+ ASSERT_EQ(tfconst_at(const_node, {0, 1, 2}), 6);
+ ASSERT_EQ(tfconst_at(const_node, {1, 2, 3}), 23);
+}
diff --git a/compiler/moco/pass/src/Passes/ConstantFoldAdd.cpp b/compiler/moco/pass/src/Passes/ConstantFoldAdd.cpp
new file mode 100644
index 000000000..018749b78
--- /dev/null
+++ b/compiler/moco/pass/src/Passes/ConstantFoldAdd.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Pass/Passes/ConstantFoldAdd.h"
+
+#include "ConstantFoldHelper.h"
+
+#include <moco/IR/Nodes/TFAdd.h>
+#include <moco/IR/Nodes/TFConst.h>
+
+#include <moco/Support/NodeAs.h>
+
+namespace
+{
+
+struct Func final : public moco::BinaryFunc
+{
+ float apply(float lhs, float rhs) const { return lhs + rhs; }
+ int32_t apply(int32_t lhs, int32_t rhs) const { return lhs + rhs; }
+};
+
+bool constantfold_add(moco::TFAdd *node)
+{
+ auto x_const = moco::as<moco::TFConst>(node->x());
+ auto y_const = moco::as<moco::TFConst>(node->y());
+ if (x_const == nullptr || y_const == nullptr)
+ return false;
+
+ if (x_const->dtype() != y_const->dtype())
+ return false;
+ // TODO support other types
+ if (x_const->dtype() != loco::DataType::S32 && x_const->dtype() != loco::DataType::FLOAT32)
+ return false;
+
+ // NOTE we support limited shape of elementwise add or add with a scalar.
+ // valid_shape_for_constfold_binary_op() explains limited shape.
+ auto x_shape = moco::tensor_shape(x_const);
+ auto y_shape = moco::tensor_shape(y_const);
+ if (!moco::valid_shape_for_constfold_binary_op(x_shape, y_shape))
+ return false;
+
+ loco::TensorShape output_shape;
+ if (y_shape.rank() == 0 || y_shape.rank() == 1)
+ output_shape = x_shape;
+ else
+ output_shape = y_shape;
+
+ auto graph = node->graph();
+ auto output_const = moco::new_const(graph, output_shape, x_const->dtype());
+ Func f;
+
+ if (x_const->dtype() == loco::DataType::S32)
+ {
+ moco::apply_binary<int32_t>(x_const, y_const, output_const, f);
+ }
+ else if (x_const->dtype() == loco::DataType::FLOAT32)
+ {
+ moco::apply_binary<float>(x_const, y_const, output_const, f);
+ }
+
+ // replace
+ loco::replace(node).with(output_const);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+
+/**
+ * @note This will Replace TFAdd with TFConst when inputs are TFConst
+ *
+ * Before
+ * A --- TFAdd --- C
+ * B --/
+ * After
+ * A --- TFAdd
+ * B --/
+ * TFConst ---------- C
+ * Where
+ * A,B : inputs of TFAdd
+ * C : a node that uses TFAdd as an input
+ * TFAdd is disconnected from C
+ * Nodes are drawn multiple times to simplify the diagram
+ */
+bool ConstantFoldAdd::run(loco::Graph *graph)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(graph)))
+ {
+ if (auto add_node = as<moco::TFAdd>(node))
+ {
+ if (constantfold_add(add_node))
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+} // namespace moco
diff --git a/compiler/moco/pass/src/Passes/ConstantFoldMul.cpp b/compiler/moco/pass/src/Passes/ConstantFoldMul.cpp
new file mode 100644
index 000000000..c1870ffee
--- /dev/null
+++ b/compiler/moco/pass/src/Passes/ConstantFoldMul.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Pass/Passes/ConstantFoldMul.h"
+
+#include "ConstantFoldHelper.h"
+
+#include <moco/IR/Nodes/TFMul.h>
+#include <moco/IR/Nodes/TFConst.h>
+
+#include <moco/Support/NodeAs.h>
+
+namespace
+{
+
+struct Func final : public moco::BinaryFunc
+{
+ float apply(float lhs, float rhs) const { return lhs * rhs; }
+ int32_t apply(int32_t lhs, int32_t rhs) const { return lhs * rhs; }
+};
+
+bool constantfold_mul(moco::TFMul *node)
+{
+ auto x_const = moco::as<moco::TFConst>(node->x());
+ auto y_const = moco::as<moco::TFConst>(node->y());
+ if (x_const == nullptr || y_const == nullptr)
+ return false;
+
+ if (x_const->dtype() != y_const->dtype())
+ return false;
+ // TODO support other types
+ if (x_const->dtype() != loco::DataType::S32 && x_const->dtype() != loco::DataType::FLOAT32)
+ return false;
+
+ // NOTE we support limited shape of elementwise mul or multiply with a scalar.
+ // valid_shape_for_constfold_binary_op() explains limited shape.
+ auto x_shape = moco::tensor_shape(x_const);
+ auto y_shape = moco::tensor_shape(y_const);
+ if (!moco::valid_shape_for_constfold_binary_op(x_shape, y_shape))
+ return false;
+
+ loco::TensorShape output_shape;
+ if (y_shape.rank() == 0 || y_shape.rank() == 1)
+ output_shape = x_shape;
+ else
+ output_shape = y_shape;
+
+ auto graph = node->graph();
+ auto output_const = moco::new_const(graph, output_shape, x_const->dtype());
+ Func f;
+
+ if (x_const->dtype() == loco::DataType::S32)
+ {
+ moco::apply_binary<int32_t>(x_const, y_const, output_const, f);
+ }
+ else if (x_const->dtype() == loco::DataType::FLOAT32)
+ {
+ moco::apply_binary<float>(x_const, y_const, output_const, f);
+ }
+
+ // replace
+ loco::replace(node).with(output_const);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+
+/**
+ * @note This will Replace TFMul with TFConst when input are TFConst
+ *
+ * Before
+ * A --- TFMul --- C
+ * B --/
+ * After
+ * A --- TFMul
+ * B --/
+ * TFConst ---------- C
+ * Where
+ * A,B : inputs of TFMul
+ * C : a node that uses TFMul as an input
+ * TFMul is disconnected from C
+ * Nodes are drawn multiple times to simplify the diagram
+ */
+bool ConstantFoldMul::run(loco::Graph *graph)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(graph)))
+ {
+ if (auto mul_node = as<moco::TFMul>(node))
+ {
+ if (constantfold_mul(mul_node))
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+} // namespace moco
diff --git a/compiler/moco/pass/src/Passes/ConstantFoldPack.cpp b/compiler/moco/pass/src/Passes/ConstantFoldPack.cpp
new file mode 100644
index 000000000..105c96cff
--- /dev/null
+++ b/compiler/moco/pass/src/Passes/ConstantFoldPack.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Pass/Passes/ConstantFoldPack.h"
+
+#include "ConstantFoldHelper.h"
+#include "TensorPackEnumerator.h"
+
+#include <moco/IR/Nodes/TFPack.h>
+#include <moco/IR/Nodes/TFConst.h>
+
+#include <moco/Support/NodeAs.h>
+
+#include <loco/IR/TensorShape.h>
+
+#include <oops/UserExn.h>
+
+#include <cassert>
+#include <vector>
+
+namespace
+{
+
+bool valid_axis_range(int32_t output_rank, int32_t pack_axis)
+{
+ // check axis range in [-r-1, r+1)
+ assert(output_rank > 0);
+ return (-output_rank <= pack_axis) && (pack_axis < output_rank);
+}
+
+bool constantfold_pack(moco::TFPack *node)
+{
+ // check if all the inputs are Const
+ std::vector<moco::TFConst *> input_nodes;
+ uint32_t num = node->N();
+
+ for (uint32_t index = 0; index < num; ++index)
+ {
+ auto in = dynamic_cast<moco::TFConst *>(node->values(index));
+ if (in == nullptr)
+ return false;
+
+ input_nodes.push_back(in);
+ }
+ assert(input_nodes.size() == num);
+
+ // check if all inputs have same shape and dtype
+ auto input_0 = input_nodes.at(0);
+ auto shape_0 = moco::tensor_shape(input_0);
+ auto dtype_0 = input_0->dtype();
+ if (dtype_0 != loco::DataType::S32 && dtype_0 != loco::DataType::FLOAT32)
+ {
+ // TODO support other types
+ assert(false);
+ return false;
+ }
+ for (uint32_t index = 1; index < num; ++index)
+ {
+ auto input_i = input_nodes.at(index);
+ auto shape_i = moco::tensor_shape(input_i);
+ auto dtype_i = input_i->dtype();
+ if (!(shape_0 == shape_i))
+ return false;
+ if (dtype_0 != dtype_i)
+ return false;
+ }
+
+ int32_t output_rank = static_cast<int32_t>(shape_0.rank() + 1);
+ int32_t pack_axis = node->axis();
+ if (!valid_axis_range(output_rank, pack_axis))
+ {
+ throw oops::UserExn("axis is out of range: ", node->name());
+ }
+
+ if (pack_axis < 0)
+ {
+ pack_axis = output_rank + pack_axis;
+ }
+
+ // define output shape
+ loco::TensorShape output_shape;
+ output_shape.rank(output_rank);
+
+ for (int32_t r = 0, s = 0; r < output_rank; ++r)
+ {
+ if (r == pack_axis)
+ {
+ output_shape.dim(r).set(num);
+ }
+ else
+ {
+ output_shape.dim(r).set(shape_0.dim(s++).value());
+ }
+ }
+
+ auto graph = node->graph();
+
+ // create new constant
+ auto output_const = moco::new_const(graph, output_shape, input_0->dtype());
+
+ moco::TensorPackEnumerator etor;
+
+ etor.shape(shape_0, output_shape);
+ etor.axis(pack_axis);
+ for (etor.start(); etor.valid(); etor.advance())
+ {
+ uint32_t inp_num = etor.inp_num();
+ uint32_t inp_element = etor.inp_element();
+ uint32_t out_element = etor.out_element();
+
+ auto inp_const = input_nodes[inp_num];
+
+ if (input_0->dtype() == loco::DataType::S32)
+ {
+ int32_t val = inp_const->at<loco::DataType::S32>(inp_element);
+ output_const->at<loco::DataType::S32>(out_element) = val;
+ }
+ else if (input_0->dtype() == loco::DataType::FLOAT32)
+ {
+ float val = inp_const->at<loco::DataType::FLOAT32>(inp_element);
+ output_const->at<loco::DataType::FLOAT32>(out_element) = val;
+ }
+ }
+
+ // replace
+ loco::replace(node).with(output_const);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+
+/**
+ * @note This will Replace TFPack with TFConst when inputs are TFConst
+ *
+ * Before
+ * A --- TFPack --- C
+ * B --/
+ * After
+ * A --- TFPack
+ * B --/
+ * TFConst ---------- C
+ * Where
+ * A, B : inputs of TFPack
+ * C : a node that uses TFPack as an input
+ * TFPack is disconnected from C
+ * Nodes are drawn multiple times to simplify the diagram
+ */
+bool ConstantFoldPack::run(loco::Graph *graph)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(graph)))
+ {
+ if (auto pack_node = as<moco::TFPack>(node))
+ {
+ if (constantfold_pack(pack_node))
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+} // namespace moco
diff --git a/compiler/moco/pass/src/Passes/ConstantFoldStridedSlice.cpp b/compiler/moco/pass/src/Passes/ConstantFoldStridedSlice.cpp
new file mode 100644
index 000000000..3542e6077
--- /dev/null
+++ b/compiler/moco/pass/src/Passes/ConstantFoldStridedSlice.cpp
@@ -0,0 +1,291 @@
+/*
+ * 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 "moco/Pass/Passes/ConstantFoldStridedSlice.h"
+
+#include "ConstantFoldHelper.h"
+#include "TensorSliceEnumerator.h"
+
+#include <moco/IR/Nodes/TFStridedSlice.h>
+#include <moco/IR/Nodes/TFConst.h>
+
+#include <moco/Support/NodeAs.h>
+#include <moco/Support/TFShapeInferenceHelper.h>
+
+#include <oops/UserExn.h>
+
+#include <cassert>
+#include <vector>
+
+namespace
+{
+
+loco::TensorShape calc_output_shape(moco::TFStridedSlice *node)
+{
+ auto const_input = loco::must_cast<moco::TFConst *>(node->input());
+ auto const_begin = loco::must_cast<moco::TFConst *>(node->begin());
+ auto const_end = loco::must_cast<moco::TFConst *>(node->end());
+ auto input_rank = const_input->rank();
+ auto output_rank = input_rank;
+ loco::TensorShape output_shape_range;
+
+ output_shape_range.rank(input_rank);
+ for (uint32_t r = 0; r < input_rank; ++r)
+ {
+ // TODO apply begin/end mask
+ // TODO apply ellipsis mask
+ // TODO apply strides
+ auto end = const_end->at<loco::DataType::S32>(r);
+ auto begin = const_begin->at<loco::DataType::S32>(r);
+ auto size = end - begin;
+ output_shape_range.dim(r).set(size);
+ }
+
+ loco::TensorShape output_tensor_shape;
+ if (node->shrink_axis_mask() != 0)
+ {
+ for (uint32_t rs = 0; rs < input_rank; ++rs)
+ {
+ int32_t bit = 1 << rs;
+ int32_t mask = node->shrink_axis_mask();
+ if (bit & mask)
+ {
+ // shrink one dimension
+ assert(output_rank > 0);
+ output_rank = output_rank - 1;
+ }
+ }
+ output_tensor_shape.rank(output_rank);
+ for (uint32_t rs = 0, rd = 0; rs < input_rank; ++rs)
+ {
+ int32_t bit = 1 << rs;
+ int32_t mask = node->shrink_axis_mask();
+ if ((bit & mask) == 0)
+ {
+ // use this dimension
+ output_tensor_shape.dim(rd).set(output_shape_range.dim(rs).value());
+ rd++;
+ }
+ // else this dimension is shrink-ed
+ }
+ }
+ else
+ {
+ output_tensor_shape = output_shape_range;
+ }
+
+ return output_tensor_shape;
+}
+
+moco::u32v_t vector_from_const(moco::TFConst *tfconst)
+{
+ moco::u32v_t result;
+
+ auto rank = tfconst->rank();
+ assert(rank == 1);
+ auto dim = tfconst->dim(0).value();
+
+ result.resize(dim);
+ for (uint32_t r = 0; r < dim; ++r)
+ {
+ auto val = tfconst->at<loco::DataType::S32>(r);
+ result.at(r) = val;
+ }
+
+ return result;
+}
+
+moco::u32v_t operator-(const moco::u32v_t &lhs, const moco::u32v_t &rhs)
+{
+ assert(lhs.size() == rhs.size());
+
+ moco::u32v_t res;
+ res.resize(lhs.size());
+ for (uint32_t r = 0; r < lhs.size(); r++)
+ {
+ res.at(r) = lhs.at(r) - rhs.at(r);
+ }
+ return res;
+}
+
+template <typename T> T tfconst_at(const moco::TFConst *tfconst, const moco::u32v_t &pos);
+
+template <> int32_t tfconst_at<int32_t>(const moco::TFConst *tfconst, const moco::u32v_t &pos)
+{
+ uint32_t rank = tfconst->rank();
+ assert(rank == pos.size());
+ uint32_t element = 0;
+ for (uint32_t r = 0; r < rank; ++r)
+ {
+ uint32_t dim = tfconst->dim(r).value();
+ element = element * dim + pos.at(r);
+ }
+ return tfconst->at<loco::DataType::S32>(element);
+}
+
+template <> float tfconst_at<float>(const moco::TFConst *tfconst, const moco::u32v_t &pos)
+{
+ uint32_t rank = tfconst->rank();
+ assert(rank == pos.size());
+ uint32_t element = 0;
+ for (uint32_t r = 0; r < rank; ++r)
+ {
+ uint32_t dim = tfconst->dim(r).value();
+ element = element * dim + pos.at(r);
+ }
+ return tfconst->at<loco::DataType::FLOAT32>(element);
+}
+
+void tfconst_at(moco::TFConst *tfconst, const moco::u32v_t &pos, int32_t value)
+{
+ // tfconst->rank() can be smaller than pos.size()
+ // i.e., tfconst: shape[3] and pos[0,1]
+ // where shape[3] is output result shape
+ // [0,1] is position of input const
+ uint32_t rank = pos.size();
+ uint32_t element = 0;
+ for (uint32_t r = 0; r < rank; ++r)
+ {
+ // this is like expand the shape from [3] to [1,3] to use same formula as in reading
+ uint32_t dim = tfconst->rank() < r ? tfconst->dim(r).value() : 1;
+ element = element * dim + pos.at(r);
+ }
+
+ tfconst->at<loco::DataType::S32>(element) = value;
+}
+
+void tfconst_at(moco::TFConst *tfconst, const moco::u32v_t &pos, float value)
+{
+ uint32_t rank = pos.size();
+ uint32_t element = 0;
+ for (uint32_t r = 0; r < rank; ++r)
+ {
+ uint32_t dim = tfconst->rank() < r ? tfconst->dim(r).value() : 1;
+ element = element * dim + pos.at(r);
+ }
+
+ tfconst->at<loco::DataType::FLOAT32>(element) = value;
+}
+
+bool constantfold_stridedslice(moco::TFStridedSlice *node)
+{
+ auto const_input = dynamic_cast<moco::TFConst *>(node->input());
+ if (const_input == nullptr)
+ {
+ // input is not TFConst, there's nothing to do
+ return false;
+ }
+
+ // TODO support full mask features: see import codes also
+ assert(node->begin_mask() == 0);
+ assert(node->end_mask() == 0);
+ assert(node->ellipsis_mask() == 0);
+ assert(node->shrink_axis_mask() == 1);
+
+ // TODO support other dtypes
+ assert(const_input->dtype() == loco::DataType::S32 ||
+ const_input->dtype() == loco::DataType::FLOAT32);
+
+ auto const_begin = dynamic_cast<moco::TFConst *>(node->begin());
+ auto const_end = dynamic_cast<moco::TFConst *>(node->end());
+ auto const_strides = dynamic_cast<moco::TFConst *>(node->strides());
+ if (const_begin == nullptr || const_end == nullptr || const_strides == nullptr)
+ {
+ return false;
+ }
+
+ // NOTE need shape but cannot depend on shape inference service module
+ auto tensor_shape = calc_output_shape(node);
+ auto input_shape = moco::tensor_shape(const_input);
+
+ auto graph = node->graph();
+
+ // Create our target TFConst node with shape from begin~end/strides
+ auto const_sliced = moco::new_const(graph, tensor_shape, const_input->dtype());
+
+ // Copy sliced elements using TensorSliceEnumerator
+ moco::TensorSliceEnumerator etor;
+ auto v_begin = vector_from_const(const_begin);
+ auto v_end = vector_from_const(const_end);
+ moco::u32v_t v_cursor;
+ moco::u32v_t v_offset;
+
+ etor.shape(input_shape);
+ etor.begin(v_begin);
+ etor.end(v_end);
+
+ for (etor.start(); etor.valid(); etor.advance())
+ {
+ v_cursor = etor.cursor();
+ v_offset = v_cursor - v_begin;
+
+ if (const_input->dtype() == loco::DataType::S32)
+ {
+ int32_t value = tfconst_at<int32_t>(const_input, v_cursor);
+ tfconst_at(const_sliced, v_offset, value);
+ }
+ else if (const_input->dtype() == loco::DataType::FLOAT32)
+ {
+ float value = tfconst_at<float>(const_input, v_cursor);
+ tfconst_at(const_sliced, v_offset, value);
+ }
+ }
+
+ // replace
+ loco::replace(node).with(const_sliced);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+
+/**
+ * @note This will Replace TFStridedSlice with TFConst when 'input' is TFConst
+ *
+ * Before
+ * A --- TFStridedSlice --- C
+ * B --/
+ * After
+ * A --- TFStridedSlice
+ * B --/
+ * TFConst ---------- C
+ * Where
+ * A,B : inputs of TFStridedSlice
+ * C : a node that uses TFStridedSlice as an input
+ * TFStridedSlice is disconnected from C
+ * Nodes are drawn multiple times to simplify the diagram
+ * Limits
+ * Only limit set of inputs are supported for now
+ */
+bool ConstantFoldStridedSlice::run(loco::Graph *graph)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(graph)))
+ {
+ if (auto sslice_node = as<moco::TFStridedSlice>(node))
+ {
+ if (constantfold_stridedslice(sslice_node))
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+} // namespace moco
diff --git a/compiler/moco/pass/src/Passes/FuseBinaryIntoPreceding.cpp b/compiler/moco/pass/src/Passes/FuseBinaryIntoPreceding.cpp
new file mode 100644
index 000000000..f97546a80
--- /dev/null
+++ b/compiler/moco/pass/src/Passes/FuseBinaryIntoPreceding.cpp
@@ -0,0 +1,538 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Pass/Passes/FuseBinaryIntoPreceding.h"
+
+#include <moco/Support/TFShapeInferenceHelper.h>
+
+#include <moco/IR/TFDialect.h>
+#include <moco/IR/Nodes/TFAdd.h>
+#include <moco/IR/Nodes/TFBiasAdd.h>
+#include <moco/IR/Nodes/TFConst.h>
+#include <moco/IR/Nodes/TFConv2D.h>
+#include <moco/IR/Nodes/TFDepthwiseConv2dNative.h>
+#include <moco/IR/Nodes/TFMul.h>
+
+#include <cassert>
+#include <memory>
+
+namespace
+{
+
+/**
+ * @brief Fusable operation type
+ */
+enum class FuseType
+{
+ Conv2D,
+ DepthwiseConv2D,
+ // TODO Support FullyConnected
+};
+
+// TODO rename this method when there is a better name
+bool is_only_one_valid(moco::TFConst *xc, moco::TFConst *yc)
+{
+ if (xc == nullptr && yc == nullptr)
+ return false;
+ if (xc != nullptr && yc != nullptr)
+ return false;
+
+ return true;
+}
+
+// TODO Put this in some common place
+void copy_shape(const moco::TFConst *src, moco::TFConst *dst)
+{
+ assert(src != nullptr);
+ assert(dst != nullptr);
+
+ uint32_t rank = src->rank();
+ dst->rank(rank);
+ for (uint32_t index = 0; index < rank; ++index)
+ {
+ if (src->dim(index).known())
+ dst->dim(index) = src->dim(index);
+ else
+ dst->dim(index).unset();
+ }
+}
+
+/**
+ * @brief return true if shape is identical
+ */
+bool shape_match(const moco::TFConst *c1, const moco::TFConst *c2)
+{
+ assert(c1 != nullptr);
+ assert(c2 != nullptr);
+
+ uint32_t rank = c1->rank();
+ if (rank != c2->rank())
+ return false;
+
+ for (uint32_t index = 0; index < rank; ++index)
+ {
+ if (!c1->dim(index).known() || !c2->dim(index).known())
+ return false;
+
+ if (c1->dim(index).value() != c2->dim(index).value())
+ return false;
+ }
+ return true;
+}
+
+template <FuseType FT>
+moco::TFConst *create_kernel_from_fuse_mulparam(loco::Graph *graph, moco::TFConst *ker,
+ moco::TFConst *mulparam);
+
+template <>
+moco::TFConst *create_kernel_from_fuse_mulparam<FuseType::Conv2D>(loco::Graph *graph,
+ moco::TFConst *ker,
+ moco::TFConst *mulparam)
+{
+ auto ker_shape_inf = moco::node_shape(ker);
+ assert(ker_shape_inf.domain() != loco::Domain::Unknown);
+ auto ker_shape = ker_shape_inf.as<loco::TensorShape>();
+
+ auto mulparam_shape_inf = moco::node_shape(mulparam);
+ assert(mulparam_shape_inf.domain() != loco::Domain::Unknown);
+ auto mulparam_shape = mulparam_shape_inf.as<loco::TensorShape>();
+
+ // create new ker_fused with same size of ker
+ auto ker_fused = graph->nodes()->create<moco::TFConst>();
+
+ assert(ker_shape.rank() == 4);
+ assert(mulparam_shape.rank() == 1);
+ assert(ker_shape.dim(3).value() == mulparam_shape.dim(0).value());
+
+ ker_fused->dtype(loco::DataType::FLOAT32);
+ copy_shape(ker, ker_fused);
+ auto ker_num_elements = ker->size<loco::DataType::FLOAT32>();
+ ker_fused->size<loco::DataType::FLOAT32>(ker_num_elements);
+
+ // TensorFlow Conv2D Kernel has HWIO format
+ // Broadcast Mul vector to Kernel tensor by the Output
+ const uint32_t ker_height = ker_shape.dim(0).value();
+ const uint32_t ker_width = ker_shape.dim(1).value();
+ const uint32_t ker_input = ker_shape.dim(2).value();
+ const uint32_t ker_output = ker_shape.dim(3).value();
+
+ for (uint32_t ker_y = 0; ker_y < ker_height; ++ker_y)
+ {
+ for (uint32_t ker_x = 0; ker_x < ker_width; ++ker_x)
+ {
+ for (uint32_t in_ch = 0; in_ch < ker_input; ++in_ch)
+ {
+ uint32_t num_items = ((ker_y * ker_width + ker_x) * ker_input + in_ch) * ker_output;
+ for (uint32_t out_ch = 0; out_ch < ker_output; ++out_ch)
+ {
+ auto mulparam_v = mulparam->at<loco::DataType::FLOAT32>(out_ch);
+ auto ker_v = ker->at<loco::DataType::FLOAT32>(num_items + out_ch);
+ ker_fused->at<loco::DataType::FLOAT32>(num_items + out_ch) = ker_v * mulparam_v;
+ }
+ }
+ }
+ }
+
+ return ker_fused;
+}
+
+/**
+ * @brief Create a kernel from fuse mulparam<FuseType::DepthwiseConv2D> object
+ * @return Kernel of fused mulparam
+ */
+template <>
+moco::TFConst *create_kernel_from_fuse_mulparam<FuseType::DepthwiseConv2D>(loco::Graph *graph,
+ moco::TFConst *ker,
+ moco::TFConst *mulparam)
+{
+ auto ker_shape_inf = moco::node_shape(ker);
+ assert(ker_shape_inf.domain() != loco::Domain::Unknown);
+ auto ker_shape = ker_shape_inf.as<loco::TensorShape>();
+
+ auto mulparam_shape_inf = moco::node_shape(mulparam);
+ assert(mulparam_shape_inf.domain() != loco::Domain::Unknown);
+ auto mulparam_shape = mulparam_shape_inf.as<loco::TensorShape>();
+
+ // create new ker_fused with same size of ker
+ auto ker_fused = graph->nodes()->create<moco::TFConst>();
+
+ assert(ker_shape.rank() == 4);
+ assert(mulparam_shape.rank() == 1);
+ assert(ker_shape.dim(2).value() * ker_shape.dim(3).value() == mulparam_shape.dim(0).value());
+
+ ker_fused->dtype(loco::DataType::FLOAT32);
+ copy_shape(ker, ker_fused);
+ auto ker_num_elements = ker->size<loco::DataType::FLOAT32>();
+ ker_fused->size<loco::DataType::FLOAT32>(ker_num_elements);
+
+ // TensorFlow DepthwiseConv2DNative Kernel has HWIM format
+ // Broadcast Mul vector to Kernel tensor by the Output
+ const uint32_t ker_height = ker_shape.dim(0).value();
+ const uint32_t ker_width = ker_shape.dim(1).value();
+ const uint32_t ker_input = ker_shape.dim(2).value();
+ const uint32_t ker_multiplier = ker_shape.dim(3).value();
+
+ for (uint32_t ker_y = 0; ker_y < ker_height; ++ker_y)
+ {
+ for (uint32_t ker_x = 0; ker_x < ker_width; ++ker_x)
+ {
+ for (uint32_t in_ch = 0; in_ch < ker_input; ++in_ch)
+ {
+ uint32_t num_items = ((ker_y * ker_width + ker_x) * ker_input + in_ch) * ker_multiplier;
+ for (uint32_t ker_ch = 0; ker_ch < ker_multiplier; ++ker_ch)
+ {
+ auto mulparam_v = mulparam->at<loco::DataType::FLOAT32>(in_ch + ker_ch * ker_input);
+ auto ker_v = ker->at<loco::DataType::FLOAT32>(num_items + ker_ch);
+ ker_fused->at<loco::DataType::FLOAT32>(num_items + ker_ch) = ker_v * mulparam_v;
+ }
+ }
+ }
+ }
+
+ return ker_fused;
+}
+
+/**
+ * @brief Create a fused convolution opertion from kernel of fused mulparam
+ * @return Fused convolution operation
+ */
+template <FuseType FT, class T>
+T *fused_conv_node(loco::Graph *graph, moco::TFConst *mulparam, T *conv_node)
+{
+ // LOGGER(l);
+
+ // ker should be constant
+ auto ker = dynamic_cast<moco::TFConst *>(conv_node->filter());
+ if (ker == nullptr)
+ {
+ // Wait until ker is becomes TFConst: there are cases when it's Identity.
+ // INFO(l) << "Mul fuse_to_preceding: precedingOp ker is not TFConst";
+ return nullptr;
+ }
+ auto ifm = conv_node->input();
+ assert(ifm != nullptr);
+
+ // we need shape information, if not wait till it's ready
+ auto ker_shape_inf = moco::node_shape(ker);
+ if (ker_shape_inf.domain() == loco::Domain::Unknown)
+ {
+ // INFO(l) << "Mul fuse_to_preceding: precedingOp ker has no shape";
+ return nullptr;
+ }
+ auto mulparam_shape_inf = moco::node_shape(mulparam);
+ if (mulparam_shape_inf.domain() == loco::Domain::Unknown)
+ {
+ // INFO(l) << "Mul fuse_to_preceding: precedingOp mulparam has no shape";
+ return nullptr;
+ }
+ // if MulParam rank is not 1 we cannot fuse, just skip
+ auto mulparam_shape = mulparam_shape_inf.as<loco::TensorShape>();
+ if (mulparam_shape.rank() != 1)
+ {
+ // INFO(l) << "Mul fuse_to_preceding: Mul rank is not 1";
+ return nullptr;
+ }
+
+ auto ker_fused = create_kernel_from_fuse_mulparam<FT>(graph, ker, mulparam);
+ auto conv_fused = graph->nodes()->create<T>();
+
+ conv_fused->input(ifm);
+ conv_fused->filter(ker_fused);
+ conv_fused->padding(conv_node->padding());
+ conv_fused->data_layout(conv_node->data_layout());
+ conv_fused->strides(conv_node->strides());
+
+ return conv_fused;
+}
+
+/**
+ * @note This creates fused ker:2 from ker:1, 'mulparam' and
+ * new precedingOp:2 that uses ker:2 as the kernel.
+ * Then make C to use precedingOp:2 as new input.
+ *
+ * <Before>
+ * mulparam-\
+ * ker:1 --\ \
+ * ifm ----- precedingOp:1 ----------- Mul --- C
+ *
+ *
+ * <After>
+ * mulparam-\
+ * ker:1 --\ \
+ * - precedingOp:1 ----------- Mul ---
+ * /
+ * ifm ----- precedingOp:2 ------------------- C
+ * ker:2 ---/
+ *
+ *
+ * [Where]
+ * - precedingOp:1 can be one of TFConv2D, TFDepthwiseConv2dNative, FullyConnected
+ * - 'mulparam' and Mul will be disconnected from the Output.
+ * - ker:2 is added with fused values of ker:1 and mulparam
+ * - precedingOp:2 is added using ifm and ker:2 and other parameters
+ * same as precedingOp:1.
+ * - ker:1, precedingOp:1, 'mulparam' and Mul should be removed in
+ * RemoveDeadNodeTransform if not used.
+ */
+bool fuse_to_preceding(loco::Graph *graph, moco::TFMul *node)
+{
+ auto xc = dynamic_cast<moco::TFConst *>(node->x());
+ auto yc = dynamic_cast<moco::TFConst *>(node->y());
+
+ // Note: if both are constants, it should be done by constant-folding
+ if (!(is_only_one_valid(xc, yc)))
+ return false;
+
+ moco::TFConst *mulparam = nullptr;
+ moco::TFNode *precedingOp = nullptr;
+
+ if (xc != nullptr)
+ {
+ mulparam = xc;
+ precedingOp = dynamic_cast<moco::TFNode *>(node->y());
+ }
+ else // yc != nullptr
+ {
+ mulparam = yc;
+ precedingOp = dynamic_cast<moco::TFNode *>(node->x());
+ }
+
+ assert(mulparam->dtype() == loco::DataType::FLOAT32);
+
+ // TODO support FullyConnected
+ moco::TFNode *fused_node = nullptr;
+ if (auto conv2d = dynamic_cast<moco::TFConv2D *>(precedingOp))
+ fused_node = fused_conv_node<FuseType::Conv2D, moco::TFConv2D>(graph, mulparam, conv2d);
+ else if (auto dw_conv2d = dynamic_cast<moco::TFDepthwiseConv2dNative *>(precedingOp))
+ fused_node = fused_conv_node<FuseType::DepthwiseConv2D, moco::TFDepthwiseConv2dNative>(
+ graph, mulparam, dw_conv2d);
+
+ // Not ready yet
+ if (fused_node == nullptr)
+ return false;
+
+ // Replace TFMul node with new precedingOp with fused kernel
+ // This will leave existing precedingOp as-is but can be removed if not used
+ // from other transformations
+ replace(node).with(fused_node);
+ // TODO check if need to disconnect
+ // node->x(nullptr);
+ // node->y(nullptr);
+ // fused_node->ifm(nullptr);
+ // fused_node->ker(nullptr);
+
+ return true;
+}
+
+/**
+ * @brief Create zero-filled BiasAdd opertion and insert after precedingOp
+ * The plan is to fuse 'addparam' to TFBiasAdd bias
+ * @return Zero-filled BiasAdd operation
+ */
+template <class T>
+moco::TFBiasAdd *create_biasadd_node(loco::Graph *graph, moco::TFConst *addparam, T *precedingOp)
+{
+ auto dtype = addparam->dtype();
+ assert(dtype == loco::DataType::FLOAT32);
+
+ // Create TFConst(bias of TFBiasAdd) with same shape and dtype of 'addparam' but
+ // with values 0.0
+ auto biasadd_param = graph->nodes()->create<moco::TFConst>();
+ biasadd_param->dtype(dtype);
+ copy_shape(addparam, biasadd_param);
+ auto biasadd_num_elements = addparam->size<loco::DataType::FLOAT32>();
+ biasadd_param->size<loco::DataType::FLOAT32>(biasadd_num_elements);
+ for (int32_t i = 0; i < biasadd_num_elements; i++)
+ {
+ biasadd_param->at<loco::DataType::FLOAT32>(i) = 0.0f;
+ }
+
+ // Create TFBiasAdd with same shape as TFAdd
+ auto data_layout = precedingOp->data_layout();
+ auto tf_biasadd = graph->nodes()->create<moco::TFBiasAdd>();
+ tf_biasadd->data_layout(data_layout);
+
+ loco::replace(precedingOp).with(tf_biasadd);
+ tf_biasadd->value(precedingOp);
+ tf_biasadd->bias(biasadd_param);
+
+ return tf_biasadd;
+}
+
+/**
+ * @note TFAdd will be fused to TFBiasAdd
+ *
+ * <Before>
+ * If precedingOp is not TFBiasAdd, then insert TFConst:1 + TFBiasAdd that
+ * TFConst:1 has zero values.
+ *
+ * addparam --\
+ * \
+ * precedingOp ---------------------------- TFAdd ----- C
+ *
+ *
+ * <Intermediate>
+ * If it's TFBiasAdd and one of the input is TFConst type,
+ * then we can fuse 'addparam' to the input TFConst:2 value of TFBiasAdd, where
+ * TFConst:2 has added values from 'addparam'
+ *
+ * addparam --\
+ * TFConst:1 --------\ \
+ * precedingOp ------- TFBiasAdd ---------- TFAdd ----- C
+ *
+ *
+ * <After>
+ * addparam --\
+ * TFConst:2 --------\ \
+ * precedingOp ------- TFBiasAdd ---------- TFAdd -----
+ * \--------------------- C
+ *
+ *
+ * [Where]
+ * - precedingOp can be TFConv2D, TFDepthwiseConv2dNative, FullyConnected,
+ * TFBiasAdd.
+ * - Intermediate is to insert TFBiasAdd + TFConst:1
+ * - After is to fuse 'addparam' of TFAdd into TFConst:1 + TFBiasAdd
+ * that becomes TFConst:2 + TFBiasAdd
+ */
+bool fuse_to_preceding(loco::Graph *graph, moco::TFAdd *node)
+{
+ // LOGGER(l);
+
+ auto xc = dynamic_cast<moco::TFConst *>(node->x());
+ auto yc = dynamic_cast<moco::TFConst *>(node->y());
+
+ // Note: if both are constants, it should be done by constant-folding
+ if (!(is_only_one_valid(xc, yc)))
+ return false;
+
+ moco::TFConst *addparam = nullptr;
+ moco::TFNode *precedingOp = nullptr;
+
+ if (xc != nullptr)
+ {
+ addparam = xc;
+ precedingOp = dynamic_cast<moco::TFNode *>(node->y());
+ }
+ else // yc != nullptr
+ {
+ addparam = yc;
+ precedingOp = dynamic_cast<moco::TFNode *>(node->x());
+ }
+
+ auto addparam_shape_inf = moco::node_shape(addparam);
+ if (addparam_shape_inf.domain() == loco::Domain::Unknown)
+ {
+ // INFO(l) << "Add fuse_to_preceding: addparam has no shape";
+ return false;
+ }
+ // if AddParam rank is not 0 or 1 we cannot fuse, just skip
+ auto addparam_shape = addparam_shape_inf.as<loco::TensorShape>();
+ if (addparam_shape.rank() > 1)
+ {
+ // INFO(l) << "Add fuse_to_preceding: Add rank is not 0 or 1";
+ return false;
+ }
+
+ // TODO do something when rank() is 0
+ if (addparam_shape.rank() == 0)
+ {
+ // Not supported yet
+ return false;
+ }
+ assert(addparam_shape.rank() != 0);
+
+ // TODO support FullyConnected
+ moco::TFBiasAdd *biasadd = nullptr;
+ if (auto conv2d = dynamic_cast<moco::TFConv2D *>(precedingOp))
+ biasadd = create_biasadd_node<moco::TFConv2D>(graph, addparam, conv2d);
+ else if (auto dw_conv2d = dynamic_cast<moco::TFDepthwiseConv2dNative *>(precedingOp))
+ biasadd = create_biasadd_node<moco::TFDepthwiseConv2dNative>(graph, addparam, dw_conv2d);
+ else if (auto old_bias_add = dynamic_cast<moco::TFBiasAdd *>(precedingOp))
+ biasadd = old_bias_add;
+
+ if (biasadd == nullptr)
+ {
+ // try next turn
+ return false;
+ }
+
+ // Let's fuse addparam into biasadd bias
+ auto biasadd_bias = loco::must_cast<moco::TFConst *>(biasadd->bias());
+ if (!shape_match(biasadd_bias, addparam))
+ {
+ // INFO(l) << "TFBiasAdd bias and TFAdd input shape mismatch";
+ return false;
+ }
+ auto add_num_elements = addparam->size<loco::DataType::FLOAT32>();
+ assert(add_num_elements == biasadd_bias->size<loco::DataType::FLOAT32>());
+ for (int32_t i = 0; i < add_num_elements; i++)
+ {
+ biasadd_bias->at<loco::DataType::FLOAT32>(i) += addparam->at<loco::DataType::FLOAT32>(i);
+ }
+
+ replace(node).with(biasadd);
+ // TODO check if need to disconnect
+ // node->x(nullptr);
+ // node->y(nullptr);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool FuseBinaryIntoPreceding::run(loco::Graph *graph)
+{
+ bool changed = false;
+ auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
+
+ for (auto node : active_nodes)
+ {
+ if (node->dialect() == moco::TFDialect::get())
+ {
+ {
+ auto tf_node = dynamic_cast<moco::TFMul *>(node);
+ if (tf_node != nullptr)
+ {
+ if (fuse_to_preceding(graph, tf_node))
+ changed = true;
+ }
+ }
+ {
+ // TODO support Div
+ }
+
+ {
+ auto tf_node = dynamic_cast<moco::TFAdd *>(node);
+ if (tf_node != nullptr)
+ {
+ if (fuse_to_preceding(graph, tf_node))
+ changed = true;
+ }
+ }
+ {
+ // TODO support Sub
+ }
+ }
+ }
+
+ return changed;
+}
+
+} // namespace moco
diff --git a/compiler/moco/pass/src/Passes/RemoveTFIdentityNode.cpp b/compiler/moco/pass/src/Passes/RemoveTFIdentityNode.cpp
new file mode 100644
index 000000000..d3d22c90e
--- /dev/null
+++ b/compiler/moco/pass/src/Passes/RemoveTFIdentityNode.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Pass/Passes/RemoveTFIdentityNode.h"
+
+#include <moco/IR/TFDialect.h>
+#include <moco/IR/TFNode.h>
+
+#include <set>
+
+namespace moco
+{
+
+bool RemoveTFIdentityNode::run(loco::Graph *g)
+{
+ struct Collector final : public moco::TFNodeMutableVisitor<void>
+ {
+ void visit(moco::TFIdentity *node) final
+ {
+ if (node->input() != nullptr)
+ {
+ candidates.insert(node);
+ }
+ }
+
+ void visit(moco::TFNode *) final { return; }
+
+ std::set<moco::TFIdentity *> candidates;
+ };
+
+ Collector collector;
+
+ for (auto node : loco::all_nodes(g))
+ {
+ if (node->dialect() == moco::TFDialect::get())
+ {
+ auto tf_node = dynamic_cast<moco::TFNode *>(node);
+ // NOTE our analysis tool reports an error for tf_node may be nullptr
+ if (tf_node != nullptr)
+ tf_node->accept(&collector);
+ }
+ }
+
+ for (auto node : collector.candidates)
+ {
+ replace(node).with(node->input());
+ node->input(nullptr);
+ }
+
+ return collector.candidates.size() > 0;
+}
+
+} // namespace moco
diff --git a/compiler/moco/pass/src/Passes/ResolveConstantShape.cpp b/compiler/moco/pass/src/Passes/ResolveConstantShape.cpp
new file mode 100644
index 000000000..2a1323fbc
--- /dev/null
+++ b/compiler/moco/pass/src/Passes/ResolveConstantShape.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Pass/Passes/ResolveConstantShape.h"
+
+#include <moco/Support/TFShapeInferenceHelper.h>
+#include <moco/Support/NodeAs.h>
+
+#include <moco/IR/Nodes/TFShape.h>
+#include <moco/IR/Nodes/TFConst.h>
+
+#include <loco.h>
+
+#include <oops/UserExn.h>
+
+#include <cassert>
+
+namespace
+{
+
+/**
+ * WHEN:
+ * - TFShape's input shape is determined
+ * DO:
+ * - Replace TFShape into TFConst
+ *
+ *
+ * <Before>
+ * in ---- TFShape ---- out(s)
+ *
+ * <After>
+ * in ---- TFShape
+ *
+ * TFConst ---- out(s)
+ */
+bool resolve_constant_shape(loco::Graph *graph, moco::TFShape *shape_node)
+{
+ auto input_shape = moco::node_shape(shape_node->input());
+
+ // Check condition
+ if (input_shape.domain() == loco::Domain::Unknown)
+ {
+ // Cannot resolve without known input_shape
+ return false;
+ }
+
+ auto input_tensor_shape = input_shape.as<loco::TensorShape>();
+
+ auto shape_rank = input_tensor_shape.rank();
+ for (uint32_t axis = 0; axis < shape_rank; ++axis)
+ {
+ if (!input_tensor_shape.dim(axis).known())
+ {
+ // Cannot resolve with unknown dimension
+ return false;
+ }
+ }
+
+ // Make TFConst to replace TFShape
+ auto const_node = graph->nodes()->create<moco::TFConst>();
+
+ // set dtype
+ auto dtype = shape_node->dtype();
+ const_node->dtype(dtype);
+
+ // set shape
+ const_node->rank(1);
+ const_node->dim(0) = shape_rank;
+
+ // set data
+ if (dtype == loco::DataType::S32)
+ {
+ // TODO Better to make template for this when support new dtype
+ const_node->size<loco::DataType::S32>(shape_rank);
+ for (uint32_t axis = 0; axis < shape_rank; ++axis)
+ {
+ int32_t dim = (int32_t)input_tensor_shape.dim(axis).value();
+ if (!(dim > 0))
+ {
+ throw oops::UserExn("Invalid input shape", shape_node->name());
+ }
+ const_node->at<loco::DataType::S32>(axis) = dim;
+ }
+ }
+ else
+ {
+ throw oops::UserExn("Unsupported data type", shape_node->name());
+ }
+
+ // replace
+ loco::replace(shape_node).with(const_node);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool ResolveConstantShape::run(loco::Graph *graph)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(graph)))
+ {
+ if (auto shape_node = as<moco::TFShape>(node))
+ {
+ if (resolve_constant_shape(graph, shape_node))
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+} // namespace moco
diff --git a/compiler/moco/pass/src/Passes/ResolveFusedBatchNorm.cpp b/compiler/moco/pass/src/Passes/ResolveFusedBatchNorm.cpp
new file mode 100644
index 000000000..6fd1474af
--- /dev/null
+++ b/compiler/moco/pass/src/Passes/ResolveFusedBatchNorm.cpp
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Pass/Passes/ResolveFusedBatchNorm.h"
+
+#include <moco/Support/NodeAs.h>
+
+#include <moco/IR/Nodes/TFAdd.h>
+#include <moco/IR/Nodes/TFConst.h>
+#include <moco/IR/Nodes/TFMul.h>
+#include <moco/IR/Nodes/TFFusedBatchNorm.h>
+
+#include <cassert>
+#include <cmath>
+#include <memory>
+
+namespace
+{
+
+bool is_same_shape(moco::TFConst *lc, moco::TFConst *rc)
+{
+ if (lc->rank() != rc->rank())
+ return false;
+
+ for (auto r = 0; r < lc->rank(); ++r)
+ {
+ if (lc->dim(r).value() != rc->dim(r).value())
+ return false;
+ }
+ return true;
+}
+
+void copy_shape(const moco::TFConst *src, moco::TFConst *dst)
+{
+ assert(src != nullptr);
+ assert(dst != nullptr);
+
+ uint32_t rank = src->rank();
+ dst->rank(rank);
+ for (uint32_t index = 0; index < rank; ++index)
+ {
+ if (src->dim(index).known())
+ dst->dim(index) = src->dim(index).value();
+ else
+ dst->dim(index).unset();
+ }
+}
+
+/**
+ * @note resolve_to_muladd() will transform TFFusedBatchNorm to TFMul, TFAdd and two ConstGen
+ *
+ * <arguments>
+ * %0:input
+ * %1:gamma : const
+ * %2:beta : const
+ * %3:mean : const
+ * %4:variance : const
+ * %5:epsilon : const
+ *
+ * <constant operations>
+ * fbn_epsilon_array = make_array(%5:epsilon)
+ * fbn_epsilon = %4:variance + fbn_epsilon_array
+ * fbn_rsqrt = 1.0 / math::sqrt(fbn_epsilon)
+ *
+ * fbn_mean = %3:mean
+ * fbn_mul = fbn_rsqrt * %1:gamma
+ * fbn_offset = %2:beta
+ *
+ * fbn_mul_0_param = fbn_mul
+ * fbn_add_param = fbn_offset - fbn_mean * fbn_mul
+ *
+ * <new replace nodes>
+ * %11:fbn_mul_0_param = ConstGen(fbn_mul_0_param)
+ * %12:fbn_mul_0 = TFMul(%0:input, %11:fbn_mul_0_param)
+ * %21:fbn_add_param = ConstGen(fbn_add_param)
+ * %22:fbn = TFAdd(%12:fbn_mul_0,%21:fbn_add_param)
+ */
+bool resolve_to_muladd(loco::Graph *graph, moco::TFFusedBatchNorm *node)
+{
+ // LOGGER(lfbn);
+
+ auto tffbn_x = node->x();
+ if (tffbn_x == nullptr)
+ {
+ // This node is already converted
+ return false;
+ }
+
+ auto tffbn_scale = dynamic_cast<moco::TFConst *>(node->scale());
+ auto tffbn_offset = dynamic_cast<moco::TFConst *>(node->offset());
+ auto tffbn_mean = dynamic_cast<moco::TFConst *>(node->mean());
+ auto tffbn_variance = dynamic_cast<moco::TFConst *>(node->variance());
+
+ // all should be const
+ if (tffbn_scale == nullptr || tffbn_offset == nullptr || tffbn_mean == nullptr ||
+ tffbn_variance == nullptr)
+ {
+ // INFO(lfbn) << "TFFBN resolve_to_muladd: One of constant input node is not a constant"
+ // << std::endl;
+ return false;
+ }
+ assert(tffbn_scale->dtype() == loco::DataType::FLOAT32);
+ assert(tffbn_offset->dtype() == loco::DataType::FLOAT32);
+ assert(tffbn_mean->dtype() == loco::DataType::FLOAT32);
+ assert(tffbn_variance->dtype() == loco::DataType::FLOAT32);
+
+ // check all const shape are the same
+ if (!is_same_shape(tffbn_scale, tffbn_offset) || !is_same_shape(tffbn_scale, tffbn_mean) ||
+ !is_same_shape(tffbn_scale, tffbn_variance))
+ {
+ // INFO(lfbn) << "TFFBN resolve_to_muladd: Shape of constant are not same" << std::endl;
+ return false;
+ }
+
+ auto tffbn_epsilon = node->epsilon();
+ // INFO(lfbn) << "TFFBN tffbn_epsilon = " << tffbn_epsilon << std::endl;
+ auto const_num_elements = tffbn_scale->size<loco::DataType::FLOAT32>();
+ // INFO(lfbn) << "TFFBN const_num_elements = " << const_num_elements << std::endl;
+
+ // fbn_epsilon = %4:variance + fbn_epsilon_array
+ std::unique_ptr<float[]> fbn_epsilon{new float[const_num_elements]};
+ for (int32_t i = 0; i < const_num_elements; i++)
+ {
+ auto variance = tffbn_variance->at<loco::DataType::FLOAT32>(i);
+ fbn_epsilon.get()[i] = variance + tffbn_epsilon;
+ }
+
+ // fbn_rsqrt = 1.0 / math::sqrt(fbn_epsilon)
+ std::unique_ptr<float[]> fbn_rsqrt{new float[const_num_elements]};
+ for (int32_t i = 0; i < const_num_elements; i++)
+ {
+ fbn_rsqrt.get()[i] = 1.0 / sqrt(fbn_epsilon.get()[i]);
+ }
+
+ // fbn_mean = %3:mean : TODO remove this block and use %3:mean
+ std::unique_ptr<float[]> fbn_mean{new float[const_num_elements]};
+ for (int32_t i = 0; i < const_num_elements; i++)
+ {
+ fbn_mean.get()[i] = tffbn_mean->at<loco::DataType::FLOAT32>(i);
+ }
+
+ // fbn_mul = fbn_rsqrt * %1:gamma
+ std::unique_ptr<float[]> fbn_mul{new float[const_num_elements]};
+ for (int32_t i = 0; i < const_num_elements; i++)
+ {
+ fbn_mul.get()[i] = fbn_rsqrt.get()[i] * tffbn_scale->at<loco::DataType::FLOAT32>(i);
+ }
+
+ // fbn_offset = %2:beta : TODO remove this block and use %2:beta
+ std::unique_ptr<float[]> fbn_offset{new float[const_num_elements]};
+ for (int32_t i = 0; i < const_num_elements; i++)
+ {
+ fbn_offset.get()[i] = tffbn_offset->at<loco::DataType::FLOAT32>(i);
+ }
+
+ // fbn_mul_0_param = fbn_mul : remove this and use fbn_mul
+ std::unique_ptr<float[]> fbn_mul_0_param{new float[const_num_elements]};
+ for (int32_t i = 0; i < const_num_elements; i++)
+ {
+ fbn_mul_0_param.get()[i] = fbn_mul.get()[i];
+ }
+
+ // fbn_add_param = fbn_offset - fbn_mean * fbn_mul
+ std::unique_ptr<float[]> fbn_add_param{new float[const_num_elements]};
+ for (int32_t i = 0; i < const_num_elements; i++)
+ {
+ fbn_add_param.get()[i] = fbn_offset.get()[i] - fbn_mean.get()[i] * fbn_mul.get()[i];
+ }
+
+ // INFO(lfbn) << "TFFBN create ConstGen" << std::endl;
+
+ /*
+ * %11:fbn_mul_0_param = ConstGen(fbn_mul_0_param)
+ * %21:fbn_add_param = ConstGen(fbn_add_param)
+ */
+ auto const_fbn_mul_0_param = graph->nodes()->create<moco::TFConst>();
+ const_fbn_mul_0_param->dtype(loco::DataType::FLOAT32);
+ copy_shape(tffbn_scale, const_fbn_mul_0_param);
+ const_fbn_mul_0_param->size<loco::DataType::FLOAT32>(const_num_elements);
+ for (int32_t i = 0; i < const_num_elements; i++)
+ {
+ const_fbn_mul_0_param->at<loco::DataType::FLOAT32>(i) = fbn_mul_0_param.get()[i];
+ }
+ auto const_fbn_add_param = graph->nodes()->create<moco::TFConst>();
+ const_fbn_add_param->dtype(loco::DataType::FLOAT32);
+ copy_shape(tffbn_scale, const_fbn_add_param);
+ const_fbn_add_param->size<loco::DataType::FLOAT32>(const_num_elements);
+ for (int32_t i = 0; i < const_num_elements; i++)
+ {
+ const_fbn_add_param->at<loco::DataType::FLOAT32>(i) = fbn_add_param.get()[i];
+ }
+
+ // INFO(lfbn) << "TFFBN create TFMul, TFAdd" << std::endl;
+ /*
+ * %12:fbn_mul_0 = TFMul(%0:input, %11:fbn_mul_0_param)
+ * %22:fbn = TFAdd(%12:fbn_mul_0,%21:fbn_add_param)
+ */
+ auto fbn_mul_0 = graph->nodes()->create<moco::TFMul>();
+ fbn_mul_0->x(tffbn_x);
+ fbn_mul_0->y(const_fbn_mul_0_param);
+
+ auto fbn = graph->nodes()->create<moco::TFAdd>();
+ fbn->x(fbn_mul_0);
+ fbn->y(const_fbn_add_param);
+
+ // replace old node with new fbn
+ replace(node).with(fbn);
+ // unlink from graph
+ node->x(nullptr);
+ node->scale(nullptr);
+ node->offset(nullptr);
+ node->mean(nullptr);
+ node->variance(nullptr);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool ResolveFusedBatchNorm::run(loco::Graph *graph)
+{
+ for (auto node : loco::active_nodes(loco::output_nodes(graph)))
+ {
+ if (as<moco::TFFusedBatchNorm>(node))
+ {
+ if (resolve_to_muladd(graph, as<moco::TFFusedBatchNorm>(node)))
+ {
+ // tree has been changed. let's return so that we don't need to
+ // considier about following node is correct or not.
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+} // namespace moco
diff --git a/compiler/moco/pass/src/Passes/ResolveReshapeWildcardDim.cpp b/compiler/moco/pass/src/Passes/ResolveReshapeWildcardDim.cpp
new file mode 100644
index 000000000..3446716cb
--- /dev/null
+++ b/compiler/moco/pass/src/Passes/ResolveReshapeWildcardDim.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Pass/Passes/ResolveReshapeWildcardDim.h"
+
+#include <moco/Support/TFShapeInferenceHelper.h>
+#include <moco/Support/NodeAs.h>
+
+#include <moco/IR/Nodes/TFReshape.h>
+#include <moco/IR/Nodes/TFConst.h>
+
+#include <cassert>
+#include <limits>
+
+namespace
+{
+
+/**
+ * @return true when 'node' has one and only one wildcard dimension
+ * @return false when 'node' has no wildcard dimension, i.e. fixed reshape case
+ *
+ * @note Assertions in this function are sanity check for 'node', Reshape's
+ * Const shape input
+ */
+bool has_one_wildcard_dim(const moco::TFConst *node)
+{
+ assert(node->dtype() == loco::DataType::S32);
+ assert(node->rank() == 1);
+
+ auto len = node->dim(0).value();
+ assert(len > 0);
+
+ // Must have one and only wildcard dimension(-1)
+ uint32_t count_wildcard_dim = 0;
+ for (uint32_t i = 0; i < len; ++i)
+ {
+ auto dim = node->at<loco::DataType::S32>(i);
+ if (dim == -1)
+ count_wildcard_dim++;
+ else
+ assert(dim >= 1);
+ }
+
+ assert(count_wildcard_dim <= 1 &&
+ "Invalid Reshape: there should be none or only one wildcard dimension");
+ return count_wildcard_dim;
+}
+
+uint32_t volume(const loco::TensorShape &shape)
+{
+ uint32_t ret = 1;
+ auto rank = shape.rank();
+ for (uint32_t axis = 0; axis < rank; ++axis)
+ {
+ ret *= shape.dim(axis).value();
+ }
+ return ret;
+}
+
+void deduce_and_fix_wildcard_dim(moco::TFConst *node, const loco::NodeShape &tensor_input_shape)
+{
+ assert(has_one_wildcard_dim(node));
+
+ assert(tensor_input_shape.domain() == loco::Domain::Tensor);
+ auto shape = tensor_input_shape.as<loco::TensorShape>();
+
+ auto len = node->dim(0).value();
+ uint32_t wildcard_index = std::numeric_limits<uint32_t>::max();
+ uint32_t product_of_non_wildcard_dims = 1;
+
+ // Deduce
+ for (uint32_t i = 0; i < len; ++i)
+ {
+ auto dim = node->at<loco::DataType::S32>(i);
+ if (dim == -1)
+ {
+ wildcard_index = i;
+ }
+ else
+ {
+ product_of_non_wildcard_dims *= dim;
+ }
+ }
+ assert(wildcard_index != std::numeric_limits<uint32_t>::max());
+
+ // Fix
+ assert(volume(shape) % product_of_non_wildcard_dims == 0);
+ node->at<loco::DataType::S32>(wildcard_index) = volume(shape) / product_of_non_wildcard_dims;
+}
+
+/**
+ * WHEN:
+ * - TFReshape's shape input is TFConst
+ * - The TFConst is valid shape input for dynamic reshape, i.e. it has one and
+ * only wildcard dimension(-1)
+ * - TFReshape's tensor input has complete shape inference data
+ * DO:
+ * - Deduce what the wildcard dimension is and fix it
+ */
+bool resolve_wildcard_dim(moco::TFReshape *reshape)
+{
+ // Check conditions (WHEN)
+ auto const_shape_input = dynamic_cast<moco::TFConst *>(reshape->shape());
+ if (!const_shape_input)
+ return false;
+
+ if (!has_one_wildcard_dim(const_shape_input))
+ return false;
+
+ auto tensor_input_shape = moco::node_shape(reshape->tensor());
+ if (tensor_input_shape.domain() == loco::Domain::Unknown)
+ return false;
+
+ // Deduce (DO)
+ deduce_and_fix_wildcard_dim(const_shape_input, tensor_input_shape);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool ResolveReshapeWildcardDim::run(loco::Graph *graph)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(graph)))
+ {
+ if (auto reshape = as<moco::TFReshape>(node))
+ {
+ if (resolve_wildcard_dim(reshape))
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+} // namespace moco
diff --git a/compiler/moco/pass/src/Passes/ResolveSquaredDifference.cpp b/compiler/moco/pass/src/Passes/ResolveSquaredDifference.cpp
new file mode 100644
index 000000000..b66add1ae
--- /dev/null
+++ b/compiler/moco/pass/src/Passes/ResolveSquaredDifference.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Pass/Passes/ResolveSquaredDifference.h"
+
+#include <moco/IR/TFDialect.h>
+#include <moco/IR/TFNodes.h>
+#include <moco/IR/TFNodeVisitor.h>
+#include <moco/IR/TFNodeImpl.h>
+
+#include <loco/IR/NodeShape.h>
+#include <loco/Service/ShapeInference.h>
+
+#include <stdex/Memory.h>
+
+namespace
+{
+
+bool decompose_sqdiff(moco::TFSquaredDifference *node)
+{
+ /**
+ * @note This will decompose TFSquaredDifference node into TFSub and TFMul
+ *
+ * Before
+ * A --- TFSquaredDifference -- C
+ * B --/
+ * After
+ * A --- TFSquaredDifference --
+ * B --/
+ * A --- TFSub == TFMul -- C
+ * B --/
+ * Where
+ * A : x of TFSquaredDifference
+ * B : y of TFSquaredDifference
+ * C : a node that uses TFSquaredDifference as an input
+ * TFSquaredDifference is disconnected from C
+ * A and B are drawn multiple times to simplify the diagram
+ */
+
+ auto node_A = node->x();
+ auto node_B = node->y();
+
+ auto sub_node = node->graph()->nodes()->create<moco::TFSub>();
+ auto mul_node = node->graph()->nodes()->create<moco::TFMul>();
+
+ // update connections
+ sub_node->x(node_A);
+ sub_node->y(node_B);
+ mul_node->x(sub_node);
+ mul_node->y(sub_node);
+
+ // replace node
+ replace(node).with(mul_node);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool ResolveSquaredDifference::run(loco::Graph *graph)
+{
+ auto active_nodes = loco::active_nodes(loco::output_nodes(graph));
+ bool changed = false;
+
+ for (auto node : active_nodes)
+ {
+ if (node->dialect() == TFDialect::get())
+ {
+ auto tf_node = dynamic_cast<moco::TFSquaredDifference *>(node);
+ if (tf_node != nullptr)
+ {
+ if (decompose_sqdiff(tf_node))
+ changed = true;
+ }
+ }
+ }
+
+ return changed;
+}
+
+} // namespace moco
diff --git a/compiler/moco/pass/src/Passes/SqueezeReduceNode.cpp b/compiler/moco/pass/src/Passes/SqueezeReduceNode.cpp
new file mode 100644
index 000000000..0d9686328
--- /dev/null
+++ b/compiler/moco/pass/src/Passes/SqueezeReduceNode.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Pass/Passes/SqueezeReduceNode.h"
+
+#include <moco/Support/NodeAs.h>
+
+#include <moco/IR/Nodes/TFConst.h>
+#include <moco/IR/Nodes/TFSqueeze.h>
+#include <moco/IR/Nodes/TFMean.h>
+
+#include <cassert>
+
+namespace
+{
+
+/**
+ * WHEN:
+ * - Reduce operations do not keep dimensions
+ * DO:
+ * - Replace original ReduceTypeOp to new ReduceTypeOp, which 'keep_dims' attribute is true
+ * - Insert TFSqueeze after new ReduceTypeOp
+ *
+ *
+ * <Before>
+ * in ---- ReduceTypeOp:0 (keep_dims = false) --- out(s)
+ *
+ * <After>
+ * --- ReduceTypeOp:0 (keep_dims = false)
+ * /
+ * in ---- ReduceTypeOp:1 (keep_dims = true) ---- TFSqueeze --- out(s)
+ *
+ * <Where>
+ * - 'keep_dims' attribute of ReduceTypeOp:0 is false
+ *
+ */
+template <class TFNode> bool squeeze_reduce_node(loco::Graph *graph, TFNode *reduce_node)
+{
+ // Don't need to squeeze reduce node
+ if (reduce_node->keep_dims())
+ return false;
+
+ // Reduction indices are not yet constant
+ auto const_reduction_indices = dynamic_cast<moco::TFConst *>(reduce_node->reduction_indices());
+ if (const_reduction_indices == nullptr)
+ return false;
+
+ auto squeeze_node = graph->nodes()->create<moco::TFSqueeze>();
+ auto new_reduce_node = graph->nodes()->create<TFNode>();
+
+ new_reduce_node->input(reduce_node->input());
+ new_reduce_node->reduction_indices(reduce_node->reduction_indices());
+ new_reduce_node->keep_dims(true);
+
+ // Insert squeeze dims
+ // TODO Support S64 type
+ assert(const_reduction_indices->dtype() == loco::DataType::S32);
+
+ std::vector<int64_t> reduction_values;
+ for (uint32_t i = 0; i < const_reduction_indices->size<loco::DataType::S32>(); ++i)
+ reduction_values.push_back(const_reduction_indices->at<loco::DataType::S32>(i));
+ squeeze_node->squeeze_dims(reduction_values);
+
+ // replace
+ loco::replace(reduce_node).with(squeeze_node);
+ squeeze_node->input(new_reduce_node);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+
+bool SqueezeReduceNode::run(loco::Graph *graph)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(graph)))
+ {
+ if (auto shape_node = as<moco::TFMean>(node))
+ {
+ if (squeeze_reduce_node(graph, shape_node))
+ changed = true;
+ }
+ // TODO Add more reduce type operations
+ }
+
+ return changed;
+}
+
+} // namespace moco
diff --git a/compiler/moco/pass/src/TensorPackEnumerator.cpp b/compiler/moco/pass/src/TensorPackEnumerator.cpp
new file mode 100644
index 000000000..61a160cfb
--- /dev/null
+++ b/compiler/moco/pass/src/TensorPackEnumerator.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TensorPackEnumerator.h"
+
+#include <cassert>
+
+namespace moco
+{
+
+void TensorPackEnumerator::shape(const loco::TensorShape &si, const loco::TensorShape &so)
+{
+ _shape_inp = si;
+ _shape_out = so;
+
+ assert(_shape_inp.rank() + 1 == _shape_out.rank());
+
+ _rank_out = _shape_out.rank();
+}
+
+void TensorPackEnumerator::increment(uint32_t r)
+{
+ _cursor_out.at(r) = _cursor_out.at(r) + 1;
+
+ if (_cursor_out.at(r) >= _boundary_out.at(r))
+ {
+ if (r > 0)
+ {
+ _cursor_out.at(r) = 0;
+ increment(r - 1);
+ }
+ else
+ {
+ // reached to the end
+ }
+ }
+}
+
+void TensorPackEnumerator::start(void)
+{
+ uint32_t rank = _rank_out;
+
+ _cursor_out.resize(rank);
+ _boundary_out.resize(rank);
+ for (uint32_t r = 0; r < rank; ++r)
+ {
+ _cursor_out.at(r) = 0;
+ _boundary_out.at(r) = _shape_out.dim(r).value();
+ }
+
+ rank = _rank_out - 1;
+ _cursor_inp.resize(rank);
+ _boundary_inp.resize(rank);
+ for (uint32_t r = 0; r < rank; ++r)
+ {
+ _cursor_inp.at(r) = 0;
+ _boundary_inp.at(r) = _shape_inp.dim(r).value();
+ }
+ _num_inp = 0;
+}
+
+bool TensorPackEnumerator::valid(void)
+{
+ uint32_t rank = _rank_out;
+ for (uint32_t r = 0; r < rank; ++r)
+ {
+ if (_cursor_out.at(r) >= _boundary_out.at(r))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+void TensorPackEnumerator::advance(void)
+{
+ uint32_t r = _rank_out - 1;
+ increment(r);
+
+ // from _cursor_out, set _cursor_inp and _num
+ for (int32_t r = 0, s = 0; r < _rank_out; ++r)
+ {
+ if (r == _axis)
+ {
+ _num_inp = _cursor_out.at(r);
+ }
+ else
+ {
+ _cursor_inp.at(s) = _cursor_out.at(r);
+ s++;
+ }
+ }
+}
+
+uint32_t TensorPackEnumerator::inp_num(void) const { return _num_inp; }
+
+uint32_t TensorPackEnumerator::inp_element(void) const
+{
+ uint32_t rank = _rank_out - 1;
+ uint32_t element = 0;
+ for (uint32_t r = 0; r < rank; ++r)
+ {
+ uint32_t dim = _boundary_inp.at(r);
+ element = element * dim + _cursor_inp.at(r);
+ }
+ return element;
+}
+
+uint32_t TensorPackEnumerator::out_element(void) const
+{
+ uint32_t rank = _rank_out;
+ uint32_t element = 0;
+ for (uint32_t r = 0; r < rank; ++r)
+ {
+ uint32_t dim = _boundary_out.at(r);
+ element = element * dim + _cursor_out.at(r);
+ }
+ return element;
+}
+
+} // namespace moco
diff --git a/compiler/moco/pass/src/TensorPackEnumerator.h b/compiler/moco/pass/src/TensorPackEnumerator.h
new file mode 100644
index 000000000..efdec3eb6
--- /dev/null
+++ b/compiler/moco/pass/src/TensorPackEnumerator.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_TENSOR_PACK_ENUMERATOR_H__
+#define __MOCO_TENSOR_PACK_ENUMERATOR_H__
+
+#include <loco/IR/TensorShape.h>
+
+#include <vector>
+
+namespace moco
+{
+
+using u32v_t = std::vector<uint32_t>;
+
+class TensorPackEnumerator
+{
+public:
+ TensorPackEnumerator() = default;
+
+public:
+ void shape(const loco::TensorShape &si, const loco::TensorShape &so);
+ void axis(uint32_t axis) { _axis = axis; }
+
+public:
+ void start(void);
+ bool valid(void);
+ void advance(void);
+
+public:
+ uint32_t inp_num(void) const;
+ uint32_t inp_element(void) const;
+ uint32_t out_element(void) const;
+
+private:
+ void increment(uint32_t);
+
+private:
+ loco::TensorShape _shape_inp;
+ loco::TensorShape _shape_out;
+
+ uint32_t _axis = 0;
+ uint32_t _rank_out = 0;
+ uint32_t _num_inp = 0;
+ u32v_t _cursor_inp;
+ u32v_t _cursor_out;
+ u32v_t _boundary_inp;
+ u32v_t _boundary_out;
+};
+
+} // namespace moco
+
+#endif // __MOCO_TENSOR_PACK_ENUMERATOR_H__
diff --git a/compiler/moco/pass/src/TensorSliceEnumerator.cpp b/compiler/moco/pass/src/TensorSliceEnumerator.cpp
new file mode 100644
index 000000000..58bd0554c
--- /dev/null
+++ b/compiler/moco/pass/src/TensorSliceEnumerator.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TensorSliceEnumerator.h"
+
+#include <cassert>
+
+namespace moco
+{
+
+void TensorSliceEnumerator::shape(loco::TensorShape &s)
+{
+ _shape_in = s;
+ _rank_in = _shape_in.rank();
+}
+
+void TensorSliceEnumerator::increment(uint32_t r)
+{
+ if (_cursor.at(r) < _boundary.at(r))
+ _cursor.at(r) = _cursor.at(r) + 1;
+ else
+ {
+ if (r > 0)
+ {
+ _cursor.at(r) = _begin[r];
+ increment(r - 1);
+ }
+ else
+ {
+ // reached to the end
+ }
+ }
+}
+
+void TensorSliceEnumerator::start(void)
+{
+ auto rank = _rank_in;
+
+ _cursor.resize(rank);
+ _boundary.resize(rank);
+ for (uint32_t r = 0; r < rank; ++r)
+ {
+ _cursor.at(r) = _begin[r];
+ _boundary.at(r) = _end[r];
+ }
+}
+
+bool TensorSliceEnumerator::valid(void)
+{
+ auto rank = _rank_in;
+ for (uint32_t r = 0; r < rank; ++r)
+ {
+ if (_cursor.at(r) >= _boundary.at(r))
+ return false;
+ }
+ return true;
+}
+
+void TensorSliceEnumerator::advance(void)
+{
+ uint32_t r = _rank_in - 1;
+ increment(r);
+}
+
+uint32_t TensorSliceEnumerator::cursor(uint32_t rank) const
+{
+ assert(rank < _rank_in);
+ return _cursor.at(rank);
+}
+
+} // namespace moco
diff --git a/compiler/moco/pass/src/TensorSliceEnumerator.h b/compiler/moco/pass/src/TensorSliceEnumerator.h
new file mode 100644
index 000000000..c8206fe9d
--- /dev/null
+++ b/compiler/moco/pass/src/TensorSliceEnumerator.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_TENSOR_SLICE_ENUMERATOR_H__
+#define __MOCO_TENSOR_SLICE_ENUMERATOR_H__
+
+#include <loco/IR/TensorShape.h>
+
+#include <vector>
+
+namespace moco
+{
+
+using u32v_t = std::vector<uint32_t>;
+
+class TensorSliceEnumerator
+{
+public:
+ TensorSliceEnumerator() = default;
+
+public:
+ void shape(loco::TensorShape &s);
+ void begin(u32v_t &b) { _begin = b; }
+ void end(u32v_t &e) { _end = e; }
+
+public:
+ void start(void);
+ bool valid(void);
+ void advance(void);
+
+ uint32_t cursor(uint32_t rank) const;
+ const u32v_t cursor(void) const { return _cursor; }
+
+private:
+ void increment(uint32_t);
+
+private:
+ loco::TensorShape _shape_in;
+
+ uint32_t _rank_in = 0;
+ u32v_t _cursor;
+ u32v_t _boundary;
+ u32v_t _begin;
+ u32v_t _end;
+};
+
+} // namespace moco
+
+#endif // __MOCO_TENSOR_SLICE_ENUMERATOR_H__
diff --git a/compiler/moco/pass/src/TensorSliceEnumerator.test.cpp b/compiler/moco/pass/src/TensorSliceEnumerator.test.cpp
new file mode 100644
index 000000000..078fe423f
--- /dev/null
+++ b/compiler/moco/pass/src/TensorSliceEnumerator.test.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TensorSliceEnumerator.h"
+
+#include <gtest/gtest.h>
+
+TEST(TensorSliceEnumeratorTest, basic_vector)
+{
+ moco::TensorSliceEnumerator iter;
+ loco::TensorShape shape;
+ uint32_t rank = 1;
+
+ shape.rank(rank);
+ shape.dim(0) = loco::Dimension(4);
+
+ std::vector<uint32_t> begin = {1};
+ std::vector<uint32_t> end = {3};
+
+ iter.shape(shape);
+ iter.begin(begin);
+ iter.end(end);
+
+ for (iter.start(); iter.valid(); iter.advance())
+ {
+ for (uint32_t r = 0; r < rank; ++r)
+ {
+ printf("%d ", iter.cursor(r));
+ }
+ printf("\n");
+ }
+
+ GTEST_SUCCEED();
+}
+
+TEST(TensorSliceEnumeratorTest, basic_matrix)
+{
+ moco::TensorSliceEnumerator etor;
+ loco::TensorShape shape;
+ uint32_t rank = 2;
+
+ shape.rank(rank);
+ shape.dim(0) = loco::Dimension(5);
+ shape.dim(1) = loco::Dimension(5);
+
+ std::vector<uint32_t> begin = {1, 1};
+ std::vector<uint32_t> end = {2, 4};
+ std::vector<uint32_t> offset;
+ std::vector<uint32_t> cursor;
+
+ etor.shape(shape);
+ etor.begin(begin);
+ etor.end(end);
+
+ for (etor.start(); etor.valid(); etor.advance())
+ {
+ cursor = etor.cursor();
+ assert(cursor.size() == begin.size());
+
+ offset.resize(cursor.size());
+ for (uint32_t r = 0; r < cursor.size(); r++)
+ {
+ offset.at(r) = cursor.at(r) - begin.at(r);
+ std::cout << offset.at(r) << " ";
+ }
+ std::cout << std::endl;
+ }
+
+ GTEST_SUCCEED();
+}
diff --git a/compiler/moco/pass/src/TestHelper.h b/compiler/moco/pass/src/TestHelper.h
new file mode 100644
index 000000000..b97491dba
--- /dev/null
+++ b/compiler/moco/pass/src/TestHelper.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TEST_HELPER_H__
+#define __TEST_HELPER_H__
+
+#include <loco.h>
+
+#include <moco/Support/NodeAs.h>
+
+namespace moco
+{
+namespace test
+{
+
+template <typename T> T *find_first_node_bytype(loco::Graph *g)
+{
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ if (auto T_node = as<T>(node))
+ {
+ return T_node;
+ }
+ }
+
+ return nullptr;
+}
+
+template <typename T> std::vector<T *> find_nodes_bytype(loco::Graph *g)
+{
+ std::vector<T *> find_nodes;
+
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ if (auto T_node = as<T>(node))
+ {
+ find_nodes.push_back(T_node);
+ }
+ }
+
+ return find_nodes;
+}
+
+/**
+ * @brief Append setup output of graph by adding loco::Push node
+ *
+ * @note This is subject to change when loco changes I/O treatment
+ */
+void setup_output_node(loco::Graph *graph, loco::Node *last_node);
+
+} // namespace test
+} // namespace moco
+
+#endif // __TEST_HELPER_H__
diff --git a/compiler/moco/pass/src/TestHelper.test.cpp b/compiler/moco/pass/src/TestHelper.test.cpp
new file mode 100644
index 000000000..59915d60f
--- /dev/null
+++ b/compiler/moco/pass/src/TestHelper.test.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestHelper.h"
+
+namespace moco
+{
+namespace test
+{
+
+void setup_output_node(loco::Graph *graph, loco::Node *last_node)
+{
+ // add push as output
+ auto push_node = graph->nodes()->create<loco::Push>();
+ push_node->from(last_node);
+
+ // set the graph output name and node object
+ auto graph_output = graph->outputs()->create();
+ graph_output->name("output");
+ graph_output->dtype(loco::DataType::FLOAT32);
+ loco::link(graph_output, push_node);
+}
+
+} // namespace test
+} // namespace moco
diff --git a/compiler/moco/requires.cmake b/compiler/moco/requires.cmake
new file mode 100644
index 000000000..1a7d36454
--- /dev/null
+++ b/compiler/moco/requires.cmake
@@ -0,0 +1,8 @@
+require("loco")
+require("locop")
+require("stdex")
+require("moco-log")
+require("plier-tf")
+require("mio-tf")
+require("logo")
+require("oops")
diff --git a/compiler/moco/service/CMakeLists.txt b/compiler/moco/service/CMakeLists.txt
new file mode 100644
index 000000000..dff0233b1
--- /dev/null
+++ b/compiler/moco/service/CMakeLists.txt
@@ -0,0 +1,24 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(moco_service SHARED ${SOURCES})
+target_include_directories(moco_service PRIVATE src)
+target_include_directories(moco_service PUBLIC include)
+target_link_libraries(moco_service PUBLIC loco)
+target_link_libraries(moco_service PUBLIC moco_lang)
+target_link_libraries(moco_service PRIVATE moco_support)
+target_link_libraries(moco_service PRIVATE nncc_common)
+target_link_libraries(moco_service PRIVATE stdex)
+target_link_libraries(moco_service PRIVATE oops)
+install(TARGETS moco_service DESTINATION lib)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(moco_service_test ${TESTS})
+target_include_directories(moco_service_test PRIVATE src)
+target_link_libraries(moco_service_test moco_service)
diff --git a/compiler/moco/service/README.md b/compiler/moco/service/README.md
new file mode 100644
index 000000000..78906dbfe
--- /dev/null
+++ b/compiler/moco/service/README.md
@@ -0,0 +1,3 @@
+# service
+
+`service` provides TensorFlow Dialect Services
diff --git a/compiler/moco/service/include/moco/Service/TFShapeInferenceRule.h b/compiler/moco/service/include/moco/Service/TFShapeInferenceRule.h
new file mode 100644
index 000000000..98d716c2a
--- /dev/null
+++ b/compiler/moco/service/include/moco/Service/TFShapeInferenceRule.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_SERVICE_SHAPE_INFERENCE_RULE_H__
+#define __MOCO_SERVICE_SHAPE_INFERENCE_RULE_H__
+
+#include <loco/Service/ShapeInferenceRule.h>
+
+namespace moco
+{
+
+/**
+ * @brief Shape inference rule for TensorFlow dialect
+ */
+struct TFShapeInferenceRule final : public loco::ShapeInferenceRule
+{
+ bool support(const API &ver) const final;
+ bool recognize(const loco::Dialect *) const final;
+ bool infer(const loco::Node *, loco::NodeShape &) const final;
+ void infer(const Context *, const loco::Node *, Sink *) const final;
+};
+
+} // namespace moco
+
+#endif // __MOCO_SERVICE_SHAPE_INFERENCE_RULE_H__
diff --git a/compiler/moco/service/include/moco/Service/TFTypeInferenceRule.h b/compiler/moco/service/include/moco/Service/TFTypeInferenceRule.h
new file mode 100644
index 000000000..f712fdb01
--- /dev/null
+++ b/compiler/moco/service/include/moco/Service/TFTypeInferenceRule.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_SERVICE_TYPE_INFERENCE_RULE_H__
+#define __MOCO_SERVICE_TYPE_INFERENCE_RULE_H__
+
+#include <loco/Service/TypeInference.h>
+
+namespace moco
+{
+
+/**
+ * @brief Type Inference Rule for TFDialect
+ */
+struct TFTypeInferenceRule final : public loco::TypeInferenceRule
+{
+ bool recognize(const loco::Dialect *) const final;
+ bool infer(const loco::Node *, loco::DataType &) const final;
+};
+
+} // namespace moco
+
+#endif // __MOCO_SERVICE_TYPE_INFERENCE_RULE_H__
diff --git a/compiler/moco/service/src/Service/TFShapeInferenceRule.cpp b/compiler/moco/service/src/Service/TFShapeInferenceRule.cpp
new file mode 100644
index 000000000..98434155e
--- /dev/null
+++ b/compiler/moco/service/src/Service/TFShapeInferenceRule.cpp
@@ -0,0 +1,890 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Service/TFShapeInferenceRule.h"
+
+#include <moco/Support/TFShapeInferenceHelper.h>
+
+#include "moco/IR/TFDialect.h"
+#include "moco/IR/TFNode.h"
+
+#include <loco/IR/NodeShape.h>
+#include <loco/Service/ShapeInference.h>
+
+#include <oops/UserExn.h>
+
+#include <cassert>
+#include <cmath>
+
+namespace
+{
+
+class ShapeInferenceAlgorithm final : public moco::TFNodeVisitor<loco::NodeShape>
+{
+public:
+ ShapeInferenceAlgorithm(const loco::ShapeInferenceRule::Context *ctx) : _ctx{ctx}
+ {
+ // DO NOTHING
+ }
+
+private:
+ const loco::ShapeInferenceRule::Context *_ctx;
+
+private:
+ bool shape_known(const loco::Node *node) const { return _ctx->known(node); }
+ loco::NodeShape node_shape(const loco::Node *node) const { return _ctx->get(node); }
+
+private:
+ loco::NodeShape binary_node_shape(const moco::TFNode::Node *node)
+ {
+ // This helper works only for binary node.
+ assert(node->arity() == 2);
+
+ auto lhs_shape = node_shape(node->arg(0));
+ auto rhs_shape = node_shape(node->arg(1));
+
+ loco::TensorShape lhs_tensorshape = lhs_shape.as<loco::TensorShape>();
+ loco::TensorShape rhs_tensorshape = rhs_shape.as<loco::TensorShape>();
+ loco::TensorShape sum_tensorshape = moco::broadcast_shape(lhs_tensorshape, rhs_tensorshape);
+
+ loco::NodeShape sum_shape({sum_tensorshape});
+
+ return sum_shape;
+ }
+
+ loco::NodeShape node_shape_with_check(const moco::TFNode::Node *node)
+ {
+ auto nodeshape = node_shape(node);
+ assert(nodeshape.domain() == loco::Domain::Tensor);
+
+ return nodeshape;
+ }
+
+ bool valid_scalar_value(moco::TFConst *node)
+ {
+ auto nodeshape = node_shape(node);
+ if (nodeshape.domain() != loco::Domain::Tensor)
+ {
+ return false;
+ }
+ if (node->dtype() != loco::DataType::S32)
+ {
+ return false;
+ }
+
+ auto tensor_shape = nodeshape.as<loco::TensorShape>();
+ if (!(tensor_shape.rank() == 0 || tensor_shape.rank() == 1))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ int32_t scalar_value(moco::TFConst *node)
+ {
+ auto nodeshape = node_shape(node);
+ assert(node->dtype() == loco::DataType::S32);
+
+ auto tensor_shape = nodeshape.as<loco::TensorShape>();
+ assert(tensor_shape.rank() == 0 || tensor_shape.rank() == 1);
+
+ return node->at<loco::DataType::S32>(0);
+ }
+
+public:
+ loco::NodeShape visit(const moco::TFAdd *node) final { return binary_node_shape(node); }
+
+ loco::NodeShape visit(const moco::TFAvgPool *node) final
+ {
+ auto value_shape = node_shape(node->value());
+ assert(value_shape.domain() != loco::Domain::Unknown);
+
+ moco::PlaneInference infer_plane_shape;
+
+ infer_plane_shape.padding(node->padding());
+ infer_plane_shape.stride(moco::stride_of(node->strides(), node->data_layout()));
+ infer_plane_shape.window(moco::window_of(node->ksize(), node->data_layout()));
+
+ auto input_feature_shape = moco::as_feature_shape(value_shape, node->data_layout());
+ auto input_plane_shape = moco::make_plane_shape(input_feature_shape);
+ auto output_feature_shape = input_feature_shape;
+ auto output_plane_shape = infer_plane_shape(input_plane_shape);
+
+ moco::update(output_feature_shape).with(output_plane_shape);
+
+ return moco::as_tensor_shape(output_feature_shape, node->data_layout());
+ }
+
+ loco::NodeShape visit(const moco::TFBiasAdd *node) final
+ {
+ return node_shape_with_check(node->value());
+ }
+
+ loco::NodeShape visit(const moco::TFConcatV2 *node) final
+ {
+ // axis shape should be available
+ auto axis_node = node->axis();
+ auto axis_shape = node_shape(axis_node);
+ assert(axis_shape.domain() != loco::Domain::Unknown);
+
+ // check all input shapes and all ranks should be same
+ auto value_a = node->values(0);
+ auto value_a_shape = node_shape(value_a);
+ assert(value_a_shape.domain() == loco::Domain::Tensor);
+ auto value_a_tensor_shape = value_a_shape.as<loco::TensorShape>();
+ uint32_t a_rank = value_a_tensor_shape.rank();
+
+ uint32_t num_values = node->num_values();
+ for (uint32_t ni = 1; ni < num_values; ++ni)
+ {
+ auto value_b = node->values(ni);
+ auto value_b_shape = node_shape(value_b);
+ assert(value_b_shape.domain() == loco::Domain::Tensor);
+ auto value_b_tensor_shape = value_b_shape.as<loco::TensorShape>();
+ assert(a_rank == value_b_tensor_shape.rank());
+ }
+
+ int32_t axis_value = 0;
+ bool axis_available = false;
+ {
+ // check for axis is TFConst
+ auto tfconst = dynamic_cast<moco::TFConst *>(axis_node);
+ if (tfconst != nullptr)
+ {
+ if (valid_scalar_value(tfconst))
+ {
+ axis_value = scalar_value(tfconst);
+ axis_available = true;
+ }
+ }
+ }
+ if (!axis_available)
+ {
+ // TODO may need to refine error message
+ throw oops::UserExn("ConcatV2 node does not have axis input", node->name());
+ }
+
+ uint32_t axis_absolute = (axis_value >= 0) ? axis_value : (int32_t)a_rank + axis_value;
+ loco::TensorShape output_tensor_shape = value_a_tensor_shape;
+
+ for (uint32_t index = 0; index < a_rank; ++index)
+ {
+ if (value_a_tensor_shape.dim(index).known())
+ {
+ uint32_t dim = value_a_tensor_shape.dim(index).value();
+ uint32_t dim_acc = dim;
+
+ for (uint32_t ni = 1; ni < num_values; ++ni)
+ {
+ auto value_b = node->values(ni);
+ auto value_b_shape = node_shape(value_b);
+ assert(value_b_shape.domain() == loco::Domain::Tensor);
+ auto value_b_tensor_shape = value_b_shape.as<loco::TensorShape>();
+ assert(value_b_tensor_shape.dim(index).known());
+ if (index == axis_absolute)
+ dim_acc += value_b_tensor_shape.dim(index).value();
+ else
+ assert(dim == value_b_tensor_shape.dim(index).value());
+ }
+ output_tensor_shape.dim(index) = dim_acc;
+ }
+ else
+ output_tensor_shape.dim(index).unset();
+ }
+ return loco::NodeShape(output_tensor_shape);
+ }
+
+ loco::NodeShape visit(const moco::TFConst *node) final
+ {
+ loco::TensorShape output_tensor_shape;
+
+ uint32_t rank = node->rank();
+ output_tensor_shape.rank(rank);
+ for (uint32_t index = 0; index < rank; ++index)
+ {
+ if (node->dim(index).known())
+ output_tensor_shape.dim(index) = node->dim(index).value();
+ else
+ output_tensor_shape.dim(index).unset();
+ }
+
+ return loco::NodeShape(output_tensor_shape);
+ }
+
+ loco::NodeShape visit(const moco::TFConv2D *node) final
+ {
+ auto input_shape = moco::node_shape(node->input());
+ auto ker_shape = moco::node_shape(node->filter());
+ auto ker_tensor_shape = ker_shape.as<loco::TensorShape>(); // in HWIO
+ auto node_stride = moco::stride_of(node->strides(), node->data_layout());
+ auto node_window = moco::window_of(ker_tensor_shape, "HWIO");
+
+ moco::PlaneInference infer_plane_shape;
+
+ infer_plane_shape.padding(node->padding());
+ infer_plane_shape.stride(node_stride);
+ infer_plane_shape.window(node_window);
+
+ auto input_feature_shape = moco::as_feature_shape(input_shape, node->data_layout());
+ auto input_plane_shape = moco::make_plane_shape(input_feature_shape);
+ // output count is from input count, depth is from kernel 'O' which is dim(3)
+ auto output_feature_shape = input_feature_shape;
+ output_feature_shape.depth() = ker_tensor_shape.dim(3).value();
+
+ auto output_plane_shape = infer_plane_shape(input_plane_shape);
+
+ moco::update(output_feature_shape).with(output_plane_shape);
+
+ return moco::as_tensor_shape(output_feature_shape, node->data_layout());
+ }
+
+ loco::NodeShape visit(const moco::TFConv2DBackpropInput *node) final
+ {
+ // TFConv2DBackpropInput's first input, named 'input_sizes', actually contains shape of node
+ // output's feature map. We can get shape of TFConv2DBackpropInput by just copying this.
+ // TODO Support when 'input_sizes' is not TFConst, or support constant folding
+ auto input_sizes_node = dynamic_cast<moco::TFConst *>(node->input_sizes());
+ if (input_sizes_node == nullptr)
+ {
+ // we are now supporting somekind of constant folding for this node, wait till it is finished
+ loco::NodeShape unknown;
+ return unknown;
+ }
+
+ // Let's support S32 for time being
+ // TODO Support other integer types
+ assert(input_sizes_node->dtype() == loco::DataType::S32);
+ assert(input_sizes_node->size<loco::DataType::S32>() == 4);
+
+ // copy!
+ loco::TensorShape ofm_tensor_shape;
+ ofm_tensor_shape.rank(4);
+ for (uint32_t i = 0; i < 4; ++i)
+ {
+ int32_t dim = input_sizes_node->at<loco::DataType::S32>(i);
+ assert(dim > 0);
+ ofm_tensor_shape.dim(i) = (uint32_t)dim;
+ }
+
+ return loco::NodeShape(ofm_tensor_shape);
+ }
+
+ loco::NodeShape visit(const moco::TFDepthwiseConv2dNative *node) final
+ {
+ auto input_shape = moco::node_shape(node->input()); // NHWC
+ auto ker_shape = moco::node_shape(node->filter());
+ auto ker_tensor_shape = ker_shape.as<loco::TensorShape>(); // in HWCM
+ auto node_stride = moco::stride_of(node->strides(), node->data_layout());
+ auto node_window = moco::window_of(ker_tensor_shape, "HWCM");
+
+ moco::PlaneInference infer_plane_shape;
+
+ infer_plane_shape.padding(node->padding());
+ infer_plane_shape.stride(node_stride);
+ infer_plane_shape.window(node_window);
+
+ auto input_feature_shape = moco::as_feature_shape(input_shape, node->data_layout());
+ auto input_plane_shape = moco::make_plane_shape(input_feature_shape);
+ // output count is from input count, depth is from kernel 'CM' which is dim(2) * dim(3)
+ auto output_feature_shape = input_feature_shape;
+ output_feature_shape.depth() =
+ loco::Dimension(ker_tensor_shape.dim(2).value() * ker_tensor_shape.dim(3).value());
+
+ auto output_plane_shape = infer_plane_shape(input_plane_shape);
+
+ moco::update(output_feature_shape).with(output_plane_shape);
+
+ return moco::as_tensor_shape(output_feature_shape, node->data_layout());
+ }
+
+ loco::NodeShape visit(const moco::TFFakeQuantWithMinMaxVars *node) final
+ {
+ return node_shape_with_check(node->inputs());
+ }
+
+ loco::NodeShape visit(const moco::TFFusedBatchNorm *node) final
+ {
+ return node_shape_with_check(node->x());
+ }
+
+ loco::NodeShape visit(const moco::TFIdentity *node) final
+ {
+ return node_shape_with_check(node->input());
+ }
+
+ loco::NodeShape visit(const moco::TFMaximum *node) final { return binary_node_shape(node); }
+
+ loco::NodeShape visit(const moco::TFMaxPool *node) final
+ {
+ auto input_shape = node_shape(node->input());
+ assert(input_shape.domain() != loco::Domain::Unknown);
+
+ moco::PlaneInference infer_plane_shape;
+
+ infer_plane_shape.padding(node->padding());
+ infer_plane_shape.stride(moco::stride_of(node->strides(), node->data_layout()));
+ infer_plane_shape.window(moco::window_of(node->ksize(), node->data_layout()));
+
+ auto input_feature_shape = moco::as_feature_shape(input_shape, node->data_layout());
+ auto input_plane_shape = moco::make_plane_shape(input_feature_shape);
+ auto output_feature_shape = input_feature_shape;
+ auto output_plane_shape = infer_plane_shape(input_plane_shape);
+
+ moco::update(output_feature_shape).with(output_plane_shape);
+
+ return moco::as_tensor_shape(output_feature_shape, node->data_layout());
+ }
+
+ loco::NodeShape visit(const moco::TFMean *node) final
+ {
+ auto input_shape = node_shape(node->input());
+ auto reduction_indices = node->reduction_indices();
+
+ // Get constant values if reduction_indices is const
+ std::vector<int32_t> reduction_values;
+ if (auto tfconst = dynamic_cast<moco::TFConst *>(reduction_indices))
+ {
+ assert(tfconst->dtype() == loco::DataType::S32);
+ auto const_size = tfconst->size<loco::DataType::S32>();
+ for (uint32_t i = 0; i < const_size; ++i)
+ {
+ int32_t axis = tfconst->at<loco::DataType::S32>(i);
+ if (axis < 0)
+ axis += input_shape.as<loco::TensorShape>().rank();
+ reduction_values.push_back(axis);
+ }
+ }
+ else
+ {
+ // we cannot find a valid reduction indices value
+ loco::NodeShape unknown;
+ return unknown;
+ }
+
+ loco::TensorShape output_shape;
+ auto input_tensor_shape = input_shape.as<loco::TensorShape>();
+
+ if (node->keep_dims())
+ {
+ output_shape.rank(input_tensor_shape.rank());
+ for (uint32_t i = 0; i < input_tensor_shape.rank(); ++i)
+ output_shape.dim(i) = input_tensor_shape.dim(i);
+ for (uint32_t i = 0; i < reduction_values.size(); ++i)
+ output_shape.dim(reduction_values.at(i)) = 1;
+ }
+ else
+ {
+ std::vector<bool> check_reduce(input_tensor_shape.rank(), false);
+ for (uint32_t i = 0; i < reduction_values.size(); ++i)
+ check_reduce.at(reduction_values.at(i)) = true;
+
+ uint32_t reduce_cnt = 0;
+ for (uint32_t i = 0; i < check_reduce.size(); ++i)
+ if (check_reduce.at(i))
+ ++reduce_cnt;
+
+ output_shape.rank(input_tensor_shape.rank() - reduce_cnt);
+ for (uint32_t i = 0, j = 0; i < check_reduce.size(); ++i)
+ if (check_reduce.at(i) == false)
+ output_shape.dim(j++) = i;
+ }
+
+ return loco::NodeShape(output_shape);
+ }
+
+ loco::NodeShape visit(const moco::TFMul *node) final { return binary_node_shape(node); }
+
+ loco::NodeShape visit(const moco::TFPack *node) final
+ {
+ loco::NodeShape unknown;
+
+ auto input_shape_0 = node_shape(node->values(0));
+ if (input_shape_0.domain() != loco::Domain::Tensor)
+ {
+ // TODO fix this for other cases
+ // We support only valid tensor shape for now
+ return unknown;
+ }
+ loco::TensorShape tensor_shape_0 = input_shape_0.as<loco::TensorShape>();
+
+ // all input shapes should be same
+ auto num_values = node->N();
+ for (uint32_t i = 1; i < num_values; ++i)
+ {
+ auto input_shape = node_shape(node->values(i));
+ if (input_shape.domain() != loco::Domain::Tensor)
+ {
+ // TODO ditto
+ return unknown;
+ }
+
+ loco::TensorShape tensor_shape = input_shape.as<loco::TensorShape>();
+ if (!(input_shape_0 == input_shape))
+ {
+ throw oops::UserExn("All input values shape should be same", node->name());
+ }
+ }
+
+ // output rank will be +1 of rank of the input
+ // axis should be in range of [-r, r), where r is rank of the output
+ auto axis = node->axis();
+ int32_t rank = static_cast<int32_t>(tensor_shape_0.rank());
+ assert(rank >= 0);
+ int32_t rank_output = rank + 1;
+ if (axis < -rank_output || rank_output <= axis)
+ {
+ throw oops::UserExn("axis is out of range", node->name());
+ }
+
+ auto axis_stack = (axis >= 0) ? axis : rank_output + axis;
+
+ loco::TensorShape output_tensor_shape;
+
+ output_tensor_shape.rank(rank_output);
+ for (int32_t r = 0; r < axis_stack; ++r)
+ {
+ output_tensor_shape.dim(r).set(tensor_shape_0.dim(r).value());
+ }
+ output_tensor_shape.dim(axis_stack).set(num_values);
+ for (int32_t r = axis_stack; r < rank; ++r)
+ {
+ output_tensor_shape.dim(r + 1).set(tensor_shape_0.dim(r).value());
+ }
+
+ return loco::NodeShape(output_tensor_shape);
+ }
+
+ loco::NodeShape visit(const moco::TFPad *node) final
+ {
+ auto input_shape = node_shape(node->input());
+ assert(input_shape.domain() == loco::Domain::Tensor);
+
+ auto const_paddings = loco::must_cast<moco::TFConst *>(node->paddings());
+ assert(const_paddings->dtype() == loco::DataType::S32);
+ assert(const_paddings->rank() == 2);
+
+ loco::TensorShape input_tensor_shape = input_shape.as<loco::TensorShape>();
+ loco::TensorShape output_tensor_shape;
+
+ output_tensor_shape.rank(input_tensor_shape.rank());
+ for (uint32_t axis = 0; axis < input_tensor_shape.rank(); ++axis)
+ {
+ output_tensor_shape.dim(axis) = input_tensor_shape.dim(axis).value() +
+ const_paddings->at<loco::DataType::S32>(axis * 2) +
+ const_paddings->at<loco::DataType::S32>(axis * 2 + 1);
+ }
+
+ return loco::NodeShape{output_tensor_shape};
+ }
+
+ loco::NodeShape visit(const moco::TFPlaceholder *node) final
+ {
+ loco::TensorShape output_tensor_shape;
+
+ uint32_t rank = node->rank();
+ output_tensor_shape.rank(rank);
+ for (uint32_t index = 0; index < rank; ++index)
+ {
+ if (node->dim(index).known())
+ output_tensor_shape.dim(index) = node->dim(index).value();
+ else
+ output_tensor_shape.dim(index).unset();
+ }
+
+ return loco::NodeShape(output_tensor_shape);
+ }
+
+ loco::NodeShape visit(const moco::TFRealDiv *node) final { return binary_node_shape(node); }
+
+ loco::NodeShape visit(const moco::TFRelu *node) final
+ {
+ return node_shape_with_check(node->features());
+ }
+
+ loco::NodeShape visit(const moco::TFRelu6 *node) final
+ {
+ return node_shape_with_check(node->features());
+ }
+
+ loco::NodeShape visit(const moco::TFReshape *node) final
+ {
+ loco::NodeShape unknown;
+
+ // For now, we only consider Fixed Reshape, i.e. Reshape with determined
+ // 'shape' input. So here we only support case when 'shape' input of
+ // TFReshape is TFConst. If 'shape' input is not TFConst, another
+ // transform (e.g. constant folding) should be done beforehand to make
+ // it TFConst.
+ // TODO Support dynamic Reshape
+ // Note that 'shape()' here is 'shape' input, not node's shape information
+ auto const_shape_input = dynamic_cast<moco::TFConst *>(node->shape());
+ if (!const_shape_input)
+ {
+ // 'shape' input of TFReshape is not TFConst, we can not do shape inference
+ return unknown;
+ }
+
+ // 'Shape' input should be integer tensor of rank 1, e.g. [2, 3, 4] or [3, -1]
+ assert(const_shape_input->dtype() == loco::DataType::S32);
+ assert(const_shape_input->rank() == 1);
+
+ auto shape_rank = const_shape_input->dim(0).value();
+ assert(shape_rank > 0);
+
+ loco::TensorShape output_shape;
+ output_shape.rank(shape_rank);
+ for (uint32_t axis = 0; axis < shape_rank; ++axis)
+ {
+ auto shape_dim = const_shape_input->at<loco::DataType::S32>(axis);
+ if (shape_dim == -1)
+ {
+ // Reshape's new shape has wildcard dimension, i.e. dynamic reshape
+ return unknown;
+ }
+ assert(shape_dim >= 1);
+ output_shape.dim(axis) = shape_dim;
+ }
+
+ // TODO Compare 'tensor' input and validate coherency?
+ // Not sure this is appropriate stage for this task.
+
+ return loco::NodeShape(output_shape);
+ }
+
+ loco::NodeShape visit(const moco::TFRsqrt *node) final
+ {
+ return node_shape_with_check(node->x());
+ }
+
+ loco::NodeShape visit(const moco::TFShape *node) final
+ {
+ auto input_shape = node_shape(node->input());
+ auto input_tensor_shape = input_shape.as<loco::TensorShape>();
+
+ loco::TensorShape output_shape;
+
+ // Note that input shape becomes node(TFShape)'s value
+ output_shape.rank(1);
+ output_shape.dim(0) = input_tensor_shape.rank();
+
+ return loco::NodeShape(output_shape);
+ }
+
+ loco::NodeShape visit(const moco::TFSoftmax *node) final
+ {
+ return node_shape_with_check(node->logits());
+ }
+
+ loco::NodeShape visit(const moco::TFSqrt *node) final { return node_shape_with_check(node->x()); }
+
+ loco::NodeShape visit(const moco::TFSquaredDifference *node) final
+ {
+ return binary_node_shape(node);
+ }
+
+ loco::NodeShape visit(const moco::TFSqueeze *node) final
+ {
+ auto input_shape = node_shape(node->input());
+
+ // TODO Not sure Squeeze only get input as Tensor
+ // Note that tensor_shape() has assertion in it
+ auto input_tensor_shape = input_shape.as<loco::TensorShape>();
+
+ auto squeeze_dims_vec = node->squeeze_dims();
+ std::set<int64_t> squeeze_dims(squeeze_dims_vec.cbegin(), squeeze_dims_vec.cend());
+
+ loco::TensorShape output_shape;
+ uint32_t output_rank = 0;
+
+ if (squeeze_dims.empty())
+ {
+ // Remove all dimensions whose value is 1
+ for (uint32_t axis = 0; axis < input_tensor_shape.rank(); ++axis)
+ {
+ assert(input_tensor_shape.dim(axis).known());
+ auto dim = input_tensor_shape.dim(axis).value();
+ if (dim != 1)
+ {
+ assert(dim > 1);
+ output_shape.rank(++output_rank);
+ output_shape.dim(output_rank - 1) = dim;
+ }
+ }
+ }
+ else
+ {
+ uint32_t input_rank = input_tensor_shape.rank();
+
+ // Sanity check for 'squeeze_dims'
+ auto is_valid_squeeze_dims = [&squeeze_dims, &input_rank]() {
+ if (!(squeeze_dims.size() < input_rank))
+ return false;
+ for (auto squeeze_dim : squeeze_dims)
+ {
+ if (!(squeeze_dim >= -(int64_t)input_rank))
+ return false;
+ if (!(squeeze_dim < (int64_t)input_rank))
+ return false;
+ }
+ return true;
+ };
+
+ if (!is_valid_squeeze_dims())
+ {
+ throw oops::UserExn("Invalid squeeze dimension", node->name());
+ }
+
+ // Resolve negative squeeze dimension
+ std::set<int64_t> resolved_squeeze_dims;
+ for (auto squeeze_dim : squeeze_dims)
+ {
+ if (squeeze_dim < 0)
+ resolved_squeeze_dims.insert(squeeze_dim + (int64_t)input_rank);
+ else
+ resolved_squeeze_dims.insert(squeeze_dim);
+ }
+
+ // Remove squeeze dimensions only
+ for (uint32_t axis = 0; axis < input_rank; ++axis)
+ {
+ assert(input_tensor_shape.dim(axis).known());
+ auto dim = input_tensor_shape.dim(axis).value();
+ if (resolved_squeeze_dims.find((int64_t)axis) == resolved_squeeze_dims.cend())
+ {
+ // Not squeeze dim
+ output_shape.rank(++output_rank);
+ output_shape.dim(output_rank - 1) = dim;
+ }
+ else
+ {
+ // Is squeeze dim
+ assert(dim == 1);
+ // DO NOTHING
+ }
+ }
+ }
+
+ assert(output_shape.rank() > 0);
+
+ return loco::NodeShape(output_shape);
+ }
+
+ loco::NodeShape visit(const moco::TFStopGradient *node) final
+ {
+ return node_shape_with_check(node->input());
+ }
+
+ loco::NodeShape visit(const moco::TFStridedSlice *node) final
+ {
+ loco::NodeShape unknown;
+ auto input_shape = node_shape(node->input());
+ if (input_shape.domain() != loco::Domain::Tensor)
+ {
+ // TODO fix this for other cases
+ // We support only tensor shape for now
+ return unknown;
+ }
+
+ // TODO support full mask features: see import codes also
+ // Limited attributes for now
+ assert(node->begin_mask() == 0);
+ assert(node->end_mask() == 0);
+ assert(node->ellipsis_mask() == 0);
+ assert(node->shrink_axis_mask() == 1);
+
+ auto const_begin = loco::must_cast<moco::TFConst *>(node->begin());
+ auto const_end = loco::must_cast<moco::TFConst *>(node->end());
+ auto const_strides = loco::must_cast<moco::TFConst *>(node->strides());
+
+ assert(dynamic_cast<moco::TFConst *>(node->input()) != nullptr);
+ assert(const_begin != nullptr);
+ assert(const_end != nullptr);
+ assert(const_strides != nullptr);
+
+ auto input_tensor_shape = input_shape.as<loco::TensorShape>();
+ auto input_rank = input_tensor_shape.rank();
+ auto output_rank = input_rank;
+
+ // TODO support strides with > 1
+ uint32_t elements = const_strides->size<loco::DataType::S32>();
+ for (uint32_t e = 0; e < elements; ++e)
+ assert(const_strides->at<loco::DataType::S32>(e) == 1);
+
+ // lets apply begin ~ end range from input shape
+ loco::TensorShape output_shape_range;
+
+ output_shape_range.rank(input_rank);
+ for (uint32_t r = 0; r < input_rank; ++r)
+ {
+ // TODO apply begin/end mask
+ // TODO apply ellipsis mask
+ // TODO apply strides
+ auto end = const_end->at<loco::DataType::S32>(r);
+ auto begin = const_begin->at<loco::DataType::S32>(r);
+ auto size = end - begin;
+ output_shape_range.dim(r).set(size);
+ }
+
+ // get final tensor shape from applying shrink mask to output_shape_range
+ loco::TensorShape output_tensor_shape;
+
+ if (node->shrink_axis_mask() != 0)
+ {
+ for (uint32_t rs = 0; rs < input_rank; ++rs)
+ {
+ int32_t bit = 1 << rs;
+ int32_t mask = node->shrink_axis_mask();
+ if (bit & mask)
+ {
+ // shrink one dimension
+ assert(output_rank > 0);
+ output_rank = output_rank - 1;
+ }
+ }
+ output_tensor_shape.rank(output_rank);
+ for (uint32_t rs = 0, rd = 0; rs < input_rank; ++rs)
+ {
+ int32_t bit = 1 << rs;
+ int32_t mask = node->shrink_axis_mask();
+ if ((bit & mask) == 0)
+ {
+ // use this dimension
+ output_tensor_shape.dim(rd).set(output_shape_range.dim(rs).value());
+ rd++;
+ }
+ // else this dimension is shrink-ed
+ }
+ }
+ else
+ {
+ output_tensor_shape = output_shape_range;
+ }
+
+ return loco::NodeShape(output_tensor_shape);
+ }
+
+ loco::NodeShape visit(const moco::TFSub *node) final { return binary_node_shape(node); }
+
+ loco::NodeShape visit(const moco::TFTanh *node) final { return node_shape_with_check(node->x()); }
+
+ // For virtual nodes
+ loco::NodeShape visit(const moco::TFPush *node) { return node_shape_with_check(node->from()); }
+
+public:
+ loco::NodeShape visit(const moco::TFNode *) final
+ {
+ loco::NodeShape unknown;
+ return unknown;
+ }
+};
+
+} // namespace
+
+namespace
+{
+namespace compat
+{
+
+struct Context final : public loco::ShapeInferenceRule::Context
+{
+ bool known(const loco::Node *node) const final { return loco::shape_known(node); }
+ loco::NodeShape get(const loco::Node *node) const final { return loco::shape_get(node); }
+};
+
+class Sink final : public loco::ShapeInferenceRule::Sink
+{
+public:
+ enum Status
+ {
+ Unknown,
+ Okay,
+ Fail,
+ };
+
+public:
+ const Status &status(void) const { return _status; }
+ const loco::NodeShape &shape(void) const { return _shape; }
+
+public:
+ void okay(const loco::NodeShape &shape) final
+ {
+ _status = Okay;
+ _shape = shape;
+ }
+
+ void fail(void) final
+ {
+ // Notify failrue
+ _status = Fail;
+ }
+
+private:
+ Status _status = Unknown;
+ loco::NodeShape _shape;
+};
+
+} // namespace compat
+} // namespace
+
+namespace moco
+{
+
+bool TFShapeInferenceRule::support(const API &api) const
+{
+ return api == API::V1 or api == API::V2;
+}
+
+bool TFShapeInferenceRule::recognize(const loco::Dialect *d) const
+{
+ // handle only TensorFlow dialect
+ return TFDialect::get() == d;
+}
+
+bool TFShapeInferenceRule::infer(const loco::Node *node, loco::NodeShape &shape) const
+{
+ ::compat::Context ctx;
+ ::compat::Sink sink;
+
+ infer(&ctx, node, &sink);
+
+ assert(sink.status() == ::compat::Sink::Okay or sink.status() == ::compat::Sink::Fail);
+
+ if (sink.status() == ::compat::Sink::Fail)
+ {
+ return false;
+ }
+
+ shape = sink.shape();
+
+ return true;
+}
+
+void TFShapeInferenceRule::infer(const Context *ctx, const loco::Node *node, Sink *sink) const
+{
+ assert(node->dialect() == TFDialect::get());
+ assert(dynamic_cast<const TFNode *>(node) != nullptr);
+
+ ShapeInferenceAlgorithm alg{ctx};
+ auto shape = loco::must_cast<const TFNode *>(node)->accept(&alg);
+
+ if (shape.domain() == loco::Domain::Unknown)
+ sink->fail();
+ else
+ sink->okay(shape);
+}
+
+} // namespace moco
diff --git a/compiler/moco/service/src/Service/TFShapeInferenceRule.test.cpp b/compiler/moco/service/src/Service/TFShapeInferenceRule.test.cpp
new file mode 100644
index 000000000..1e1b48ca7
--- /dev/null
+++ b/compiler/moco/service/src/Service/TFShapeInferenceRule.test.cpp
@@ -0,0 +1,500 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Service/TFShapeInferenceRule.h"
+
+#include "TestHelper.h"
+
+#include "moco/IR/TFNodes.h"
+
+#include <loco.h>
+#include <loco/Service/ShapeInference.h>
+
+#include <gtest/gtest.h>
+
+using namespace moco::test;
+
+namespace
+{
+
+moco::TFAvgPool *avgpool_network_simple1331(loco::Graph *graph)
+{
+ auto avgpool_node = graph->nodes()->create<moco::TFAvgPool>();
+
+ avgpool_node->data_layout("NHWC");
+ avgpool_node->ksize({1, 3, 3, 1});
+ avgpool_node->strides({1, 1, 1, 1});
+
+ // Dummy const node as ifm, just to fake TFShapeInferenceRule for TFAvgPool.
+ auto const_node = graph->nodes()->create<moco::TFConst>();
+ {
+ const_node->rank(4);
+ const_node->dim(0).set(1);
+ const_node->dim(1).set(3);
+ const_node->dim(2).set(3);
+ const_node->dim(3).set(1);
+ }
+ avgpool_node->value(const_node);
+
+ setup_output_node(graph, avgpool_node);
+
+ return avgpool_node;
+}
+
+} // namespace
+
+TEST(TFShapeInferenceRule, avgpool_same)
+{
+ moco::TFShapeInferenceRule shape_infer;
+ loco::Graph graph;
+
+ auto avgpool_node = avgpool_network_simple1331(&graph);
+ avgpool_node->padding("SAME");
+
+ bool cont = true;
+ while (cont)
+ {
+ cont = loco::apply(&shape_infer).to(&graph);
+ };
+
+ auto nodeshape = loco::shape_get(avgpool_node);
+ auto tshape = nodeshape.as<loco::TensorShape>();
+ ASSERT_EQ(tshape.rank(), 4);
+ ASSERT_EQ(tshape.dim(0).value(), 1);
+ ASSERT_EQ(tshape.dim(1).value(), 3);
+ ASSERT_EQ(tshape.dim(2).value(), 3);
+ ASSERT_EQ(tshape.dim(3).value(), 1);
+}
+
+TEST(TFShapeInferenceRule, avgpool_valid)
+{
+ moco::TFShapeInferenceRule shape_infer;
+ loco::Graph graph;
+
+ auto avgpool_node = avgpool_network_simple1331(&graph);
+ avgpool_node->padding("VALID");
+
+ bool cont = true;
+ while (cont)
+ {
+ cont = loco::apply(&shape_infer).to(&graph);
+ };
+
+ auto nodeshape = loco::shape_get(avgpool_node);
+ auto tshape = nodeshape.as<loco::TensorShape>();
+ ASSERT_EQ(tshape.rank(), 4);
+ ASSERT_EQ(tshape.dim(0).value(), 1);
+ ASSERT_EQ(tshape.dim(1).value(), 1);
+ ASSERT_EQ(tshape.dim(2).value(), 1);
+ ASSERT_EQ(tshape.dim(3).value(), 1);
+}
+
+namespace
+{
+
+void conv2d_test(const std::array<uint32_t, 4> ifm_shape, const std::array<uint32_t, 4> ker_shape,
+ const std::array<uint32_t, 2> stride_h_w, std::string padding,
+ const std::array<uint32_t, 4> expected_shape)
+{
+ moco::TFShapeInferenceRule shape_infer;
+ loco::Graph graph;
+
+ auto conv2d_node = graph.nodes()->create<moco::TFConv2D>();
+ conv2d_node->data_layout("NHWC");
+ conv2d_node->strides({1, stride_h_w[0], stride_h_w[1], 1});
+ conv2d_node->padding(padding);
+
+ auto ifm_node = graph.nodes()->create<moco::TFConst>();
+ {
+ ifm_node->rank(4);
+ ifm_node->dim(0).set(ifm_shape[0]);
+ ifm_node->dim(1).set(ifm_shape[1]);
+ ifm_node->dim(2).set(ifm_shape[2]);
+ ifm_node->dim(3).set(ifm_shape[3]);
+ }
+
+ auto ker_node = graph.nodes()->create<moco::TFConst>();
+ {
+ ker_node->rank(4);
+ ker_node->dim(0).set(ker_shape[0]);
+ ker_node->dim(1).set(ker_shape[1]);
+ ker_node->dim(2).set(ker_shape[2]);
+ ker_node->dim(3).set(ker_shape[3]);
+ }
+
+ conv2d_node->input(ifm_node);
+ conv2d_node->filter(ker_node);
+
+ setup_output_node(&graph, conv2d_node);
+
+ bool cont = true;
+ while (cont)
+ {
+ cont = loco::apply(&shape_infer).to(&graph);
+ };
+
+ auto nodeshape = loco::shape_get(conv2d_node);
+ auto tshape = nodeshape.as<loco::TensorShape>();
+ ASSERT_EQ(tshape.rank(), 4);
+ ASSERT_EQ(tshape.dim(0).value(), expected_shape[0]);
+ ASSERT_EQ(tshape.dim(1).value(), expected_shape[1]);
+ ASSERT_EQ(tshape.dim(2).value(), expected_shape[2]);
+ ASSERT_EQ(tshape.dim(3).value(), expected_shape[3]);
+}
+
+} // namespace
+
+/*
+ Testing "InceptionV3/InceptionV3/Conv2d_1a_3x3/Conv2D" Conv2D node in Inception_v3:
+ The result shape of this test is generated with the code below:
+
+ ifm = tf.constant(value=1.1, shape=[1, 299, 299, 3])
+ ker = tf.constant(value=1.1, shape=[3, 3, 3, 32])
+
+ out = tf.nn.conv2d(ifm, ker, strides = [1, 2, 2, 1], padding= 'VALID')
+
+ with tf.Session() as sess:
+ res = sess.run(out)
+ print(res.shape)
+ */
+TEST(TFShapeInferenceRule, conv2d_VALID)
+{
+ conv2d_test({1, 299, 299, 3}, // ifm
+ {3, 3, 3, 32}, // ker
+ {2, 2}, // strides
+ "VALID", // padding
+ {1, 149, 149, 32}); // expected shape after FixShape
+}
+
+/*
+ Testing "InceptionV3/InceptionV3/Conv2d_2b_3x3/Conv2D" Conv2D node in Inception_v3:
+ The result shape of this test is generated with the code below:
+
+ ifm = tf.constant(value=1.1, shape=[1, 147, 147, 32])
+ ker = tf.constant(value=1.1, shape=[3, 3, 32, 64])
+
+ out = tf.nn.conv2d(ifm, ker, strides = [1, 1, 1, 1], padding= 'SAME')
+
+ with tf.Session() as sess:
+ res = sess.run(out)
+ print(res.shape)
+ */
+TEST(TFShapeInferenceRule, conv2d_SAME)
+{
+ conv2d_test({1, 147, 147, 32}, // ifm
+ {3, 3, 32, 64}, // ker
+ {1, 1}, // strides
+ "SAME", // padding
+ {1, 147, 147, 64}); // expected shape after FixShape
+}
+
+/*
+ Testing Pack
+*/
+namespace
+{
+
+moco::TFConst *const_scalar(loco::Graph *graph, int32_t val)
+{
+ auto const_node = graph->nodes()->create<moco::TFConst>();
+
+ const_node->dtype(loco::DataType::S32);
+ const_node->rank(0);
+ const_node->size<loco::DataType::S32>(1);
+ const_node->at<loco::DataType::S32>(0) = val;
+
+ return const_node;
+}
+
+moco::TFConst *const_vector(loco::Graph *graph, int32_t dim)
+{
+ auto const_node = graph->nodes()->create<moco::TFConst>();
+
+ const_node->dtype(loco::DataType::S32);
+ const_node->rank(1);
+ const_node->dim(0).set(dim);
+
+ const_node->size<loco::DataType::S32>(dim);
+ for (int32_t i = 0; i < dim; ++i)
+ const_node->at<loco::DataType::S32>(i) = i;
+
+ return const_node;
+}
+
+moco::TFConst *const_vector_init(loco::Graph *graph, std::vector<int32_t> values)
+{
+ auto const_node = graph->nodes()->create<moco::TFConst>();
+ auto dim = values.size();
+
+ const_node->dtype(loco::DataType::S32);
+ const_node->rank(1);
+ const_node->dim(0).set(dim);
+
+ const_node->size<loco::DataType::S32>(dim);
+ for (int32_t i = 0; i < dim; ++i)
+ const_node->at<loco::DataType::S32>(i) = values[i];
+
+ return const_node;
+}
+
+moco::TFConst *const_matrix(loco::Graph *graph, int32_t dimh, int32_t dimw)
+{
+ auto const_node = graph->nodes()->create<moco::TFConst>();
+
+ const_node->dtype(loco::DataType::S32);
+ const_node->rank(2);
+ const_node->dim(0).set(dimh);
+ const_node->dim(1).set(dimw);
+
+ auto elements = dimh * dimw;
+ const_node->size<loco::DataType::S32>(elements);
+ for (int32_t i = 0; i < elements; ++i)
+ const_node->at<loco::DataType::S32>(i) = i;
+
+ return const_node;
+}
+
+} // namespace
+
+TEST(TFShapeInferenceRule, pack_scalar_2)
+{
+ moco::TFShapeInferenceRule shape_infer;
+ loco::Graph graph;
+
+ auto pack_node = graph.nodes()->create<moco::TFPack>(2);
+ pack_node->axis(0);
+ {
+ auto const_node_0 = const_scalar(&graph, 1);
+ pack_node->values(0, const_node_0);
+ auto const_node_1 = const_scalar(&graph, 1);
+ pack_node->values(1, const_node_1);
+ }
+ setup_output_node(&graph, pack_node);
+
+ bool cont = true;
+ while (cont)
+ {
+ cont = loco::apply(&shape_infer).to(&graph);
+ };
+
+ auto nodeshape = loco::shape_get(pack_node);
+ auto tshape = nodeshape.as<loco::TensorShape>();
+ ASSERT_EQ(tshape.rank(), 1);
+ ASSERT_EQ(tshape.dim(0).value(), 2);
+}
+
+TEST(TFShapeInferenceRule, pack_vector3_2)
+{
+ moco::TFShapeInferenceRule shape_infer;
+ loco::Graph graph;
+
+ auto pack_node = graph.nodes()->create<moco::TFPack>(2);
+ pack_node->axis(0);
+ {
+ auto const_node_0 = const_vector(&graph, 3);
+ pack_node->values(0, const_node_0);
+ auto const_node_1 = const_vector(&graph, 3);
+ pack_node->values(1, const_node_1);
+ }
+ setup_output_node(&graph, pack_node);
+
+ bool cont = true;
+ while (cont)
+ {
+ cont = loco::apply(&shape_infer).to(&graph);
+ };
+
+ auto nodeshape = loco::shape_get(pack_node);
+ auto tshape = nodeshape.as<loco::TensorShape>();
+
+ ASSERT_EQ(tshape.rank(), 2);
+ ASSERT_EQ(tshape.dim(0).value(), 2);
+ ASSERT_EQ(tshape.dim(1).value(), 3);
+}
+
+TEST(TFShapeInferenceRule, pack_vector3_2_axis_1)
+{
+ moco::TFShapeInferenceRule shape_infer;
+ loco::Graph graph;
+
+ auto pack_node = graph.nodes()->create<moco::TFPack>(2);
+ pack_node->axis(1);
+ {
+ auto const_node_0 = const_vector(&graph, 3);
+ pack_node->values(0, const_node_0);
+ auto const_node_1 = const_vector(&graph, 3);
+ pack_node->values(1, const_node_1);
+ }
+ setup_output_node(&graph, pack_node);
+
+ bool cont = true;
+ while (cont)
+ {
+ cont = loco::apply(&shape_infer).to(&graph);
+ };
+
+ auto nodeshape = loco::shape_get(pack_node);
+ auto tshape = nodeshape.as<loco::TensorShape>();
+
+ ASSERT_EQ(tshape.rank(), 2);
+ ASSERT_EQ(tshape.dim(0).value(), 3);
+ ASSERT_EQ(tshape.dim(1).value(), 2);
+}
+
+TEST(TFShapeInferenceRule, pack_vector3_2_axis_m2)
+{
+ moco::TFShapeInferenceRule shape_infer;
+ loco::Graph graph;
+
+ auto pack_node = graph.nodes()->create<moco::TFPack>(2);
+ pack_node->axis(-2);
+ {
+ auto const_node_0 = const_vector(&graph, 3);
+ pack_node->values(0, const_node_0);
+ auto const_node_1 = const_vector(&graph, 3);
+ pack_node->values(1, const_node_1);
+ }
+ setup_output_node(&graph, pack_node);
+
+ bool cont = true;
+ while (cont)
+ {
+ cont = loco::apply(&shape_infer).to(&graph);
+ };
+
+ auto nodeshape = loco::shape_get(pack_node);
+ auto tshape = nodeshape.as<loco::TensorShape>();
+
+ ASSERT_EQ(tshape.rank(), 2);
+ ASSERT_EQ(tshape.dim(0).value(), 2);
+ ASSERT_EQ(tshape.dim(1).value(), 3);
+}
+
+TEST(TFShapeInferenceRule, pack_vector3_2_axis_m3)
+{
+ moco::TFShapeInferenceRule shape_infer;
+ loco::Graph graph;
+
+ auto pack_node = graph.nodes()->create<moco::TFPack>(2);
+ pack_node->axis(-3);
+ {
+ auto const_node_0 = const_vector(&graph, 3);
+ pack_node->values(0, const_node_0);
+ auto const_node_1 = const_vector(&graph, 3);
+ pack_node->values(1, const_node_1);
+ }
+ setup_output_node(&graph, pack_node);
+
+ // -3 is out of range and should throw
+ EXPECT_ANY_THROW(loco::apply(&shape_infer).to(&graph));
+}
+
+TEST(TFShapeInferenceRule, pack_matrix3x4_2_axis_1)
+{
+ moco::TFShapeInferenceRule shape_infer;
+ loco::Graph graph;
+
+ auto pack_node = graph.nodes()->create<moco::TFPack>(2);
+ pack_node->axis(1);
+ {
+ auto const_node_0 = const_matrix(&graph, 3, 4);
+ pack_node->values(0, const_node_0);
+ auto const_node_1 = const_matrix(&graph, 3, 4);
+ pack_node->values(1, const_node_1);
+ }
+ setup_output_node(&graph, pack_node);
+
+ bool cont = true;
+ while (cont)
+ {
+ cont = loco::apply(&shape_infer).to(&graph);
+ };
+
+ auto nodeshape = loco::shape_get(pack_node);
+ auto tshape = nodeshape.as<loco::TensorShape>();
+
+ ASSERT_EQ(tshape.rank(), 3);
+ ASSERT_EQ(tshape.dim(0).value(), 3);
+ ASSERT_EQ(tshape.dim(1).value(), 2);
+ ASSERT_EQ(tshape.dim(2).value(), 4);
+}
+
+TEST(TFShapeInferenceRule, stridedslice_matrix5x5_shrink)
+{
+ moco::TFShapeInferenceRule shape_infer;
+ loco::Graph graph;
+
+ auto sslice_node = graph.nodes()->create<moco::TFStridedSlice>();
+ {
+ auto const_input = const_matrix(&graph, 5, 5);
+ sslice_node->input(const_input);
+
+ auto const_begin = const_vector_init(&graph, {1, 1});
+ sslice_node->begin(const_begin);
+ auto const_end = const_vector_init(&graph, {2, 4});
+ sslice_node->end(const_end);
+ auto const_strides = const_vector_init(&graph, {1, 1});
+ sslice_node->strides(const_strides);
+
+ sslice_node->shrink_axis_mask(1);
+ }
+ setup_output_node(&graph, sslice_node);
+
+ bool cont = true;
+ while (cont)
+ {
+ cont = loco::apply(&shape_infer).to(&graph);
+ };
+
+ auto nodeshape = loco::shape_get(sslice_node);
+ auto tshape = nodeshape.as<loco::TensorShape>();
+
+ ASSERT_EQ(tshape.rank(), 1);
+ ASSERT_EQ(tshape.dim(0).value(), 3);
+}
+
+TEST(TFShapeInferenceRule, stridedslice_4_shrink)
+{
+ moco::TFShapeInferenceRule shape_infer;
+ loco::Graph graph;
+
+ auto sslice_node = graph.nodes()->create<moco::TFStridedSlice>();
+ {
+ auto const_input = const_vector(&graph, 4);
+ sslice_node->input(const_input);
+
+ auto const_begin = const_vector_init(&graph, {0});
+ sslice_node->begin(const_begin);
+ auto const_end = const_vector_init(&graph, {1});
+ sslice_node->end(const_end);
+ auto const_strides = const_vector_init(&graph, {1});
+ sslice_node->strides(const_strides);
+
+ sslice_node->shrink_axis_mask(1);
+ }
+ setup_output_node(&graph, sslice_node);
+
+ bool cont = true;
+ while (cont)
+ {
+ cont = loco::apply(&shape_infer).to(&graph);
+ };
+
+ auto nodeshape = loco::shape_get(sslice_node);
+ auto tshape = nodeshape.as<loco::TensorShape>();
+
+ ASSERT_EQ(tshape.rank(), 0);
+}
diff --git a/compiler/moco/service/src/Service/TFTypeInferenceRule.cpp b/compiler/moco/service/src/Service/TFTypeInferenceRule.cpp
new file mode 100644
index 000000000..f168c80ff
--- /dev/null
+++ b/compiler/moco/service/src/Service/TFTypeInferenceRule.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Service/TFTypeInferenceRule.h"
+
+#include "moco/IR/TFDialect.h"
+#include "moco/IR/TFNodeVisitor.h"
+#include "moco/IR/TFNodes.h"
+
+#include "moco/IR/TFNodeImpl.h"
+
+#include <cassert>
+
+namespace
+{
+
+using namespace moco;
+
+struct TypeForwardAlgorithm final : public moco::TFNodeVisitor<loco::DataType>
+{
+ loco::DataType visit(const TFAdd *node) { return dtype_get(node->x()); }
+ loco::DataType visit(const TFAvgPool *node) { return dtype_get(node->value()); }
+ loco::DataType visit(const TFBiasAdd *node) { return dtype_get(node->value()); }
+ loco::DataType visit(const TFConcatV2 *node) { return dtype_get(node->values(0)); }
+
+ loco::DataType visit(const TFConst *node) { return node->dtype(); }
+
+ loco::DataType visit(const TFConv2D *node) { return dtype_get(node->input()); }
+ loco::DataType visit(const TFConv2DBackpropInput *node)
+ {
+ return dtype_get(node->out_backprop());
+ }
+ loco::DataType visit(const TFDepthwiseConv2dNative *node) { return dtype_get(node->input()); }
+ loco::DataType visit(const TFFakeQuantWithMinMaxVars *node) { return dtype_get(node->inputs()); }
+ loco::DataType visit(const TFFusedBatchNorm *node) { return dtype_get(node->x()); }
+ loco::DataType visit(const TFIdentity *node) { return dtype_get(node->input()); }
+ loco::DataType visit(const TFMaximum *node) { return dtype_get(node->x()); }
+ loco::DataType visit(const TFMaxPool *node) { return dtype_get(node->input()); }
+ loco::DataType visit(const TFMean *node) { return dtype_get(node->input()); }
+ loco::DataType visit(const TFMul *node) { return dtype_get(node->x()); }
+ loco::DataType visit(const TFPack *node) { return dtype_get(node->values(0)); }
+ loco::DataType visit(const TFPad *node) { return dtype_get(node->input()); }
+
+ loco::DataType visit(const TFPlaceholder *node) { return node->dtype(); }
+
+ loco::DataType visit(const TFRealDiv *node) { return dtype_get(node->x()); }
+ loco::DataType visit(const TFRelu *node) { return dtype_get(node->features()); }
+ loco::DataType visit(const TFRelu6 *node) { return dtype_get(node->features()); }
+ loco::DataType visit(const TFReshape *node) { return dtype_get(node->tensor()); }
+ loco::DataType visit(const TFRsqrt *node) { return dtype_get(node->x()); }
+
+ loco::DataType visit(const TFShape *node) { return node->dtype(); }
+
+ loco::DataType visit(const TFSoftmax *node) { return dtype_get(node->logits()); }
+ loco::DataType visit(const TFSqrt *node) { return dtype_get(node->x()); }
+ loco::DataType visit(const TFSquaredDifference *node) { return dtype_get(node->x()); }
+ loco::DataType visit(const TFSqueeze *node) { return dtype_get(node->input()); }
+ loco::DataType visit(const TFStopGradient *node) { return dtype_get(node->input()); }
+ loco::DataType visit(const TFStridedSlice *node) { return dtype_get(node->input()); }
+ loco::DataType visit(const TFSub *node) { return dtype_get(node->x()); }
+ loco::DataType visit(const TFTanh *node) { return dtype_get(node->x()); }
+
+ // For virtual nodes
+ loco::DataType visit(const TFPush *node) { return dtype_get(node->from()); }
+};
+
+} // namespace
+
+namespace moco
+{
+
+bool TFTypeInferenceRule::recognize(const loco::Dialect *d) const
+{
+ // This rule recognizes only "TFDialect" dialect!
+ return TFDialect::get() == d;
+}
+
+bool TFTypeInferenceRule::infer(const loco::Node *node, loco::DataType &dtype) const
+{
+ assert(node->dialect() == TFDialect::get());
+
+ TypeForwardAlgorithm alg;
+
+// clang-format off
+#define TENSORFLOW_NODE(OPCODE,CLASS) \
+ if (dynamic_cast<const moco::CLASS *>(node)) \
+ { \
+ auto tfnode = loco::must_cast<const moco::CLASS *>(node); \
+ dtype = tfnode->accept(&alg); \
+ assert(dtype != loco::DataType::Unknown); \
+ return true; \
+ }
+#include "moco/IR/TFNodes.lst"
+#undef TENSORFLOW_NODE
+ // clang-format on
+
+ return false;
+}
+
+} // namespace moco
diff --git a/compiler/moco/service/src/TestHelper.h b/compiler/moco/service/src/TestHelper.h
new file mode 100644
index 000000000..8f3ff764e
--- /dev/null
+++ b/compiler/moco/service/src/TestHelper.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TEST_HELPER_H__
+#define __TEST_HELPER_H__
+
+#include <loco.h>
+
+namespace moco
+{
+namespace test
+{
+
+template <typename T> T *find_first_node_bytype(loco::Graph *g)
+{
+ T *first_node = nullptr;
+ loco::Graph::NodeContext *nodes = g->nodes();
+ uint32_t count = nodes->size();
+
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ first_node = dynamic_cast<T *>(nodes->at(i));
+ if (first_node != nullptr)
+ break;
+ }
+
+ return first_node;
+}
+
+template <typename T> std::vector<T *> find_nodes_bytype(loco::Graph *g)
+{
+ std::vector<T *> find_nodes;
+ loco::Graph::NodeContext *nodes = g->nodes();
+ uint32_t count = nodes->size();
+
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ auto node = dynamic_cast<T *>(nodes->at(i));
+ if (node != nullptr)
+ find_nodes.push_back(node);
+ }
+
+ return find_nodes;
+}
+
+/**
+ * @brief Append setup output of graph by adding loco::Push node
+ *
+ * @note This is subject to change when loco changes I/O treatment
+ */
+void setup_output_node(loco::Graph *graph, loco::Node *last_node);
+
+} // namespace test
+} // namespace moco
+
+#endif // __TEST_HELPER_H__
diff --git a/compiler/moco/service/src/TestHelper.test.cpp b/compiler/moco/service/src/TestHelper.test.cpp
new file mode 100644
index 000000000..59915d60f
--- /dev/null
+++ b/compiler/moco/service/src/TestHelper.test.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestHelper.h"
+
+namespace moco
+{
+namespace test
+{
+
+void setup_output_node(loco::Graph *graph, loco::Node *last_node)
+{
+ // add push as output
+ auto push_node = graph->nodes()->create<loco::Push>();
+ push_node->from(last_node);
+
+ // set the graph output name and node object
+ auto graph_output = graph->outputs()->create();
+ graph_output->name("output");
+ graph_output->dtype(loco::DataType::FLOAT32);
+ loco::link(graph_output, push_node);
+}
+
+} // namespace test
+} // namespace moco
diff --git a/compiler/moco/support/CMakeLists.txt b/compiler/moco/support/CMakeLists.txt
new file mode 100644
index 000000000..2a896d495
--- /dev/null
+++ b/compiler/moco/support/CMakeLists.txt
@@ -0,0 +1,9 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+add_library(moco_support SHARED ${SOURCES})
+target_include_directories(moco_support PRIVATE src)
+target_include_directories(moco_support PUBLIC include)
+target_link_libraries(moco_support PUBLIC loco)
+target_link_libraries(moco_support PUBLIC moco_lang)
+target_link_libraries(moco_support PRIVATE oops)
+install(TARGETS moco_support DESTINATION lib)
diff --git a/compiler/moco/support/README.md b/compiler/moco/support/README.md
new file mode 100644
index 000000000..081f65d39
--- /dev/null
+++ b/compiler/moco/support/README.md
@@ -0,0 +1,3 @@
+# support
+
+_support_ privides _moco_ support libraries
diff --git a/compiler/moco/support/include/moco/Support/NodeAs.h b/compiler/moco/support/include/moco/Support/NodeAs.h
new file mode 100644
index 000000000..dc78ff94a
--- /dev/null
+++ b/compiler/moco/support/include/moco/Support/NodeAs.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_SUPPORT_NODE_AS_H__
+#define __MOCO_SUPPORT_NODE_AS_H__
+
+#include <loco.h>
+
+namespace moco
+{
+
+template <typename DERIVED> DERIVED *as(loco::Node *node) { return dynamic_cast<DERIVED *>(node); }
+
+} // namespace moco
+
+#endif // __MOCO_SUPPORT_NODE_AS_H__
diff --git a/compiler/moco/support/include/moco/Support/TFShapeInferenceHelper.h b/compiler/moco/support/include/moco/Support/TFShapeInferenceHelper.h
new file mode 100644
index 000000000..52324700a
--- /dev/null
+++ b/compiler/moco/support/include/moco/Support/TFShapeInferenceHelper.h
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOCO_SUPPORT_SHAPE_INFERENCE_HELPER_H__
+#define __MOCO_SUPPORT_SHAPE_INFERENCE_HELPER_H__
+
+#include <moco/IR/TFDataLayout.h>
+#include <moco/IR/TFPadding.h>
+
+#include <loco.h>
+#include <loco/IR/NodeShape.h>
+#include <loco/IR/Padding2D.h>
+#include <loco/IR/Stride.h>
+#include <loco/IR/Window.h>
+
+#include <cassert>
+
+namespace moco
+{
+
+/**
+ * @note Helper for return broadcasted shape for binary operators having
+ * different shape for input x and y
+ */
+loco::TensorShape broadcast_shape(const loco::TensorShape &x, const loco::TensorShape &y);
+
+} // namespace moco
+
+namespace moco
+{
+
+/**
+ * @brief Return true if node has shape inference data for checking shape
+ * inference is done or not
+ *
+ * @note Will be deprecated in near future
+ */
+bool shape_inference_done(const loco::Node *node);
+
+/**
+ * @note While in shape inference, Node maybe Canonical, TF dialect or other dialects
+ * This will provide common loco::NodeShape as shape information
+ */
+loco::NodeShape node_shape(const loco::Node *node);
+bool node_shape(const loco::Node *node, loco::NodeShape &nodeshape);
+
+loco::TensorShape as_tensor_shape(const loco::FeatureShape &feature_shape,
+ const TFDataLayout &data_layout);
+
+loco::FeatureShape as_feature_shape(const loco::NodeShape &nodeshape,
+ const TFDataLayout &data_layout);
+
+} // namespace moco
+
+namespace moco
+{
+
+struct PlaneShape
+{
+ loco::Dimension height;
+ loco::Dimension width;
+};
+
+class FeatureShapeUpdater final
+{
+public:
+ FeatureShapeUpdater(loco::FeatureShape *ptr) : _feature_shape_ptr{ptr}
+ {
+ // DO NOTHING
+ }
+
+public:
+ void with(const PlaneShape &plane_shape) const
+ {
+ _feature_shape_ptr->height() = plane_shape.height;
+ _feature_shape_ptr->width() = plane_shape.width;
+ }
+
+private:
+ loco::FeatureShape *_feature_shape_ptr;
+};
+
+PlaneShape make_plane_shape(const loco::FeatureShape &feature_shape);
+
+FeatureShapeUpdater update(loco::FeatureShape &feature_shape);
+
+class PlaneInference
+{
+protected:
+ struct Parameters
+ {
+ PlaneShape input;
+ PlaneShape stride;
+ PlaneShape window;
+ PlaneShape dilation;
+ PlaneShape effective_window;
+ PlaneShape output;
+ };
+
+ void fill(Parameters &p, const PlaneShape &in)
+ {
+ p.input.height = in.height;
+ p.input.width = in.width;
+
+ p.stride.height = _stride.vertical();
+ p.stride.width = _stride.horizontal();
+
+ p.window.height = _window.vertical();
+ p.window.width = _window.horizontal();
+
+ // TODO support dilation
+ p.dilation.height = 1;
+ p.dilation.width = 1;
+
+ p.effective_window.height = p.dilation.height.value() * (p.window.height.value() - 1) + 1;
+ p.effective_window.width = p.dilation.width.value() * (p.window.width.value() - 1) + 1;
+ }
+
+ PlaneShape infer(const Parameters &p, const PlaneShape &)
+ {
+ PlaneShape res;
+
+ if (_padding == "VALID")
+ {
+ res.height =
+ (p.input.height.value() + p.stride.height.value() - p.effective_window.height.value()) /
+ p.stride.height.value();
+ res.width =
+ (p.input.width.value() + p.stride.width.value() - p.effective_window.width.value()) /
+ p.stride.width.value();
+ }
+ else if (_padding == "SAME")
+ {
+ res.height = (p.input.height.value() + p.stride.height.value() - 1) / p.stride.height.value();
+ res.width = (p.input.width.value() + p.stride.width.value() - 1) / p.stride.width.value();
+ }
+ else
+ assert(false);
+
+ return res;
+ }
+
+public:
+ PlaneShape operator()(const PlaneShape &in)
+ {
+ Parameters p;
+
+ fill(p, in);
+
+ return infer(p, in);
+ }
+
+public:
+ void padding(const TFPadding &value) { _padding = value; }
+ void window(const loco::Window<2> value) { _window = value; }
+ void stride(const loco::Stride<2> value) { _stride = value; }
+
+private:
+ TFPadding _padding;
+ loco::Window<2> _window;
+ loco::Stride<2> _stride;
+};
+
+class Padding2DInference final : public PlaneInference
+{
+public:
+ loco::Padding2D operator()(const PlaneShape &in)
+ {
+ Parameters p;
+
+ fill(p, in);
+
+ auto output = infer(p, in);
+
+ int64_t i_height = (int64_t)(output.height.value() - 1) * (int64_t)p.stride.height.value() +
+ (int64_t)p.effective_window.height.value() - (int64_t)p.input.height.value();
+ int64_t i_width = (int64_t)(output.width.value() - 1) * (int64_t)p.stride.width.value() +
+ (int64_t)p.effective_window.width.value() - (int64_t)p.input.width.value();
+
+ uint32_t pad_height = i_height >= 0 ? (uint32_t)i_height : 0U;
+ uint32_t pad_width = i_width >= 0 ? (uint32_t)i_width : 0U;
+
+ loco::Padding2D padding2d;
+
+ padding2d.top(pad_height / 2);
+ padding2d.bottom(pad_height - padding2d.top());
+ padding2d.left(pad_width / 2);
+ padding2d.right(pad_width - padding2d.left());
+
+ return padding2d;
+ }
+};
+
+} // namespace moco
+
+namespace moco
+{
+
+using TFStrides = std::vector<int64_t>;
+using TFKSize = std::vector<int64_t>;
+
+loco::Stride<2> stride_of(const TFStrides &strides, const TFDataLayout &datalayout);
+loco::Window<2> window_of(const TFKSize &ksize, const TFDataLayout &datalayout);
+loco::Window<2> window_of(const loco::TensorShape &shape, const TFDataLayout &datalayout);
+
+} // namespace moco
+
+#endif // __MOCO_SERVICE_SHAPE_INFERENCE_HELPER_H__
diff --git a/compiler/moco/support/src/TFShapeInferenceHelper.cpp b/compiler/moco/support/src/TFShapeInferenceHelper.cpp
new file mode 100644
index 000000000..13e514a78
--- /dev/null
+++ b/compiler/moco/support/src/TFShapeInferenceHelper.cpp
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "moco/Support/TFShapeInferenceHelper.h"
+
+#include <loco/Service/ShapeInference.h>
+
+#include <oops/InternalExn.h>
+
+#include <cassert>
+
+namespace
+{
+
+// TODO Use codes in loco and remove duplicate broadcast_shape() and related
+/**
+ * @brief Create a higher-rank TensorShape following NumPy broadcasting semantics
+ *
+ * HOW TO USE:
+ *
+ * auto expanded_tensor_shape = expand(tensor_shape).to(N);
+ */
+class TensorShapeExpander
+{
+public:
+ TensorShapeExpander(const loco::TensorShape &shape) : _shape{shape}
+ {
+ // DO NOTHING
+ }
+
+public:
+ loco::TensorShape to(uint32_t output_rank)
+ {
+ auto const &input_shape = _shape;
+ uint32_t const input_rank = input_shape.rank();
+
+ assert(input_rank <= output_rank && "Cannot shrink rank");
+ uint32_t const axis_shift = output_rank - input_rank;
+
+ loco::TensorShape output_shape;
+
+ output_shape.rank(output_rank);
+ for (uint32_t axis = 0; axis < output_rank; ++axis)
+ {
+ output_shape.dim(axis) = (axis < axis_shift) ? 1 : input_shape.dim(axis - axis_shift);
+ }
+
+ return output_shape;
+ }
+
+private:
+ const loco::TensorShape _shape;
+};
+
+/**
+ * @breif Expand shape x and y to same rank by align right and filling with 1
+ */
+void expand_rank(loco::TensorShape &x, loco::TensorShape &y)
+{
+ auto x_rank = x.rank();
+ auto y_rank = y.rank();
+
+ if (x_rank == y_rank)
+ return;
+
+ TensorShapeExpander x_exp(x);
+ TensorShapeExpander y_exp(y);
+
+ auto xy_rank = std::max(x_rank, y_rank);
+
+ x = x_rank > y_rank ? x : x_exp.to(xy_rank);
+ y = y_rank > x_rank ? y : y_exp.to(xy_rank);
+}
+
+/**
+ * @breif Returns shape of expanded dimension of input x and y having same rank
+ */
+loco::TensorShape expand_dimension(const loco::TensorShape &x, const loco::TensorShape &y)
+{
+ assert(x.rank() == y.rank());
+
+ auto rank = x.rank();
+
+ loco::TensorShape output_shape;
+
+ output_shape.rank(rank);
+ for (uint32_t axis = 0; axis < rank; ++axis)
+ {
+ assert(x.dim(axis).known() && y.dim(axis).known());
+
+ auto x_dim = x.dim(axis).value();
+ auto y_dim = y.dim(axis).value();
+
+ // each dimension of x and y should be same or one must be 1 if different
+ if (!((x_dim == y_dim) || (x_dim == 1 || y_dim == 1)))
+ {
+ // TODO may need to refine message
+ INTERNAL_EXN("ShapeInference: Input shapes don't match");
+ }
+
+ output_shape.dim(axis) = std::max(x_dim, y_dim);
+ }
+
+ return output_shape;
+}
+
+} // namespace
+
+namespace moco
+{
+
+loco::TensorShape broadcast_shape(const loco::TensorShape &x, const loco::TensorShape &y)
+{
+ auto x_match = x;
+ auto y_match = y;
+
+ expand_rank(x_match, y_match);
+
+ auto output_shape = expand_dimension(x_match, y_match);
+
+ return output_shape;
+}
+
+} // namespace moco
+
+namespace moco
+{
+
+loco::NodeShape node_shape(const loco::Node *node)
+{
+ loco::NodeShape nodeshape; // default domain is Unknown
+
+ if (loco::shape_known(node))
+ {
+ nodeshape = loco::shape_get(node);
+ }
+
+ return nodeshape;
+}
+
+bool node_shape(const loco::Node *node, loco::NodeShape &nodeshape)
+{
+ nodeshape = node_shape(node);
+ return (nodeshape.domain() != loco::Domain::Unknown);
+}
+
+loco::TensorShape as_tensor_shape(const loco::FeatureShape &feature_shape,
+ const TFDataLayout &data_layout)
+{
+ loco::TensorShape tensor_shape;
+
+ tensor_shape.rank(4);
+ if (data_layout == "NHWC")
+ {
+ tensor_shape.dim(0) = feature_shape.count();
+ tensor_shape.dim(1) = feature_shape.height();
+ tensor_shape.dim(2) = feature_shape.width();
+ tensor_shape.dim(3) = feature_shape.depth();
+ }
+ else if (data_layout == "NCHW")
+ {
+ tensor_shape.dim(0) = feature_shape.count();
+ tensor_shape.dim(1) = feature_shape.depth();
+ tensor_shape.dim(2) = feature_shape.height();
+ tensor_shape.dim(3) = feature_shape.width();
+ }
+ else
+ {
+ // TODO support for other data_layout if needed
+ INTERNAL_EXN_V("ShapeInference: Unknown data_format", data_layout);
+ }
+
+ return tensor_shape;
+}
+
+loco::FeatureShape as_feature_shape(const loco::NodeShape &nodeshape,
+ const TFDataLayout &data_layout)
+{
+ if (nodeshape.domain() == loco::Domain::Feature)
+ return nodeshape.as<loco::FeatureShape>();
+
+ loco::FeatureShape feature_shape;
+
+ // only convert from tensor to feature
+ if (nodeshape.domain() != loco::Domain::Tensor)
+ {
+ INTERNAL_EXN("ShapeInference: Invalid shape information");
+ }
+
+ loco::TensorShape tensor_shape = nodeshape.as<loco::TensorShape>();
+
+ if (tensor_shape.rank() != 4)
+ {
+ INTERNAL_EXN("ShapeInference: Rank is not 4");
+ }
+
+ if (data_layout == "NHWC")
+ {
+ feature_shape.count() = tensor_shape.dim(0);
+ feature_shape.height() = tensor_shape.dim(1);
+ feature_shape.width() = tensor_shape.dim(2);
+ feature_shape.depth() = tensor_shape.dim(3);
+ }
+ else if (data_layout == "NCHW")
+ {
+ feature_shape.count() = tensor_shape.dim(0);
+ feature_shape.depth() = tensor_shape.dim(1);
+ feature_shape.height() = tensor_shape.dim(2);
+ feature_shape.width() = tensor_shape.dim(3);
+ }
+ else
+ {
+ // TODO support for other data_layout if needed
+ INTERNAL_EXN_V("ShapeInference: Unknown data_format", data_layout);
+ }
+
+ return feature_shape;
+}
+
+} // namespace moco
+
+namespace moco
+{
+
+PlaneShape make_plane_shape(const loco::FeatureShape &feature_shape)
+{
+ PlaneShape plane_shape;
+
+ plane_shape.height = feature_shape.height();
+ plane_shape.width = feature_shape.width();
+
+ return plane_shape;
+}
+
+FeatureShapeUpdater update(loco::FeatureShape &feature_shape)
+{
+ return FeatureShapeUpdater{&feature_shape};
+}
+
+} // namespace moco
+
+namespace
+{
+
+/**
+ * @brief Class to represent TensorFlow "data_format" attr.
+ */
+enum class DataLayout
+{
+ NHWC,
+ NCHW,
+};
+
+DataLayout as_data_layout(const std::string &tf_layout_str)
+{
+ if (tf_layout_str == "NHWC")
+ return DataLayout::NHWC;
+ else if (tf_layout_str == "NCHW")
+ return DataLayout::NCHW;
+ else
+ /// @note data layout tag in TensorFlow is 'data_format'
+ INTERNAL_EXN_V("ShapeInference: Unknown data_format", tf_layout_str);
+}
+
+} // namespace
+
+namespace moco
+{
+
+loco::Stride<2> stride_of(const TFStrides &strides, const TFDataLayout &datalayout)
+{
+ loco::Stride<2> stride;
+
+ auto data_layout = as_data_layout(datalayout);
+ if (data_layout == DataLayout::NHWC)
+ {
+ stride.vertical(strides[1]);
+ stride.horizontal(strides[2]);
+ }
+ else if (data_layout == DataLayout::NCHW)
+ {
+ stride.vertical(strides[2]);
+ stride.horizontal(strides[3]);
+ }
+ else
+ {
+ // TODO add more datalayout supports if needed
+ INTERNAL_EXN("ShapeInference: Unknown data_format");
+ }
+
+ return stride;
+}
+
+loco::Window<2> window_of(const TFKSize &ksize, const TFDataLayout &datalayout)
+{
+ loco::Window<2> window;
+
+ auto data_layout = as_data_layout(datalayout);
+ if (data_layout == DataLayout::NHWC)
+ {
+ window.vertical(ksize[1]);
+ window.horizontal(ksize[2]);
+ }
+ else if (data_layout == DataLayout::NCHW)
+ {
+ window.vertical(ksize[2]);
+ window.horizontal(ksize[3]);
+ }
+ else
+ {
+ // TODO add more datalayout supports if needed
+ INTERNAL_EXN("ShapeInference: Unknown data_format");
+ }
+
+ return window;
+}
+
+loco::Window<2> window_of(const loco::TensorShape &shape, const TFDataLayout &datalayout)
+{
+ loco::Window<2> window;
+
+ if (datalayout == "HWIO")
+ {
+ window.vertical(shape.dim(0).value());
+ window.horizontal(shape.dim(1).value());
+ }
+ else if (datalayout == "HWCM")
+ {
+ window.vertical(shape.dim(0).value());
+ window.horizontal(shape.dim(1).value());
+ }
+ else
+ {
+ // TODO add more datalayout supports if needed
+ INTERNAL_EXN_V("ShapeInference: Unknown data_format", datalayout);
+ }
+
+ return window;
+}
+
+} // namespace moco
diff --git a/compiler/mocotest-onnx/CMakeLists.txt b/compiler/mocotest-onnx/CMakeLists.txt
deleted file mode 100644
index 385a2a784..000000000
--- a/compiler/mocotest-onnx/CMakeLists.txt
+++ /dev/null
@@ -1,53 +0,0 @@
-option(MOCO_ONNX_TEST "Enable moco test for ONNX" ON)
-
-if(NOT MOCO_ONNX_TEST)
- return()
-endif(NOT MOCO_ONNX_TEST)
-
-if(NOT TARGET onnxkit)
- message(STATUS "moco: Skip test material preparation as onnxkit is not defined")
- return()
-endif(NOT TARGET onnxkit)
-
-#
-# Copy [Testcase]/test.pbtxt to Testcase.pbtxt in binary folder
-# Encode Testcase.pbtxt to Testcase.pb
-#
-set(TEST_PBTXT_FILE "test.pbtxt")
-set(TEST_REPO "${CMAKE_CURRENT_SOURCE_DIR}") # Where to find tests
-set(TEST_SPACE "${CMAKE_CURRENT_BINARY_DIR}") # Where to run tests
-
-file(GLOB PBTXTFILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*/${TEST_PBTXT_FILE}")
-
-unset(TEST_DEPS)
-
-foreach(PBTXTFILE IN ITEMS ${PBTXTFILES})
- get_filename_component(DIR_NAME ${PBTXTFILE} DIRECTORY)
-
- set(PBTXT_SOURCE_FILE "${DIR_NAME}.pbtxt")
- set(PBTXT_SOURCE_PATH "${TEST_SPACE}/${PBTXT_SOURCE_FILE}")
-
- set(PB_OUTPUT_FILE "${DIR_NAME}.pb")
- set(PB_OUTPUT_PATH "${TEST_SPACE}/${PB_OUTPUT_FILE}")
-
- # Copy files
- add_custom_command(
- OUTPUT ${PBTXT_SOURCE_PATH}
- COMMAND ${CMAKE_COMMAND} -E copy "${TEST_REPO}/${PBTXTFILE}" "${PBTXT_SOURCE_PATH}"
- COMMENT "Copy ${PBTXT_SOURCE_FILE}"
- DEPENDS "${TEST_REPO}/${PBTXTFILE}"
- )
-
- # Use onnxkit to encode
- add_custom_command(
- OUTPUT ${PB_OUTPUT_PATH}
- COMMAND $<TARGET_FILE:onnxkit> encode ${PBTXT_SOURCE_PATH} ${PB_OUTPUT_PATH}
- DEPENDS ${PBTXT_SOURCE_PATH}
- COMMENT "Generate ${PB_OUTPUT_FILE}"
- )
-
- list(APPEND TEST_DEPS "${PB_OUTPUT_PATH}")
-endforeach(PBTXTFILE)
-
-# This target enforces CMake to generate all the dependencies during "build" phase
-add_custom_target(mocotest_onnx_deps ALL DEPENDS ${TEST_DEPS})
diff --git a/compiler/mocotest-tf/CMakeLists.txt b/compiler/mocotest-tf/CMakeLists.txt
deleted file mode 100644
index 35a3f185d..000000000
--- a/compiler/mocotest-tf/CMakeLists.txt
+++ /dev/null
@@ -1,99 +0,0 @@
-option(MOCO_TF_TEST "Enable moco test for TensorFlow" ON)
-
-if(NOT MOCO_TF_TEST)
- return()
-endif(NOT MOCO_TF_TEST)
-
-if(NOT TARGET tfkit)
- message(STATUS "moco: Skip test material preparation as tfkit is not defined")
- return()
-endif(NOT TARGET tfkit)
-
-nncc_find_resource(TensorFlowTests)
-
-#
-# Copy [Testcase]/test.pbtxt to Testcase.pbtxt in binary folder
-# Copy [Testcase]/test.info to Testcase.info in binary folder
-# Encode Testcase.pbtxt to Testcase.pb
-#
-set(TEST_PBTXT_FILE "test.pbtxt")
-set(TEST_INFO_FILE "test.info")
-set(TEST_REPO "${TensorFlowTests_DIR}") # Where to find tests
-set(TEST_SPACE "${CMAKE_CURRENT_BINARY_DIR}") # Where to run tests
-
-unset(TESTCASES)
-
-macro(add NAME)
- list(APPEND TESTCASES ${NAME})
-endmacro(add)
-
-# Read "test.lst"
-include("test.lst")
-# Read "test.local.lst" if exists
-include("test.local.lst" OPTIONAL)
-
-unset(MOCO_TF_DEPS)
-
-foreach(PREFIX IN ITEMS ${TESTCASES})
- set(PBTXTFILE "${PREFIX}/${TEST_PBTXT_FILE}")
- get_filename_component(DIR_NAME ${PBTXTFILE} DIRECTORY)
-
- set(PBTXT_SOURCE_FILE "${DIR_NAME}.pbtxt")
- set(PBTXT_SOURCE_PATH "${TEST_SPACE}/${DIR_NAME}.pbtxt")
-
- set(PBTXT_INFO_FILE "${DIR_NAME}.info")
- set(PBTXT_INFO_PATH "${TEST_SPACE}/${PBTXT_INFO_FILE}")
-
- set(PB_OUTPUT_FILE "${DIR_NAME}.pb")
- set(PB_PATH "${TEST_SPACE}/${PB_OUTPUT_FILE}")
-
- # Copy files
- add_custom_command(
- OUTPUT ${PBTXT_SOURCE_PATH}
- COMMAND ${CMAKE_COMMAND} -E copy "${TEST_REPO}/${DIR_NAME}/${TEST_PBTXT_FILE}"
- "${PBTXT_SOURCE_PATH}"
- DEPENDS "${TEST_REPO}/${DIR_NAME}/${TEST_PBTXT_FILE}"
- COMMENT "Copy ${PBTXT_SOURCE_FILE}"
- )
-
- add_custom_command(
- OUTPUT ${PBTXT_INFO_PATH}
- COMMAND ${CMAKE_COMMAND} -E copy "${TEST_REPO}/${DIR_NAME}/${TEST_INFO_FILE}"
- "${PBTXT_INFO_PATH}"
- DEPENDS "${TEST_REPO}/${DIR_NAME}/${TEST_INFO_FILE}"
- COMMENT "Copy ${PBTXT_INFO_FILE}"
- )
-
- # Use tfkit to encode
- add_custom_command(
- OUTPUT ${PB_OUTPUT_FILE}
- COMMAND $<TARGET_FILE:tfkit> encode ${PBTXT_SOURCE_PATH} ${PB_OUTPUT_FILE}
- DEPENDS tfkit ${PBTXT_SOURCE_PATH}
- COMMENT "Generate ${PB_OUTPUT_FILE}"
- )
-
- list(APPEND MOCO_TF_TESTS ${DIR_NAME})
- list(APPEND MOCO_TF_DEPS ${PBTXT_INFO_FILE} ${PB_OUTPUT_FILE})
-
-endforeach(PREFIX)
-
-nncc_find_package(TensorFlow QUIET)
-if(NOT TensorFlow_FOUND)
- message(STATUS "moco: Skip adding test as TensorFlow is not found")
- return()
-endif(NOT TensorFlow_FOUND)
-
-# This target enforces CMake to generate all the dependencies during "build" phase
-add_custom_target(moco_test_tf_deps ALL DEPENDS ${MOCO_TF_DEPS})
-
-# Run tests
-add_test(NAME moco_test_tf
- COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/runall.sh"
- $<TARGET_FILE:nnkit-run>
- $<TARGET_FILE:nnkit_tf_backend>
- $<TARGET_FILE:nnkit_moco_tf_backend>
- $<TARGET_FILE:nnkit_randomize_action>
- $<TARGET_FILE:nnkit_HDF5_export_action>
- $<TARGET_FILE:nnkit_HDF5_import_action>
- "${TEST_SPACE}"
- ${MOCO_TF_TESTS})
diff --git a/compiler/mocotest-tf/README.md b/compiler/mocotest-tf/README.md
deleted file mode 100644
index 8228daa91..000000000
--- a/compiler/mocotest-tf/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# mocotest-tf
diff --git a/compiler/mocotest-tf/runall.sh b/compiler/mocotest-tf/runall.sh
deleted file mode 100755
index 9aa233566..000000000
--- a/compiler/mocotest-tf/runall.sh
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/bin/bash
-
-if [[ $# -le 7 ]]; then
- echo "USAGE: $0 [nnkit-run path] [TF backend path] [moco TF backend path] [randomize action path] [HDF5 export action path] [HDF5 import action path] [WORKDIR] [Prefix1] [Prefix2] ..."
- exit 255
-fi
-
-NNKIT_RUN_PATH="$1"; shift
-TF_BACKEND_PATH="$1"; shift
-MOCO_TF_BACKEND_PATH="$1"; shift
-RANDOMIZE_ACTION_PATH="$1"; shift
-HDF5_EXPORT_ACTION_PATH="$1"; shift
-HDF5_IMPORT_ACTION_PATH="$1"; shift
-WORKDIR="$1"; shift
-
-echo "-- starting moco test tf"
-echo "-- Found nnkit-run: ${NNKIT_RUN_PATH}"
-echo "-- Found TF backend: ${TF_BACKEND_PATH}"
-echo "-- Found moco TF backend: ${MOCO_TF_BACKEND_PATH}"
-echo "-- Found randomize action: ${RANDOMIZE_ACTION_PATH}"
-echo "-- Found HDF5 export action: ${HDF5_EXPORT_ACTION_PATH}"
-echo "-- Found HDF5 import action: ${HDF5_IMPORT_ACTION_PATH}"
-echo "-- Found workdir: ${WORKDIR}"
-
-TESTED=()
-PASSED=()
-FAILED=()
-
-pushd "${WORKDIR}"
-while [[ $# -ne 0 ]]; do
- PREFIX="$1"; shift
-
- TESTED+=("${PREFIX}")
-
- PASSED_TAG="${PREFIX}.passed"
-
- rm -f "${PASSED_TAG}"
-
- cat > "${PREFIX}.log" <(
- exec 2>&1
-
- echo "-- Found pb: ${PREFIX}.pb"
-
- # Show commands
- set -x
- "${NNKIT_RUN_PATH}" \
- --backend "${TF_BACKEND_PATH}" \
- --backend-arg "${WORKDIR}/${PREFIX}.pb" \
- --backend-arg "${WORKDIR}/${PREFIX}.info" \
- --pre "${RANDOMIZE_ACTION_PATH}" \
- --pre "${HDF5_EXPORT_ACTION_PATH}" \
- --pre-arg "${WORKDIR}/${PREFIX}.input.h5" \
- --post "${HDF5_EXPORT_ACTION_PATH}" \
- --post-arg "${WORKDIR}/${PREFIX}.expected.h5"
-
- "${NNKIT_RUN_PATH}" \
- --backend "${MOCO_TF_BACKEND_PATH}" \
- --backend-arg "${WORKDIR}/${PREFIX}.pb" \
- --backend-arg "${WORKDIR}/${PREFIX}.info" \
- --pre "${HDF5_IMPORT_ACTION_PATH}" \
- --pre-arg "${WORKDIR}/${PREFIX}.input.h5" \
- --post "${HDF5_EXPORT_ACTION_PATH}" \
- --post-arg "${WORKDIR}/${PREFIX}.obtained.h5"
-
- h5diff -d 0.001 "${PREFIX}.expected.h5" "${PREFIX}.obtained.h5"
-
- if [[ $? -eq 0 ]]; then
- touch "${PASSED_TAG}"
- fi
- )
-
- if [[ -f "${PASSED_TAG}" ]]; then
- PASSED+=("$PREFIX")
- else
- FAILED+=("$PREFIX")
- 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/mocotest-tf/test.lst b/compiler/mocotest-tf/test.lst
deleted file mode 100644
index f0ae92c77..000000000
--- a/compiler/mocotest-tf/test.lst
+++ /dev/null
@@ -1,71 +0,0 @@
-add(NET_0000)
-add(NET_0001)
-add(NET_0002)
-add(NET_0003)
-add(NET_0004)
-add(NET_0005)
-add(NET_0006)
-add(NET_0007)
-add(NET_0008)
-add(NET_0009)
-add(NET_0010)
-add(NET_0011)
-add(NET_0012)
-add(NET_0013)
-add(NET_0014)
-add(NET_0015)
-add(NET_0016)
-add(NET_0017)
-add(NET_0018)
-add(NET_0019)
-add(NET_0020)
-add(NET_0021)
-add(NET_0022)
-add(NET_0023)
-add(NET_0024)
-add(NET_0025)
-add(NET_0026)
-add(REGRESSION_0000)
-add(UNIT_Add_000)
-add(UNIT_Add_001)
-add(UNIT_Add_002)
-add(UNIT_AvgPool_000)
-add(UNIT_AvgPool_001)
-add(UNIT_BiasAdd_000)
-add(UNIT_BiasAdd_001)
-add(UNIT_BiasAdd_002)
-add(UNIT_Concat_000)
-add(UNIT_Concat_001)
-add(UNIT_Concat_002)
-add(UNIT_Const_000)
-add(UNIT_Conv2D_000)
-add(UNIT_DepthwiseConv2D_000)
-add(UNIT_DepthwiseConv2D_001)
-add(UNIT_FusedBatchNorm_000)
-add(UNIT_FusedBatchNorm_001)
-add(UNIT_MaxPool_000)
-add(UNIT_MaxPool_001)
-add(UNIT_Mul_000)
-add(UNIT_Mul_001)
-add(UNIT_Mul_002)
-add(UNIT_Placeholder_000)
-add(UNIT_RealDiv_000)
-add(UNIT_Relu_000)
-add(UNIT_Relu6_000)
-add(UNIT_Reshape_000)
-add(UNIT_Rsqrt_000)
-add(UNIT_Softmax_000)
-add(UNIT_Softmax_001)
-add(UNIT_Softmax_002)
-add(UNIT_Softmax_003)
-add(UNIT_Sqrt_000)
-add(UNIT_SquaredDifference_000)
-add(UNIT_Squeeze_000)
-add(UNIT_Squeeze_001)
-add(UNIT_Squeeze_002)
-add(UNIT_Squeeze_003)
-add(UNIT_StopGradient_000)
-add(UNIT_StopGradient_001)
-add(UNIT_Sub_000)
-add(UNIT_Sub_001)
-add(UNIT_Tanh_000)
diff --git a/compiler/morph/CMakeLists.txt b/compiler/morph/CMakeLists.txt
index 36c254b78..ec7da8d30 100644
--- a/compiler/morph/CMakeLists.txt
+++ b/compiler/morph/CMakeLists.txt
@@ -8,7 +8,7 @@ target_include_directories(morph PUBLIC include)
target_link_libraries(morph PRIVATE nncc_common)
target_link_libraries(morph PUBLIC angkor)
-nncc_find_package(GTest QUIET)
+nnas_find_package(GTest QUIET)
if(NOT GTest_FOUND)
return()
diff --git a/compiler/nest/core/CMakeLists.txt b/compiler/nest/core/CMakeLists.txt
index b6754b081..b603f9ae9 100644
--- a/compiler/nest/core/CMakeLists.txt
+++ b/compiler/nest/core/CMakeLists.txt
@@ -15,7 +15,7 @@ foreach(EXAMPLE_FILE IN ITEMS ${EXAMPLE_FILES})
target_link_libraries(${TARGET_NAME} nest_core)
endforeach(EXAMPLE_FILE)
-nncc_find_package(GTest QUIET)
+nnas_find_package(GTest QUIET)
if(NOT GTest_FOUND)
return()
diff --git a/compiler/nest/core/src/Block.test.cpp b/compiler/nest/core/src/Block.test.cpp
index b40fbeaac..d8faa0bdb 100644
--- a/compiler/nest/core/src/Block.test.cpp
+++ b/compiler/nest/core/src/Block.test.cpp
@@ -30,12 +30,12 @@ TEST(BLOCK, use_case_1)
{
nest::Block block;
- ASSERT_EQ(block.size(), 0);
+ ASSERT_EQ(0, block.size());
auto stmt = std::make_shared<DummyNode>();
block.append(stmt);
- ASSERT_EQ(block.size(), 1);
- ASSERT_EQ(block.at(0), stmt);
+ ASSERT_EQ(1, block.size());
+ ASSERT_EQ(stmt, block.at(0));
}
diff --git a/compiler/nest/core/src/Bound.test.cpp b/compiler/nest/core/src/Bound.test.cpp
index 7b2f0b62e..a4c3d4d38 100644
--- a/compiler/nest/core/src/Bound.test.cpp
+++ b/compiler/nest/core/src/Bound.test.cpp
@@ -22,6 +22,6 @@ TEST(BOUND, ctor)
{
const nest::Bound b{-10, 20};
- ASSERT_EQ(b.min(), -10);
- ASSERT_EQ(b.max(), 20);
+ ASSERT_EQ(-10, b.min());
+ ASSERT_EQ(20, b.max());
}
diff --git a/compiler/nest/core/src/Closure.test.cpp b/compiler/nest/core/src/Closure.test.cpp
index 1dae849a3..495e2186a 100644
--- a/compiler/nest/core/src/Closure.test.cpp
+++ b/compiler/nest/core/src/Closure.test.cpp
@@ -30,8 +30,8 @@ TEST(Closure, ctor)
nest::DomainID dom_id{0};
nest::Closure closure{dom_id, std::make_shared<DummyNode>()};
- ASSERT_EQ(closure.id().value(), 0);
- ASSERT_EQ(closure.sub().rank(), 1);
+ ASSERT_EQ(0, closure.id().value());
+ ASSERT_EQ(1, closure.sub().rank());
}
TEST(Closure, cast)
diff --git a/compiler/nest/core/src/Domain.test.cpp b/compiler/nest/core/src/Domain.test.cpp
index 5f973ecf7..8d1845905 100644
--- a/compiler/nest/core/src/Domain.test.cpp
+++ b/compiler/nest/core/src/Domain.test.cpp
@@ -36,6 +36,6 @@ TEST(_DOMAIN, base_usecase)
nest::Closure clo = dom(std::make_shared<::expr::DummyNode>());
- ASSERT_EQ(clo.id(), dom_id);
- ASSERT_EQ(clo.sub().rank(), 1);
+ ASSERT_EQ(dom_id, clo.id());
+ ASSERT_EQ(1, clo.sub().rank());
}
diff --git a/compiler/nest/core/src/DomainContext.test.cpp b/compiler/nest/core/src/DomainContext.test.cpp
index 10882df70..cc553eaa0 100644
--- a/compiler/nest/core/src/DomainContext.test.cpp
+++ b/compiler/nest/core/src/DomainContext.test.cpp
@@ -24,30 +24,30 @@ TEST(DOMAIN_CONTEXT, usecase)
auto dom_0 = ctx.make({1, 3, 4});
- ASSERT_EQ(ctx.count(), 1);
+ ASSERT_EQ(1, ctx.count());
auto check_dom_0 = [&](void) {
- ASSERT_EQ(ctx.info(dom_0).rank(), 3);
- ASSERT_EQ(ctx.info(dom_0).dim(0), 1);
- ASSERT_EQ(ctx.info(dom_0).dim(1), 3);
- ASSERT_EQ(ctx.info(dom_0).dim(2), 4);
+ ASSERT_EQ(3, ctx.info(dom_0).rank());
+ ASSERT_EQ(1, ctx.info(dom_0).dim(0));
+ ASSERT_EQ(3, ctx.info(dom_0).dim(1));
+ ASSERT_EQ(4, ctx.info(dom_0).dim(2));
};
check_dom_0();
auto dom_1 = ctx.make({7, 6, 2, 1});
- ASSERT_EQ(ctx.count(), 2);
+ ASSERT_EQ(2, ctx.count());
// Domain ID should be unique for each domain
ASSERT_FALSE(dom_0.id() == dom_1.id());
auto check_dom_1 = [&](void) {
- ASSERT_EQ(ctx.info(dom_1).rank(), 4);
- ASSERT_EQ(ctx.info(dom_1).dim(0), 7);
- ASSERT_EQ(ctx.info(dom_1).dim(1), 6);
- ASSERT_EQ(ctx.info(dom_1).dim(2), 2);
- ASSERT_EQ(ctx.info(dom_1).dim(3), 1);
+ ASSERT_EQ(4, ctx.info(dom_1).rank());
+ ASSERT_EQ(7, ctx.info(dom_1).dim(0));
+ ASSERT_EQ(6, ctx.info(dom_1).dim(1));
+ ASSERT_EQ(2, ctx.info(dom_1).dim(2));
+ ASSERT_EQ(1, ctx.info(dom_1).dim(3));
};
// make() SHOULD NOT affect the existing domain information
diff --git a/compiler/nest/core/src/DomainID.test.cpp b/compiler/nest/core/src/DomainID.test.cpp
index 6b1ce8360..3a7705025 100644
--- a/compiler/nest/core/src/DomainID.test.cpp
+++ b/compiler/nest/core/src/DomainID.test.cpp
@@ -22,7 +22,7 @@ TEST(DOMAIN_ID, ctor)
{
nest::DomainID id{0};
- ASSERT_EQ(id.value(), 0);
+ ASSERT_EQ(0, id.value());
}
TEST(DOMAIN_ID, operator_eq)
diff --git a/compiler/nest/core/src/DomainInfo.test.cpp b/compiler/nest/core/src/DomainInfo.test.cpp
index 7a5d81144..ddee683a4 100644
--- a/compiler/nest/core/src/DomainInfo.test.cpp
+++ b/compiler/nest/core/src/DomainInfo.test.cpp
@@ -22,9 +22,9 @@ TEST(DOMAIN_INFO, ctor)
{
nest::DomainInfo info{1, 2, 3, 4};
- ASSERT_EQ(info.rank(), 4);
- ASSERT_EQ(info.dim(0), 1);
- ASSERT_EQ(info.dim(1), 2);
- ASSERT_EQ(info.dim(2), 3);
- ASSERT_EQ(info.dim(3), 4);
+ ASSERT_EQ(4, info.rank());
+ ASSERT_EQ(1, info.dim(0));
+ ASSERT_EQ(2, info.dim(1));
+ ASSERT_EQ(3, info.dim(2));
+ ASSERT_EQ(4, info.dim(3));
}
diff --git a/compiler/nest/core/src/Expr.test.cpp b/compiler/nest/core/src/Expr.test.cpp
index 0c96f7714..2e26c234a 100644
--- a/compiler/nest/core/src/Expr.test.cpp
+++ b/compiler/nest/core/src/Expr.test.cpp
@@ -38,8 +38,8 @@ TEST(EXPR, operator_sum)
auto add = expr->asAdd();
- ASSERT_EQ(add->lhs().get(), left.get());
- ASSERT_EQ(add->rhs().get(), right.get());
+ ASSERT_EQ(left.get(), add->lhs().get());
+ ASSERT_EQ(right.get(), add->rhs().get());
}
TEST(EXPR, operator_mul)
@@ -53,6 +53,6 @@ TEST(EXPR, operator_mul)
auto add = expr->asMul();
- ASSERT_EQ(add->lhs().get(), left.get());
- ASSERT_EQ(add->rhs().get(), right.get());
+ ASSERT_EQ(left.get(), add->lhs().get());
+ ASSERT_EQ(right.get(), add->rhs().get());
}
diff --git a/compiler/nest/core/src/FV.test.cpp b/compiler/nest/core/src/FV.test.cpp
index 55f5f5877..8bb061cc9 100644
--- a/compiler/nest/core/src/FV.test.cpp
+++ b/compiler/nest/core/src/FV.test.cpp
@@ -27,7 +27,7 @@ TEST(FV, var_expr)
auto fvs = nest::FV::in(var);
- ASSERT_EQ(fvs.size(), 1);
+ ASSERT_EQ(1, fvs.size());
ASSERT_NE(fvs.find(var.id()), fvs.end());
}
@@ -40,7 +40,7 @@ TEST(FV, deref_expr)
auto fvs = nest::FV::in(dom(var));
- ASSERT_EQ(fvs.size(), 1);
+ ASSERT_EQ(1, fvs.size());
ASSERT_NE(fvs.find(var.id()), fvs.end());
}
@@ -53,7 +53,7 @@ TEST(FV, add_expr)
auto fvs = nest::FV::in(v_0 + v_1);
- ASSERT_EQ(fvs.size(), 2);
+ ASSERT_EQ(2, fvs.size());
ASSERT_NE(fvs.find(v_0.id()), fvs.end());
ASSERT_NE(fvs.find(v_1.id()), fvs.end());
}
@@ -69,7 +69,7 @@ TEST(FV, mul_expr)
auto fvs = nest::FV::in(v_0 * v_1);
- ASSERT_EQ(fvs.size(), 2);
+ ASSERT_EQ(2, fvs.size());
ASSERT_NE(fvs.find(v_0.id()), fvs.end());
ASSERT_NE(fvs.find(v_1.id()), fvs.end());
}
diff --git a/compiler/nest/core/src/Level.test.cpp b/compiler/nest/core/src/Level.test.cpp
index b9e203d9d..cc447e8d2 100644
--- a/compiler/nest/core/src/Level.test.cpp
+++ b/compiler/nest/core/src/Level.test.cpp
@@ -22,7 +22,7 @@ TEST(LEVEL, constructor)
{
nest::Level lv{3};
- ASSERT_EQ(lv.value(), 3);
+ ASSERT_EQ(3, lv.value());
}
TEST(LEVEL, operator_eq)
diff --git a/compiler/nest/core/src/Module.test.cpp b/compiler/nest/core/src/Module.test.cpp
index 01e414d25..70a6c473c 100644
--- a/compiler/nest/core/src/Module.test.cpp
+++ b/compiler/nest/core/src/Module.test.cpp
@@ -29,7 +29,7 @@ TEST(MODULE, create_var)
auto check = [](const nest::Module &m) {
// This code will invoke 'const VarContext &var(void) const' method
- ASSERT_EQ(m.var().count(), 1);
+ ASSERT_EQ(1, m.var().count());
};
create(m);
@@ -47,7 +47,7 @@ TEST(MODULE, create_domain)
auto check = [](const nest::Module &m) {
// This code will invoke 'const DomainContext &domain(void) const' method
- ASSERT_EQ(m.domain().count(), 1);
+ ASSERT_EQ(1, m.domain().count());
};
create(m, {1, 3, 3});
@@ -66,7 +66,7 @@ TEST(MODULE, push)
m.push(ifm(var_ch, var_row, var_col));
- ASSERT_EQ(m.block().size(), 1);
+ ASSERT_EQ(1, m.block().size());
ASSERT_NE(m.block().at(0)->asPush(), nullptr);
}
@@ -82,8 +82,8 @@ TEST(MODULE, ret)
m.push(ifm(ind));
m.ret(ofm(ind));
- ASSERT_EQ(m.ret().id(), ofm.id());
- ASSERT_EQ(m.ret().sub().rank(), 1);
+ ASSERT_EQ(ofm.id(), m.ret().id());
+ ASSERT_EQ(1, m.ret().sub().rank());
}
TEST(MODULE, copy)
@@ -95,5 +95,5 @@ TEST(MODULE, copy)
orig.var().make();
- ASSERT_EQ(copy.var().count(), 0);
+ ASSERT_EQ(0, copy.var().count());
}
diff --git a/compiler/nest/core/src/Ret.test.cpp b/compiler/nest/core/src/Ret.test.cpp
index 703f04901..a85223578 100644
--- a/compiler/nest/core/src/Ret.test.cpp
+++ b/compiler/nest/core/src/Ret.test.cpp
@@ -32,8 +32,8 @@ TEST(RET, ctor)
nest::Ret ret{dom_id, sub};
- ASSERT_EQ(ret.id().value(), 0);
- ASSERT_EQ(ret.sub().rank(), 1);
+ ASSERT_EQ(0, ret.id().value());
+ ASSERT_EQ(1, ret.sub().rank());
}
TEST(RET, copy)
@@ -48,11 +48,11 @@ TEST(RET, copy)
nest::Ret dst{dst_id, dst_sub};
- ASSERT_EQ(dst.id().value(), 1);
- ASSERT_EQ(dst.sub().rank(), 2);
+ ASSERT_EQ(1, dst.id().value());
+ ASSERT_EQ(2, dst.sub().rank());
dst = src;
- ASSERT_EQ(dst.id().value(), 0);
- ASSERT_EQ(dst.sub().rank(), 1);
+ ASSERT_EQ(0, dst.id().value());
+ ASSERT_EQ(1, dst.sub().rank());
}
diff --git a/compiler/nest/core/src/Schedule.test.cpp b/compiler/nest/core/src/Schedule.test.cpp
index 8f0ddb23c..7994d7abe 100644
--- a/compiler/nest/core/src/Schedule.test.cpp
+++ b/compiler/nest/core/src/Schedule.test.cpp
@@ -26,7 +26,7 @@ TEST(SCHEDULE, module)
nest::Schedule sch{m};
- ASSERT_EQ(sch.level(var_1).value(), 0);
+ ASSERT_EQ(0, sch.level(var_1).value());
}
TEST(SCHEDULE, module_copy)
@@ -40,5 +40,5 @@ TEST(SCHEDULE, module_copy)
// Update on 'm' does not affect the schedule
m.var().make();
- ASSERT_EQ(sch.var().count(), 1);
+ ASSERT_EQ(1, sch.var().count());
}
diff --git a/compiler/nest/core/src/Var.test.cpp b/compiler/nest/core/src/Var.test.cpp
index 29f879558..aea8c5dde 100644
--- a/compiler/nest/core/src/Var.test.cpp
+++ b/compiler/nest/core/src/Var.test.cpp
@@ -23,7 +23,7 @@ TEST(VAR, ctor)
nest::VarID id{0};
nest::Var var{id};
- ASSERT_EQ(var.id(), id);
+ ASSERT_EQ(id, var.id());
}
TEST(VAR, cast)
@@ -34,5 +34,5 @@ TEST(VAR, cast)
nest::Expr expr = var;
ASSERT_NE(expr->asVar(), nullptr);
- ASSERT_EQ(expr->asVar()->id(), id);
+ ASSERT_EQ(id, expr->asVar()->id());
}
diff --git a/compiler/nest/core/src/VarContext.test.cpp b/compiler/nest/core/src/VarContext.test.cpp
index 169bd6126..d953f2f6c 100644
--- a/compiler/nest/core/src/VarContext.test.cpp
+++ b/compiler/nest/core/src/VarContext.test.cpp
@@ -32,15 +32,15 @@ TEST(VAR_CONTEXT, count)
{
nest::VarContext ctx;
- ASSERT_EQ(ctx.count(), 0);
+ ASSERT_EQ(0, ctx.count());
auto var_0 = ctx.make();
- ASSERT_EQ(ctx.count(), 1);
+ ASSERT_EQ(1, ctx.count());
auto var_1 = ctx.make();
- ASSERT_EQ(ctx.count(), 2);
+ ASSERT_EQ(2, ctx.count());
}
TEST(VAR_CONTEXT, bound_one)
@@ -49,13 +49,13 @@ TEST(VAR_CONTEXT, bound_one)
auto var_0 = ctx.make();
- ASSERT_EQ(ctx.bound(var_0).min(), 0);
- ASSERT_EQ(ctx.bound(var_0).max(), 0);
+ ASSERT_EQ(0, ctx.bound(var_0).min());
+ ASSERT_EQ(0, ctx.bound(var_0).max());
ctx.bound(var_0) = nest::Bound{-3, 5};
- ASSERT_EQ(ctx.bound(var_0).min(), -3);
- ASSERT_EQ(ctx.bound(var_0).max(), 5);
+ ASSERT_EQ(-3, ctx.bound(var_0).min());
+ ASSERT_EQ(5, ctx.bound(var_0).max());
}
TEST(VAR_CONTEXT, bound_independent)
@@ -64,19 +64,19 @@ TEST(VAR_CONTEXT, bound_independent)
auto var_0 = ctx.make();
- ASSERT_EQ(ctx.bound(var_0).min(), 0);
- ASSERT_EQ(ctx.bound(var_0).max(), 0);
+ ASSERT_EQ(0, ctx.bound(var_0).min());
+ ASSERT_EQ(0, ctx.bound(var_0).max());
auto var_1 = ctx.make();
- ASSERT_EQ(ctx.bound(var_1).min(), 0);
- ASSERT_EQ(ctx.bound(var_1).max(), 0);
+ ASSERT_EQ(0, ctx.bound(var_1).min());
+ ASSERT_EQ(0, ctx.bound(var_1).max());
ctx.bound(var_0) = nest::Bound{-3, 5};
- ASSERT_EQ(ctx.bound(var_0).min(), -3);
- ASSERT_EQ(ctx.bound(var_0).max(), 5);
+ ASSERT_EQ(-3, ctx.bound(var_0).min());
+ ASSERT_EQ(5, ctx.bound(var_0).max());
- ASSERT_EQ(ctx.bound(var_1).min(), 0);
- ASSERT_EQ(ctx.bound(var_1).max(), 0);
+ ASSERT_EQ(0, ctx.bound(var_1).min());
+ ASSERT_EQ(0, ctx.bound(var_1).max());
}
diff --git a/compiler/nest/core/src/VarID.test.cpp b/compiler/nest/core/src/VarID.test.cpp
index e4a17a5c1..05e4b9aa9 100644
--- a/compiler/nest/core/src/VarID.test.cpp
+++ b/compiler/nest/core/src/VarID.test.cpp
@@ -22,7 +22,7 @@ TEST(VAR_ID, ctor)
{
nest::VarID id{0};
- ASSERT_EQ(id.value(), 0);
+ ASSERT_EQ(0, id.value());
}
TEST(VAR_ID, operator_eq)
diff --git a/compiler/nest/core/src/expr/AddNode.test.cpp b/compiler/nest/core/src/expr/AddNode.test.cpp
index 5c44c4743..dba6cc826 100644
--- a/compiler/nest/core/src/expr/AddNode.test.cpp
+++ b/compiler/nest/core/src/expr/AddNode.test.cpp
@@ -36,8 +36,8 @@ TEST(ADD_NODE, cast)
std::shared_ptr<nest::expr::Node> base = derived;
ASSERT_NE(derived.get(), nullptr);
- ASSERT_EQ(base->asAdd(), derived.get());
+ ASSERT_EQ(derived.get(), base->asAdd());
- ASSERT_EQ(derived->lhs().get(), left.get());
- ASSERT_EQ(derived->rhs().get(), right.get());
+ ASSERT_EQ(left.get(), derived->lhs().get());
+ ASSERT_EQ(right.get(), derived->rhs().get());
}
diff --git a/compiler/nest/core/src/expr/DerefNode.test.cpp b/compiler/nest/core/src/expr/DerefNode.test.cpp
index e02a7de0b..125d8bf1e 100644
--- a/compiler/nest/core/src/expr/DerefNode.test.cpp
+++ b/compiler/nest/core/src/expr/DerefNode.test.cpp
@@ -35,5 +35,5 @@ TEST(DEREF_NODE, cast)
std::shared_ptr<nest::expr::Node> base = derived;
ASSERT_NE(derived.get(), nullptr);
- ASSERT_EQ(base->asDeref(), derived.get());
+ ASSERT_EQ(derived.get(), base->asDeref());
}
diff --git a/compiler/nest/core/src/expr/MulNode.test.cpp b/compiler/nest/core/src/expr/MulNode.test.cpp
index b2d29471c..85cb5a56e 100644
--- a/compiler/nest/core/src/expr/MulNode.test.cpp
+++ b/compiler/nest/core/src/expr/MulNode.test.cpp
@@ -36,8 +36,8 @@ TEST(MUL_NODE, cast)
std::shared_ptr<nest::expr::Node> base = derived;
ASSERT_NE(derived.get(), nullptr);
- ASSERT_EQ(base->asMul(), derived.get());
+ ASSERT_EQ(derived.get(), base->asMul());
- ASSERT_EQ(derived->lhs().get(), left.get());
- ASSERT_EQ(derived->rhs().get(), right.get());
+ ASSERT_EQ(left.get(), derived->lhs().get());
+ ASSERT_EQ(right.get(), derived->rhs().get());
}
diff --git a/compiler/nest/core/src/expr/Subscript.test.cpp b/compiler/nest/core/src/expr/Subscript.test.cpp
index 2f187b86c..cfcdd4473 100644
--- a/compiler/nest/core/src/expr/Subscript.test.cpp
+++ b/compiler/nest/core/src/expr/Subscript.test.cpp
@@ -31,7 +31,7 @@ TEST(SUBSCRIPT, ctor)
nest::expr::Subscript sub{expr_0, expr_1};
- ASSERT_EQ(sub.rank(), 2);
- ASSERT_EQ(sub.at(0), expr_0);
- ASSERT_EQ(sub.at(1), expr_1);
+ ASSERT_EQ(2, sub.rank());
+ ASSERT_EQ(expr_0, sub.at(0));
+ ASSERT_EQ(expr_1, sub.at(1));
}
diff --git a/compiler/nest/core/src/expr/VarNode.test.cpp b/compiler/nest/core/src/expr/VarNode.test.cpp
index e8b2764e4..9400551b5 100644
--- a/compiler/nest/core/src/expr/VarNode.test.cpp
+++ b/compiler/nest/core/src/expr/VarNode.test.cpp
@@ -31,7 +31,7 @@ TEST(VAR_NODE, ctor)
auto node = make(4);
// NOTE 'id' should be copied
- ASSERT_EQ(node->id().value(), 4);
+ ASSERT_EQ(4, node->id().value());
}
TEST(VAR_NODE, cast)
@@ -43,5 +43,5 @@ TEST(VAR_NODE, cast)
// NOTE Cast method should be overrided
ASSERT_NE(derived.get(), nullptr);
- ASSERT_EQ(base->asVar(), derived.get());
+ ASSERT_EQ(derived.get(), base->asVar());
}
diff --git a/compiler/nest/core/src/stmt/PushNode.test.cpp b/compiler/nest/core/src/stmt/PushNode.test.cpp
index a54efbb54..c02c69220 100644
--- a/compiler/nest/core/src/stmt/PushNode.test.cpp
+++ b/compiler/nest/core/src/stmt/PushNode.test.cpp
@@ -33,5 +33,5 @@ TEST(STMT_PUSH_NODE, cast)
std::shared_ptr<nest::stmt::Node> base = derived;
ASSERT_NE(derived.get(), nullptr);
- ASSERT_EQ(base->asPush(), derived.get());
+ ASSERT_EQ(derived.get(), base->asPush());
}
diff --git a/compiler/nike/CMakeLists.txt b/compiler/nike/CMakeLists.txt
index 57feffa41..737c73b8f 100644
--- a/compiler/nike/CMakeLists.txt
+++ b/compiler/nike/CMakeLists.txt
@@ -5,13 +5,11 @@ list(REMOVE_ITEM SOURCES ${TESTS})
add_library(nike STATIC ${SOURCES})
target_include_directories(nike PUBLIC include)
-nncc_find_package(GTest QUIET)
+nnas_find_package(GTest QUIET)
if(NOT GTest_FOUND)
return()
endif(NOT GTest_FOUND)
-add_executable(nike_test ${TESTS})
+GTest_AddTest(nike_test ${TESTS})
target_link_libraries(nike_test nike)
-target_link_libraries(nike_test gtest_main)
-add_test(nike_test nike_test)
diff --git a/compiler/nnc/CMakeLists.txt b/compiler/nnc/CMakeLists.txt
index e2e7578e8..ab91cd4b6 100644
--- a/compiler/nnc/CMakeLists.txt
+++ b/compiler/nnc/CMakeLists.txt
@@ -13,7 +13,7 @@ configure_file(${NNC_ROOT_SRC_DIR}/include/Definitions.h.in
# add interface header files
# target for compiler executable
-add_executable(${NNC_TARGET_EXECUTABLE} ${NNC_DRIVER_DIR}/main.cpp ${NNC_DRIVER_DIR}/Driver.cpp ${OPTIONS_SRC})
+add_executable(${NNC_TARGET_EXECUTABLE} ${NNC_DRIVER_DIR}/main.cpp ${NNC_DRIVER_DIR}/Driver.cpp ${NNC_DRIVER_DIR}/Options.cpp)
# install compiler
nnc_install_executable(${NNC_TARGET_EXECUTABLE})
diff --git a/compiler/nnc/backends/acl_soft_backend/AclCppGenerator.cpp b/compiler/nnc/backends/acl_soft_backend/AclCppGenerator.cpp
index 65b67f60b..3a5b9ecaf 100644
--- a/compiler/nnc/backends/acl_soft_backend/AclCppGenerator.cpp
+++ b/compiler/nnc/backends/acl_soft_backend/AclCppGenerator.cpp
@@ -17,16 +17,22 @@
#include "backends/acl_soft_backend/AclCppGenerator.h"
#include "AclCppOpGenerator.h"
#include "backends/acl_soft_backend/AclCppException.h"
-#include "option/Options.h"
-#include <fstream>
+#include <boost/filesystem.hpp>
-#include <sys/stat.h>
+#include <fstream>
+#include <utility>
namespace nnc
{
using namespace std;
+namespace fs = boost::filesystem;
+
+AclCppCodeGenerator::AclCppCodeGenerator(string output_dir, string artifact_name)
+ : _output_dir(std::move(output_dir)), _artifact_name(std::move(artifact_name))
+{
+}
void AclCppCodeGenerator::run(mir::Graph *data)
{
@@ -34,13 +40,9 @@ void AclCppCodeGenerator::run(mir::Graph *data)
assert(g);
// Create a directory for generated artifact files.
- int res = mkdir(cli::artifactDir.c_str(),
- S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); // NOLINT(hicpp-signed-bitwise)
-
- if (res != 0 && errno != EEXIST)
- throw AclCppException("Failed to create output directory");
+ fs::create_directory(_output_dir);
- string base_path = cli::artifactDir + "/" + cli::artifactName;
+ string base_path = _output_dir + "/" + _artifact_name;
string code_path = base_path + ".cpp";
string decl_path = base_path + ".h";
string par_path = base_path + ".par";
@@ -65,7 +67,7 @@ void AclCppCodeGenerator::run(mir::Graph *data)
ArtifactGeneratorCppDecl decl_gen(decl_out);
// Generate the artifact.
- AclCppOpGenerator op_generator(cli::artifactName, par_out);
+ AclCppOpGenerator op_generator(_artifact_name, par_out);
auto dom = op_generator.generate(g);
dom.accept(&code_gen);
dom.accept(&decl_gen);
diff --git a/compiler/nnc/backends/acl_soft_backend/AclCppOpGenerator.cpp b/compiler/nnc/backends/acl_soft_backend/AclCppOpGenerator.cpp
index e3e45db94..b5e3734ae 100644
--- a/compiler/nnc/backends/acl_soft_backend/AclCppOpGenerator.cpp
+++ b/compiler/nnc/backends/acl_soft_backend/AclCppOpGenerator.cpp
@@ -18,13 +18,13 @@
#include "backends/acl_soft_backend/AclCppException.h"
#include "mir/ShapeRange.h"
#include "mir/TensorUtil.h"
-#include "option/Options.h"
#include "mir/Tensor.h"
#include "mir/Operation.h"
#include "mir/OpDefs.h"
#include <algorithm>
+#include <map>
namespace nnc
{
@@ -59,7 +59,7 @@ const ArtifactModule &AclCppOpGenerator::generate(mir::Graph *g)
// Input parameter stream preparation.
_parInVar = _artifactClass->var(false, "std::ifstream", "_parIn");
_parIn = _parInVar->use();
- string par_file_name = cli::artifactName + ".par";
+ string par_file_name = _module.name() + ".par";
_constrBlock->call("open", {AF::lit("\"" + par_file_name + "\""),
AF::lit("std::ios_base::in | std::ios_base::binary")},
_parIn);
@@ -102,8 +102,8 @@ void AclCppOpGenerator::visit(ops::ConcatOp &op)
auto inputs_var = _constrBlock->var("std::vector<arm_compute::ICLTensor*>", prefix + "_inputs");
auto inputs = inputs_var->use();
- for (const auto &ir_input : ir_inputs)
- _constrBlock->call("push_back", {AF::ref(AF::id(tensorName(ir_input.getProducer())))}, inputs);
+ for (const Operation::Output *ir_input : ir_inputs)
+ _constrBlock->call("push_back", {AF::ref(AF::id(tensorName(ir_input)))}, inputs);
auto layer = genLayer("arm_compute::CLConcatenateLayer", prefix,
{inputs, AF::ref(out), AF::lit(axis_name)});
@@ -114,6 +114,7 @@ void AclCppOpGenerator::visit(ops::ConcatOp &op)
void AclCppOpGenerator::visit(ops::Conv2DOp &op)
{
+ assert(op.getNumGroups() == 1);
genConvolution(op, "arm_compute::CLConvolutionLayer", "_convolution_layer");
}
@@ -125,7 +126,7 @@ void AclCppOpGenerator::visit(ops::DepthwiseConv2DOp &op)
void AclCppOpGenerator::visit(ops::SoftmaxOp &op)
{
assert(op.getNumInputs() == 1);
- const auto *ir_input = op.getInput(0)->getProducer();
+ const auto *ir_input = op.getInput(0);
const auto *ir_output = op.getOutput(0);
auto in = AF::id(tensorName(ir_input));
@@ -262,8 +263,8 @@ void AclCppOpGenerator::visit(ops::MaxPool2DOp &op)
void AclCppOpGenerator::visit(ops::FullyConnectedOp &op)
{
assert(op.getNumInputs() == 2);
- const auto *ir_input = op.getInput(0)->getProducer();
- const auto *ir_weights = op.getInput(1)->getProducer();
+ const auto *ir_input = op.getInput(0);
+ const auto *ir_weights = op.getInput(1);
const auto *ir_output = op.getOutput(0);
auto ir_weights_op = dynamic_cast<const mir::ops::ConstantOp *>(ir_weights->getNode());
@@ -317,17 +318,16 @@ static bool shouldSerializeConstant(const ops::ConstantOp &op)
static std::map<Operation::Type, std::size_t> self_serializing_ops_to_inputs{
{Operation::Type::conv2D, 1}, {Operation::Type::fullyConnected, 1}};
- for (const auto *consumer : op.getOutput(0)->getConsumers())
+ for (Operation::Use use : op.getOutput(0)->getUses())
{
- auto self_serializing_op_it =
- self_serializing_ops_to_inputs.find(consumer->getNode()->getType());
+ auto self_serializing_op_it = self_serializing_ops_to_inputs.find(use.getNode()->getType());
// Serialize if next_node type not from 'self_serializing_ops_to_inputs'
if (self_serializing_op_it == self_serializing_ops_to_inputs.end())
return true;
// If next_node has current ConstantOp as it's previous node, but not with appropriate index -
// serialize current ConstantOp
- if (self_serializing_op_it->second != consumer->getIndex())
+ if (self_serializing_op_it->second != use.getIndex())
return true;
}
@@ -350,7 +350,7 @@ void AclCppOpGenerator::visit(ops::ReluOp &op) { genActivation(op, "RELU"); }
void AclCppOpGenerator::visit(ops::ReshapeOp &op)
{
assert(op.getNumInputs() == 1);
- const auto *ir_input = op.getInput(0)->getProducer();
+ const auto *ir_input = op.getInput(0);
const auto *ir_output = op.getOutput(0);
// Get the id of the input tensor in the generated artifact.
@@ -401,7 +401,7 @@ void AclCppOpGenerator::visit(ops::EluOp & /*op*/)
void AclCppOpGenerator::visit(ops::PadOp &op)
{
assert(op.getNumInputs() == 1);
- const auto *ir_input = op.getInput(0)->getProducer();
+ const auto *ir_input = op.getInput(0);
const auto *ir_output = op.getOutput(0);
// Get the id of the input tensor.
@@ -438,7 +438,7 @@ template <typename Op>
void AclCppOpGenerator::genPooling(Op &op, const std::string &pooling_type, bool exclude_padding)
{
assert(op.getNumInputs() == 1);
- const auto *ir_input = op.getInput(0)->getProducer();
+ const auto *ir_input = op.getInput(0);
const auto *ir_output = op.getOutput(0);
string in_name = tensorName(ir_input);
@@ -492,8 +492,8 @@ void AclCppOpGenerator::genPooling(Op &op, const std::string &pooling_type, bool
template <typename Op>
void AclCppOpGenerator::genConvolution(Op &op, const string &acl_func_name, const string &suffix)
{
- const auto *ir_input = op.getInput(0)->getProducer();
- const auto *ir_weights = op.getInput(1)->getProducer();
+ const auto *ir_input = op.getInput(0);
+ const auto *ir_weights = op.getInput(1);
const auto *ir_output = op.getOutput(0);
auto ir_weights_op = dynamic_cast<const ops::ConstantOp *>(ir_weights->getNode());
@@ -574,7 +574,7 @@ void AclCppOpGenerator::genActivation(const Operation &op, const std::string &ac
float a, float b)
{
assert(op.getNumInputs() == 1);
- const auto *ir_input = op.getInput(0)->getProducer();
+ const auto *ir_input = op.getInput(0);
const auto *ir_output = op.getOutput(0);
// Get the id of the input tensor.
@@ -777,7 +777,7 @@ void AclCppOpGenerator::genNamed(Graph *graph)
const auto *output_op = outputs[0];
auto f = _artifactClass->func(true, "arm_compute::CLTensor&", "getOutput");
auto b = f->getBlock();
- auto id = AF::id(tensorName(output_op->getInput(0)->getProducer()));
+ auto id = AF::id(tensorName(output_op->getInput(0)));
b->ret(id);
}
}
@@ -919,7 +919,7 @@ void AclCppOpGenerator::genTranspose(const std::shared_ptr<nnc::ArtifactId> &inp
void AclCppOpGenerator::visit(mir::ops::TransposeOp &op)
{
assert(op.getNumInputs() == 1);
- const auto *ir_input = op.getInput(0)->getProducer();
+ const auto *ir_input = op.getInput(0);
const auto *ir_output = op.getOutput(0);
// Get the input node tensor id in the DOM.
@@ -956,8 +956,8 @@ void AclCppOpGenerator::visit(mir::ops::OutputOp & /*op*/)
void AclCppOpGenerator::visit(mir::ops::AddOp &op)
{
assert(op.getNumInputs() == 2);
- const auto *ir_lhs = op.getInput(0)->getProducer();
- const auto *ir_rhs = op.getInput(1)->getProducer();
+ const auto *ir_lhs = op.getInput(0);
+ const auto *ir_rhs = op.getInput(1);
const auto *ir_output = op.getOutput(0);
// Create the output tensor in the DOM and obtain its identifier.
@@ -978,8 +978,8 @@ void AclCppOpGenerator::visit(mir::ops::MaxOp &) { throw AclCppException("NYI");
void AclCppOpGenerator::visit(mir::ops::MulOp &op)
{
assert(op.getNumInputs() == 2);
- const auto *ir_lhs = op.getInput(0)->getProducer();
- const auto *ir_rhs = op.getInput(1)->getProducer();
+ const auto *ir_lhs = op.getInput(0);
+ const auto *ir_rhs = op.getInput(1);
const auto *ir_output = op.getOutput(0);
// Create the output tensor in the DOM and obtain its identifier.
diff --git a/compiler/nnc/backends/acl_soft_backend/CMakeLists.txt b/compiler/nnc/backends/acl_soft_backend/CMakeLists.txt
index dbaeaf8cc..8f55303b1 100644
--- a/compiler/nnc/backends/acl_soft_backend/CMakeLists.txt
+++ b/compiler/nnc/backends/acl_soft_backend/CMakeLists.txt
@@ -1,3 +1,5 @@
+nnas_find_package(Boost REQUIRED COMPONENTS filesystem)
+
set(ACL_SOFT_BACKEND_CPP_SOURCES AclCppGenerator.cpp AclCppOpGenerator.cpp
ArtifactGeneratorCppCode.cpp ArtifactGeneratorCppDecl.cpp ArtifactModel.cpp)
@@ -5,9 +7,8 @@ file(GLOB_RECURSE ACL_IN_SOURCES "*.in")
nnc_make_generated_sources("${ACL_IN_SOURCES}" ${CMAKE_CURRENT_BINARY_DIR} ACL_GENERATED_SOURCES)
nnc_add_library(acl_soft_backend_cpp SHARED ${ACL_SOFT_BACKEND_CPP_SOURCES} ${ACL_GENERATED_SOURCES})
-target_include_directories(acl_soft_backend_cpp PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
-target_link_libraries(acl_soft_backend_cpp PRIVATE nnc_support)
-target_link_libraries(acl_soft_backend_cpp PRIVATE mir)
+target_include_directories(acl_soft_backend_cpp PRIVATE ${Boost_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR})
+target_link_libraries(acl_soft_backend_cpp PRIVATE mir ${Boost_LIBRARIES})
# install soft backend c++ library
nnc_install_library(acl_soft_backend_cpp)
diff --git a/compiler/nnc/backends/interpreter/CMakeLists.txt b/compiler/nnc/backends/interpreter/CMakeLists.txt
index 0426fdd89..9c016b494 100644
--- a/compiler/nnc/backends/interpreter/CMakeLists.txt
+++ b/compiler/nnc/backends/interpreter/CMakeLists.txt
@@ -1,6 +1,6 @@
-file(GLOB_RECURSE interp_src ./*.cpp ./*.h)
+set(interp_src InterpreterBackend.cpp)
nnc_add_library(nnc_interpreter SHARED ${interp_src})
-target_link_libraries(nnc_interpreter PRIVATE mir nnc_support)
+target_link_libraries(nnc_interpreter PRIVATE mir_interpreter)
if(NNC_HDF5_SUPPORTED)
target_include_directories(nnc_interpreter PRIVATE ${HDF5_INCLUDE_DIRS})
diff --git a/compiler/nnc/backends/interpreter/Interpreter.cpp b/compiler/nnc/backends/interpreter/Interpreter.cpp
deleted file mode 100644
index 04375ab7d..000000000
--- a/compiler/nnc/backends/interpreter/Interpreter.cpp
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "backends/interpreter/Interpreter.h"
-
-#include "ops/Add.h"
-#include "ops/AvgPool2D.h"
-#include "ops/Concat.h"
-#include "ops/Conv2D.h"
-#include "ops/DeConv2D.h"
-#include "ops/DepthwiseConv2D.h"
-#include "ops/Div.h"
-#include "ops/FullyConnected.h"
-#include "ops/Gather.h"
-#include "ops/Max.h"
-#include "ops/MaxPool2D.h"
-#include "ops/Mul.h"
-#include "ops/Pad.h"
-#include "ops/ReduceMean.h"
-#include "ops/Reshape.h"
-#include "ops/Softmax.h"
-#include "ops/Sub.h"
-#include "ops/Transpose.h"
-#include "ops/common.h"
-
-#include "mir/OpDefs.h"
-
-#include <cmath>
-#include <cassert>
-#include <iostream>
-#include <vector>
-
-namespace nnc
-{
-
-using namespace mir;
-
-std::vector<std::reference_wrapper<const TensorVariant>>
-NNInterpreter::getInputTensors(const Operation &op)
-{
- std::vector<std::reference_wrapper<const TensorVariant>> tensors;
- for (const auto &input : op.getInputs())
- {
- const auto *producer = input.getProducer();
- tensors.emplace_back(_opResults.at(producer->getNode()).at(producer->getIndex()));
- }
- return tensors;
-}
-
-void NNInterpreter::setOutputTensors(const Operation &op, std::vector<TensorVariant> &&outputs)
-{
- _opResults.emplace(&op, std::move(outputs));
-}
-
-void NNInterpreter::setInput(const std::string &name, const TensorVariant &data)
-{
- _inputTensors.emplace(name, data);
-}
-
-TensorVariant NNInterpreter::getResult(const Operation::Output *tensor)
-{
- return _opResults.at(tensor->getNode()).at(tensor->getIndex());
-}
-
-void NNInterpreter::visit(ops::InputOp &op)
-{
- const auto &input_name = op.getOutput(0)->getName();
- const auto it = _inputTensors.find(input_name);
- if (it == _inputTensors.end())
- throw std::runtime_error("Can't find data for input tensor \"" + input_name +
- ". Input data was not set correctly?");
- setOutputTensors(op, {it->second});
-}
-
-void NNInterpreter::visit(ops::AvgPool2DOp &op)
-{
- auto inputs = getInputTensors(op);
- auto outputs = AvgPool2D(inputs[0], op)();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::ConstantOp &op) { setOutputTensors(op, {op.getValue()}); }
-
-void NNInterpreter::visit(ops::ConcatOp &op)
-{
- auto inputs = getInputTensors(op);
- auto outputs = Concat<float>(inputs, op.getOutputShape(0), op.getAxis())();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::Conv2DOp &op)
-{
- auto inputs = getInputTensors(op);
- auto outputs = Conv2D(inputs[0], inputs[1], op)();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::MaxPool2DOp &op)
-{
- auto inputs = getInputTensors(op);
- auto outputs = MaxPool2D(inputs[0], op)();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::ReshapeOp &op)
-{
- auto inputs = getInputTensors(op);
- auto outputs = Reshape<float>(inputs[0], op.getOutputShape(0))();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::ReluOp &op)
-{
- auto inputs = getInputTensors(op);
- Tensor<float> input(inputs[0]);
- auto outputs = Fill<float>(op.getOutputShape(0),
- [&input](const Index &id) { return std::max(input.at(id), 0.0f); })();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::SigmoidOp &op)
-{
- auto inputs = getInputTensors(op);
- Tensor<float> input(inputs[0]);
- auto outputs = Fill<float>(op.getOutputShape(0), [&input](const Index &id) {
- return 1.f / (1.f + std::exp(-input.at(id)));
- })();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::SoftmaxOp &op)
-{
- auto inputs = getInputTensors(op);
- auto outputs = Softmax<float>(inputs[0], op.getAxis())();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::FullyConnectedOp &op)
-{
- auto inputs = getInputTensors(op);
- auto outputs = FullyConnected(inputs[0], inputs[1], op)();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::CappedReluOp &op)
-{
- auto inputs = getInputTensors(op);
- Tensor<float> input(inputs[0]);
- auto outputs = Fill<float>(op.getOutputShape(0), [&input, &op](const Index &id) {
- return std::min(std::max(input.at(id), 0.0f), op.getCap());
- })();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::DepthwiseConv2DOp &op)
-{
- auto inputs = getInputTensors(op);
- auto outputs = DepthwiseConv2D(inputs[0], inputs[1], op)();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::SliceOp &op)
-{
- auto inputs = getInputTensors(op);
- auto input = Tensor<float>(inputs[0]);
- auto outputs = Fill<float>(op.getOutputShape(0), [&input, &op](const Index &id) {
- Index idx = nnc::shift(id, op.getStarts());
- return input.at(idx);
- })();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::TanhOp &op)
-{
- auto inputs = getInputTensors(op);
- Tensor<float> input(inputs[0]);
- auto outputs = Fill<float>(op.getOutputShape(0),
- [&input](const Index &id) { return std::tanh(input.at(id)); })();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::DeConv2DOp &op)
-{
- auto inputs = getInputTensors(op);
- auto outputs = DeConv2D(inputs[0], inputs[1], op)();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::EluOp &op)
-{
- auto inputs = getInputTensors(op);
- Tensor<float> input(inputs[0]);
- auto outputs = Fill<float>(op.getOutputShape(0), [&input, &op](const Index &id) {
- if (input.at(id) >= 0)
- return input.at(id);
- else
- return op.getAlpha() * (expf(input.at(id)) - 1);
- })();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::SqueezeOp &op)
-{
- auto inputs = getInputTensors(op);
- // Squeeze is just a special case of reshape.
- auto outputs = Reshape<float>(inputs[0], op.getOutputShape(0))();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::PadOp &op)
-{
- auto inputs = getInputTensors(op);
- auto outputs = Pad(inputs[0], op)();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::SqrtOp &op)
-{
- auto inputs = getInputTensors(op);
- Tensor<float> input(inputs[0]);
- auto outputs =
- Fill<float>(op.getOutputShape(0), [&input](const Index id) { return sqrt(input.at(id)); })();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::ResizeOp &op)
-{
- auto inputs = getInputTensors(op);
- Tensor<float> input(inputs[0]);
- assert(op.getMode() == ops::ResizeOp::ResizeMethod::nearestNeighbor);
- auto scales = op.getScales();
- auto outputs = Fill<float>(op.getOutputShape(0), [&scales, &input](const Index &id) {
- Index in_idx;
- in_idx.resize(4);
- for (int i = 0; i < input.getShape().rank(); i++)
- {
- in_idx.at(i) = static_cast<int>(floorf(id.at(i) / scales[i]));
- }
- return input.at(in_idx);
- })();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::ReduceMeanOp &op)
-{
- auto inputs = getInputTensors(op);
- auto outputs = ReduceMean<float>(inputs[0], op)();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::TransposeOp &op)
-{
- auto inputs = getInputTensors(op);
- auto outputs = Transpose(inputs[0], op)();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::GatherOp &op)
-{
- auto inputs = getInputTensors(op);
- auto outputs = Gather(inputs[0], inputs[1], op)();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::LeakyReluOp &op)
-{
- auto inputs = getInputTensors(op);
- float alpha = op.getAlpha();
- Tensor<float> input(inputs[0]);
- auto outputs = Fill<float>(op.getOutputShape(0), [&input, alpha](const Index &id) {
- float val = input.at(id);
- return val > 0.0f ? val : val * alpha;
- })();
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(ops::OutputOp & /*op*/)
-{
- // No-op.
-}
-
-void NNInterpreter::visit(ops::AddOp &op)
-{
- auto inputs = getInputTensors(op);
- auto outputs = Add(op, inputs[0], inputs[1]);
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(mir::ops::DivOp &op)
-{
- auto inputs = getInputTensors(op);
- auto outputs = Div(op, inputs[0], inputs[1]);
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(mir::ops::MaxOp &op)
-{
- auto inputs = getInputTensors(op);
- auto outputs = Max(op, inputs[0], inputs[1]);
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(mir::ops::MulOp &op)
-{
- auto inputs = getInputTensors(op);
- auto outputs = Mul(op, inputs[0], inputs[1]);
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit(mir::ops::SubOp &op)
-{
- auto inputs = getInputTensors(op);
- auto outputs = Sub(op, inputs[0], inputs[1]);
- setOutputTensors(op, std::move(outputs));
-}
-
-void NNInterpreter::visit_fallback(mir::Operation &) { throw std::runtime_error("NYI operation"); }
-
-} // namespace nnc
diff --git a/compiler/nnc/backends/interpreter/InterpreterBackend.cpp b/compiler/nnc/backends/interpreter/InterpreterBackend.cpp
index 4fc5a1ad4..923a7cfc7 100644
--- a/compiler/nnc/backends/interpreter/InterpreterBackend.cpp
+++ b/compiler/nnc/backends/interpreter/InterpreterBackend.cpp
@@ -14,21 +14,24 @@
* limitations under the License.
*/
+#include <cstring>
+#include <utility>
#include <vector>
#include <fstream>
#include <sstream>
#include <algorithm>
-#include "option/Options.h"
#include "Definitions.h"
#ifdef NNC_HDF5_SUPPORTED
#include <H5Cpp.h>
+#else
+#include <iostream>
#endif // NNC_HDF5_SUPPORTED
#include "mir/Shape.h"
-#include "backends/interpreter/Interpreter.h"
+#include "MirInterpreter.h"
#include "backends/interpreter/InterpreterBackend.h"
#include "mir/Graph.h"
@@ -64,14 +67,17 @@ static void writeTensorToHDF5File(const TensorVariant &tensor, std::string tenso
dims[axis] = static_cast<hsize_t>(shape.dim(axis));
}
- // Create float array from tensor
- std::vector<float> values;
- values.reserve(shape.numElements());
+ // Create array from tensor
+ std::vector<char> values;
+ const auto elem_size = tensor.getElementSize();
+ values.resize(elem_size * shape.numElements());
+ char *values_ptr = values.data();
ShapeRange out_range(shape);
- Tensor<float> tensor_accessor(tensor);
-
for (auto &out_idx : out_range)
- values.push_back(tensor_accessor.at(out_idx));
+ {
+ std::memcpy(values_ptr, tensor.at(out_idx), elem_size);
+ values_ptr += elem_size;
+ }
// Backslashes are not allowed in tensor names
std::replace(tensor_name.begin(), tensor_name.end(), '/', '_');
@@ -80,17 +86,25 @@ static void writeTensorToHDF5File(const TensorVariant &tensor, std::string tenso
// Write to .hdf5 file
H5::H5File h5File(filename, H5F_ACC_TRUNC);
H5::DataSpace dataspace(rank, dims);
- auto dataset = h5File.createDataSet(tensor_name, H5::PredType::IEEE_F32BE, dataspace);
- dataset.write(values.data(), H5::PredType::NATIVE_FLOAT);
+ H5::DataType h5_data_type;
+
+ if (tensor.getDataType() == DataType::FLOAT32)
+ h5_data_type = H5::PredType::NATIVE_FLOAT;
+ else if (tensor.getDataType() == DataType::UINT8)
+ h5_data_type = H5::PredType::NATIVE_UINT8;
+ else
+ throw std::runtime_error("NYI writing that DataType!");
+
+ auto dataset = h5File.createDataSet(tensor_name, h5_data_type, dataspace);
+ dataset.write(values.data(), h5_data_type);
}
#endif // NNC_HDF5_SUPPORTED
-static TensorVariant readTensorFromFile(const std::string &filename, DataType data_type,
- const Shape &shape)
+static TensorVariant readTensorFromFile(const std::string &filename, const TensorType &type)
{
- assert(data_type == DataType::FLOAT32);
- std::size_t input_data_size = shape.numElements() * sizeof(float);
+ const std::size_t input_data_size =
+ type.getShape().numElements() * getDataTypeSize(type.getElementType());
std::ifstream stream(filename, std::ios::in | std::ios::binary);
if (stream.fail())
@@ -112,34 +126,42 @@ static TensorVariant readTensorFromFile(const std::string &filename, DataType da
if (stream.fail())
throw std::runtime_error("Couldn't read file \"" + filename + "\".");
- return TensorVariant(data_type, shape, data.get());
+ return TensorVariant(type, data.get());
+}
+
+InterpreterBackend::InterpreterBackend(std::string input_dir, std::string output_dir)
+ : _input_dir(std::move(input_dir)), _output_dir(std::move(output_dir))
+{
}
void InterpreterBackend::run(mir::Graph *graph)
{
assert(graph);
- NNInterpreter interpreter;
+ mir_interpreter::MIRInterpreter interpreter;
for (const auto *input_op : graph->getInputs())
{
- std::string tensor_name = input_op->getOutput(0)->getName();
+ const Operation::Output *input = input_op->getOutput(0);
+
+ std::string tensor_name = input->getName();
assert(!tensor_name.empty());
std::replace(tensor_name.begin(), tensor_name.end(), '/', '_');
- std::string filename = cli::interInputDataDir + "/" + tensor_name + ".dat";
- auto tensor = readTensorFromFile(filename, DataType::FLOAT32, input_op->getOutputShape(0));
- interpreter.setInput(input_op->getOutput(0)->getName(), tensor);
+ std::string filename = _input_dir + "/" + tensor_name + ".dat";
+
+ TensorVariant tensor = readTensorFromFile(filename, input->getType());
+ interpreter.setTensor(input, std::move(tensor));
}
graph->accept(&interpreter);
for (const auto *output_op : graph->getOutputs())
{
- const auto &tensor = interpreter.getResult(output_op->getInput(0)->getProducer());
- const auto &output_name = output_op->getInput(0)->getProducer()->getName();
+ const auto &output_name = output_op->getInput(0)->getName();
#ifdef NNC_HDF5_SUPPORTED
- writeTensorToHDF5File(tensor, output_name, cli::artifactDir);
+ const auto &tensor = interpreter.getTensor(output_op->getInput(0));
+ writeTensorToHDF5File(tensor, output_name, _output_dir);
#else
std::cout << "Result <" << output_name << "> wasn't saved, due to lack of HDF5" << std::endl;
#endif // NNC_HDF5_SUPPORTED
diff --git a/compiler/nnc/backends/interpreter/ops/Add.h b/compiler/nnc/backends/interpreter/ops/Add.h
deleted file mode 100644
index 64776ed5d..000000000
--- a/compiler/nnc/backends/interpreter/ops/Add.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_ADD_
-#define _NNC_CORE_BACKEND_INTERPRETER_ADD_
-
-#include "mir/ops/AddOp.h"
-#include "mir/Tensor.h"
-#include "mir/ShapeRange.h"
-
-namespace nnc
-{
-
-std::vector<mir::TensorVariant> Add(const mir::ops::AddOp &op, const mir::TensorVariant &lhs,
- const mir::TensorVariant &rhs)
-{
- mir::TensorVariant broadcasted_lhs(lhs, op.getOutputShape(0));
- mir::TensorVariant broadcasted_rhs(rhs, op.getOutputShape(0));
- mir::TensorVariant res(mir::DataType::FLOAT32, op.getOutputShape(0));
- mir::Tensor<float> lhs_accessor(broadcasted_lhs);
- mir::Tensor<float> rhs_accessor(broadcasted_rhs);
- mir::Tensor<float> res_accessor(res);
-
- for (const auto &index : mir::ShapeRange(op.getOutputShape(0)))
- {
- res_accessor.at(index) = lhs_accessor.at(index) + rhs_accessor.at(index);
- }
-
- return {res};
-}
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_ADD_
diff --git a/compiler/nnc/backends/interpreter/ops/AvgPool2D.cpp b/compiler/nnc/backends/interpreter/ops/AvgPool2D.cpp
deleted file mode 100644
index fd3a94edf..000000000
--- a/compiler/nnc/backends/interpreter/ops/AvgPool2D.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "AvgPool2D.h"
-
-#include "mir/ShapeRange.h"
-
-namespace nnc
-{
-
-using namespace mir;
-
-std::vector<TensorVariant> AvgPool2D::operator()()
-{
- const auto &input_shape = _op.getInputShape(0);
- const auto &output_shape = _op.getOutputShape(0);
- const auto &window_size = _op.getWindowSize();
- const auto &strides = _op.getStrides();
- const auto &padding_before = _op.getPaddingBefore();
- const auto &padding_after = _op.getPaddingAfter();
-
- constexpr int num_spatial_dims = 2;
- assert(_input.getShape().rank() == 4);
- assert(window_size.size() == num_spatial_dims);
- assert(strides.size() == num_spatial_dims);
- assert(padding_before.size() == num_spatial_dims);
- assert(padding_after.size() == num_spatial_dims);
-
- auto res = allocate_tensor(output_shape);
- Tensor<float> res_accessor(res);
-
- ShapeRange in_range(input_shape);
- Index in_index(input_shape.rank());
-
- for (const auto &out_index : ShapeRange(output_shape))
- {
- float result = 0.0f;
- size_t num_elements = 0;
-
- // Assuming NHWC format.
- in_index.at(0) = out_index.at(0);
- in_index.at(3) = out_index.at(3);
-
- for (const auto &window_index : ShapeRange(Shape(window_size)))
- {
- // Assuming NHWC format.
- for (int i = 0; i < num_spatial_dims; ++i)
- in_index.at(1 + i) =
- out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i];
-
- if (in_range.contains(in_index))
- {
- num_elements++;
- result += _input.at(in_index);
- }
- else if (_op.getIncludePad())
- {
- num_elements++;
- }
- }
-
- result /= num_elements;
- res_accessor.at(out_index) = result;
- }
-
- return {res};
-}
-
-} // namespace nnc
diff --git a/compiler/nnc/backends/interpreter/ops/AvgPool2D.h b/compiler/nnc/backends/interpreter/ops/AvgPool2D.h
deleted file mode 100644
index f6f175057..000000000
--- a/compiler/nnc/backends/interpreter/ops/AvgPool2D.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_AVG_POOL_2D_
-#define _NNC_CORE_BACKEND_INTERPRETER_AVG_POOL_2D_
-
-#include "OperationImpl.h"
-#include "mir/ops/AvgPool2DOp.h"
-#include "mir/Tensor.h"
-
-namespace nnc
-{
-
-class AvgPool2D : public OperationImpl<float>
-{
-public:
- std::vector<mir::TensorVariant> operator()() override;
-
- AvgPool2D(const mir::TensorVariant &input, const mir::ops::AvgPool2DOp &op)
- : _op(op), _input(input)
- {
- }
-
-private:
- const mir::ops::AvgPool2DOp &_op;
- const mir::Tensor<float> _input;
-};
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_AVG_POOL_2D_
diff --git a/compiler/nnc/backends/interpreter/ops/Concat.h b/compiler/nnc/backends/interpreter/ops/Concat.h
deleted file mode 100644
index 31632ec0d..000000000
--- a/compiler/nnc/backends/interpreter/ops/Concat.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_FILL_IMPL_
-#define _NNC_CORE_BACKEND_INTERPRETER_FILL_IMPL_
-
-#include "Fill.h"
-
-namespace nnc
-{
-
-template <typename T> class Concat : public Fill<T>
-{
-public:
- Concat(const std::vector<std::reference_wrapper<const mir::TensorVariant>> &inputs,
- const mir::Shape &outputShape, int32_t axis)
- : Fill<T>(outputShape, getSingleFunction(inputs, axis))
- {
- }
-
-private:
- const std::function<T(const mir::Index &)>
- getSingleFunction(const std::vector<std::reference_wrapper<const mir::TensorVariant>> &inputs,
- int32_t axis)
- {
- std::vector<mir::Tensor<T>> inputAccessors;
- inputAccessors.reserve(inputs.size());
- for (auto &in : inputs)
- inputAccessors.emplace_back(in);
-
- return std::function<T(const mir::Index &)>([inputAccessors, axis](const mir::Index &id) -> T {
- unsigned int mi = 0;
- int32_t along_axis = id.at(axis);
-
- while (along_axis >= inputAccessors.at(mi).getShape().dim(axis))
- {
- along_axis -= inputAccessors[mi].getShape().dim(axis);
- mi++;
- }
-
- mir::Index local_id = id;
- local_id.at(axis) = along_axis;
-
- return inputAccessors[mi].at(local_id);
- });
- }
-};
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_FILL_IMPL_
diff --git a/compiler/nnc/backends/interpreter/ops/Conv2D.cpp b/compiler/nnc/backends/interpreter/ops/Conv2D.cpp
deleted file mode 100644
index 91dc07851..000000000
--- a/compiler/nnc/backends/interpreter/ops/Conv2D.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Conv2D.h"
-#include "mir/ShapeRange.h"
-#include "mir/TensorUtil.h"
-
-namespace nnc
-{
-
-using namespace mir;
-
-// Mostly compatible with tensorflow implementation
-// Assuming input is in NHWC format with batch omitted( [in_height, in_width, in_channels] )
-// Kernel is in [filter_height, filter_width, in_channels, out_channels]
-// Refer to https://www.tensorflow.org/api_docs/python/tf/nn/conv2d for info
-std::vector<TensorVariant> Conv2D::operator()()
-{
- const Shape &in_shape = _input.getShape();
- const Shape &kernel_shape = _kernel.getShape();
- const Shape &out_shape = _op.getOutputShape(0);
- const auto &strides = _op.getStrides();
- const std::vector<int32_t> &pads = _op.getPaddingBefore();
-
- assert(in_shape.rank() == 4);
- assert(kernel_shape.rank() == 4);
- assert(kernel_shape.dim(2) == in_shape.dim(3));
- assert(kernel_shape.dim(3) == out_shape.dim(3));
- assert(strides.size() == 2);
- assert(pads.size() == 2);
-
- int32_t num_kernels = kernel_shape.dim(3);
-
- Tensor<float> kernel(_kernel);
- auto res = allocate_tensor(_op.getOutputShape(0));
- Tensor<float> res_accessor(res);
-
- ShapeRange in_range(in_shape);
- ShapeRange out_range(Shape{out_shape.dim(0), out_shape.dim(1), out_shape.dim(2), 1});
- ShapeRange kernel_range(Shape{kernel_shape.dim(0), kernel_shape.dim(1), kernel_shape.dim(2), 1});
-
- Index in_index;
- in_index.resize(4);
-
- for (const auto &out_index : out_range)
- {
- auto out_region = res_accessor.getRegion(out_index);
- assert(out_region.size() == num_kernels);
- for (const auto &kernel_index : kernel_range)
- {
- in_index.at(0) = out_index.at(0);
- for (int i = 0; i < 2; ++i)
- in_index.at(1 + i) = out_index.at(1 + i) * strides[i] + kernel_index.at(i) - pads[i];
- in_index.at(3) = kernel_index.at(2);
-
- if (in_range.contains(in_index))
- {
- auto kernel_region = kernel.getRegion(kernel_index);
- assert(kernel_region.size() == num_kernels);
- float in_val = _input.at(in_index);
- for (int32_t kernel_i = 0; kernel_i < num_kernels; ++kernel_i)
- {
- out_region.base()[kernel_i] += in_val * kernel_region.base()[kernel_i];
- }
- }
- }
- }
-
- return {res};
-}
-
-Conv2D::Conv2D(const TensorVariant &input, const TensorVariant &kernel, const ops::Conv2DOp &op)
- : _input(input), _kernel(transposeTensor<1, 2, 3, 0>(kernel)), _op(op)
-{
-}
-
-} // namespace nnc
diff --git a/compiler/nnc/backends/interpreter/ops/Conv2D.h b/compiler/nnc/backends/interpreter/ops/Conv2D.h
deleted file mode 100644
index 760cfee2f..000000000
--- a/compiler/nnc/backends/interpreter/ops/Conv2D.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_CONV2D_IMPL_
-#define _NNC_CORE_BACKEND_INTERPRETER_CONV2D_IMPL_
-
-#include "OperationImpl.h"
-#include "mir/ops/Conv2DOp.h"
-#include "mir/Tensor.h"
-
-namespace nnc
-{
-
-class Conv2D : public OperationImpl<float>
-{
-public:
- Conv2D(const mir::TensorVariant &input, const mir::TensorVariant &kernel,
- const mir::ops::Conv2DOp &op);
-
- std::vector<mir::TensorVariant> operator()() override;
-
-private:
- const mir::Tensor<float> _input;
- mir::TensorVariant _kernel;
- const mir::ops::Conv2DOp &_op;
-};
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_CONV2D_IMPL
diff --git a/compiler/nnc/backends/interpreter/ops/DeConv2D.cpp b/compiler/nnc/backends/interpreter/ops/DeConv2D.cpp
deleted file mode 100644
index bba13ffb9..000000000
--- a/compiler/nnc/backends/interpreter/ops/DeConv2D.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cstdlib>
-
-#include "mir/TensorUtil.h"
-#include "mir/ShapeRange.h"
-
-#include "DeConv2D.h"
-
-namespace nnc
-{
-
-using namespace mir;
-using namespace mir::ops;
-
-std::vector<mir::TensorVariant> nnc::DeConv2D::operator()()
-{
- const auto &strides = _op.getStrides();
- Shape out_shape = _op.getOutputShape(0);
- auto res = allocate_tensor(out_shape);
- Tensor<float> res_accesor(res);
- Index pads({_op.getPaddingBefore().at(0), _op.getPaddingBefore().at(1), 0});
-
- out_shape.dim(3) = 1;
- ShapeRange out_range(out_shape);
-
- Shape in_shape = _input.getShape();
- ShapeRange in_range(in_shape);
-
- auto tr_kernel = transposeTensor<0, 1, 3, 2>(_kernel);
- Tensor<float> kernel(tr_kernel);
-
- Shape k_shape = kernel.getShape();
- int32_t num_kernels = k_shape.dim(3);
- k_shape.dim(3) = 1;
- ShapeRange kernel_range(k_shape);
-
- Index input_idx;
- input_idx.resize(in_shape.rank());
-
- Index kernel_idx;
- kernel_idx.resize(k_shape.rank());
-
- for (auto &out_idx : out_range)
- {
- auto out_region = res_accesor.getRegion(out_idx);
- assert(out_region.size() == num_kernels);
-
- for (auto &kernel_idx_r : kernel_range)
- {
- // rotate kernel 180 deg around last axis
- // by index transform
- for (int32_t d = 0; d < 2; ++d)
- {
- kernel_idx.at(d) = kernel.getShape().dim(d) - kernel_idx_r.at(d) - 1;
- }
- kernel_idx.at(2) = kernel_idx_r.at(2);
- kernel_idx.at(3) = kernel_idx_r.at(3);
-
- // flag that keeps info on whether the current input element is from input
- // or is from dilation by stride
- bool is_from_input = true;
- for (int32_t d = 1; d < input_idx.rank() - 1; ++d)
- {
- const auto num = (out_idx.at(d) + pads.at(d - 1) - kernel_idx.at(d - 1));
- auto stride = strides[d - 1];
- const auto div_res = num / stride;
- const auto rem = num - div_res * stride;
- is_from_input = is_from_input && rem == 0;
- if (rem != 0)
- break;
- input_idx.at(d) = div_res;
- }
- if (is_from_input)
- {
- // batch is same as output's
- input_idx.at(0) = out_idx.at(0);
- // channel index - same as kernel's
- input_idx.at(3) = kernel_idx.at(2);
-
- if (in_range.contains(input_idx))
- {
- auto kernel_region = kernel.getRegion(kernel_idx);
- assert(kernel_region.size() == num_kernels);
-
- auto in = _input.at(input_idx);
-
- for (int32_t kernel_index = 0; kernel_index < num_kernels; kernel_index++)
- {
- out_region.base()[kernel_index] += in * kernel_region.base()[kernel_index];
- }
- }
- }
- }
- }
- return {res};
-}
-
-DeConv2D::DeConv2D(const TensorVariant &input, const TensorVariant &kernel, const DeConv2DOp &op)
- : _input(input), _kernel(kernel), _op(op)
-{
- // Input shape: [N, Hi, Wi, Ci]
- // Kernel shape: [Hk, Wk, Co, Ci]
- assert(_op.getInputShape(0).rank() == 4);
- const auto &input_shape = _input.getShape();
- const auto &kernel_shape = _kernel.getShape();
- assert(input_shape.rank() == 4);
- assert(kernel_shape.rank() == 4);
- assert(kernel_shape.dim(3) == input_shape.dim(3));
- assert(_op.getPaddingBefore().size() == 2);
- assert(_op.getPaddingAfter().size() == 2);
-}
-
-} // namespace nnc
diff --git a/compiler/nnc/backends/interpreter/ops/DeConv2D.h b/compiler/nnc/backends/interpreter/ops/DeConv2D.h
deleted file mode 100644
index 89f9dfc2b..000000000
--- a/compiler/nnc/backends/interpreter/ops/DeConv2D.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_DECONV2D_IMPL_
-#define _NNC_CORE_BACKEND_INTERPRETER_DECONV2D_IMPL_
-
-#include "OperationImpl.h"
-#include "mir/ops/Deconv2DOp.h"
-#include "mir/Tensor.h"
-
-namespace nnc
-{
-
-/**
- * @brief Transposed convolution (or Deconvolution)
- * @param input The Input tensor
- * @param op The DeConvolution operation object
- *
- * This is basically the backward pass for the convolution operation,
- * hence all the indexing can be deducted by expressing the input index
- * of Conv in terms of it's output index.
- */
-class DeConv2D : public OperationImpl<float>
-{
-public:
- DeConv2D(const mir::TensorVariant &input, const mir::TensorVariant &kernel,
- const mir::ops::DeConv2DOp &op);
-
- std::vector<mir::TensorVariant> operator()() override;
-
-private:
- const mir::Tensor<float> _input;
- const mir::TensorVariant _kernel;
- const mir::ops::DeConv2DOp &_op;
-};
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_DECONV2D_IMPL_
diff --git a/compiler/nnc/backends/interpreter/ops/DepthwiseConv2D.cpp b/compiler/nnc/backends/interpreter/ops/DepthwiseConv2D.cpp
deleted file mode 100644
index c840af70f..000000000
--- a/compiler/nnc/backends/interpreter/ops/DepthwiseConv2D.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "DepthwiseConv2D.h"
-#include "mir/ShapeRange.h"
-
-namespace nnc
-{
-
-using namespace mir;
-
-std::vector<TensorVariant> DepthwiseConv2D::operator()()
-{
- const Shape &in_shape = _op.getInputShape(0);
- const Shape &kernel_shape = _op.getInputShape(1);
- const Shape &out_shape = _op.getOutputShape(0);
- const auto &strides = _op.getStrides();
- const std::vector<int32_t> &pads = _op.getPaddingBefore();
-
- assert(in_shape.rank() == 4);
- assert(kernel_shape.rank() == 4);
- assert(kernel_shape.dim(2) == in_shape.dim(3));
- assert(in_shape.dim(3) * kernel_shape.dim(3) == out_shape.dim(3));
- assert(strides.size() == 2);
- assert(pads.size() == 2);
-
- int32_t channel_multiplier = kernel_shape.dim(3);
-
- TensorVariant res = allocate_tensor(out_shape);
- Tensor<float> res_accessor(res);
-
- ShapeRange in_range(in_shape);
- ShapeRange kernel_range(kernel_shape);
- ShapeRange out_range(Shape{out_shape.dim(0), out_shape.dim(1), out_shape.dim(2), 1});
-
- Index in_index;
- in_index.resize(4);
-
- for (const auto &out_index : out_range)
- {
- Index out_index_k = out_index;
- for (const auto &kernel_index : kernel_range)
- {
- in_index.at(0) = out_index.at(0);
- for (int i = 0; i < 2; ++i)
- in_index.at(1 + i) = out_index.at(1 + i) * strides[i] + kernel_index.at(i) - pads[i];
- in_index.at(3) = kernel_index.at(2);
-
- if (in_range.contains(in_index))
- {
- out_index_k.at(3) = kernel_index.at(2) * channel_multiplier + kernel_index.at(3);
- res_accessor.at(out_index_k) += _input.at(in_index) * _kernel.at(kernel_index);
- }
- }
- }
-
- return {res};
-}
-
-DepthwiseConv2D::DepthwiseConv2D(const TensorVariant &input, const TensorVariant &kernel,
- const ops::DepthwiseConv2DOp &op)
- : _input(input), _kernel(kernel), _op(op)
-{
-}
-
-} // namespace nnc
diff --git a/compiler/nnc/backends/interpreter/ops/DepthwiseConv2D.h b/compiler/nnc/backends/interpreter/ops/DepthwiseConv2D.h
deleted file mode 100644
index ab6c65aef..000000000
--- a/compiler/nnc/backends/interpreter/ops/DepthwiseConv2D.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_DEPTHWISE_CONV2D_IMPL_
-#define _NNC_CORE_BACKEND_INTERPRETER_DEPTHWISE_CONV2D_IMPL_
-
-#include "OperationImpl.h"
-#include "mir/ops/DepthwiseConv2DOp.h"
-#include "mir/Tensor.h"
-
-namespace nnc
-{
-
-class DepthwiseConv2D : public OperationImpl<float>
-{
-public:
- DepthwiseConv2D(const mir::TensorVariant &input, const mir::TensorVariant &kernel,
- const mir::ops::DepthwiseConv2DOp &op);
-
- std::vector<mir::TensorVariant> operator()() override;
-
-private:
- const mir::Tensor<float> _input;
- const mir::Tensor<float> _kernel;
- const mir::ops::DepthwiseConv2DOp &_op;
-};
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_DEPTHWISE_CONV2D_IMPL_
diff --git a/compiler/nnc/backends/interpreter/ops/Div.h b/compiler/nnc/backends/interpreter/ops/Div.h
deleted file mode 100644
index 862147eb8..000000000
--- a/compiler/nnc/backends/interpreter/ops/Div.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_DIV_
-#define _NNC_CORE_BACKEND_INTERPRETER_DIV_
-
-#include "mir/ops/DivOp.h"
-#include "mir/Tensor.h"
-#include "mir/ShapeRange.h"
-
-namespace nnc
-{
-
-std::vector<mir::TensorVariant> Div(const mir::ops::DivOp &op, const mir::TensorVariant &lhs,
- const mir::TensorVariant &rhs)
-{
- mir::TensorVariant broadcasted_lhs(lhs, op.getOutputShape(0));
- mir::TensorVariant broadcasted_rhs(rhs, op.getOutputShape(0));
- mir::TensorVariant res(mir::DataType::FLOAT32, op.getOutputShape(0));
- mir::Tensor<float> lhs_accessor(broadcasted_lhs);
- mir::Tensor<float> rhs_accessor(broadcasted_rhs);
- mir::Tensor<float> res_accessor(res);
-
- for (const auto &index : mir::ShapeRange(op.getOutputShape(0)))
- {
- res_accessor.at(index) = lhs_accessor.at(index) / rhs_accessor.at(index);
- }
-
- return {res};
-}
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_DIV_
diff --git a/compiler/nnc/backends/interpreter/ops/Fill.h b/compiler/nnc/backends/interpreter/ops/Fill.h
deleted file mode 100644
index dfdd44b37..000000000
--- a/compiler/nnc/backends/interpreter/ops/Fill.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_FILL_
-#define _NNC_CORE_BACKEND_INTERPRETER_FILL_
-
-#include "OperationImpl.h"
-#include "mir/ShapeRange.h"
-#include "mir/Tensor.h"
-#include <functional>
-
-namespace nnc
-{
-
-template <typename T> class Fill : public OperationImpl<T>
-{
-public:
- Fill(const mir::Shape &shape, std::function<T(const mir::Index &)> f)
- : _shape(shape), _fval(std::move(f))
- {
- }
-
- std::vector<mir::TensorVariant> operator()() override
- {
- auto res = OperationImpl<T>::allocate_tensor(_shape);
- mir::Tensor<T> res_accessor(res);
-
- for (const auto &index : mir::ShapeRange(_shape))
- {
- res_accessor.at(index) = _fval(index);
- }
-
- return {res};
- }
-
-private:
- const mir::Shape _shape;
- const std::function<T(const mir::Index &)> _fval;
-};
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_FILL_
diff --git a/compiler/nnc/backends/interpreter/ops/FullyConnected.cpp b/compiler/nnc/backends/interpreter/ops/FullyConnected.cpp
deleted file mode 100644
index 36b646032..000000000
--- a/compiler/nnc/backends/interpreter/ops/FullyConnected.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "FullyConnected.h"
-
-namespace nnc
-{
-
-FullyConnected::FullyConnected(const mir::TensorVariant &input, const mir::TensorVariant &weights,
- const mir::ops::FullyConnectedOp &_op)
- : _op(_op), _input(input), _weights(weights)
-{
-}
-
-std::vector<mir::TensorVariant> FullyConnected::operator()()
-{
- mir::TensorVariant res = OperationImpl<float>::allocate_tensor(_op.getOutputShape(0));
- mir::Tensor<float> accessor(res);
-
- const mir::Shape &in_shape = _input.getShape();
- int32_t in_rank = in_shape.rank();
-
- const mir::Shape &w_shape = _weights.getShape();
- int32_t w_rank = w_shape.rank();
-
- assert(in_shape.dim(in_rank - 1) == w_shape.dim(w_rank - 2));
- (void)in_rank;
-
- mir::ShapeRange out_range(res.getShape());
-
- int32_t len = w_shape.dim(w_rank - 2);
-
- for (auto &out_index : out_range)
- {
- mir::Index t_index = out_index;
- float &output_element = accessor.at(out_index);
- int32_t col = t_index.at(w_rank - 1);
- int32_t row = t_index.at(w_rank - 2);
- for (int32_t i = 0; i < len; ++i)
- {
- t_index.at(w_rank - 1) = i;
- float in = _input.at(t_index);
- t_index.at(w_rank - 1) = col;
- t_index.at(w_rank - 2) = i;
- float w = _weights.at(t_index);
- t_index.at(w_rank - 2) = row;
- output_element += in * w;
- }
- }
-
- return {res};
-}
-
-} // namespace nnc
diff --git a/compiler/nnc/backends/interpreter/ops/FullyConnected.h b/compiler/nnc/backends/interpreter/ops/FullyConnected.h
deleted file mode 100644
index 941170bf1..000000000
--- a/compiler/nnc/backends/interpreter/ops/FullyConnected.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_FULLYCONNECTED_
-#define _NNC_CORE_BACKEND_INTERPRETER_FULLYCONNECTED_
-
-#include "OperationImpl.h"
-#include "mir/ops/FullyConnectedOp.h"
-#include "mir/Tensor.h"
-#include "mir/ShapeRange.h"
-
-namespace nnc
-{
-
-class FullyConnected : public OperationImpl<float>
-{
-public:
- FullyConnected(const mir::TensorVariant &input, const mir::TensorVariant &weights,
- const mir::ops::FullyConnectedOp &_op);
-
- std::vector<mir::TensorVariant> operator()() override;
-
-private:
- const mir::ops::FullyConnectedOp &_op;
- const mir::Tensor<float> _input;
- const mir::Tensor<float> _weights;
-};
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_FULLYCONNECTED_
diff --git a/compiler/nnc/backends/interpreter/ops/Gather.cpp b/compiler/nnc/backends/interpreter/ops/Gather.cpp
deleted file mode 100644
index dca6f11f2..000000000
--- a/compiler/nnc/backends/interpreter/ops/Gather.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Gather.h"
-
-namespace nnc
-{
-
-using namespace mir;
-
-Gather::Gather(const TensorVariant &data, const TensorVariant &indices, const ops::GatherOp &op)
- : _data(data), _indices(indices), _op(op)
-{
-}
-
-std::vector<TensorVariant> Gather::operator()()
-{
- const auto &data_shape = _data.getShape();
- const auto &indices_shape = _indices.getShape();
- auto res = allocate_tensor(_op.getOutputShape(0));
- Tensor<float> data(_data);
- Tensor<float> indices(_indices);
- Tensor<float> output(res);
-
- int32_t axis = _op.getAxis();
- if (axis < 0)
- axis += data_shape.rank();
- assert(axis >= 0 && axis < data_shape.rank());
- int32_t axis_size = data_shape.dim(axis);
- int32_t num_indices = indices_shape.numElements();
-
- int32_t outer_size = 1;
- for (int32_t i = 0; i < axis; ++i)
- outer_size *= data_shape.dim(i);
-
- int32_t inner_size = 1;
- for (int32_t i = axis + 1; i < data_shape.rank(); ++i)
- inner_size *= data_shape.dim(i);
-
- for (int32_t outer = 0; outer < outer_size; ++outer)
- {
- for (int32_t i = 0; i < num_indices; ++i)
- {
- auto index = static_cast<int32_t>(indices.atOffset(i));
- assert(index >= 0 && index < axis_size);
- for (int32_t inner = 0; inner < inner_size; inner++)
- {
- output.atOffset((outer * num_indices + i) * inner_size + inner) =
- data.atOffset((outer * axis_size + index) * inner_size + inner);
- }
- }
- }
-
- return {res};
-}
-
-} // namespace nnc
diff --git a/compiler/nnc/backends/interpreter/ops/Gather.h b/compiler/nnc/backends/interpreter/ops/Gather.h
deleted file mode 100644
index 22fe077c6..000000000
--- a/compiler/nnc/backends/interpreter/ops/Gather.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_GATHER_
-#define _NNC_CORE_BACKEND_INTERPRETER_GATHER_
-
-#include "OperationImpl.h"
-#include "mir/ops/GatherOp.h"
-#include "mir/Tensor.h"
-
-namespace nnc
-{
-
-class Gather : public OperationImpl<float>
-{
-public:
- Gather(const mir::TensorVariant &data, const mir::TensorVariant &indices,
- const mir::ops::GatherOp &op);
-
- std::vector<mir::TensorVariant> operator()() override;
-
-private:
- const mir::Tensor<float> _data;
- const mir::Tensor<float> _indices;
- const mir::ops::GatherOp &_op;
-};
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_GATHER_
diff --git a/compiler/nnc/backends/interpreter/ops/Max.h b/compiler/nnc/backends/interpreter/ops/Max.h
deleted file mode 100644
index 52cb387b6..000000000
--- a/compiler/nnc/backends/interpreter/ops/Max.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_MAX_
-#define _NNC_CORE_BACKEND_INTERPRETER_MAX_
-
-#include "mir/ops/MaxOp.h"
-#include "mir/Tensor.h"
-#include "mir/ShapeRange.h"
-
-#include <algorithm>
-
-namespace nnc
-{
-
-std::vector<mir::TensorVariant> Max(const mir::ops::MaxOp &op, const mir::TensorVariant &lhs,
- const mir::TensorVariant &rhs)
-{
- mir::TensorVariant broadcasted_lhs(lhs, op.getOutputShape(0));
- mir::TensorVariant broadcasted_rhs(rhs, op.getOutputShape(0));
- mir::TensorVariant res(mir::DataType::FLOAT32, op.getOutputShape(0));
- mir::Tensor<float> lhs_accessor(broadcasted_lhs);
- mir::Tensor<float> rhs_accessor(broadcasted_rhs);
- mir::Tensor<float> res_accessor(res);
-
- for (const auto &index : mir::ShapeRange(op.getOutputShape(0)))
- {
- res_accessor.at(index) = std::max(lhs_accessor.at(index), rhs_accessor.at(index));
- }
-
- return {res};
-}
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_MAX_
diff --git a/compiler/nnc/backends/interpreter/ops/MaxPool2D.cpp b/compiler/nnc/backends/interpreter/ops/MaxPool2D.cpp
deleted file mode 100644
index 5ad6c5edb..000000000
--- a/compiler/nnc/backends/interpreter/ops/MaxPool2D.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "MaxPool2D.h"
-
-#include "mir/ShapeRange.h"
-
-#include <limits>
-
-namespace nnc
-{
-
-using namespace mir;
-
-std::vector<TensorVariant> MaxPool2D::operator()()
-{
- const auto &input_shape = _op.getInputShape(0);
- const auto &output_shape = _op.getOutputShape(0);
- const auto &window_size = _op.getWindowSize();
- const auto &strides = _op.getStrides();
- const auto &padding_before = _op.getPaddingBefore();
- const auto &padding_after = _op.getPaddingAfter();
-
- constexpr int num_spatial_dims = 2;
- assert(_input.getShape().rank() == 4);
- assert(window_size.size() == num_spatial_dims);
- assert(strides.size() == num_spatial_dims);
- assert(padding_before.size() == num_spatial_dims);
- assert(padding_after.size() == num_spatial_dims);
-
- auto res = allocate_tensor(output_shape);
- Tensor<float> res_accessor(res);
-
- ShapeRange in_range(input_shape);
- Index in_index(input_shape.rank());
-
- for (const auto &out_index : ShapeRange(output_shape))
- {
- float result = std::numeric_limits<float>::lowest();
-
- // Assuming NHWC format.
- in_index.at(0) = out_index.at(0);
- in_index.at(3) = out_index.at(3);
-
- for (const auto &window_index : ShapeRange(Shape(window_size)))
- {
- // Assuming NHWC format.
- for (int i = 0; i < num_spatial_dims; ++i)
- in_index.at(1 + i) =
- out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i];
-
- if (in_range.contains(in_index))
- {
- result = std::max(result, _input.at(in_index));
- }
- }
-
- res_accessor.at(out_index) = result;
- }
-
- return {res};
-}
-
-} // namespace nnc
diff --git a/compiler/nnc/backends/interpreter/ops/MaxPool2D.h b/compiler/nnc/backends/interpreter/ops/MaxPool2D.h
deleted file mode 100644
index 9950af102..000000000
--- a/compiler/nnc/backends/interpreter/ops/MaxPool2D.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_MAX_POOL_2D_
-#define _NNC_CORE_BACKEND_INTERPRETER_MAX_POOL_2D_
-
-#include "OperationImpl.h"
-#include "mir/ops/MaxPool2DOp.h"
-#include "mir/Tensor.h"
-
-namespace nnc
-{
-
-class MaxPool2D : public OperationImpl<float>
-{
-public:
- std::vector<mir::TensorVariant> operator()() override;
-
- MaxPool2D(const mir::TensorVariant &input, const mir::ops::MaxPool2DOp &op)
- : _op(op), _input(input)
- {
- }
-
-private:
- const mir::ops::MaxPool2DOp &_op;
- const mir::Tensor<float> _input;
-};
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_MAX_POOL_2D_
diff --git a/compiler/nnc/backends/interpreter/ops/Mul.h b/compiler/nnc/backends/interpreter/ops/Mul.h
deleted file mode 100644
index fb3ce3eda..000000000
--- a/compiler/nnc/backends/interpreter/ops/Mul.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_MUL_
-#define _NNC_CORE_BACKEND_INTERPRETER_MUL_
-
-#include "mir/ops/MulOp.h"
-#include "mir/Tensor.h"
-#include "mir/ShapeRange.h"
-
-namespace nnc
-{
-
-std::vector<mir::TensorVariant> Mul(const mir::ops::MulOp &op, const mir::TensorVariant &lhs,
- const mir::TensorVariant &rhs)
-{
- mir::TensorVariant broadcasted_lhs(lhs, op.getOutputShape(0));
- mir::TensorVariant broadcasted_rhs(rhs, op.getOutputShape(0));
- mir::TensorVariant res(mir::DataType::FLOAT32, op.getOutputShape(0));
- mir::Tensor<float> lhs_accessor(broadcasted_lhs);
- mir::Tensor<float> rhs_accessor(broadcasted_rhs);
- mir::Tensor<float> res_accessor(res);
-
- for (const auto &index : mir::ShapeRange(op.getOutputShape(0)))
- {
- res_accessor.at(index) = lhs_accessor.at(index) * rhs_accessor.at(index);
- }
-
- return {res};
-}
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_MUL_
diff --git a/compiler/nnc/backends/interpreter/ops/OperationImpl.h b/compiler/nnc/backends/interpreter/ops/OperationImpl.h
deleted file mode 100644
index 6dbdf8efa..000000000
--- a/compiler/nnc/backends/interpreter/ops/OperationImpl.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_OPERATION_IMPL_
-#define _NNC_CORE_BACKEND_INTERPRETER_OPERATION_IMPL_
-
-#include "mir/TensorVariant.h"
-#include "mir/Shape.h"
-#include <vector>
-
-namespace nnc
-{
-
-template <typename T> class OperationImpl
-{
-public:
- virtual std::vector<mir::TensorVariant> operator()() = 0;
-
-protected:
- mir::TensorVariant allocate_tensor(const mir::Shape &shape)
- {
- // Use hardcoded DataType for now, since theres no support for operations on types other than
- // floats
- std::vector<float> zeros(static_cast<std::size_t>(shape.numElements()), 0.0f);
- return mir::TensorVariant(mir::DataType::FLOAT32, shape, zeros.data());
- }
-};
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_OPERATION_IMPL_
diff --git a/compiler/nnc/backends/interpreter/ops/Pad.cpp b/compiler/nnc/backends/interpreter/ops/Pad.cpp
deleted file mode 100644
index 94dd63b88..000000000
--- a/compiler/nnc/backends/interpreter/ops/Pad.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "mir/ShapeRange.h"
-
-#include "Pad.h"
-
-namespace nnc
-{
-
-using namespace mir;
-
-std::vector<TensorVariant> Pad::operator()()
-{
- auto result = allocate_tensor(_op.getOutputShape(0));
- Tensor<float> result_accessor(result);
-
- Shape out_shape = result_accessor.getShape();
-
- ShapeRange out_range(out_shape);
- const int rank = _op.getInputShape(0).rank();
- const auto &padding_before = _op.getPaddingBefore();
- const auto &padding_after = _op.getPaddingAfter();
-
- Index temp_index;
- temp_index.resize(rank);
-
- bool index_on_padding(false);
- for (const Index &ind : out_range)
- {
- index_on_padding = false;
-
- for (int32_t i = 0; i < rank; i++)
- {
- // index on input values
- if (ind.at(i) >= padding_before[i] && ind.at(i) < out_shape.dim(i) - padding_after[i])
- {
- temp_index.at(i) = ind.at(i) - padding_before[i];
- }
- else
- { // not in input
- index_on_padding = true;
- break;
- }
- }
- if (index_on_padding)
- {
- result_accessor.at(ind) = _op.getPaddingValue();
- }
- else
- {
- result_accessor.at(ind) = _input.at(temp_index);
- }
- }
-
- return {result};
-}
-
-} // namespace nnc
diff --git a/compiler/nnc/backends/interpreter/ops/Pad.h b/compiler/nnc/backends/interpreter/ops/Pad.h
deleted file mode 100644
index 60232dd11..000000000
--- a/compiler/nnc/backends/interpreter/ops/Pad.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_PAD_IMPL_
-#define _NNC_CORE_BACKEND_INTERPRETER_PAD_IMPL_
-
-#include "OperationImpl.h"
-#include "mir/ops/PadOp.h"
-#include "mir/Tensor.h"
-
-namespace nnc
-{
-/**
- * @brief Implements PadOp for interpreter backend
- *
- * This operation pads a tensor according to the paddings
- * you specify. For each dimension of input add values
- * before and after of contents.
- */
-class Pad : public OperationImpl<float>
-{
-public:
- /**
- * @param input The Input tensor
- * @param op The Pad operation object
- */
- Pad(const mir::TensorVariant &input, const mir::ops::PadOp &op) : _input(input), _op(op) {}
-
- /**
- * @brief Computes operation aplication result
- * @return Vector of all outputs from this node
- */
- std::vector<mir::TensorVariant> operator()() override;
-
-private:
- const mir::Tensor<float> _input;
- const mir::ops::PadOp &_op;
-};
-
-} // namespace nnc
-
-#endif // _NNC_CORE_BACKEND_INTERPRETER_PAD_IMPL_
diff --git a/compiler/nnc/backends/interpreter/ops/ReduceMean.h b/compiler/nnc/backends/interpreter/ops/ReduceMean.h
deleted file mode 100644
index b36e429ed..000000000
--- a/compiler/nnc/backends/interpreter/ops/ReduceMean.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_REDUCE_MEAN_
-#define _NNC_CORE_BACKEND_INTERPRETER_REDUCE_MEAN_
-
-#include "OperationImpl.h"
-#include "mir/ops/ReduceMeanOp.h"
-#include "mir/Tensor.h"
-#include "mir/ShapeRange.h"
-
-namespace nnc
-{
-
-template <typename T> class ReduceMean : public OperationImpl<T>
-{
-public:
- ReduceMean(const mir::TensorVariant &input, const mir::ops::ReduceMeanOp &op)
- : _input(input), _op(op)
- {
- }
-
- std::vector<mir::TensorVariant> operator()() override
- {
- const auto &input_shape = _op.getInputShape(0);
- const auto &output_shape = _op.getOutputShape(0);
- const auto &reduction_dims = _op.getReductionDims();
- const bool keep_dims = _op.getKeepDims();
-
- const auto reductor = [](T result, T x) { return result + x; };
-
- auto res = OperationImpl<T>::allocate_tensor(output_shape);
- mir::Tensor<T> res_accessor(res);
-
- // This mask contains 'true' for dimensions that should be reduced. For example, if we want
- // to reduce dimensions 1 and 3 with total number of dimensions of 4, the mask will be
- // [false, true, false, true].
- std::vector<bool> reduction_dims_mask(input_shape.rank(), false);
- for (const int dim : reduction_dims)
- {
- reduction_dims_mask[dim] = true;
- }
-
- mir::Index out_index(output_shape.rank());
- for (const mir::Index &in_index : mir::ShapeRange(input_shape))
- {
- int out_index_dim = 0;
- for (int dim = 0; dim < input_shape.rank(); ++dim)
- {
- if (keep_dims)
- {
- out_index.at(out_index_dim++) = reduction_dims_mask[dim] ? 0 : in_index.at(dim);
- }
- else
- {
- if (!reduction_dims_mask[dim])
- {
- out_index.at(out_index_dim++) = in_index.at(dim);
- }
- }
- }
- res_accessor.at(out_index) = reductor(res_accessor.at(out_index), _input.at(in_index));
- }
-
- const std::int32_t reduction_factor = input_shape.numElements() / output_shape.numElements();
-
- for (const auto &index : mir::ShapeRange(output_shape))
- {
- res_accessor.at(index) /= reduction_factor;
- }
-
- return {res};
- }
-
-private:
- const mir::Tensor<T> _input;
- const mir::ops::ReduceMeanOp &_op;
-};
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_REDUCE_MEAN_
diff --git a/compiler/nnc/backends/interpreter/ops/Reshape.h b/compiler/nnc/backends/interpreter/ops/Reshape.h
deleted file mode 100644
index 1979f5c64..000000000
--- a/compiler/nnc/backends/interpreter/ops/Reshape.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_RESHAPE_IMPL_
-#define _NNC_CORE_BACKEND_INTERPRETER_RESHAPE_IMPL_
-
-#include "mir/ops/ReshapeOp.h"
-
-#include "OperationImpl.h"
-#include "Fill.h"
-
-namespace nnc
-{
-
-template <typename T> class Reshape : public OperationImpl<T>
-{
-public:
- Reshape(const mir::TensorVariant &input, const mir::Shape &output_shape)
- : _input(input), _output_shape(output_shape)
- {
-
- assert(input.getShape().numElements() == _output_shape.numElements());
- }
-
- std::vector<mir::TensorVariant> operator()() override
- {
- mir::ShapeRange inRange(_input.getShape());
- auto inIter = inRange.begin();
-
- auto out = OperationImpl<T>::allocate_tensor(_output_shape);
-
- // Shapes element count compared in Reshape ctor
- return Fill<T>(_output_shape,
- [this, &inIter](const mir::Index &) -> T { return _input.at(*inIter++); })();
- }
-
-private:
- mir::Tensor<T> _input;
- const mir::Shape &_output_shape;
-};
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_RESHAPE_IMPL_
diff --git a/compiler/nnc/backends/interpreter/ops/Softmax.h b/compiler/nnc/backends/interpreter/ops/Softmax.h
deleted file mode 100644
index 50916c9b9..000000000
--- a/compiler/nnc/backends/interpreter/ops/Softmax.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_SOFTMAX_
-#define _NNC_CORE_BACKEND_INTERPRETER_SOFTMAX_
-
-#include "OperationImpl.h"
-#include "mir/ShapeRange.h"
-#include "mir/Tensor.h"
-#include <cmath>
-
-namespace nnc
-{
-
-template <typename T> class Softmax : public OperationImpl<T>
-{
-public:
- Softmax(const mir::TensorVariant &input, int32_t axis) : _input(input), _axis(axis) {}
-
- std::vector<mir::TensorVariant> operator()() override
- {
- mir::Shape expsum_shape = _input.getShape();
- expsum_shape.dim(_axis) = 1;
-
- auto expsum = OperationImpl<T>::allocate_tensor(expsum_shape);
- auto res = OperationImpl<T>::allocate_tensor(_input.getShape());
- mir::Tensor<T> input_accessor(_input);
- mir::Tensor<T> expsum_accessor(expsum);
- mir::Tensor<T> res_accessor(res);
-
- for (const auto &out_idx : mir::ShapeRange(expsum_shape))
- {
- T element = T();
- mir::Index in_idx = out_idx;
- int32_t end = _input.getShape().dim(_axis);
- for (int32_t i = 0; i < end; ++i)
- {
- in_idx.at(_axis) = i;
- element += std::exp(input_accessor.at(in_idx));
- }
- expsum_accessor.at(out_idx) = element;
- }
-
- for (const auto &out_idx : mir::ShapeRange(_input.getShape()))
- {
- mir::Index expsum_idx = out_idx;
- expsum_idx.at(_axis) = 0;
- res_accessor.at(out_idx) =
- std::exp(input_accessor.at(out_idx)) / expsum_accessor.at(expsum_idx);
- }
-
- return {res};
- };
-
-private:
- const mir::TensorVariant _input;
- const int32_t _axis;
-};
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_SOFTMAX_
diff --git a/compiler/nnc/backends/interpreter/ops/Sub.h b/compiler/nnc/backends/interpreter/ops/Sub.h
deleted file mode 100644
index 90bce4dda..000000000
--- a/compiler/nnc/backends/interpreter/ops/Sub.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_SUB_
-#define _NNC_CORE_BACKEND_INTERPRETER_SUB_
-
-#include "mir/ops/SubOp.h"
-#include "mir/Tensor.h"
-#include "mir/ShapeRange.h"
-
-namespace nnc
-{
-
-std::vector<mir::TensorVariant> Sub(const mir::ops::SubOp &op, const mir::TensorVariant &lhs,
- const mir::TensorVariant &rhs)
-{
- mir::TensorVariant broadcasted_lhs(lhs, op.getOutputShape(0));
- mir::TensorVariant broadcasted_rhs(rhs, op.getOutputShape(0));
- mir::TensorVariant res(mir::DataType::FLOAT32, op.getOutputShape(0));
- mir::Tensor<float> lhs_accessor(broadcasted_lhs);
- mir::Tensor<float> rhs_accessor(broadcasted_rhs);
- mir::Tensor<float> res_accessor(res);
-
- for (const auto &index : mir::ShapeRange(op.getOutputShape(0)))
- {
- res_accessor.at(index) = lhs_accessor.at(index) - rhs_accessor.at(index);
- }
-
- return {res};
-}
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_SUB_
diff --git a/compiler/nnc/backends/interpreter/ops/Transpose.cpp b/compiler/nnc/backends/interpreter/ops/Transpose.cpp
deleted file mode 100644
index 7cb264d0d..000000000
--- a/compiler/nnc/backends/interpreter/ops/Transpose.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Transpose.h"
-#include "mir/Tensor.h"
-#include "mir/ShapeRange.h"
-
-namespace nnc
-{
-
-using namespace mir;
-
-Transpose::Transpose(const mir::TensorVariant &input, const mir::ops::TransposeOp &op)
- : _op(op), _input(input)
-{
-}
-
-std::vector<mir::TensorVariant> Transpose::operator()()
-{
- auto res = allocate_tensor(_op.getOutputShape(0));
- Tensor<float> res_accessor(res);
-
- auto &input_shape = _op.getInputShape(0);
- auto &axis_order = _op.getAxisOrder();
- std::size_t num_axes = axis_order.size();
-
- ShapeRange in_range(input_shape);
- Index out_index;
- out_index.resize(input_shape.rank());
-
- for (auto &in_index : in_range)
- {
- for (std::size_t i = 0; i < num_axes; ++i)
- out_index.at(static_cast<int32_t>(i)) = in_index.at(static_cast<int32_t>(axis_order.at(i)));
- res_accessor.at(out_index) = _input.at(in_index);
- }
-
- return {res};
-}
-
-} // namespace nnc
diff --git a/compiler/nnc/backends/interpreter/ops/Transpose.h b/compiler/nnc/backends/interpreter/ops/Transpose.h
deleted file mode 100644
index 5266c2bcb..000000000
--- a/compiler/nnc/backends/interpreter/ops/Transpose.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_CORE_BACKEND_INTERPRETER_TRANSPOSE_
-#define _NNC_CORE_BACKEND_INTERPRETER_TRANSPOSE_
-
-#include "OperationImpl.h"
-#include "mir/ops/TransposeOp.h"
-#include "mir/Tensor.h"
-
-namespace nnc
-{
-
-class Transpose : public OperationImpl<float>
-{
-public:
- std::vector<mir::TensorVariant> operator()() override;
-
- Transpose(const mir::TensorVariant &input, const mir::ops::TransposeOp &op);
-
-private:
- const mir::ops::TransposeOp &_op;
- const mir::Tensor<float> _input;
-};
-
-} // namespace nnc
-
-#endif //_NNC_CORE_BACKEND_INTERPRETER_TRANSPOSE_
diff --git a/compiler/nnc/backends/interpreter/ops/common.cpp b/compiler/nnc/backends/interpreter/ops/common.cpp
deleted file mode 100644
index 6434d357a..000000000
--- a/compiler/nnc/backends/interpreter/ops/common.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cassert>
-
-#include "common.h"
-
-namespace nnc
-{
-
-using namespace mir;
-
-Index shift(const Index &in_index, const Shape &shift_from)
-{
- Index index = in_index;
- assert(index.rank() == shift_from.rank());
- for (int32_t d = 0; d < in_index.rank(); ++d)
- {
- index.at(d) = index.at(d) + shift_from.dim(d);
- }
- return index;
-}
-
-} // namespace nnc
diff --git a/compiler/nnc/backends/interpreter/ops/common.h b/compiler/nnc/backends/interpreter/ops/common.h
deleted file mode 100644
index 33093ab2e..000000000
--- a/compiler/nnc/backends/interpreter/ops/common.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "mir/Index.h"
-#include "mir/Shape.h"
-
-namespace nnc
-{
-
-/**
- * Shift in_index by `shift`
- * @param[in] in_index argument
- * @param[in] shift
- * @return the
- */
-mir::Index shift(const mir::Index &in_index, const mir::Shape &shift_from);
-
-} // namespace nnc
diff --git a/compiler/nnc/backends/soft_backend/CMakeLists.txt b/compiler/nnc/backends/soft_backend/CMakeLists.txt
index c1f1fa6ce..ea0cd1ac6 100644
--- a/compiler/nnc/backends/soft_backend/CMakeLists.txt
+++ b/compiler/nnc/backends/soft_backend/CMakeLists.txt
@@ -1,3 +1,5 @@
+nnas_find_package(Boost REQUIRED COMPONENTS filesystem)
+
set(SOFT_BACKEND_CPP_SOURCES CPPGenerator.cpp ModelAnalyzer.cpp SBSerializer.cpp SequencedIR.cpp)
file(GLOB_RECURSE SOFT_DEF_SOURCES "*.def")
@@ -5,9 +7,8 @@ file(GLOB_RECURSE SOFT_DEF_SOURCES "*.def")
nnc_make_generated_sources("${SOFT_DEF_SOURCES}" ${CMAKE_CURRENT_BINARY_DIR} SOFT_GENERATED_SOURCES)
nnc_add_library(soft_backend_cpp SHARED ${SOFT_BACKEND_CPP_SOURCES} ${SOFT_GENERATED_SOURCES})
-target_include_directories(soft_backend_cpp PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
-
-target_link_libraries(soft_backend_cpp PRIVATE nnc_support mir)
+target_include_directories(soft_backend_cpp PRIVATE ${Boost_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR})
+target_link_libraries(soft_backend_cpp PRIVATE mir ${Boost_LIBRARIES})
# install soft backend c++ library
nnc_install_library(soft_backend_cpp)
diff --git a/compiler/nnc/backends/soft_backend/CPPGenerator.cpp b/compiler/nnc/backends/soft_backend/CPPGenerator.cpp
index f2188c3d3..236881b80 100644
--- a/compiler/nnc/backends/soft_backend/CPPGenerator.cpp
+++ b/compiler/nnc/backends/soft_backend/CPPGenerator.cpp
@@ -19,7 +19,6 @@
#include "mir/Operation.h"
#include "ModelAnalyzer.h"
#include "SBSerializer.h"
-#include "option/Options.h"
#include "CommonData.def"
@@ -49,18 +48,21 @@
#include "cpp_pad.generated.h"
#include "cpp_transpose.generated.h"
#include "cpp_gather.generated.h"
+#include "cpp_broadcast.generated.h"
+
+#include <boost/filesystem.hpp>
-#include <cerrno>
#include <cstring>
#include <fstream>
#include <stdexcept>
-#include <sys/stat.h>
+#include <utility>
namespace nnc
{
using namespace sir;
using namespace std;
+namespace fs = boost::filesystem;
/**
* @brief Creates pointer to some output stream to encapsulate resource management into deleter
@@ -77,17 +79,9 @@ static unique_ptr<ofstream> getStream(const string &path)
return ofs;
}
-/**
- * @brief Creates directory
- * @param path Path to desired directory
- * @throws runtime_error in did not succeed
- */
-static void createDir(const string &path)
+CPPCodeGenerator::CPPCodeGenerator(std::string output_dir, std::string artifact_name)
+ : _output_dir(std::move(output_dir)), _artifact_name(std::move(artifact_name))
{
- int res =
- mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); // NOLINT(hicpp-signed-bitwise)
- if (res != 0 && errno != EEXIST)
- throw runtime_error("Failed to create output directory");
}
void CPPCodeGenerator::materializeModelParams(ostream &out, const Serializer &s)
@@ -126,9 +120,9 @@ void CPPCodeGenerator::run(mir::Graph *graph)
// rename tensors for specific backend language
formatTensorNames(ma);
- createDir(cli::artifactDir);
+ fs::create_directory(_output_dir);
- const string base_path = cli::artifactDir + "/" + cli::artifactName;
+ const string base_path = _output_dir + "/" + _artifact_name;
const string header_path = base_path + ".h";
const string code_path = base_path + ".cpp";
const string params_path = base_path + ".params";
@@ -402,7 +396,7 @@ void CPPCodeGenerator::materializeCode(ostream &out, const ModelAnalyzer &ma, co
{
string class_name = ma.getModelName() + "Model";
- out << "#include \"" << cli::artifactName << ".h\"\n";
+ out << "#include \"" << _artifact_name << ".h\"\n";
// put operations from tflite
out.write(eigen, sizeof(eigen));
@@ -430,6 +424,7 @@ void CPPCodeGenerator::materializeCode(ostream &out, const ModelAnalyzer &ma, co
out.write(cpp_conv_transpose, sizeof(cpp_conv_transpose));
out.write(cpp_transpose, sizeof(cpp_transpose));
out.write(cpp_gather, sizeof(cpp_gather));
+ out.write(cpp_broadcast, sizeof(cpp_broadcast));
// Operations calls into all of the above
out.write(cpp_operations, sizeof(cpp_operations));
// Below call into operations
diff --git a/compiler/nnc/backends/soft_backend/ModelAnalyzer.cpp b/compiler/nnc/backends/soft_backend/ModelAnalyzer.cpp
index b4c025a8e..82e62b531 100644
--- a/compiler/nnc/backends/soft_backend/ModelAnalyzer.cpp
+++ b/compiler/nnc/backends/soft_backend/ModelAnalyzer.cpp
@@ -54,7 +54,7 @@ void ModelAnalyzer::appendOperationToInference(Operation *op, const string &func
}
else if (op->getType() == Operation::Type::output)
{
- assert(!op->getInput(0)->getProducer()->getName().empty());
+ assert(!op->getInput(0)->getName().empty());
}
else
{
@@ -69,10 +69,10 @@ void ModelAnalyzer::appendOperationToInference(Operation *op, const string &func
// process operation inputs
vector<size_t> node_input_tensors;
- for (const auto &input : op->getInputs())
+ for (const Operation::Output *input : op->getInputs())
{
- size_t idx = input.getProducer()->getIndex();
- const Operation *prev_op = input.getProducer()->getNode();
+ size_t idx = input->getIndex();
+ const Operation *prev_op = input->getNode();
assert(_opToDescr.find(prev_op) != _opToDescr.end());
auto call = dynamic_cast<const CallFunction *>(_opToDescr[prev_op]);
assert(call);
@@ -269,9 +269,9 @@ void ModelAnalyzer::analyze(const mir::Graph *g)
std::vector<Operation *> next_nodes;
for (const auto &out : node->getOutputs())
{
- const auto &consumers = out.getConsumers();
- std::transform(consumers.begin(), consumers.end(), std::back_inserter(next_nodes),
- [](Operation::Input *input) { return input->getNode(); });
+ const auto &uses = out.getUses();
+ std::transform(uses.begin(), uses.end(), std::back_inserter(next_nodes),
+ [](Operation::Use use) { return use.getNode(); });
}
if (edge == next_nodes.size())
{
@@ -301,6 +301,7 @@ void ModelAnalyzer::visit(ops::ConcatOp &op) { appendOperationToInference(&op, "
void ModelAnalyzer::visit(ops::Conv2DOp &op)
{
+ assert(op.getNumGroups() == 1);
const auto &kernel_shape = op.getInputShape(1);
const auto &out_shape = op.getOutputShape(0);
const int32_t tmp_size = kernel_shape.dim(1) * kernel_shape.dim(2) * kernel_shape.dim(3) *
@@ -325,6 +326,8 @@ void ModelAnalyzer::visit(ops::FullyConnectedOp &op)
appendOperationToInference(&op, "fullConnect");
}
+void ModelAnalyzer::visit(ops::BroadcastOp &op) { appendOperationToInference(&op, "broadcast"); }
+
void ModelAnalyzer::visit(ops::CappedReluOp &op) { appendOperationToInference(&op, "cappedRelu"); }
void ModelAnalyzer::visit(ops::InputOp &op)
@@ -340,7 +343,7 @@ void ModelAnalyzer::visit(ops::ConstantOp &op)
// FIXME This is to work around deserializeTensors not being able to deserialize tensors of type
// other than float32.
const auto *output = op.getOutput(0);
- if (output->getConsumers().empty())
+ if (output->getUses().empty())
return;
appendOperationToInference(&op, "constant");
@@ -417,6 +420,8 @@ void ModelAnalyzer::visit(mir::ops::LeakyReluOp &op)
void ModelAnalyzer::visit(mir::ops::OutputOp &op) { appendOperationToInference(&op, "out"); }
+void ModelAnalyzer::visit(mir::ops::AbsOp &op) { appendOperationToInference(&op, "absFN"); }
+
void ModelAnalyzer::visit(mir::ops::AddOp &op)
{
appendOperationToInference(&op, "ElementWise<Add>");
diff --git a/compiler/nnc/backends/soft_backend/ModelAnalyzer.h b/compiler/nnc/backends/soft_backend/ModelAnalyzer.h
index fffd28f1a..471c31011 100644
--- a/compiler/nnc/backends/soft_backend/ModelAnalyzer.h
+++ b/compiler/nnc/backends/soft_backend/ModelAnalyzer.h
@@ -47,8 +47,10 @@ public:
*/
void analyze(const mir::Graph *g);
+ void visit(mir::ops::AbsOp &) override;
void visit(mir::ops::AddOp &op) override;
void visit(mir::ops::AvgPool2DOp &op) override;
+ void visit(mir::ops::BroadcastOp &op) override;
void visit(mir::ops::CappedReluOp &op) override;
void visit(mir::ops::ConcatOp &op) override;
void visit(mir::ops::ConstantOp &op) override;
diff --git a/compiler/nnc/backends/soft_backend/SBSerializer.cpp b/compiler/nnc/backends/soft_backend/SBSerializer.cpp
index 025728f90..96fa51580 100644
--- a/compiler/nnc/backends/soft_backend/SBSerializer.cpp
+++ b/compiler/nnc/backends/soft_backend/SBSerializer.cpp
@@ -31,8 +31,8 @@ static_assert(std::numeric_limits<float>::is_iec559, "Unsupported float type");
using namespace std;
-using mir::Shape;
using mir::Index;
+using mir::Shape;
using mir::ShapeRange;
using mir::TensorVariant;
@@ -202,6 +202,12 @@ void Serializer::visit(ops::FullyConnectedOp &op)
serializeShape(op.getOutputShape(0));
}
+void Serializer::visit(ops::BroadcastOp &op)
+{
+ _curOp->paramStartOffset = _buffer.size();
+ serializeShape(op.getOutputShape(0));
+}
+
void Serializer::visit(ops::CappedReluOp &op)
{
_curOp->paramStartOffset = _buffer.size();
@@ -362,11 +368,16 @@ void Serializer::visit(mir::ops::OutputOp & /*op*/)
// no parameters to dump
}
+void Serializer::visit(mir::ops::AbsOp &)
+{
+ _curOp->paramStartOffset = _buffer.size();
+ // no parameters to dump
+}
+
void Serializer::visit(mir::ops::AddOp &op)
{
_curOp->paramStartOffset = _buffer.size();
// Op type is known at codegen Time
- serializeT(static_cast<int32_t>(op.getBroadcast()));
serializeShape(op.getOutputShape(0));
}
@@ -374,7 +385,6 @@ void Serializer::visit(mir::ops::DivOp &op)
{
_curOp->paramStartOffset = _buffer.size();
// Op type is known at codegen Time
- serializeT(static_cast<int32_t>(op.getBroadcast()));
serializeShape(op.getOutputShape(0));
}
@@ -382,7 +392,6 @@ void Serializer::visit(mir::ops::MaxOp &op)
{
_curOp->paramStartOffset = _buffer.size();
// Op type is known at codegen Time
- serializeT(static_cast<int32_t>(op.getBroadcast()));
serializeShape(op.getOutputShape(0));
}
@@ -390,7 +399,6 @@ void Serializer::visit(mir::ops::MulOp &op)
{
_curOp->paramStartOffset = _buffer.size();
// Op type is known at codegen Time
- serializeT(static_cast<int32_t>(op.getBroadcast()));
serializeShape(op.getOutputShape(0));
}
@@ -398,7 +406,6 @@ void Serializer::visit(mir::ops::SubOp &op)
{
_curOp->paramStartOffset = _buffer.size();
// Op type is known at codegen Time
- serializeT(static_cast<int32_t>(op.getBroadcast()));
serializeShape(op.getOutputShape(0));
}
diff --git a/compiler/nnc/backends/soft_backend/SBSerializer.h b/compiler/nnc/backends/soft_backend/SBSerializer.h
index 7659cf242..98b9ce605 100644
--- a/compiler/nnc/backends/soft_backend/SBSerializer.h
+++ b/compiler/nnc/backends/soft_backend/SBSerializer.h
@@ -41,8 +41,10 @@ namespace nnc
class Serializer : public mir::Visitor
{
public:
+ void visit(mir::ops::AbsOp &op) override;
void visit(mir::ops::AddOp &op) override;
void visit(mir::ops::AvgPool2DOp &op) override;
+ void visit(mir::ops::BroadcastOp &op) override;
void visit(mir::ops::CappedReluOp &op) override;
void visit(mir::ops::ConcatOp &op) override;
void visit(mir::ops::ConstantOp &op) override;
diff --git a/compiler/nnc/backends/soft_backend/code_snippets/cpp_broadcast.def b/compiler/nnc/backends/soft_backend/code_snippets/cpp_broadcast.def
new file mode 100644
index 000000000..1d170eb48
--- /dev/null
+++ b/compiler/nnc/backends/soft_backend/code_snippets/cpp_broadcast.def
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+template <int N>
+inline void NdArrayDescForBroadcast(const RuntimeShape &input_shape,
+ const RuntimeShape &output_shape, NdArrayDesc<N> *desc_out)
+{
+ TFLITE_DCHECK(desc_out != nullptr);
+
+ auto extended_input_shape = RuntimeShape::ExtendedShape(N, input_shape);
+ auto extended_output_shape = RuntimeShape::ExtendedShape(N, output_shape);
+
+ int desc_stride = 1;
+ for (int i = N - 1; i >= 0; --i)
+ {
+ desc_out->extents[i] = extended_input_shape.Dims(i);
+ desc_out->strides[i] = desc_stride;
+ desc_stride *= extended_input_shape.Dims(i);
+ }
+
+ for (int i = 0; i < N; ++i)
+ {
+ const int extent0 = extended_input_shape.Dims(i);
+ const int extent1 = extended_output_shape.Dims(i);
+ if (extent0 != extent1)
+ {
+ TFLITE_DCHECK_EQ(extent0, 1);
+ desc_out->strides[i] = 0;
+ desc_out->extents[i] = extent1;
+ }
+ }
+}
+
+void Broadcast4DSlow(const RuntimeShape &input_shape, const float *input_data,
+ const RuntimeShape &output_shape, float *output_data)
+{
+ TFLITE_DCHECK_LE(input_shape.DimensionsCount(), 4);
+ TFLITE_DCHECK_LE(output_shape.DimensionsCount(), 4);
+ const RuntimeShape extended_output_shape = RuntimeShape::ExtendedShape(4, output_shape);
+
+ NdArrayDesc<4> desc;
+ NdArrayDescForBroadcast(input_shape, output_shape, &desc);
+
+ 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)
+ {
+ auto out_idx = Offset(extended_output_shape, b, y, x, c);
+ auto in_idx = SubscriptToIndex(desc, b, y, x, c);
+ output_data[out_idx] = input_data[in_idx];
+ }
+ }
+ }
+ }
+}
diff --git a/compiler/nnc/backends/soft_backend/code_snippets/cpp_conv_transpose.def b/compiler/nnc/backends/soft_backend/code_snippets/cpp_conv_transpose.def
index 9b2aebb54..016ff15e1 100644
--- a/compiler/nnc/backends/soft_backend/code_snippets/cpp_conv_transpose.def
+++ b/compiler/nnc/backends/soft_backend/code_snippets/cpp_conv_transpose.def
@@ -108,4 +108,4 @@ inline void TransposeConv(
MapAsMatrixWithLastDimAsRows(output_data, output_shape);
Gemm(filter_matrix_map.transpose(), im2col_matrix_map, &output_matrix_map);
-} \ No newline at end of file
+}
diff --git a/compiler/nnc/backends/soft_backend/code_snippets/cpp_elementwise.def b/compiler/nnc/backends/soft_backend/code_snippets/cpp_elementwise.def
index 9fc95ecde..422b34242 100644
--- a/compiler/nnc/backends/soft_backend/code_snippets/cpp_elementwise.def
+++ b/compiler/nnc/backends/soft_backend/code_snippets/cpp_elementwise.def
@@ -105,11 +105,10 @@ struct Sub {
}
static inline void Call(
- const float* input1_data, RuntimeShape in1_shape,
- const float* input2_data, RuntimeShape in2_shape,
- float* output_data, RuntimeShape out_shape,
- bool needsBroadcast) {
- if (needsBroadcast) {
+ const float* input1_data, const RuntimeShape& in1_shape,
+ const float* input2_data, const RuntimeShape& in2_shape,
+ float* output_data, const RuntimeShape& out_shape) {
+ if (in1_shape != in2_shape) {
BroadcastBinaryFunction4DSlow<float, float, float>(
in1_shape, input1_data,
in2_shape, input2_data,
@@ -159,11 +158,10 @@ struct Add {
}
static inline void Call(
- const float* input1_data, RuntimeShape in1_shape,
- const float* input2_data, RuntimeShape in2_shape,
- float* output_data, RuntimeShape out_shape,
- bool needsBroadcast) {
- if (needsBroadcast) {
+ const float* input1_data, const RuntimeShape& in1_shape,
+ const float* input2_data, const RuntimeShape& in2_shape,
+ float* output_data, const RuntimeShape& out_shape) {
+ if (in1_shape != in2_shape) {
BroadcastBinaryFunction4DSlow<float, float, float>(
in1_shape, input1_data,
in2_shape, input2_data,
@@ -178,11 +176,10 @@ struct Add {
struct Max {
static inline void Call(
- const float* input1_data, RuntimeShape in1_shape,
- const float* input2_data, RuntimeShape in2_shape,
- float* output_data, RuntimeShape out_shape,
- const bool needsBroadcast) {
- if (needsBroadcast) {
+ const float* input1_data, const RuntimeShape& in1_shape,
+ const float* input2_data, const RuntimeShape& in2_shape,
+ float* output_data, const RuntimeShape& out_shape) {
+ if (in1_shape != in2_shape) {
BroadcastBinaryFunction4DSlow<float, float, float>(
in1_shape, input1_data,
in2_shape, input2_data,
@@ -190,18 +187,19 @@ struct Max {
[](float a, float b) { return std::max(a, b); }
);
} else {
+ auto input1 = MapAsVector(input1_data, in1_shape.FlatSize());
+ auto input2 = MapAsVector(input2_data, in2_shape.FlatSize());
auto output = MapAsVector(output_data, out_shape.FlatSize());
- output = output.cwiseMax(MapAsVector(input2_data, out_shape.FlatSize()));
+ output = input1.cwiseMax(input2);
}
}
};
struct Mul {
- static inline void Call(const float* input1_data, RuntimeShape in1_shape,
- const float* input2_data, RuntimeShape in2_shape,
- float* output_data, RuntimeShape out_shape,
- const bool needsBroadcast) {
- if (needsBroadcast) {
+ static inline void Call(const float* input1_data, const RuntimeShape& in1_shape,
+ const float* input2_data, const RuntimeShape& in2_shape,
+ float* output_data, const RuntimeShape& out_shape) {
+ if (in1_shape != in2_shape) {
BroadcastBinaryFunction4DSlow<float, float, float>(
in1_shape, input1_data,
in2_shape, input2_data,
@@ -255,11 +253,10 @@ struct Mul {
//23.11.2018
struct Div {
static inline void Call(
- const float* input1_data, RuntimeShape in1_shape,
- const float* input2_data, RuntimeShape in2_shape,
- float* output_data, RuntimeShape out_shape,
- bool needsBroadcast) {
- if (needsBroadcast) {
+ const float* input1_data, const RuntimeShape& in1_shape,
+ const float* input2_data, const RuntimeShape& in2_shape,
+ float* output_data, const RuntimeShape& out_shape) {
+ if (in1_shape != in2_shape) {
BroadcastBinaryFunction4DSlow<float, float, float>(
in1_shape, input1_data,
in2_shape, input2_data,
@@ -267,8 +264,10 @@ struct Div {
[](float a, float b) { return a / b; }
);
} else {
+ auto input1 = MapAsVector(input1_data, in1_shape.FlatSize());
+ auto input2 = MapAsVector(input2_data, in2_shape.FlatSize());
auto output = MapAsVector(output_data, out_shape.FlatSize());
- output = output.cwiseQuotient(MapAsVector(input2_data, out_shape.FlatSize()));
+ output = input1.cwiseQuotient(input2);
}
}
};
diff --git a/compiler/nnc/backends/soft_backend/code_snippets/cpp_operations.def b/compiler/nnc/backends/soft_backend/code_snippets/cpp_operations.def
index 2e4acd8a4..f78274e5c 100644
--- a/compiler/nnc/backends/soft_backend/code_snippets/cpp_operations.def
+++ b/compiler/nnc/backends/soft_backend/code_snippets/cpp_operations.def
@@ -495,48 +495,18 @@ void tanhActivation(Tensor &out, const char* params, const Tensor& in) {
Tanh(input, inp_d, output, out_d);
}
-// These operations (add, mul, max) takes as input multiple tensors, at least 2, likely less then 7
-// parameter pack provides generalization for all possible number of inputs
-template <typename F, typename ...Args>
-void ElementWise(Tensor &out, const char* params, const Args& ...inputs) {
- static_assert(sizeof...(inputs) >= 2, "ElementWise op must have >= 2 inputs");
-
- const float* input[] = {inputs.getData()...};
- Shape in_shapes[] = {inputs.getShape()...};
- RuntimeShape in_runtime_shapes[] = {shapeToRuntimeShape(inputs.getShape())...};
+template <typename F>
+void ElementWise(Tensor &out, const char *params, const Tensor &lhs, const Tensor &rhs)
+{
+ const float *lhs_data = lhs.getData();
+ const float *rhs_data = rhs.getData();
- const int32_t num_inputs = sizeof...(inputs);
- const bool needs_broadcast = (bool)deserializeT<int32_t>(params);
const Shape out_shape = deserializeShape(params);
-
out.reshape(out_shape);
- out.fillData(input[0], in_shapes[0].getNumElems());
- const auto out_rt = shapeToRuntimeShape(out_shape);
- if (!needs_broadcast) {
- for (int32_t i = 1; i < num_inputs; ++i) {
- F::Call(out.getData(), out_rt,
- input[i], in_runtime_shapes[i],
- out.getData(), out_rt,
- false);
- }
- } else {
- auto running_shape = RuntimeShape::ExtendedShape(4, in_runtime_shapes[0]);
- std::vector<float> inp_tmp(static_cast<size_t>(out_shape.getNumElems()));
-
- for (int32_t i = 1; i < num_inputs; ++i) {
- assert(running_shape.FlatSize() <= out_shape.getNumElems());
-
- std::copy(out.getData(), out.getData() + running_shape.FlatSize(), inp_tmp.begin());
-
- F::Call(inp_tmp.data(), running_shape,
- input[i], in_runtime_shapes[i],
- out.getData(), out_rt,
- true);
- // This modifies the running shape
- running_shape.maxShape(RuntimeShape::ExtendedShape(4, in_runtime_shapes[i]));
- }
- }
+ F::Call(lhs_data, shapeToRuntimeShape(lhs.getShape()),
+ rhs_data, shapeToRuntimeShape(rhs.getShape()),
+ out.getData(), shapeToRuntimeShape(out_shape));
}
// TODO refactor tflite's code for this op
@@ -627,6 +597,18 @@ void sqrtFN(Tensor& out, const char* params, const Tensor& in) {
Sqrt(input, inp_d, out.getData());
}
+void absFN(Tensor &out, const char *params, const Tensor& in) {
+ out.reshape(in.getShape());
+
+ const float* in_data = in.getData();
+ float* out_data = out.getData();
+ const index_t num_elements = in.getShape().getNumElems();
+
+ for (index_t i = 0; i < num_elements; ++i) {
+ out_data[i] = abs(in_data[i]);
+ }
+}
+
void transpose(Tensor &out, const char *params, const Tensor &in) {
TransposeParams transpose_params;
transpose_params.perm_count = static_cast<int8>(deserializeT<int32_t>(params));
@@ -657,6 +639,15 @@ void gather(Tensor &out, const char *params, const Tensor &data, const Tensor &i
shapeToRuntimeShape(out.getShape()), out.getData());
}
+void broadcast(Tensor &out, const char *params, const Tensor &in)
+{
+ Shape out_shape = deserializeShape(params);
+ out.reshape(out_shape);
+
+ Broadcast4DSlow(shapeToRuntimeShape(in.getShape()), in.getData(),
+ shapeToRuntimeShape(out_shape), out.getData());
+}
+
void constant(Tensor& out, const char* params) {
out = deserializeTensor(params);
}
diff --git a/compiler/nnc/backends/soft_backend/code_snippets/cpp_reduce.def b/compiler/nnc/backends/soft_backend/code_snippets/cpp_reduce.def
index cbf2a4873..e38277802 100644
--- a/compiler/nnc/backends/soft_backend/code_snippets/cpp_reduce.def
+++ b/compiler/nnc/backends/soft_backend/code_snippets/cpp_reduce.def
@@ -182,4 +182,4 @@ inline bool Mean(const T* input_data, const int* input_dims,
}
}
return true;
-} \ No newline at end of file
+}
diff --git a/compiler/nnc/cmake/config.cmake b/compiler/nnc/cmake/config.cmake
index efff43f98..8623c8c11 100644
--- a/compiler/nnc/cmake/config.cmake
+++ b/compiler/nnc/cmake/config.cmake
@@ -8,8 +8,6 @@ set(NNC_INTERPRETER_DIR ${NNC_ROOT_SRC_DIR}/backends/interpreter)
set(NNC_SUPPORT_DIR ${NNC_ROOT_SRC_DIR}/support)
set(NNC_PASS_DIR ${NNC_ROOT_SRC_DIR}/pass)
-set(OPTIONS_SRC ${NNC_DRIVER_DIR}/Options.cpp)
-
#
# Other additional useful cmake variables
#
@@ -23,7 +21,8 @@ set(NNC_INSTALL_LIB_PATH ${NNC_INSTALL_PATH}/lib) # directory that contains othe
#
# find necessary packages
#
-find_package(HDF5 COMPONENTS CXX QUIET)
+nnas_find_package(HDF5 QUIET)
+
# defines if hdf5 package was found
if(HDF5_FOUND)
set(NNC_HDF5_SUPPORTED ON)
diff --git a/compiler/nnc/doc/COC_for_nnc_developer.rst b/compiler/nnc/doc/COC_for_nnc_developer.rst
deleted file mode 100644
index e1f17f2c5..000000000
--- a/compiler/nnc/doc/COC_for_nnc_developer.rst
+++ /dev/null
@@ -1,20 +0,0 @@
-Code Of Conduct for NNC developer
-=================================
-
-Pull requests
--------------
-
-- Each pull request should be less than 300 lines(excluding includes, namespaces and empty lines)
-- Do not make pull requests more than +500 lines in total.
- Exception are PRs mostly consisting of non-code changes(e.g. docs, licenses)
-- If you make any changes not directly related to PR's main purpose (e.g. formatting, code style fixes),
- make a note in PR description
-
-The Pull Requests review comments rules
---------------------------------
-
-- The comments should be clear and descriptive.
-- Don't use any empty lines with single symbol, smile, etc.
-- Try to put all your requirements in one review to avoid multi-step iterations of the review.
-- All comments must be informative and clear for every developer. It's not allowed to use jokes or other
- constructions with ambiguous meaning.
diff --git a/compiler/nnc/doc/codestyle.rst b/compiler/nnc/doc/codestyle.rst
deleted file mode 100644
index f2fd3388a..000000000
--- a/compiler/nnc/doc/codestyle.rst
+++ /dev/null
@@ -1,223 +0,0 @@
-Low-level issues
-================
-
-Naming styles:
---------------
-
-- `snake_case` - name starts with lower case, words divided by underscore
-- `CamelCase` - name starts with Upper case, words starts with Upper case, no divider
-- `camelCase` - name starts with lower case, words(except first) starts with Upper case, no divider
-- `_camelCase` - name starts with underscore, first word starts with lower case, other with Upper case, no divider
-- `ALL_UPPER` - all characters in Upper case, words divided by underscore
-
-General indentations and limits:
---------------------------------
-
-- Line of code should be no longer than 100 characters(including comments)
-- Opening brace should stay on the same line with corresponding statement
-- Stick **\*** and **&** in variable declaration to type name: **Shape* s;**
-- Do not declare multiple pointer or reference variables separated by commas
-- Block contents of **{}** should be indented with 2 spaces, excluding:
-
- + access modifiers in classes
- + namespaces
- + initializer lists
- + **case** contents
-
-- No tabs allowed, only spaces
-
-Directories and file names:
----------------------------
-
-- Directory names should be in snake_case
-- File names should be in CamelCase(SomeFile.cpp, SomeFile.h)
-
-Namespaces:
------------
-
-- Namespaces names should be in came_case, one word names are prefered: **cli**, **mir**, **some_long_namespace**
-- Use unnamed namespaces to hide all non-interface functions and classes of translation unit
-- No **using namespace** or **using namespace::classname** in headers
-- Namespace contents are not indented
-
-Enum & Macros:
---------------
-
-- Enum(class and non-class) names should be in `CamelCase`
-- Elements of class enum should be in `camelCase`
-- Non-class enum elements should be in `ALL_UPPER` case
-- Macro names should be in `ALL_UPPER` case
-
-Functions & Methods:
---------------------
-
-- Global functions and method names should be in `camelCase`
-- Virtual methods should be marked with **override** in derived class
-
-Global variables:
------------------
-
-- Global variables(static && non static) should be in `camelCase` (including constant data)
-
-Local variables:
-----------------
-
-- Names should be in snake_case
-
-Classes & Structures & Unions:
-------------------------------
-
-- Names of classes, structures and unions should be in `CamelCase`
-- Private(and protected) data should be in `_camelCase`
-- Public data should be in `camelCase`
-- Classes should have only one section of each type(public, private)
-- Access modifiers(public, private, protected) should not be indented relative to class declaration
-- Sections should go in order: public, protected, private
-- Methods go first, then data
-- If class has constructors they should be first defined methods
-- If class has destructor it should go after constructors
-
-.. code-block:: c++
-
- class AnotherFoo : public Foo {
- public:
- explicit AnotherFoo() = default;
- ~AnotherFoo() = default;
-
- /**
- * @brief Definitely does something
- */
- void doSomething() noexcept {
- //...//
- }
- protected:
- private:
- int _foosInt;
- }
-
-Templates:
-----------
-
-- Add space before angle brackets in template declaration and no spaces inside: **template <int>**
-- Do not add space before angle brackets in template instantiation and no spaces inside: **foo<12321>()**
-- Use **typename** instead of **class** in template parameters
-
-Control flow contructions:
---------------------------
-
-- Function call should have no spaces around parentheses and have spaces after commas: **someFunc(arg1, arg2);**
-- **if**, **while**, **for** should have space after reserved word: **if (cond)**, **while (cond)**, **for (cond)**
-- **else**, **catch** key words should be placed on the same line as closing code block brace:
-
-.. code-block:: c++
-
- if (cond1) {
- doThis();
- } else if (cond2) {
- doThat();
- } else {
- getConfused();
- }
-
-.. code-block:: c++
-
- try {
- doThis();
- } catch (PassException& e) {
- doThat();
- } catch (OtherException& e) {
- doABarrelRoll();
- }
-
-- **switch** should always have **default** case, if all cases are checked and **default** is redundant, place **assert(false)** in it
-- Avoid redundant braces inside switch **case**
-- **case** labels should be indented by 2 spaces relative to **switch**
-- Code in **code** section sould be indented by 2 spaces relative to corresponding **case**
-- Do not use non-empty cases with fallthrough( i.e. always add **break** )
-- Braces after **if**, **for**, **while** are optional if condition and predicate code are one lined
-- If **if** operator has else branch it's content should be one lined too to apply "no braces" rule:
-
-.. code-block:: c++
-
- if (cond)
- a = this;
- else
- b = that;
-
-.. code-block:: c++
-
- switch(cond) {
- case 0:
- case 1:
- doThis();
- break;
- case 2:
- doThat();
- break;
- case 3: {
- int x = 42;
- useX(x);
- }
- default:
- assert(false);
- }
-
-
-Comments:
----------
-
-- All source files have to start with the following banner
-
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-- Function, methods(except trivial getters and setters), classes, structures, enums, should be commented in described doxygen format.
-- All mentioned entities should have **@brief** field, exception is getters, they could define **@return** instead
-- Functions have to describe their arguments(**@param**), template arguments(**@tparam**), return value if non-void(**@return**), what kinds of exceptions could be thrown(**@throws**)
-- It's appreciated to comment all complex algorithms, long code lines, local and global variables as well.
-- One line comments starts with ///
-- Multiline comments should start on a second line, all lines start from *****, text separated from ***** with whitespace and goes like this:
-- Don't keep any commented out lines inside source code: simply remove them from sources.
-
-.. code-block:: c++
-
- /**
- * @brief some method that does something
- * @param arg some input data
- * continue arg explanation on second line
- * @throws some exceptions
- * continue explanation of throws
- *
- * Detailed description
- */
-
-Type aliases:
--------------
-
-- Use **using** instead of **typedef**
-- Aliases for basic types should have names in `snake_case`: **using axis_t = int32_t;**
-- Aliases for user-defined types should have names in `CamelCase`: **using Shape = mir::Shape;**
-
-Misc:
------
-
-- No spaces around braces in initializer lists, spaces after commas: **{1, 2, 3}**
-- No spaces around square brackets in arrays: **int a[5];** **arr[53] = 1;**
-- Declare methods as const if they do not change the object
-- Declare objects and pointers with const modifier if possible
-- Always add spaces around assignment operators(**=**, **+=**, etc): **a = 32;**
-- Add spaces around operators reasonably, do not add spaces around operators if you want to group operations without parentheses: **res = a*b + c*d;**
-- Add **noexcept** specifier if function does not throw
diff --git a/compiler/nnc/doc/nnc_debug.rst b/compiler/nnc/doc/nnc_debug.rst
deleted file mode 100644
index 8eccdb6bf..000000000
--- a/compiler/nnc/doc/nnc_debug.rst
+++ /dev/null
@@ -1,76 +0,0 @@
-Debugging nnc
--------------
-
-Enabling and disabling debug
-````````````````````````````
-To enable debug in nnc, run it with ``--debug [<filename>]`` command line option specified.
-If ``filename`` is provided, debug info will be dumped into that file (``std::cerr`` is default destination).
-
-Debugging your code and using NNC_DEBUG macro
-`````````````````````````````````````````````
-To use nnc debugging in your code do the following:
-
-1. Include ``debug.h`` (currently located in nnc/include).
-
-2. Define a ``DEBUG_AREA`` around your code as a string. Example::
-
- ...
- #define DEBUG_AREA "areaname"
- <your_code>
- #undef DEBUG_AREA
- ...
-
- In practice, it is enough to only set ``DEBUG_AREA`` at the top of a file, to specify the debug type for the entire module. Be careful that you only do this after including ``debug.h`` and not around an ``#include`` of headers. However, you may specify several different debug areas in a single file by undefining DEBUG_AREA and then redefining it again::
-
- ...
- #define DEBUG_AREA areaname1
- <some code>
- #undef DEBUG_AREA
- ...
- #define DEBUG_AREA areaname2
- <some other code>
- #undef DEBUG_AREA
- ...
-
-3. Place debug operations in your code inside ``NNC_DEBUG`` macro (``DEBUG_AREA`` must be defined!). Use ``dbgs()`` to get ``std::ostream`` used for debugging purposes. Example::
-
- ...
- NNC_DEBUG(dbgs() << "Some info" << std::endl);
- NNC_DEBUG(debugPrintCounter++);
- ...
-
-4. Another way is using ``DEBUG_SPECIFIED`` macro. It does not need ``DEBUG_AREA`` to be defined. Instead that area is provided as an argument to it. This can be useful to prevent excessive #define/#undef lines. Example::
-
- ...
- DEBUG_SPECIFIED("areaname1", dbgs() << "Some info" << std::endl);
- DEBUG_SPECIFIED("areaname2", debugPrintCounter++);
- ...
-
- Instead of::
-
- ...
- #define DEBUG_AREA areaname1
- NNC_DEBUG(dbgs() << "Some info" << std::endl);
- #undef DEBUG_AREA
- #define DEBUG_AREA areaname1
- NNC_DEBUG(debugPrintCounter++);
- #undef DEBUG_AREA
- ...
-
-Managing debug areas
-````````````````````
-To execute debug operations only in specific areas use ``--debug-area <area_names>`` command line option. ``<area_names>`` must be one or more area names separated by comma. Note that this option does not enable debug by itself (``--debug`` option still has to be specified for that). Example::
-
- nnc --debug log.txt --debug-area importer,interpreter ...
-
-In that case debug operations will only be executed in "importer" and "interpreter" areas.
-::
-
- DEBUG_SPECIFIED("interpreter", debugCounter++); // Will be executed
- DEBUG_SPECIFIED("importer", debugCounter++); // Will be executed
- DEBUG_SPECIFIED("other_area", debugCounter++); // Will not be executed
-
-Building for release
-````````````````````
-To completely remove all debug code from executable, define ``NO_NNC_DEBUG`` when building nnc.
-
diff --git a/compiler/nnc/doc/nnc_interpreter_testing.rst b/compiler/nnc/doc/nnc_interpreter_testing.rst
deleted file mode 100644
index af733e216..000000000
--- a/compiler/nnc/doc/nnc_interpreter_testing.rst
+++ /dev/null
@@ -1,114 +0,0 @@
-NNC Interpreter Testing System
-##############################
-
-Requirements
-************
-
-To generate testing data you need Python 3 and following Python libraries:
-
-#. tensorflow
-#. flatbuffers
-#. numpy
-
-All of them are listed in `gen/requirements.txt` and can be installed using `pip -r requirements.txt`.
-
-Step 1: Generating test data
-****************************
-
-#. `cd gen`
-#. `./run_gen.sh`
-
-After this a number of `.fb` files will appear in `test_data` directory. Each of them is a flatbuffers-serialized file containing test data - an array of structures containing information about a single NN operation.
-
-This information includes:
-
-* Type
-* Inputs
-* Outputs
-* Kernels (if applicable)
-* Padding type (if applicable)
-* Strides (if applicable)
-* Other parameters like pooling type, axis etc (if applicable)
-
-Step 2: Running tests
-*********************
-
-Running cmake after generating the test data will produce a number of test targets named "interpreter_<op_name>_test". They can be run using:
-
-`ctest -R "interp"`
-
-from the cmake build directory.
-
-Adding new operation type to the testing system
-***********************************************
-
-Flatbuffers schema describing serialized test info file layout is located in the `op_info.fbs` file.
-
-First, new operation name should be added to the `enum OperatorType`.
-
-Second, information about the parameters that this operation takes should be added to the `OP_FORMATS` dict in the `gen/gen_test_data.py` script.
-
-This information is represented by a tuple of strings, which denote the order of operation parameters in the `test_data/test_description.txt` file (see `Test description file format <test_descr_>`_). At the moment, each string is one of:
-
-* `kernels`
-* `padType`
-* `poolType`
-* `shapes`
-* `axis`
-
-Third, a section describing test parameters for the new operation should be added to `test_data/test_description.txt` file, analagously to other operations in there (see `Test description file format <test_descr_>`_).
-
-Fourth, another method for generating the operation result should be added to the `OpInfoProvider` class in `gen/get_test_data.py` script. This method should use Tensorflow to calculate the reference result for a given set of operation parameters, analagously to how it is done for the other operation types. Note that the name of the method **has** to be of the form `get_<lowercase_op_name_as_in_test_description.txt>_result`.
-
-.. _test_descr:
-
-Test description file format
-****************************
-
-This file consists of lines, where each line is one of:
-
-* an empty line
-* a comment, starting with `#`
-* an operation name
-* operation parameters description
-
-A line with an operation name acts a start of a block of lines containing parameter descriptions for this operation.
-
-Each line with an operation parameter description should follow the pattern described in the `OP_FORMATS` dict in the `gen/get_test_data.py` script. Each operation has `inputs` implicitly as a first element, and `inputs` description is always first in every operation parameter description line.
-
-Example of operation parameters description line for CONV_2D operation:
-
-`[64, 64, 4] [3, 3, 4, 2] VALID [1, 1]`
-
-* `[64, 64, 4]` means that for this operation an input of shape `[64, 64, 4]` will be generated
-* `[3, 3, 4, 2]` means that for this operation a kernel of shape `[3, 3, 4, 2]` will be generated
-* `VALID` denotes padding type
-* `[1, 1]` denotes strides
-
-This format follows what can be found in the `OP_FORMATS` map: `'CONV_2D': ('kernels', 'padType', 'shapes')`.
-
-In general, there may be multiple inputs, kernels or shapes that operation has. In this case, multiple parameters of the same type, like `shapes` for example, should be wrapped in an additional pair of square brackets.
-
-For example for `POOL_2D`:
-
-`[64, 64, 4] VALID MAX [[3, 3] [1, 1]]`
-
-* `[3, 3]` denotes pooling window shape
-* `[1, 1]` denotes strides
-
-As both windows shape and strides are parameters of type `shapes` as denoted in `OP_FORMATS` dict - `'POOL_2D': ('padType', 'poolType', 'shapes')` - they are grouped using another pair of square brackets (note the absence of commas in there).
-
-The fact that the first shape is interpreted as a pooling window shape, and the second as strides, is represented in the C++ part of this testing system, where these serialized files are read and Model IR graph is constructed.
-
-Random tensor generation
-************************
-
-Currently, random values for the inputs and kernels are taken from a uniform distribution over [-5, 5).
-
-This is not the only way to randomly initialize the tensors, and other methods might also be desirable in the future. One way to support this could be to define a number of named (and possibly parametrized) initialization methods, and add them to input and kernel shape descriptions, for example like this:
-
-.. code-block::
-
- CONV_2D
- [28, 28, 3]=UNIFORM(-2, 3) [3, 3, 3, 10]=NEAR_ZERO VALID [1, 1]
-
diff --git a/compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_DLD.rst b/compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_DLD.rst
deleted file mode 100644
index aee187e83..000000000
--- a/compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_DLD.rst
+++ /dev/null
@@ -1,1006 +0,0 @@
-========================
-SW Detailed Level Design
-========================
-
-.. contents::
-
-.. section-numbering::
-
-.. raw:: pdf
-
- PageBreak oneColumn
-
-
-**Revision history**
-
-+--------+---------------+----------------------------+---------------------------------------+---------------------+
-| Ver. | Date | Contents | Authors | Approver |
-+========+===============+============================+=======================================+=====================+
-| 0.1 | 2018.06.20 | Initial version | Vostokov Sergey | Sung-Jae Lee |
-+--------+---------------+----------------------------+---------------------------------------+---------------------+
-| 0.2 | 2018.06.21 | SE member review | Alexey Kondrashov | |
-+--------+---------------+----------------------------+---------------------------------------+---------------------+
-| 1.0 | 2018.06.22 | Final DR1 version | Vostokov Sergey | Sung-Jae Lee |
-+--------+---------------+----------------------------+---------------------------------------+---------------------+
-| 2.0 | 2018.09.03 | Add ModelIR,Caffe frontend,| Vostokov Sergey | Sung-Jae Lee |
-| | | CPU Soft backend, CLI | | |
-+--------+---------------+----------------------------+---------------------------------------+---------------------+
-
-|
-
-**Terminology and Abbreviation**
-
-.. list-table::
- :widths: 10 30
- :header-rows: 0
-
- * - OS
- - Operating System
- * - OS API
- - Application interface of OS
- * - HW
- - Hardware
- * - SW
- - Software
- * - NN
- - Neural Network
- * - NN model
- - Neural network model (Instance of NN built with ML framework)
- * - NN compiler
- - The compiler for neural network
- * - ML framework
- - The machine learning framework
- * - TF/TF Lite
- - Tensorflow/Tensorflow Lite ML framework
- * - IR
- - Intermediate representation
- * - CI/CI system
- - Continuous integration system
- * - UI
- - The user interface
- * - GUI
- - The graphical user interface
- * - CLI
- - The command-line interface
- * - CG
- - Computational Graph
-
-|
-
-**References**
-
-[1] Vostokov Sergey, `SW Requirements Specification <18_NN_Compiler_and_Optimizer_SRS.rst>`_
-
-[2] Vostokov Sergey, `SW High-Level Design <18_NN_Compiler_and_Optimizer_HLD.rst>`_
-
-
-
-Overview
-========
-
-Scope
------
-
-The main goal of the project is to develop a compiler for neural networks to produce executable artifact for specified SW and HW platform.
-
-The development scope includes the following components:
-
-- Develop importer module to parse, verify and represent NN model for further optimization and compilation
-- Develop code emitters to produce executable binary for CPU and GPU
-
-|
-| **2018 year goals:**
-
-- Support TensorFlow Lite NN model format
-- Support Caffe NN model format
-- Support Caffe2 NN model format (Optional)
-- Support compilation of MobileNet NN
-- Support compilation of Inception v3 NN
-- Support ARM CPU
-- Support ARM GPU (Mali)
-- Support Tizen OS
-- Support SmartMachine OS (Optional)
-|
-
-.. list-table:: Table 1-1. Target Model
- :widths: 23 50 20
- :header-rows: 1
-
- * - Product
- - Target Model Name
- - Comment
-
- * - Tizen phone
- - Tizen TM2
- - Reference device
-
- * - Tizen device
- - Odroid XU4
- - Reference board
-
- * - SmartMachine target
- - Microvision mv8890, exynos8890
- - Reference device
-
-
-Design Consideration
---------------------
-
-Deep learning software demands reliability and performance. The common approach which comes from the history is to develop
-a SW framework (machine learning framework) which would compute each step of the neural network inference process using
-supported hardware. This approach is used in many popular solutions like Google Tensorflow/Tensorflow Lite, Caffe/2, etc.
-Traditionally, neural network developers build a computation graph and then an appropriate machine
-learning framework interprets it. The latest discoveries in AI field show that the node-visitor method of execution is inefficient. As a result,
-a second approach has been worked out by the industry, which is a neural network compiler that executes code more efficiently.
-
-This document presents the design of the *nncc*, a neural network compiler collection. The design should provide the easiest
-way to extend the functionality of the *nncc* by adding new modules with the following features:
-
- - Support neural networks produced by various machine learning frameworks;
- - Produce an artifact taking advantages of various hardware including specialized processors like NPU;
- - Apply new domain specific optimization techniques over given NN.
-
-Non-functional requirements to the developed software are well-described in the SW Requirements Specification, such requirements are not shown here to avoid duplication.
-
-Constraints
------------
-
-See constraints in SW Requirements Specification.
-
-|
-
-.. list-table:: Table 1-2. Assumptions, Dependencies and the Constraints
- :widths: 23 40 23
- :header-rows: 1
-
- * - Item
- - Assumptions, Dependencies and the Constraints
- - Reference
-
- * - Tizen SW Platform
- - The following items should be provided:
- - Tizen API
- - Tizen kernel
- - Tizen FW
- - Tizen SDK
- - Tizen naming convention
- |
- -
- - `www.tizen.org <www.tizen.org>`_
- - `wiki.tizen.org <wiki.tizen.org>`_
- - `developer.tizen.org <developer.tizen.org>`_
-
- * - SmartMachine OS Platform
- - The following items should be provided:
- - SmartMachine API
- - SmartMachine kernel
- - SmartMachine FW
- - SmartMachine SDK
- - SmartMachine naming convention
- |
- -
- - `Platform confluence <http://suprem.sec.samsung.net/confluence/pages/viewpage.action?pageId=81833987>`_
- - `Github <https://github.sec.samsung.net/RS7-SmartMachine>`_
- - `Functional Safety confluence <http://suprem.sec.samsung.net/confluence/display/ASEC/Adaptive+AUTOSAR>`_
-
-
- * - Host OS
- - Linux-based OS (Ubuntu, Archlinux, etc)
- -
- - `Ubuntu site <https://www.ubuntu.com/>`_
- - `Archlinux site <https://www.archlinux.org/>`_
-
- * - Tizen target HW
- - The reference device should be provided: Tizen TM2
- -
-
- * - SmartMachine target HW
- - The reference device should be provided
- -
-
-
-SW Detailed Structure Design
-============================
-
-SW Block Structure
-------------------
-
-Top-Level Components of the nncc described in HLD. More detailed structure and class diagram will be available after development completion.
-
-
-SW Block Feature
-----------------
-#. Initialization: configure all internal modules (see `{Initialization} Detailed Design`_)
-#. IR: representation of input NN model for further processing (see `{NN model Intermediate Representation} Detailed Design`_)
-#. Frontend: Import NN model (see `{Import NN model} Detailed Design`_)
-
- - *Caffe frontend*: includes the parser of Caffe NN model format, verifier to ensure that parsed data is valid and consentient, and Caffe-specific IR converter
- - *Tensorflow Lite frontend*: includes the parser of Tensorflow NN model format with automatic version recognition feature, verifier to ensure that parsed data is valid and consentient, and Tensorflow Lite-specific IR converter to Model IR
-
-#. Backend: Generate the code (see `{Generate the code} Detailed Design`_)
-
- - *Interpreter:* As it was described in SW High-Level Document imported NN model may proceed through three step of Intermediate representation: Model IR, Coarse-Grained IR, Fine-Grained IR. The Interpreter backed uses each this IR to do inference of given NN model. As the output, the user gets the resulting calculation of all NN ops included into original computation graph.
- - *Binary*:This type refers to generating binary code that can be executed on the target device. NN compiler can generate code that is either executed solely on CPU or takes advantage of the GPU when possible if the corresponding target was specified. The user may want to incorporate 3rd party libraries included into target firmware or delivered with the application package. In this case, the compiler prepares the data following EABI convention and embeds an invocation of high-level functions by appropriate symbol.
- - *Soft*: Resulting program is a generated source code in high-level programming language C or C++. Here there are two options: the first one is to generate the source code that does not depend on libraries outside of itself, with the exception of system libraries. The second one is to include the code to invoke high-level functions from 3rd party libraries. For example, it may be an invocation of matrix multiplication from GEMM library.
-
-
-
-SW Detailed Operation Design
-============================
-
-
-
-{Initialization} Detailed Design
---------------------------------
-
-Major Function
-``````````````
-
-To provide a valid configuration session for all modules of *nncc* using user input from the command line/config file/environment variables.
-
-Operation Sequence
-``````````````````
-
-Initialization of the *nncc* includes command line option processing, configuration of its subsystems as well as any error checking possible at this stage. It consists of the following steps:
-
- #. Collect all command line options and verify their format for validity (no syntax errors etc.)
- #. Check for validity and then process general options
- #. Load subsystem modules
- #. For each one of them:
-
- - Configure
- - Pass command line options
- - Check command line options for validity (for example, check that every required option is present)
-
-At the end of this process each subsystem is configured and has access to all data needed for its operation.
-
-
-{NN model Intermediate Representation} Detailed Design
-------------------------------------------------------
-
-Major Function
-``````````````
-
-To provide access to NN model representation in order to perform transformations and optimizations improving performance of code generation.
-
-Overview
-````````
-Model IR consists of 3 main parts:
-
-* Graph - represents the computation graph
-* Operation - represents single operation
-* Visitor - declares an interface used for graph traversal
-
-Graph
-`````
-Graph contains information about graph input/output nodes and list of all nodes in graph.
-
-Responsible for allocating operations and keeps all allocated operation references.
-`Graph` class takes care of graph traversal considering all node input/output dependencies.
-
-Operation
-`````````
-Each operation contains:
-
-- Operation id (used to uniquely identify operation in computation graph for debugging purposes)
-- Operation name (set by importer, used to distinguish inputs/outputs and for debugging purposes)
-- Number of inputs and outputs
-- List of inputs (each represented by operation reference and output index from that node)
-- List of outputs (list of operations which take any resulting data from this operation)
-- Shapes of output tensors
-- Any information specific to operation (i.e. convolution kernel)
-
-Visitor
-```````
-Base class used for traversing computation graph.
-
-Defines set of operations on IR nodes to be provided by IR user.
-
-Supposed to be the only way used for graph processing.
-
-
-{Import NN model} Detailed Design
----------------------------------
-
-Major Function
-``````````````
-
-To convert given NN model from framework-specific IR to Model IR for further processing.
-
-Operation Sequence
-``````````````````
-
-As you may see on the diagram, neural network import is the main function of the compiler front-end part. The result
-of this operation is a computation graph which is presented as Model IR.
-
-.. image:: images/nncc_idef0_a12.png
- :scale: 100%
-
-The import process consists of three parts:
-
- #. NN model parsing
- #. Verification of the result from the previous step
- #. Converting the model to the Model IR
-
-During the first step, file or files containing the model are read and represented in some format specific to each NN framework.
-
-Verification step is included to ensure that:
-
- - None of the files constituting the model are damaged
- - Model format corresponds to the specified one
- - Version of the model format corresponds to the specified one
-
-The most important step is accurately converting the model from the framework-specific representation to the Model IR.
-This conversion includes:
-
- - *Translation of the NN model computation graph to the Model IR computation graph.*
- During the translation new nodes may be introduced - for example, a high-level NN operation may be split into a few smaller ones.
- - *NN model parameter layout conversion.*
- The way parameters (also known as weights) of a model are layed out in each specific NN framework may differ, and it is necessary to convert such layout into a unified format.
- - *NN operation parameter conversion.*
- Each NN operation has a set of its own parameters describing the way this operation should be performed, and these parameters also differ between frameworks.
-
-Resulting Model IR is equivalent to the initial NN model in terms of how NN model inputs would be transformed into its outputs if all the operations in the Model IR were executed.
-
-
-TensorFlow Lite importer
-````````````````````````
-
-Development of the TF Lite importer (frontend) is in progress. The detailed design description will be available after development completion.
-
-
-Caffe importer
-``````````````
-
-Basics
-######
-
-Caffe models consist of *layers*, which is more or less a synonym to "NN operation".
-
-Layers input and output *blobs*, which is more or less a synonym to "tensor".
-
-Every layer has a type (for example "Convolution"), a name, bottom blobs (input tensors), top blobs (output tensors).
-
-Note that frequently layer's name and output blob might be identical, which might be confusing. So remember: "name" is just a name of a layer. But "top" and "bottom" are names of the *blobs*, and they should be consistent over the sequence of layers (i.e. a bottom blob of every layer must have the same name as a top blob of some other layer).
-
-Example:
-
-.. code-block:: none
-
- layer {
- name: "conv1_3x3_s2"
- type: "Convolution"
- bottom: "data"
- top: "conv1_3x3_s2"
- param {
- lr_mult: 1
- decay_mult: 1
- }
- phase: TEST
- convolution_param {
- num_output: 32
- bias_term: false
- pad: 0
- kernel_size: 3
- stride: 2
- weight_filler {
- type: "xavier"
- std: 0.01
- }
- }
- }
-
-Model files
-###########
-
-Models are frequently distributed as a pair of files:
-
-* `.prototxt` file, containing the text version of the model used for deployment (without various layers needed for training etc)
-* `.caffemodel` binary file, containing the version of the model that was (or still can) be used for training, and containing trained model weights
-
-Ideally, Caffe importer should also support this - it should also accept two files like this, read the first one to get the NN model architecture, read the second one to get the weights.
-
-Instead, currently we do the following - we just take the first file (`.prototxt`), fill it with weights (it will still remain a `.prototxt` file), and then use it as input to the importer. Filling a `.prototxt` with weights can be done using `caffegen` tool in `contrib/caffegen` - just run `caffegen init < "path-to-prototxt" > "path-to-output"`. This command will result in another `.prototxt` file, but this time it will be filled with weights (yes, the size of this file will be large).
-
-Now this `.prototxt` file can be turned into a binary `.caffemodel` file. Unfortunately, I don't know a convenient way to do this, so currently I just take the code from `src/caffe/util/io.cpp` from Caffe sources, which has functions that can read and write Caffe models in text and binary formats (these functions are `ReadProtoFromTextFile`, `WriteProtoToTextFile`, `ReadProtoFromBinaryFile` and `WriteProtoToBinaryFile`), then I insert them to the `proto_reader.cpp` in nncc sources, and use them to read and write Caffe models.
-
-Caffe model preparation for importer
-####################################
-
-Currently we do not support Caffe layers like `BatchNorm`, `Scale`, `Split`, so we have to manually remove them from the `.prototxt` models.
-
-After this it is necessary to make sure that top blobs of previous layers connect to the bottom blobs of following layers. Example:
-
-.. code-block:: none
-
- layer {
- bottom: "x"
- top: "blob1"
- }
- layer {
- type: "Split"
- bottom: "blob1"
- top: "blob2"
- }
- layer {
- bottom: "blob2"
- top: "y"
- }
-
-After removing the `Split` layer the first layer will output "blob1", but the last layer will accept "blob2", which doesn't exist. So, the result should be:
-
-.. code-block:: none
-
- layer {
- bottom: "x"
- top: "blob1"
- }
- layer {
- bottom: "blob1"
- top: "y"
- }
-
-
-Model format
-############
-
-Defined by Protocol Buffers library schema, can be found in Caffe's sources in `src/caffe/proto/caffe.proto`.
-
-Note that layers are not called layers there, they are called *parameters*.
-
-The main structure describing the whole model is called `NetParameter`.
-
-`NetParameter` contains a sequence of `LayerParameters`. Each of them has properties "name", "type", "top", "bottom", "blobs" (basically - "kernels", if it is applicable for this layer of course), and a property corresponding to one specific layer type, for example `ConvolutionParameter`.
-
-**Note1:** most of these properties are technically optional; some of them are *repeated*, which means there can be zero or more of them (like in the case of "top" and "bottom" - a layer may have zero or more inputs and outputs).
-
-**Note2:** sometimes you'll see that for some layers bottom and top blobs have the same name. It means, that Caffe will reuse the memory for this layer (i.e. it will put the calculation result for this layer into the same memory as its input). Still, the computation graph is still correctly defined, because the order of layers is significant.
-
-Important notes
-###############
-
-* `InnerProduct` layer is just another name for `FullyConnected` or `Dense` layer.
-* Layers such `Pooling`, `InnerProduct`, `Convolution` all have 4D inputs and 4D outputs. This can be unexpected for `InnerProduct` especially. Check Caffe docs for details (for example `InnerProduct <http://caffe.berkeleyvision.org/tutorial/layers/innerproduct.html>`_).
-* `Pooling` layer has `global_pooling` property which basically a way to automatically pool over the whole height and width of the input tensor. It means that pooling window size won't be set as numbers, which in turn means that it is impossible to implement this in the Caffe importer without knowing the shape of the input. Currently I just change this `global_pooling` property to concrete pooling windows size.
-* At the time of writing Caffe **does not** have a DepthwiseConv layer.
-* `Split` layer, quite surprisingly, just makes a few identical copies of the input (bottom) blob; it doesn't actually split the blob into parts.
-
-{Generate the code} Detailed Design
------------------------------------
-
-Major Function
-``````````````
-
-To generate output artifact represented in code (source or binary) for specific target platform for further execution.
-
-Soft backend
-````````````
-
-Generation of C++ source code for CPU
-#####################################
-
-Glossary
-~~~~~~~~
-+ **Tensor** - class that represents multidimensional array.
- Provides user artifact interface (holds input data and results) and
- keep temporary data generated by one operation and used by other.
-+ **Operation** - neural network layer implementation, like 2d convolution, activation function, etc.
- It consumes ``Tensor`` objects as input and produces one or multiple output ``Tensors``
-+ **Shape** - class that stores number and values of ``Tensor`` dimensions.
-+ **Artifact** - product of soft backend execution, this artifact provides interface for user and
- implements inference of provided computational graph
-
-Overview
-~~~~~~~~
-Soft backend takes a pointer to a computational graph(Model IR) and
-generates artifact in form of C++ source code file, header with artifact interfaces and
-binary file that contains parameters of compiled network.
-
-Example of generated artifact interface:
-
-.. code-block:: c++
-
- class NNModel
- {
- public:
- // Constructor of artifact, it takes path to file with NN parameters
- // Contents of this file is not stored in code directly
- // because it could be enormous
- NNModel(const std::string &parametersPath);
-
- // Setter of NN input named "layer1"
- // contents of in Tensor are copied to internals of Model
- bool set_layer1(const Tensor &in);
-
- // This setter is generated if NN has only one input tensor
- // It's name is independent from operation names
- bool setInput(const Tensor &in);
-
- // Getter of NN output named "result"
- // Model creates result object during inference and
- // holds reference to it until model is destroyed
- // or another inference executed
- std::stared_ptr<Tensor> get_result();
-
- // This getter is generated if NN has only one output
- // It's name is independent from operation names
- std::stared_ptr<Tensor> getOutput();
-
- // This method exists in every aftifact,
- // has fixed name that is not dependent on Model
- // It is responsible for inference of result tensors from input data
- void doInference();
- };
-
-Common usage:
-
-.. code-block:: c++
-
- std::string pathToParameters = "nnmodel.params";
- NNModel model(pathToParameters);
- Tensor inputTensor;
- for (int i = 0; i < numInputs; ++i)
- {
- fillInput(inputTensor);
- bool initialized = model.set_layer1(inputTensor);
- if (!initialized)
- throw "invalid input tensor";
- model.doInference();
- std::shared_ptr<Tensor> result = model.get_result();
- showResult(*result);
- }
-
-
-Soft backend has three main phases: analysis, serialization and artifact generation.
-
-* Analysis implemented by ``ModelAnalyzer`` class,
-* Serialization implemented by ``Serializer`` class,
-* Generation of header, code and parameter files implemented by ``BaseCodeGenerator`` class and derived classes.
-
-General generation sequence
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Main backend sequence can be found in ``BaseCodeGenerator::generate`` method:
-
-1. Apply ``ModelAnalyzer`` visitor to generate inference sequence and
- find artifact inputs, output and temporary Tensors.
-2. Apply ``Serializer`` visitor on inference sequence generated by ``ModelAnalyzer``
- to create binary array of parameters.
-3. Call ``formatTensorNames`` method that adjusts input and output names
- to target language naming convention(remove symbols(like '/') invalid in C++ names, etc.).
-4. Create artifact output directory(set by ``--output-dir`` option,
- possibility of this operation should be checked by driver systems).
-5. Create header file in output directory, write it contents
- by calling virtual ``materializeHeader`` method overrided
- in particular soft backend class(CPPCodeGenerator, CCodeGenerator, etc).
- This phase consumes data gathered by ``ModelAnalyzer``.
-6. Create code file in output directory, write it contents
- by calling virtual ``materializeCode`` method overrided
- in particular soft backend class(CPPCodeGenerator, CCodeGenerator, etc);
- This phase consumes data gathered by ``ModelAnalyzer``.
-7. Create and fill file with model parameters.
- This file contains a header (magic number, version of protocol, etc.)
- to identify this file and avoid errors of wrong model + parameters combination
- and raw data collected by serializer.
-
-Inference sequence construction
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-``ModelAnalyzer`` object walks computational graph on phase 1 from general sequence
-in topological order and creates layout of it's operations. This sequence is represented
-by list of ``ModelAnalyzer::Action`` objects.
-
-``ModelAnalyzer::Action`` object contains name of operation,
-pointer to corresponding CG Node, identifiers of input and output Tensors.
-
-Information about artifact variables(Tensors) is stored in array of ``TensorDescriptor`` objects,
-Mentioned object holds name of ``Tensor`` and it's properties(is it input/output/temporary).
-Every ``InputOp`` node emits "input" type ``Tensor`` variable.
-Every node with name(what is not ``InputOp``) emits "output" type ``Tensor`` variable that holds operation output.
-Node without particular name creates temporary ``Tensor``, that is not accessible outside Model;
-
-Serialization
-~~~~~~~~~~~~~
-``Serializer`` object visits CG nodes and stores corresponding data in raw binary format
-in internal array of bytes. Every operation receives unique
-(with exception of operations that has nothing to serialize, for example relu) offset in this array
-where it's data stored. This offset is stored in ``_paramStartOffset`` of ``ModelAnalyzer::Action`` object.
-
-Shape, strides are stored as arrays of integers: first value serialized is array size, then all dimension values.
-Pads are stored as vectors too, but they have additional value to save:
-padding type. It is not used in inference yet, because all ``Tensor`` ``Shapes`` are available at compile time.
-To serialize ``Tensor`` objects (like convolution kernel, fully-connected weights) ``Serializer`` dumps ``Shape`` first,
-then actual data in form of C multidimensional array(data stored continuously like ``int a[1][100][100][3]``).
-
-Def files
-~~~~~~~~~
-Generator has number of ``.def`` files. This files contain code snippets used to generate the artifact.
-This is classes and type declarations for artifact, library of operations, support functions.
-
-Build system converts them into headers containing char arrays with contents of ``def`` files.
-Is generator need to include some snippet into generated code it just prints contents of this generated array.
-
-Header generation
-~~~~~~~~~~~~~~~~~
-``materializeHeader`` method of derivative of ``BaseCodeGenerator`` implements generation of header file.
-
-C++ backend generates artifact class in header file that contains:
-
-+ constructor that takes path to parameter file
-+ destructor that frees taken resources
-+ setters of model inputs. Every function has unique name taken from CG node.
-These setters correspond to "input" tensors found by ``ModelAnalyzer`` object.
-+ getters of model products. Every function has unique name taken from CG node;
-These getters correspond to "output" tensors found by `ModelAnalyzer` object.
-
-Also header file contains a number of helper functions and types(``Shape``, ``Tensor``) defined in ``.def`` files.
-These types and methods are needed by users of the artifact.
-
-Code generation
-~~~~~~~~~~~~~~~
-``materializeCode`` method of derivative of ``BaseCodeGenerator`` implements generation of code file.
-
-First the backend writes the required snippets from ``.def`` files. This includes operation implementations,
-helper functions (like parameter file mapping and unmapping).
-
-Then the artifact interface implementation is written: artifact constructor, destructor, setters, getters, ``doInference``.
-
-Constructor and destructor call support functions from included snippets to manage parameters.
-
-Setter checks input correctness and stores given data as input of NN.
-
-Getter is trivial and contains single ``return`` statement.
-
-``doInference`` function contains "initilizer" section and actual inference.
-"initializer" section resets all "output" variables so reference to them are dropped.
-Inference part is generated from the inference sequence that was constructed by ``ModelAnalyzer`` object.
-Operations are represented by calls from support library.
-General form of operation looks like ``opName(outputTensor, paramsPtr, inputTensor1, inputTensor2);``.
-If an operation defines a temporary variable, then the temporary variable is allocated before the point of call.
-``paramsPtr`` arguments corresponds to expression
-``<serialized parameters array> + <data offset for this operation>``
-data offset is defined by ``Serializer``.
-
-ACL Soft backend
-````````````````
-
-Generation of C++ source code for GPU
-#####################################
-
-Glossary
-~~~~~~~~
-+ **ACL** - refers to the ARM Compute Library, the Computer Vision and Machine Learning library
- consisting of a set of functions optimized for ARM CPUs and GPUs and supplying optimized implementations
- of many popular neural networks layers.
-+ **Artifact** - refers to the generated class implementation. This class provides methods
- implementations making it computationally equivalent to the input neural network.
-+ **DOM** - refers to the Document Object Model, an abstract description of a program as a decomposition
- to hierarchically organized computational and structural blocks using a simple and limited set of
- basic abstractions. This approach is used to subdivide the process of code generation into two stages:
- a more sophisticated stage of converting the computational graph into DOM, and a pretty straightforward stage
- of translating DOM into the final target language representation. This can allow to facilitate the support
- of new target languages in the backends.
-
-Overview
-~~~~~~~~
-The ACL Soft backend walks through the computational graph (Model IR) and converts the encountered
-operation descriptions to the lines of C++ code consisting of the ACL layer classes instantiations,
-calling their instances methods and some other auxiliary statements like operation results output
-tensors declarations as well as declarations and settings up of the ACL layers configuration parameters.
-The output of this backend contains a single (artifact) class definition and consists of three files:
-an artifact header file, an artifact implementation file and a so called parameter files containing
-serialized operation kernels weights.
-
-The ``nnc`` executable must be called with ``--target=arm-gpu-c++`` option for ACL Soft backend was called.
-The ``-o`` option determines how the generated artifact is called.
-
-The artifact declaration looks like:
-
-.. code-block:: c++
-
- class AclArtifact {
- public:
- AclArtifact();
- void Inference();
- arm_compute::CLTensor& get_tensor1();
- arm_compute::CLTensor& get_tensor2();
- arm_compute::CLTensor& getInput();
- arm_compute::CLTensor& getOutput();
-
- private:
- std::ifstream _parIn;
- arm_compute::CLTensor _tensor1;
- arm_compute::CLTensor _tensor2;
- arm_compute::CLTensor _tensor2_convolution_layer_weights;
- arm_compute::CLConvolutionLayer _tensor2_convolution_layer;
-};
-
-The usage looks like:
-
-.. code-block:: c++
-
- AclArtifact artifact;
- CLTensor& artifact_in = artifact.getInput();
- readTensorFromHDF5File(artifact_in, "in.hdf5");
-
- artifact.Inference();
-
- CLTensor& artifact_out = artifact.getOutput();
- writeTensorToHDF5File(artifact_out, "out", "out.hdf5");
-
-ACL soft backend needs the shape inference was done on the computational graph before it proceeds.
-Then it does its work in three passes: generate a DOM from the computational graph, generate the artifact
-header from the DOM, generated the implementation source file from the DOM.
-
-* DOM generation is implemented by the ``AclCppOpGenerator`` class,
-* Header generation is done by the ``ArtifactGeneratorCppDecl`` class,
-* Implementation generation is done by the ``ArtifactGeneratorCppCode`` class.
-
-Besides the DOM generation the ``AclCppOpGenerator`` class provides the operation kernels weights serialization.
-
-Generation sequence
-~~~~~~~~~~~~~~~~~~~
-The ACL backend generation sequence can be observed in the ``AclCppCodeGenerator::run()`` method:
-1. Create the output directory if it is not present.
-2. Create the header, implementation and parameter files in this directory.
-3. Create ``ArtifactGeneratorCppCode`` and ``ArtifactGeneratorCppDecl`` generators instances, used to generate
- the source and header files from DOM.
-4. Create a ``AclCppOpGenerator`` generator instance to produce DOM from the model IR. An instance of this
- class traverses the model IR Computational Graph being its ``visitor`` in terms of the ``Visitor`` pattern.
-5. The Computational Graph ``accepts`` the ``AclCppOpGenerator`` instance as its visitor to allow it oneself
- and generate DOM having an ``ArtifactModule`` instance as a root node. As a side effect action the
- operation kernels weights are serialized during this traversing.
-6. The ``ArtifactModule`` root instance ``accepts`` the ``ArtifactGeneratorCppCode`` and ``ArtifactGeneratorCppDecl''
- instances in course to allow them traverse oneself and generate the source and header artifact files.
-
-Artifact structure
-~~~~~~~~~~~~~~~~~~
-Conceptually a generated ACL Soft backend artifact is a C++ class. Physically this is a set of three
-files: header, implementation and (binary) parameter file with the neural network operation kernels weights.
-
-The class has a public default constructor, where all the configuration tasks are performed: all the operation
-layer are configured, all the used tensors are allocated, and the operation kernels weights are read.
-The class has an ``Inference()`` function, where the underlying neural network inference is implemented.
-And the class also has a set of ``get_<tensor name>()`` functions returning references for all the named tensors generated.
-There are also two special accessor functions: ``getInput()`` and ``getOutput()`` which are generated if
-there is the only input or the only output to the neural network respectively.
-
-DOM
-~~~
-Document Object Model of the ACL Soft backend is implemented in the ``ArtifactModel.h`` and ``ArtifactModel.cpp``
-header and source files respectively. The following classes are the main DOM building bricks:
-
-1. ``ArtifactEntity`` is the very basic class in the DOM hierarchy. All the other DOM classes are derivative classes of ````.
-2. ``ArtifactNamed`` is a basic class for all named DOM entities: variables, functions, classes etc.
-3. ``ArtifactExpr`` is a basic class for different kinds of expressions: identifiers, unary and binary expressions, function calls etc.
-4. ``ArtifactId`` used to reference named entities by name.
-5. ``ArtifactRef`` has an ``address of`` (``&``) semantics of ``C/C++``.
-6. ``ArtifactDeref`` has a ``dereference`` (``*``) semantics of ``C/C++``.
-7. ``ArtifactFunctionCall`` is a function call expression.
-8. ``ArtifactUnOp`` is an unary operation.
-9. ``ArtifactBinaryExpr`` is a binary operation.
-10. ``ArtifactIndex`` is an indexing (``[]``) operation.
-11. ``ArtifactRet`` represent a value return from a function.
-12. ``ArtifactVariable`` represents a variable.
-13. ``ArtifactBlock`` represents a block of instructions.
-14. ``ArtifactFunction`` represents a function definition.
-15. ``ArtifactClass`` represents a class.
-16. ``ArtifactModule`` represents a whole program module.
-
-There are several other DOM entities used for the artifact DOM representation not mentioned in the list above.
-There is also the ``ArtifactFactory`` class used as factory for producing the DOM building blocks.
-The is composed as a graph of ``ArtifactEntity`` nodes allocated in the dynamical memory with lifetimes
-controlled by the ``std::shared_ptr<>s``.
-
-ACL Specifics
-~~~~~~~~~~~~~
-ACL library supplies its own classes and other types which should be used when working with the artifact
-generated by the ACL Soft backend. The most important of them is, probably, the ``CLTensor`` type,
-which is, as obvious from its name, a type for tensors. All input, output and intermediate data in the
-generated ACL backend artifacts are variables having the ``CLTensor`` type.
-
-The tensors layout in ACL is different from such in the Model IR, so reshapes must be done when the ACL tensors
-are generated from the corresponding Model IR tensors. The axes order is NHWC in the Model IR and WHCN in the
-ACL library.
-
-A very important ACL feature is related to the way how the ``paddings`` are calculated for ACL tensors.
-There are two practical ways to instruct the ACL library how two calculate tensors ``paddings``:
-
-* Do a tensor allocation after ``all`` of the operation layers ``cofigure()`` function calls which use this tensor.
- Call the ``TensorInfo::init()`` function in this case. This option is recommended, as it guarantees smart
- ``paddings`` size calculation and keeps the tensor sizes moderate.
-* Use auto-padding: call the ``TensorInfo::init_auto_padding()`` for tensors ``TensorInfo`` initialization.
- The order of calls to the tensor allocation function and to the operation layers ``configure()`` does not matter.
- This approach is ``highly``, ``HIGHLY`` not recommended! In practice it can lead to the memory allocated for
- tensors storing volume inflation by several orders of magnitude!
-
-In the generated ACL Soft backend artifact all the tensors allocations are done after all the operation
-layers are configured.
-
-Interface Design
-================
-
-Overview
---------
-``nnc`` provides a command line user interface to run compilation pipeline with settings that user wants.
-To get all available options of ``nnc`` the flag ``--help`` is to be passed to command line.
-
-Here is a list of available ``nnc`` options:
-
-``$ nnc --help``
-
-::
-
- Usage: nnc OPTIONS
- Available OPTIONS
- --help, -h - print usage and exit
- --debug - turn debugging on (optional: provide filename)
- --debug-area - if specified, debug code will be executed
- only in given areas
- --caffe - treat input file as Caffe model
- --tflite - treat input file as Tensor Flow Lite model
- --target - select target language to emit for given
- architecture. Valid values are 'x86-c++',
- 'interpreter'
- --nnmodel, -m - specify input file with NN model
- --output, -o - specify name for output files
- --output-dir, -d - specify directory for output files
- --input-model-data - interpreter option: specify file with
- neural network input data. This file
- contains array of floats in binary form
- --input-node - interpreter option: set input node
- in Computational Graph
- --output-node - interpreter option: set output node
- in Computational Graph
- --res-filename - interpreter option: file for result tensor
-
-
-
-Option declaration design
--------------------------
-``nnc`` has a convenient and flexible mechanism for declaring command line options that allows users to adjust various option settings.
-A command line option is represented by ``Option`` template class and defined in global scope which allows option to be constructed before
-``main`` function. This allows calling ``parseCommandLine`` (see `Command line parser design`_) in arbitrary point of program having already all declared options.
-The ``Option`` class has only one constructor with the following parameters:
-
-.. list-table::
- :widths: 10 30
- :header-rows: 0
-
- * - **optnames**
- - Names of option. The option can have a several names (`aliases`), for example: ``-f``, ``--file-name``
- * - **descr**
- - Option Description. This text will be shown if ``--help`` option is passed or if command line is incorrect
- * - **default_val**
- - Option value accepted by default. This value will be set to option value if value for option isn't passed to command line
- * - **is_optional**
- - If this parameter set to ``false`` and option isn't passed to command line then error message will be shown and ``nnc`` will be terminated
- * - **vals**
- - Valid values for option. Other values are interpreted as invalid
- * - **checker**
- - Pointer to function that will be called by command line parser to verify option
- * - **seps** (by default is spaces)
- - Symbols that separate option name from value
- * - **enabled**
- - If this option is set to ``false`` then it won't be shown for users
-
-|
-
-When ``Option`` is constructed it registers itself to command line parser that is singleton of ``CommandLine`` class object,
-so that when all options are registered the command line parser will contain all of them.
-
-Most of option parameters should be set with special helper functions that give the user an opportunity to declare options in a simpler form:
-
-.. list-table::
- :widths: 10 30
- :header-rows: 0
-
- * - **optname**
- - Convert option names for ``Option`` constructor, if option has several names then they must be separated by a comma
- * - **overview**
- - Convert string contains option description for ``Option`` constructor. This function can split long lines of description for more pretty printing
- * - **optvalues**
- - Convert option valid values for ``Optioin`` constructor, if option has several values then they must be separated by a comma
- * - **separators**
- - Convert string of symbols separated by a comma for ``Option`` constructor
-
-|
-
-This is an example of a declaration a ``target`` option that selects a specific `backend`:
-
-.. code-block:: c++
-
- Option<std::string> target(
- optname("--target"),
- overview("select target language"
- "to emit for given architecture. "
- "Valid values are 'x86-c++', 'interpreter'),
- std::string(), // default value is empty
- optional(false), // required option
- optvalues("x86-c++, interpreter"),
- nullptr, // no option checker
- separators("="));
-
-|
-
-After command line parsing ``Option`` object can be used as an object of type with which ``Option`` was instantiated, for example:
-
-.. code-block:: c++
-
- ...
- if ( target == "x86-c++" )
- ...
- ...
- std::string targ_val = target;
- ...
- f(target.c_str());
- ...
-
-
-Command line parser design
---------------------------
-Command line parser is presented by a singleton object of ``CommandLine`` class that contains all registered options (see `Option declaration design`_).
-The ``CommandLine`` class has a main public method ``parseCommandLine`` that parses the command line. When this method is invoked the command line parser implements the following steps:
-
-- verify that next option in command line is either option that was registered in command line parser or print error message that option is not recognized
-- set the value of the current command line argument to ``Option`` object and check that this value is valid for the option, if value is invalid error message will be printed
-- verify that all required options are present in command line if they are not an error message will be shown
-- invoke checker function for all options if these functions are available
-
-|
-
-**Note**. Since ``Option`` class is template class and can take a various type the command line parser accesses to options via interface that presented by ``BaseOption`` class and that is supperclass for ``Options`` class
-
-
-SW Code Structure
-=================
-
-.. list-table::
- :widths: 20 60
- :header-rows: 1
-
- * - Directory
- - Description
-
- * - /
- - source codes of the build system, main README file
-
- * - /contrib
- - Incubating projects
-
- * - /doc
- - Contains the documentation of the project
-
- * - /doc/project
- - Contains project management documents (SRS, SDD, STD, HLD, DLD, etc)
-
- * - /libs
- - Contains the source of the libraries which are used by the nncc
-
- * - /libs/core
- - Contains the source code of the core library of nncc
-
- * - /libs/frontend
- - Contains the source code of supported frontend's plugins
-
- * - /libs/frontend/caffe
- - The source code for the Caffe frontend
-
- * - /libs/frontend/tflite
- - The source code for the Tensorflow Lite frontend
-
- * - /libs/backend
- - Contains the source code of supported backend’ plugins
-
- * - /libs/backend/cpu
- - Contains the source code of CPU backend
-
- * - /libs/backend/gpu
- - Contains the source code of GPU backend
-
- * - /libs/backend/3rd_party
- - Contains the source code of backend to utilize 3rd party libraries
-
- * - /scripts
- - Various scripts for building and testing the nncc
-
- * - /tools
- - The source code of the executables
diff --git a/compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_HLD.rst b/compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_HLD.rst
deleted file mode 100644
index baefccf0e..000000000
--- a/compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_HLD.rst
+++ /dev/null
@@ -1,572 +0,0 @@
-====================
-SW High Level Design
-====================
-
-.. contents::
-
-.. section-numbering::
-
-.. raw:: pdf
-
- PageBreak oneColumn
-
-
-**Revision history**
-
-+-------+-------------+----------------------------+--------------------------+---------------------+
-| Ver. | Date | Contents | Author | Approver |
-+=======+=============+============================+==========================+=====================+
-| 0.1 | 2018.05.25 | Initial version | Vostokov Sergey | Sung-Jae Lee |
-+-------+-------------+----------------------------+--------------------------+---------------------+
-| 0.2 | 2018.06.21 | SE member review | Alexey Kondrashov | |
-+-------+-------------+----------------------------+--------------------------+---------------------+
-| 1.0 | 2018.06.22 | Final DR1 version | Vostokov Sergey | Sung-Jae Lee |
-+-------+-------------+----------------------------+--------------------------+---------------------+
-| 2.0 | 2018.09.03 | Add IR description | Vostokov Sergey | Sung-Jae Lee |
-+-------+-------------+----------------------------+--------------------------+---------------------+
-
-|
-
-**Terminology and Abbreviation**
-
-.. list-table::
- :widths: 10 30
- :header-rows: 0
-
- * - OS
- - Operating System
- * - OS API
- - Application interface of OS
- * - HW
- - Hardware
- * - SW
- - Software
- * - NN
- - Neural Network
- * - NN model
- - Neural network model (Instance of NN built with ML framework)
- * - NN compiler
- - The compiler for neural network
- * - ML framework
- - The machine learning framework
- * - TF/TF Lite
- - Tensorflow/Tensorflow Lite ML framework
- * - IR
- - Intermediate representation
- * - CI/CI system
- - Continuous integration system
- * - UI
- - The user interface
- * - GUI
- - The graphical user interface
- * - CLI
- - The command-line interface
- * - CG
- - Computational Graph
-
-|
-
-**References**
-
-[1] Vostokov Sergey, `SW Requirements Specification <18_NN_Compiler_and_Optimizer_SRS.rst>`_
-
-
-Overview
-========
-
-Scope
------
-
-The main goal of the project is to develop a compiler for neural networks to produce executable artifact for specified SW and HW platform.
-
-The development scope includes the following components:
-
-- Develop importer module to parse, verify and represent NN model for further optimization and compilation
-- Develop code emitters to produce executable binary for CPU and GPU
-
-|
-| **2018 year goals:**
-
-- Support TensorFlow Lite NN model format
-- Support Caffe NN model format
-- Support Caffe2 NN model format (Optional)
-- Support compilation of MobileNet NN
-- Support compilation of Inception v3 NN
-- Support ARM CPU
-- Support ARM GPU (Mali)
-- Support Tizen OS
-- Support SmartMachine OS (Optional)
-
-|
-
-.. list-table:: Table 1-1. Target Model
- :widths: 23 50 20
- :header-rows: 1
-
- * - Product
- - Target Model Name
- - Comment
-
- * - Tizen phone
- - Tizen TM2
- - Reference device
-
- * - Tizen device
- - Odroid XU4
- - Reference board
-
- * - SmartMachine target
- - Microvision mv8890, exynos8890
- - Reference device
-
-
-Design Consideration
---------------------
-
-Deep learning software demands reliability and performance. The common approach which comes from the history is to develop
-a SW framework (machine learning framework) which would compute each step of the neural network inference process using
-supported hardware. This approach is used in many popular solutions like Google Tensorflow/Tensorflow Lite, Caffe/2, etc.
-Traditionally, neural network developers build a computation graph and then an appropriate machine
-learning framework interprets it. The latest discoveries in AI field show that the node-visitor method of execution is inefficient. As a result,
-a second approach has been worked out by the industry, which is a neural network compiler that executes code more efficiently.
-
-This document presents the design of the *nncc*, a neural network compiler collection. The design should provide the easiest
-way to extend the functionality of the *nncc* by adding new modules with the following features:
-
- - Support neural networks produced by various machine learning frameworks;
- - Produce an artefact taking advantages of various hardware including specialized processors like NPU;
- - Apply new domain specific optimization techniques over given NN.
-
-
-Constraints
------------
-
-See constraints in SW Requirements Specification.
-
-|
-
-.. list-table:: Table 1-2. Assumptions, Dependencies and the Constraints
- :widths: 23 40 23
- :header-rows: 1
-
- * - Item
- - Assumptions, Dependencies and the Constraints
- - Reference
-
- * - Tizen SW Platform
- - The following items should be provided:
- - Tizen API
- - Tizen kernel
- - Tizen FW
- - Tizen SDK
- - Tizen naming convention
- |
- -
- - `www.tizen.org <www.tizen.org>`_
- - `wiki.tizen.org <wiki.tizen.org>`_
- - `developer.tizen.org <developer.tizen.org>`_
-
- * - SmartMachine OS Platform
- - The following items should be provided:
- - SmartMachine API
- - SmartMachine kernel
- - SmartMachine FW
- - SmartMachine SDK
- - SmartMachine naming convention
- |
- -
- - `Platform confluence <http://suprem.sec.samsung.net/confluence/pages/viewpage.action?pageId=81833987>`_
- - `Github <https://github.sec.samsung.net/RS7-SmartMachine>`_
- - `Functional Safety confluence <http://suprem.sec.samsung.net/confluence/display/ASEC/Adaptive+AUTOSAR>`_
-
-
- * - Host OS
- - Linux-based OS (Ubuntu, Archlinux, etc)
- -
- - `Ubuntu site <https://www.ubuntu.com/>`_
- - `Archlinux site <https://www.archlinux.org/>`_
-
- * - Tizen target HW
- - The reference device should be provided: Tizen TM2
- -
-
- * - SmartMachine target HW
- - The reference device should be provided
- -
-
-
-SW System Architecture Design
-=============================
-
-Overall Architecture
---------------------
-
-The picture below presents the result of high-level analysis of the requirements which **nncc** should satisfy. It describes the
-main function **Compilation** of the compiler collection using IDEF0 (functional modeling) notation. The full information on IDEF
-family of modeling languages is available at this link on `Wikipedia: IDEF <https://en.wikipedia.org/wiki/IDEF>`_.
-
-
-.. image:: images/nncc_idef0_a0.png
- :scale: 100%
-
-Figure 1. Top-Level Context Diagram of compilation function.
-
-|
-| The short explanation of the **Figure 1**:
-|
-
-**1. Input entities:**
-
- - *NN Model instance:* It is the main input of *nncc*. The compiler takes from a user information describing a neural network which should be compiled. In most cases, this NN is produced by a machine learning framework and stored in one or many files. The contents of these files constitute the essence of the neural network. Here it is denoted as an instance of NN model.
- - *Command line options:* In order to provide the most convenient way to use the compiler, it should be configurable. Current design presents a tool which has a Command Line Interface (CLI). Command line options are a symbolic representation of directions instructing the compiler how to set up a working session to get the desired result.
-
-|
-
-**2. Output:**
-
- - *Target binaries:* Everything that is produced by the compilation operation. In general case the result may consist of one or more files. Each of them may be one of the following: an executable, a source code file, a log/verification/error report. For example, when we require the compiler to compile a neural network for execution on GPU, the output artefact may be OpenCL/C/C++ source code, or a binary containing invocation of the procedures delegating the calculations to GPU.
-
-|
-
-**3. Rules and notations:**
-
- - *NN Model specification:* Each machine learning framework has its own architecture design and uses its own format to serialize/deserialize computation graphs which represent neural networks. On a storage device, it may be saved as a file or many files using a unique markup of binary data. To enable *nncc* to read such data and process it, in the future it should recognize the format of the container. Importer/parser subsystem of *nncc* stores the full knowledge of the NN specifications and is responsible for reading and parsing NN models (see `Import NN model`_).
- - *High-Level and Low-Level Optimization techniques:* Before deployment, a neural network developer might want to verify their product and optimize it by size and performance. There are many techniques for reducing the common size of neural network weights and improving performance of the inference. NN optimization activity can be automated by implementing each technique in the middleend according to its specifications (see `Apply Optimizations`_).
- - *Target Runtime Environment (TRE):* In the case when the compiler produces the binary for execution on a specific SW platform, it should take into account the common API of this SW Platform. It includes the full public API of a chosen OS available to the 3rd party developers.
- - *Target Instruction Set Architecture (Target ISA):* Resulting artefact is always executed on a SW Platform using some specified API. The user may want to generate the artefact that would use OpenBlas or Arm Compute Library or something else (if supported by the compiler), to perform calculations. In order to provide such possibility, *nncc* should be aware of the API to the specified 3rd party libraries.
- - *Device specifications:* Some of the optimization techniques may take into account the technological features of the computing device, like the time to perform some specific calculations. Such information is very helpful during optimization of the final code of the compiled artefact because it may be used to select an optimal sequence of command invocations in order to achieve the best performance.
-
-|
-
-**4. Mechanism:**
-
- - *Optimizing NN Compiler:* The implemented compiler itself. Since *nncc* is dedicated to producing the code for the most efficient execution, we may regard the tool as optimizing.
- - *Host OS:* Since the compiler is a tool that works in some SW Environment, the main Top-Level SW system is an Operating System. In the SW Requirements specification it may be defined as a Linux-like OS, for example Ubuntu, Archlinux, etc.
-
-|
-
-Composition of Architecture
----------------------------
-
-The compiler consists of three main parts: frontend, middleend, backend. Together they form a Neural Network instance processing pipeline. Moreover, there is one additional part that is in charge of the compiler configuration.
-
-
-|
-
-.. image:: images/nncc_components.png
- :scale: 50%
-
-Figure 2. Top-Level Components of the *nncc*.
-
-|
-
-.. list-table::
- :widths: 20 50
- :header-rows: 1
-
- * - Layer or Subsystem Name
- - Description
-
- * - Frontend
- - Imports a specified Neural Network, presents it as a computation graph
-
- * - Middleend
- - Provides various optimizations over the computation graph; at the end transforms it to internal IR
-
- * - Backend
- - Produces the specified artefact as a result of compilation procedure using specified parameters describing the target OS, target HW, etc
-
- * - Configuration system
- - Accepts command line options and configures *nncc* according to their contents
-
-|
-| The detailed decomposition of the main function **Compilation** is presented on the diagram A1 below.
-|
-
-Interface
----------
-
-Similar to any console application the *nncc* CLI accepts two types of options:
-
- - Options that have values, for example, a name of the output executable
- - Options that don't have values (switches) that turn various features on and off
-
-Additionally, options can be general and subsystem-specific.
-
-General options direct the process of the neural network compilation as a whole, and also control the utility functions like the verbosity of the messages that *nncc* outputs during the compilation process.
-
-Subsystem-specific options control each respective subsystem:
-
- - Frontend subsystem takes options that point to the NN model to compile, which format it has, which version of the format and so on.
- - Middleend subsystem takes options that either turn on specific optimizations for the NN model, or just point at the more desired outcome, for example "target performance efficiency" or "target memory efficiency".
- - Backend subsystem takes options that describe the desired target device or architecture and so on.
-
-For better usability, high-level options are also supported. A single high-level option is mapped to a group of lower level options, similarly to how it is done with conventional compiler drivers, like gcc. This way by choosing a single Middleend option "target performance", nncc will automatically choose a number of performance optimizations by itself.
-
-SW System Operation Design
-==========================
-
-The Figure 3 presents a more detailed composition of the main function **Compilation**. As it was shown in previous section `Composition of Architecture`_ it is composed of 5 subfunctions:
-
-- Setup and configure each module - *Block 1* (See `Initialization`_ section)
-- Import the specified neural network - *Block 2* (See `Import NN model`_ section)
-- Apply High-Level optimizations - *Block 3* (See `Apply Optimizations`_ section)
-- Apply Low-Level optimizations - *Block 4* (See `Apply Optimizations`_ section)
-- Generate the output code for specified target - *Block 5* (See `Generate the code`_ section)
-
-
-|
-
-.. image:: images/nncc_idef0_a1.png
- :scale: 100%
-
-Figure 3. Decomposition of top-Level function **Compilation**.
-
-
-Initialization
---------------
-
-At this stage the initialization of all submodules of the *nncc* happens. This procedure starts from command line option processing till selection of all required and correctly configured modules.
-At the parsing stage the configuration system checks its own consistency. If command line option set is not enought to establish a valid configuration the environment variables will be used. Also, almost all configuration options can be read from config file if it is specified in command line.
-
-
-NN model Intermediate Representation
-------------------------------------
-
-Intermediate Representation is an internal language for the compiler that allows performing various optimizations, analysis and transformations to generate efficient code for specific target platform.
-
-Import NN model
----------------
-
-The major function of the *nncc* frontend is to import specified NN model. It means that frontend should recognize the format of given NN model, parse all internal structures (load computation graph using framework specific IR: NN topology, NN ops, weights), verify their correctness and convert to Model IR.
-
-
-Apply Optimizations
--------------------
-
-There are two levels of neural network optimizations in *nncc*.
-
-First one is High-Level Optimizations, they are applied to the Model IR, which is output by the NN Import subsystem.
-
-High-Level Optimizations
-````````````````````````
-High-Level optimizations can be divided into two groups:
-
- - optimizations aimed at reducing the size of the resulting model - *size optimizations*
- - optimizations aimed at reducing the inference time of the model - *performance optimizations*
-
-These two groups are not mutually exclusive. Some optimization techniques positively affect both size and performance, while some of them might reduce the size of the model at some performance cost.
-
-High-Level Optimizations in this sense are purely neural-network-specific, as they attempt to improve the model by manipulating the computation graph and the weights. For example, some techniques search for unused parts of the computation graph and remove them, or they search for the parts of the graph that can be merged together and thus gain some performance. Other techniques manipulate the neural network weights - either reduce their amount or modify their values in a way that allows for the reduced storage consumption.
-
-Currently, High-Level Optimizations are out of scope of the project.
-
-
-Low-Level Optimization
-``````````````````````
-
-The Low-Level Optimizations are applied by the compiler closer to the end of the whole compilation process, before the executable generation. The input for this stage of *nncc* is the Coarse-Grained IR, which is output but High-Level Optimization subsystem.
-
-Generate the code
------------------
-
-
-Present architecture allows for several backend solutions, depending on target specified. Those solutions can be divided into 3 types:
-
- - *Interpretation.*
- At every step inference can be carried out by interpreting IR produced after that step.
- - *Soft backend.*
- Resulting program can be generated as source code in high-level programming language (e.g., C/C++) that does not depend on libraries outside of itself, with the exception of system libraries.
- - *Hardware (Binary) backend.*
- This type refers to generating binary code that can be executed on target device. NN compiler can generate code that is either executed solely on CPU, or takes advantage of the GPU when possible if corresponding target was specified.
-
-Third-party libraries incorporation can be done either in form of source code or by compiling a binary artefact.
-
-
-
-
-Appendix 1. Traceability Matrix
-===============================
-
-The following table shows mapping between SW Requirements Specification and SW High-Level Design Document.
-
-.. list-table::
- :widths: 15 40 15
- :header-rows: 1
-
- * - Requirement
- - Description
- - Section
-
- * - RF-1 (Frontend: Tensorflow Lite)
- - The compiler should support import of NN model in Tensorflow Lite format (parsing & verification of data scheme v0-v3, 50 NN ops)
- - `Import NN model`_
-
- * - RF-2 (Frontend: Caffe)
- - The compiler should support import of NN model in Caffe format (parsing & verification)
- - `Import NN model`_
-
- * - RF-3 (Frontend: Caffe2 (Optional))
- - The compiler should support import of NN model in Caffe2 format (parsing & verification)
- - `Import NN model`_
-
- * - RF-4 (Frontend: lossless import)
- - The frontend should use the lossless approach while it is converting any NN model to IR
- - `Import NN model`_
-
- * - RF-5 (Frontend: Inception_v3)
- - The frontend should successful import the Inception V3 NN model
- - `Import NN model`_
-
- * - RF-6 (Frontend: MobileNet)
- - The frontend should successful import the MobileNet NN model
- - `Import NN model`_
-
- * - RF-7 (Backend: ARM CPU)
- - The compiler should produce executable for ARM CPU
- - `Generate the code`_
-
- * - RF-8 (Backend: ARM GPU)
- - The compiler should produce the binary that takes advantages of GPU when it was specified before compilation
- - `Generate the code`_
-
- * - RF-9 (Backend: Artefact type)
- - The compiler should produce executable as a shared library or as a static library
- - `Generate the code`_
-
- * - RF-10 (Backend: Inception_v3)
- - The compiler should produce the valid compiled artefact for Inception v3 NN model
- - `Generate the code`_
-
- * - RF-11 (Backend: MobileNet)
- - The compiler should produce the valid compiled artefact for MobileNet NN model
- - `Generate the code`_
-
- * - RF-12 (Config: command line)
- - The compiler should get configuration parameters from command line
- - `Initialization`_
-
- * - RF-13 (Config: config file (Optional))
- - The compiler should get configuration parameters from config file
- - `Initialization`_
-
- * - RF-14 (Config: environment variable (Optional))
- - The compiler should get configuration parameters from environment variables
- - `Initialization`_
-
- * - RF-15 (Artefact: result)
- - The artefact should provide comparable result to the original NN model for the same input data
- - `Generate the code`_
-
- * - RF-16 (Artefact: input verifications)
- - The artefact should verify any input data and check consistency
- - `Generate the code`_
-
- * - RF-17 (Artefact: GPU)
- - The artefact should take advantage of the GPU for GPU-enabled operations
- - `Generate the code`_
-
- * - RF-18 (Artefact: CPU)
- - The artefact should take advantage of CPU if it was specified
- - `Generate the code`_
-
-
-.. list-table:: Design Module of S/W Architecture
- :widths: 40 10 15 15
- :header-rows: 1
-
- * - Requirement
- - Import NN model
- - Generate the code
- - Initialization
-
-
- * - RF-1 (Frontend: Tensorflow Lite)
- - O
- -
- -
-
- * - RF-2 (Frontend: Caffe)
- - O
- -
- -
-
- * - RF-3 (Frontend: Caffe2 (Optional))
- - O
- -
- -
-
- * - RF-4 (Frontend: lossless import)
- - O
- -
- -
-
- * - RF-5 (Frontend: Inception_v3)
- - O
- -
- -
-
- * - RF-6 (Frontend: MobileNet)
- - O
- -
- -
-
- * - RF-7 (Backend: ARM CPU)
- -
- - O
- -
-
- * - RF-8 (Backend: ARM GPU)
- -
- - O
- -
-
- * - RF-9 (Backend: Artefact type)
- -
- - O
- -
-
- * - RF-10 (Backend: Inception_v3)
- -
- - O
- -
-
- * - RF-11 (Backend: MobileNet)
- -
- - O
- -
-
- * - RF-12 (Config: command line)
- -
- -
- - O
-
- * - RF-13 (Config: config file (Optional))
- -
- -
- - O
-
- * - RF-14 (Config: environment variable (Optional))
- -
- -
- - O
-
- * - RF-15 (Artefact: result)
- -
- - O
- -
-
- * - RF-16 (Artefact: input verifications)
- -
- - O
- -
-
- * - RF-17 (Artefact: GPU)
- -
- - O
- -
-
- * - RF-18 (Artefact: CPU)
- -
- - O
- -
diff --git a/compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_SDD.rst b/compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_SDD.rst
deleted file mode 100644
index 8d1c8ae9f..000000000
--- a/compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_SDD.rst
+++ /dev/null
@@ -1,884 +0,0 @@
-=======================
-SW Development Document
-=======================
-
-.. contents::
-
-.. section-numbering::
-
-.. raw:: pdf
-
- PageBreak oneColumn
-
-
-**Revision history**
-
-+-------+-------------+-----------------------------+--------------------------+---------------------+
-| Ver. | Date | Contents | Author | Approver |
-+=======+=============+=============================+==========================+=====================+
-| 0.1 | 2018.04.12 | Initial version | Vostokov Sergey | Sung-Jae Lee |
-+-------+-------------+-----------------------------+--------------------------+---------------------+
-| 0.2 | 2018.04.16 | SE member in-charge review | Ilya Lopatin | |
-+-------+-------------+-----------------------------+--------------------------+---------------------+
-| 1.0 | 2018.04.17 | Final Execution DR version | Vostokov Sergey | Sung-Jae Lee |
-+-------+-------------+-----------------------------+--------------------------+---------------------+
-| 1.1 | 2018.04.17 | Add SW Quality Verification | Vostokov Sergey | Sung-Jae Lee |
-+-------+-------------+-----------------------------+--------------------------+---------------------+
-| 2.0 | 2018.09.24 | DR2 | Vostokov Sergey | Sung-Jae Lee |
-+-------+-------------+-----------------------------+--------------------------+---------------------+
-| 2.5 | 2018.12.07 | Initial Completion DR ver. | Rusyaev Roman | Sung-Jae Lee |
-+-------+-------------+-----------------------------+--------------------------+---------------------+
-| 2.6 | 2018.12.07 | SE member in-charge review | Aleksei Kondrashov | |
-+-------+-------------+-----------------------------+--------------------------+---------------------+
-| 3.0 | 2018.12.07 | Final Completion DR version | Rusyaev Roman | Sung-Jae Lee |
-+-------+-------------+-----------------------------+--------------------------+---------------------+
-
-|
-
-**Terminology and Abbreviation**
-
-.. list-table::
- :widths: 10 30
- :header-rows: 0
-
- * - OS
- - Operating System
- * - OS API
- - Application interface of OS
- * - HW
- - Hardware
- * - SW
- - Software
- * - NN
- - Neural Network
- * - NN model
- - Neural network model (Instance of NN built with ML framework)
- * - NN compiler
- - The compiler for neural network
- * - ML framework
- - The machine learning framework
- * - TF/TF Lite
- - Tensorflow/Tensorflow Lite ML framework
- * - IR
- - Intermediate representation
- * - CI/CI system
- - Continuous integration system
- * - UI
- - The user interface
- * - GUI
- - The graphical user interface
- * - CLI
- - The command-line interface
- * - CG
- - Computational Graph
- * - ASan
- - AddressSanitizer
-
-
-Project Overview
-================
-
-Purpose and Scope
------------------
-
-
-The main goal of the project is to develop a compiler for neural networks to produce executable artifact for specified SW and HW platform.
-
-The development scope includes the following components:
-
-- Develop importer module to parse, verify and represent NN model for further optimization and compilation
-- Develop code emitters to produce executable binary for CPU and GPU
-
-|
-| **2018 year goals:**
-
-- Support TensorFlow Lite NN model format
-- Support Caffe NN model format
-- Support Caffe2 NN model format (Optional)
-- Support compilation of MobileNet NN
-- Support compilation of Inception v3 NN
-- Support ARM CPU
-- Support ARM GPU (Mali)
-- Support Tizen OS
-- Support SmartMachine OS (Optional)
-|
-
-.. list-table:: Table 1-1. Target Model
- :widths: 23 50 20
- :header-rows: 1
-
- * - Product
- - Target Model Name
- - Comment
-
- * - Tizen phone
- - Tizen TM2
- - Reference device
-
- * - Tizen device
- - Odroid XU4
- - Reference board
-
- * - SmartMachine target
- - Microvision mv8890, exynos8890
- - Reference device
-
-Assumptions, Dependencies and Constraints
------------------------------------------
-
-|
-
-.. list-table:: Table 1-2. Assumptions, Dependencies and the Constraints
- :widths: 23 40 23
- :header-rows: 1
-
- * - Item
- - Assumptions, Dependencies and the Constraints
- - Reference
-
- * - Tizen SW Platform
- - The following items should be provided:
- - Tizen API
- - Tizen kernel
- - Tizen FW
- - Tizen SDK
- - Tizen naming convention
- |
- -
- - `www.tizen.org <www.tizen.org>`_
- - `wiki.tizen.org <wiki.tizen.org>`_
- - `developer.tizen.org <developer.tizen.org>`_
-
- * - SmartMachine OS Platform
- - The following items should be provided:
- - SmartMachine API
- - SmartMachine kernel
- - SmartMachine FW
- - SmartMachine SDK
- - SmartMachine naming convention
- |
- -
- - `Platform confluence <http://suprem.sec.samsung.net/confluence/pages/viewpage.action?pageId=81833987>`_
- - `Github <https://github.sec.samsung.net/RS7-SmartMachine>`_
- - `Functional Safety confluence <http://suprem.sec.samsung.net/confluence/display/ASEC/Adaptive+AUTOSAR>`_
-
-
- * - Host OS
- - Linux-based OS (Ubuntu, Archlinux, etc)
- -
- - `Ubuntu site <https://www.ubuntu.com/>`_
- - `Archlinux site <https://www.archlinux.org/>`_
-
- * - Tizen target HW
- - The reference device should be provided: Tizen TM2
- -
-
- * - SmartMachine target HW
- - The reference device should be provided
- -
-
-
-Development Plan And Result
-===========================
-
-Development Schedule
---------------------
-
-|
- **Table 2-1. SW Development Schedule**
-|
-
-+-------------------+----------------------------+------------+------------+--------------+------------+--------------------+
-| Task | Deliverable | Plan start | Plan end | Result start | Result end | Responsibility |
-+===================+============================+============+============+==============+============+====================+
-| Prepare SW | SRS | 04.2018 | 04.2018 | 04.2018 | 04.2018 | S\. Vostokov |
-| requirements | | | | | | |
-+-------------------+----------------------------+------------+------------+--------------+------------+--------------------+
-| Prepare initial | STD | 04.2018 | 04.2018 | 04.2018 | 04.2018 | S\. Vostokov |
-| SW Test Document | | | | | | |
-+-------------------+----------------------------+------------+------------+--------------+------------+--------------------+
-| Prepare Initial | SDD | 04.2018 | 04.2018 | 04.2018 | 04.2018 | S\. Vostokov |
-| Project Plan | | | | | | |
-+-------------------+----------------------------+------------+------------+--------------+------------+--------------------+
-| Prepare | STD | 04.2018 | 06.2018 | 04.2018 | 06.2018 | S\. Vostokov |
-| SW Test Document | | | | | | |
-+-------------------+----------------------------+------------+------------+--------------+------------+--------------------+
-| Prepare design | HLD, DLD | 05.2018 | 08.2018 | 05.2018 | 08.2018 | S\. Vostokov |
-| document | | | | | | |
-+-------------------+----------------------------+------------+------------+--------------+------------+--------------------+
-| Prepare test | STD, UTR | 04.2018 | 10.2018 | 09.2018 | 12.2018 | R\. Rusyaev |
-| result | | | | | | |
-+-------------------+----------------------------+------------+------------+--------------+------------+--------------------+
-| Prepare project | SDD, | 05.2018 | 12.2018 | 12.2018 | 12.2018 | R\. Rusyaev |
-| completion | Project completion report | | | | | |
-| documents | | | | | | |
-+-------------------+----------------------------+------------+------------+--------------+------------+--------------------+
-| Implement Caffe | Caffe NN model Importer | 05.2018 | 09.2018 | 05.2018 | 09.2018 | S\. Vostokov |
-| Importer | | | | | | |
-+-------------------+----------------------------+------------+------------+--------------+------------+--------------------+
-| Implement code | Code emitter | 05.2018 | 09.2018 | 05.2018 | 09.2018 | S\. Vostokov |
-| emitter for CPU | | | | | | |
-+-------------------+----------------------------+------------+------------+--------------+------------+--------------------+
-| Implement TF Lite | TensorFlow Lite NN model | 05.2018 | 11.2018 | 05.2018 | 07.2018 | S\. Vostokov |
-| Importer | Importer | | | | | |
-+-------------------+----------------------------+------------+------------+--------------+------------+--------------------+
-| Implement code | Code emitter | 02.2018 | 11.2018 | 08.2018 | 12.2018 | R\. Rusyaev |
-| emitter for GPU | | | | | | |
-+-------------------+----------------------------+------------+------------+--------------+------------+--------------------+
-
-
-SW Metrics
-----------
-
-.. list-table:: Table 2-2. SW Metrics
- :widths: 5 10 5 7 5 4 8
- :header-rows: 1
-
- * - Category
- - Metric
- - Collection Method
- - Collection Period
- - Planned
- - Actual
- - Responsibility
-
- * - Quality
- - Test pass rate
- - GTest
- - 22.02.2018 - 31.12.2018
- - 100%
- - 100%
- - R\. Rusyaev
-
- * - Quality
- - Defects density
- - Manual (GTest/Scripts)
- - 22.02.2018 - 31.12.2018
- - <= 1 defect/KLOC
- - 0.0013
- - R\. Rusyaev
-
- * - Quality
- - Defects removal rate
- - Manual (GTest/Scripts)
- - 22.02.2018 - 31.12.2018
- - 100%
- - 86%
- - R\. Rusyaev
-
- * - Quality
- - Critical defects (Static Analysis)
- - SVACE
- - 22.02.2018 - 31.12.2018
- - 0
- - 0
- - R\. Rusyaev
-
- * - Quality
- - Major defects (Static Analysis)
- - SVACE
- - 22.02.2018 - 31.12.2018
- - 0
- - 0
- - R\. Rusyaev
-
- * - Quality
- - Memory Leaks (Dynamic Analysis)
- - ASan
- - 22.02.2018 - 31.12.2018
- - 0
- - 0
- - R\. Rusyaev
-
- * - Quality
- - Code review issue removal
- - Samsung Research github
- - 22.02.2018 - 31.12.2018
- - 100%
- - 94%
- - R\. Rusyaev
-
- * - Quality
- - Comments Rate
- - ``cloc`` tool
- - 22.02.2018 - 31.12.2018
- - Exceed 20%
- - 12%
- - R\. Rusyaev
-
- * - Quality
- - Cyclomatic Complexity
- - Manual (Scripts)
- - 22.02.2018 - 31.12.2018
- - < 50
- - 24
- - R\. Rusyaev
-
- * - Quality
- - Unused Items (Unused Files, Unused Functions, Unused Global Variables)
- - gcc/g++
- - 22.02.2018 - 31.12.2018
- - 0
- - 1
- - R\. Rusyaev
-
- * - Process
- - Project On-time Completion Rate
- - PLM
- - 22.02.2018 - 31.12.2018
- - 100%
- - 100%
- - R\. Rusyaev
-
- * - Process
- - Milestone On-time Completion Rate
- - PLM
- - 22.02.2018 - 31.12.2018
- - 100%
- - 100%
- - R\. Rusyaev
-
- * - Process
- - Process compliance
- - Audit
- - 22.02.2018 - 31.12.2018
- - 100%
- - 100%
- - A\. Kondrashov
-
-
-SW Configurations Management
-----------------------------
-
-Document
-````````
-
-
-.. list-table:: Table 2-3. Document Configuration Item
- :widths: 2 20 5 8
- :header-rows: 1
-
- * - No
- - Configuration Item
- - Location
- - Submitter
-
- * - 1
- - SW Requirement Specification
- - PLM
- - S\. Vostokov
-
- * - 2
- - SW Development Document
- - PLM
- - S\. Vostokov
-
- * - 3
- - SW High Level Document
- - PLM
- - S\. Vostokov
-
-
- * - 4
- - SW Detailed Level Document
- - PLM
- - S\. Vostokov
-
- * - 5
- - SW System Test Document
- - PLM
- - S\. Vostokov
-
- * - 6
- - SW Unit Test Report
- - PLM
- - S\. Vostokov
-
-
-SW Source Code
-``````````````
-
-
-SW Repository: https://github.sec.samsung.net/STAR/nncc
-
-::
-
- git clone https://github.sec.samsung.net/STAR/nncc.git
-
-
-
-Baseline
-````````
-
-.. list-table:: Table 2-4. Baseline Establishment
- :widths: 5 5 8
- :header-rows: 1
-
- * - Phase
- - Baseline Name
- - SW Configuration Item
-
- * - 04.2018 Plan
- - Execution DR
- - SW Requirement Specification, SW Development Document, System Test Document initial version
-
- * - 06.2018 Execution
- - DR1
- - System Test Document
-
- * - 08.2018 Execution
- - Design document
- - SW High Level Document, SW Detailed Design Document
-
- * - 09.2018 Execution
- - DR2
- - Update design documents, UTR (1st round), STD (1st round)
-
- * - 10.2018 Execution
- - Test report
- - SW System Test Document (result), SW Unit Test Report
-
- * - 12.2018 Completion
- - Project Completion
- - Project Completion Report
-
-Development Results
--------------------
-
-TFLite and Caffe importers were implemented as well as Code generators for CPU and GPU were developed.
-In the next year it will be planned to develop new ``llvm`` backend, second IR and various optimizations that allow
-to produce more efficient code. It's also planned to support new architectures of neural networks.
-
-|
-
-SW Quality Verification
-=======================
-
-SW Verification
----------------
-
-
-.. list-table:: Table 3-1. SW Verification Plan and Result
- :widths: 4 11 11 8 8 10 7 6
- :header-rows: 1
-
- * - No
- - Verification Item
- - Quality Goal
- - Tool
- - Phase
- - Development Team Member in Charge
- - Result
- - Note
-
- * - 1
- - Open source License Verification
- - Clear violations of open source obligation
- - ProtexIP
- - Execution
- - Vostokov Sergey
- - `Appendix 1. ProtexIP Report`_
- -
-
- * - 2
- - Potential Defect
- - Fix all defects
- - Svace
- - Test
- - Vostokov Sergey
- - The defects are not fixed yet
- -
-
- * - 3
- - System Defect
- - Fix Critical/ Major defects
- - GitHub
- - Test
- - Vostokov Sergey
- - all critical defects were fixed
- -
-
-Static Analysis
----------------
-
-.. list-table:: Table 3-3. Static Analysis Verification
- :widths: 5 20 10 7 20
- :header-rows: 1
-
- * - No
- - Activity
- - Schedule
- - Result
- - Comment
-
- * - 1
- - SA Verification I (SVACE)
- - 28.09.2018
- - 20 defects
- -
-
- * - 2
- - SA Verification II (SVACE)
- - 30.11.2018
- - 30 defects
- -
-
- * - 3
- - SA Verification III (SVACE)
- - 31.12.2018
- - no defects
- -
-
-Coding Standard
----------------
-
-.. list-table:: Table 3-4. Coding Standard Verification
- :widths: 5 20 10 7 20
- :header-rows: 1
-
- * - No
- - Activity
- - Schedule
- - Result
- - Comment
-
- * - 1
- - Coding standard enforcement with ``clang-format`` tool.
- - Regular
- - ``clang-format`` has not been used yet to verify coding style
- - our coding style document `coding style document <https://github.sec.samsung.net/STAR/nncc/blob/master/contrib/nnc/doc/codestyle.rst>`_
-
-
-Convergence (integration testing)
----------------------------------
-
-Out of scope since the integration with other SW is not required by SW Requirement Specification.
-
-Dynamic Analysis
-----------------
-
-Dynamic analysis for NNCC memory leaks investigation performed with AddressSanitizer (ASan) tool.
-
-.. list-table:: Table 3-5. Dynamic Analysis Verification
- :widths: 5 20 10 7 20
- :header-rows: 1
-
- * - No
- - Activity
- - Schedule
- - Result
- - Comment
-
- * - 1
- - DA Verification I
- - 28.09.2018
- - 1 memory leak
- - It comes from unit test. To be fixed at project completion
-
- * - 2
- - DA Verification II
- - 30.11.2018
- - memory leaks are absent
- -
-
- * - 2
- - DA Verification III
- - 31.12.2018
- - memory leaks are absent
- -
-
-Architecture Analysis
----------------------
-
-SW architecture verification is managed by HQ.
-
-
-SW Security
------------
-
-Out of the project scope since the project is currently for internal use only and is not operating with any personal information of Samsung customers.
-
-
-Code Review
------------
-
-
-.. list-table::
- :widths: 5 20 10 7 20
- :header-rows: 1
-
- * - No
- - Activity
- - Schedule
- - Result
- - Comment
-
- * - 1
- - Code review
- - Regular
- - 416
- - All code is reviewed manually using ``github`` tool before committing
-
-Risk Management
-===============
-
-
-.. list-table:: Table 4-1. SW Risk Management
- :widths: 7 12 12 10 12 12
- :header-rows: 1
-
- * - Priority
- - Risk Description
- - Risk Reduction Solution
- - Schedule
- - Result
- - Responsibility
-
- * - 1
- - Project scope is changed due extra HQ request
- - Discuss the new requirements via email and messenger, update SRS
- - 02.2018 - 12.2018
- - no risks
- - R\. Rusyaev
-
- * - 2
- - Unavoidable technical difficulties during requirements implementation
- - Submit requirements changes and get confirmation from HQ
- - 02.2018 - 12.2018
- - no risks
- - R\. Rusyaev
-
- * - 3
- - Not enough HR
- - Hire team members as soon as possible, request assistance from other teams
- - 02.2018 - 12.2018
- - no risks
- - R\. Rusyaev
-
- * - 4
- - Use of GPL code
- - Minimize usage of GPL code, wrap GPL modules with well-defined interfaces
- so they can be easily replaced.
- - 02.2018 - 12.2018
- - no risks
- - R\. Rusyaev
-
- * - 5
- - Requirements would change due external or internal circumstances, e.g. new technology or product launch
- - Discuss project changes and make corrections
- - 02.2018 - 12.2018
- - no risks
- - R\. Rusyaev
-
-Appendix 1. ProtexIP Report
-===========================
-
-Summary
--------
-
-.. list-table::
-
- * - **Name**
- - nncc_latest_MV_13188_STAR+nncc
-
- * - **Project Creator**
- - move.swc.tizen SWC
-
- * - **License**
- - Apache License Version 2.0 [Reviewed and Approved]
-
- * - **Description**
- -
-
- * - **Number of Files**
- - 1,320
-
- * - **Files Pending Identification**
- - 0 (0%)
-
- * - **Files with Violations**
- - 0 (0%)
-
- * - **Server**
- - 165.213.149.122
-
-
-Analysis Summary
-----------------
-
-.. list-table::
-
- * - **Last Updated:**
- - 6.12.2018, 8:40
-
- * - **Scan Started:**
- - 6.12.2018, 8:33
-
- * - **Scan Finished:**
- - 6.12.2018, 8:40
-
- * - **Files Analyzed:**
- - 1,320 Files
-
- * - **Bytes Analyzed:**
- - 4.998 MB (5,240,297 B)
-
- * - **Files Skipped:**
- - 13 Files
-
- * - **Bytes Skipped:**
- - 468,187 B
-
- * - **Analysis Release Description:**
- - Product version 7.5.0 KB update 16, custom codeprints modified March 21, 2018 03:06 PM
-
- * - **Analyzed From Host:**
- - solveweb2
-
- * - **Analyzed By:**
- - move.swc.tizen SWC (move.swc.tizen@samsung.com)
-
- * - **Analyzed With OS:**
- - Linux
-
- * - **Analyzed With Locale:**
- - ko-KR
-
- * - **Analyzed With Options:**
- -
-
- * - File Matches
- - Yes
-
- * - Snippet Matches
- - Yes
-
- * - Snippet Match Sensitivity
- - 8 - Default
-
- * - Java Import Statements
- - Yes
-
- * - Java Package Statements
- - Yes
-
- * - Binary Dependencies
- - Yes
-
- * - String Searches
- - Yes
-
- * - Allow wild cards (*) in string search queries
- - No
-
- * - Allow regular expression search queries
- - No
-
- * - Decompress Compressed Files
- - No
-
- * - Discard Rejected Code Matches
- - No
-
- * - Keep Only To Discoveries To Codeprinted Components
- - No
-
- * - Keep Only Discoveries To Components With Best Matching Source Path
- - No
-
- * - Keep Only Discoveries To Components Released On Or After
- - No
-
- * - Keep Only Discoveries To Top Component Matches
- - No
-
- * - Keep Only Discoveries To Maven Artifacts
- - No
-
- * - Expand Archive Files
- - No
-
- * - Enable Multi-User File Comparison
- - Yes
-
- * - Store non-precision matches
- - Yes
-
- * - Enable Rapid Identification
- - No
-
-
-Bill of Materials
------------------
-
-.. list-table::
- :header-rows: 1
-
- * - Approval Status
- - License Conflict
- - Component
- - Version
- - Home Page
- - Component Comment
-
- * - N/A
- - N/A
- - nncc_latest_MV_13188_STAR+nncc
- - Unspecified
- -
- -
-
-.. list-table::
- :header-rows: 1
-
- * - License
- - External IDs
- - Usage
- - Ship Status
- - Manual Code Match
- - Rapid ID Code Match
- - Depends
- - Search
- - Used By
-
- * - Apache License Version 2.0 [Reviewed and Approved]
- -
- - Original Code
- - Ship
- - 1,320
- - 0
- - 0
- - 0
- -
-
-Licenses in Effect
-------------------
-
-.. list-table::
- :header-rows: 1
-
- * - License Name
- - Ship Status
- - Custom Fields
-
- * - Apache License Version 2.0 [Reviewed and Approved]
- - Ship
- -
-
-License Conflicts
------------------
-
-There are no license conflicts in this project.
-
-
-**References**
-
-Competitors:
-
-.. [#] `NNVM: Open Compiler for AI Frameworks <https://github.com/dmlc/nnvm>`_.
-.. [#] `Latte.jl: A high-performance DSL for deep neural networks in Julia <https://github.com/IntelLabs/Latte.jl>`_.
-.. [#] `DNNDK: Full-stack solution for deep learning development & deployment <http://www.deephi.com/dnndk.html>`_.
-.. [#] `nngraph: Neural Network Graph Package <https://github.com/torch/nngraph>`_.
-.. [#] `Richard Wei, Lane Schwartz, Vikram Adve, DLVM: A modern compiler infrastructure for deep learning systems, arXiv:1711.03016, 2017 <http://dlvm.org/>`_.
-.. [#] `XLA (Accelerated Linear Algebra): a domain-specific compiler for linear algebra that optimizes TensorFlow computations <https://www.tensorflow.org/performance/xla>`_.
-.. [#] `nGraph: an open source C++ library, compiler and runtime for Deep Learning frameworks <https://github.com/NervanaSystems/ngraph>`_.
diff --git a/compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_SRS.rst b/compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_SRS.rst
deleted file mode 100644
index e4e382d47..000000000
--- a/compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_SRS.rst
+++ /dev/null
@@ -1,559 +0,0 @@
-=============================
-SW Requirements Specification
-=============================
-
-.. contents::
-
-.. section-numbering::
-
-.. raw:: pdf
-
- PageBreak oneColumn
-
-**Revision history**
-
-+-------+-------------+----------------------------+--------------------------+---------------------+
-| Ver. | Date | Contents | Author | Approver |
-+=======+=============+============================+==========================+=====================+
-| 0.1 | 2018.04.11 | Initial version | Vostokov Sergey | Sung-Jae Lee |
-+-------+-------------+----------------------------+--------------------------+---------------------+
-| 0.2 | 2018.04.11 | SE member in-charge review | Aleksei Kondrashov | |
-+-------+-------------+----------------------------+--------------------------+---------------------+
-| 1.0 | 2018.04.13 | Final Execution DR version | Vostokov Sergey | Sung-Jae Lee |
-+-------+-------------+----------------------------+--------------------------+---------------------+
-| 1.1 | 2018.05.24 | Add new requirement in | Vostokov Sergey | Sung-Jae Lee |
-| | | Source code section | | |
-+-------+-------------+----------------------------+--------------------------+---------------------+
-
-
-Introduction
-============
-
-Purpose and scope
------------------
-
-The main goal of the project is to develop a compiler for neural networks to produce executable artifact for specified SW and HW platform.
-
-The development scope includes the following components:
-
-- Develop importer module to parse, verify and represent NN model for further optimization and compilation
-- Develop code emitters to produce executable binary for CPU and GPU
-
-|
-| **2018 year goals:**
-
-- Support TensorFlow Lite NN model format
-- Support Caffe NN model format
-- Support Caffe2 NN model format (Optional)
-- Support compilation of MobileNet NN
-- Support compilation of Inception v3 NN
-- Support ARM CPU
-- Support ARM GPU (Mali)
-- Support Tizen OS
-- Support SmartMachine OS (Optional)
-
-|
-
-**Terminology and Abbreviation**
-
-.. list-table::
- :widths: 10 30
- :header-rows: 0
-
- * - OS
- - Operating System
- * - OS API
- - Application interface of OS
- * - HW
- - Hardware
- * - SW
- - Software
- * - NN
- - Neural Network
- * - NN model
- - Neural network model (Instance of NN built with ML framework)
- * - NN compiler
- - The compiler for neural network
- * - ML framework
- - The machine learning framework
- * - TF/TF Lite
- - Tensorflow/Tensorflow Lite ML framework
- * - IR
- - Intermediate representation
- * - CI/CI system
- - Continuous integration system
- * - UI
- - The user interface
- * - GUI
- - The graphical user interface
- * - CLI
- - The command-line interface
- * - CG
- - Computational Graph
-
-
-SW System Architecture
-----------------------
-
-The main components of the compiler are the following:
-
-- Configuration system
-- Importer (convert supported NN model to Model IR before optimization)
-- High-Level optimization (Applies HW independent optimizations)
-- Low-Level optimization (Applies optimizations appropriate to the specified target HW)
-- Code emitter (Produces the binary to take advantages of CPU and/or GPU)
-
-
-.. image:: images/nncc_idef0_a1.png
- :scale: 100%
-
-
-Relevant Industry Standards
----------------------------
-
-Architecture design is described using IDEF notation. Since the nncc is a part of open source STAR Platform project any other industry standards not required and/or applicable.
-
-
-SW Functional Requirements
-==========================
-
-Frontend
---------
-
-.. list-table::
- :widths: 23 50 100
- :header-rows: 1
-
- * - ID
- - Requirement Name
- - Description
-
- * - RF-1
- - Frontend: Tensorflow Lite
- - The compiler should support import of NN model in Tensorflow Lite format (parsing & verification of data scheme v0-v3, 50 NN ops)
-
- * - RF-2
- - Frontend: Caffe
- - The compiler should support import of NN model in Caffe format (parsing & verification)
-
-
- * - RF-3
- - Frontend: Caffe2 (Optional)
- - The compiler should support import of NN model in Caffe2 format (parsing & verification)
-
-
- * - RF-4
- - Frontend: lossless import
- - The front-end should use the lossless approach while it is converting any NN model to IR
-
- * - RF-5
- - Frontend: Inception_v3
- - The front-end should successful import the Inception V3 NN model
-
- * - RF-6
- - Frontend: MobileNet
- - The front-end should successful import the MobileNet NN model
-
-High-Level optimization
------------------------
-
-No special requirements
-
-
-Low-Level optimization
-----------------------
-
-No special requirements
-
-
-Backend
--------
-
-.. list-table::
- :widths: 23 50 100
- :header-rows: 1
-
- * - ID
- - Requirement Name
- - Description
-
- * - RF-7
- - Backend: ARM CPU
- - The compiler should produce executable for ARM CPU
-
- * - RF-8
- - Backend: ARM GPU
- - The compiler should produce the binary that takes advantages of GPU when it was specified before compilation
-
- * - RF-9
- - Backend: Artefact type
- - The compiler should produce executable as a shared library or as a static library
-
- * - RF-10
- - Backend: Inception_v3
- - The compiler should produce the valid compiled artefact for Inception v3 NN model
-
- * - RF-11
- - Backend: MobileNet
- - The compiler should produce the valid compiled artefact for MobileNet NN model
-
-
-Configuration
--------------
-
-.. list-table::
- :widths: 23 50 100
- :header-rows: 1
-
- * - ID
- - Requirement Name
- - Description
-
- * - RF-12
- - Config: command line
- - The compiler should get configuration parameters from command line
-
- * - RF-13
- - Config: config file (Optional)
- - The compiler should get configuration parameters from config file
-
- * - RF-14
- - Config: environment variable (Optional)
- - The compiler should get configuration parameters from environment variables
-
-Compiled Artefact
------------------
-
-.. list-table::
- :widths: 23 50 100
- :header-rows: 1
-
- * - ID
- - Requirement Name
- - Description
-
- * - RF-15
- - Artefact: result
- - The artefact should provide comparable result to the original NN model for the same input data
-
- * - RF-16
- - Artefact: input verifications
- - The artefact should verify any input data and check consistency
-
- * - RF-17
- - Artefact: GPU
- - The artefact should take advantage of the GPU for GPU-enabled operations
-
- * - RF-18
- - Artefact: CPU
- - The artefact should take advantage of CPU if it was specified
-
-SW Non-Functional Requirements
-==============================
-
-
-
-The compiler
-------------
-
-Performance
-```````````
-
-No special requirements
-
-SW capacity
-```````````
-
-No special requirements
-
-
-Reliability
-```````````
-
-.. list-table::
- :widths: 23 50 100
- :header-rows: 1
-
- * - ID
- - Requirement Name
- - Description
- * - RNF-1
- - Reliability: input
- - The compiler should produce correct executable in order to utilize CPU and GPU when the correct input data is provided. If the incorrect input data are provided the compiler should not produce a compiled artefact, but inform user about all errors which were met
-
-Security
-````````
-
-No special requirements
-
-Usability
-`````````
-
-No special requirements
-
-Availability
-````````````
-
-No special requirements
-
-Maintainability
-```````````````
-
-No special requirements
-
-Extendibility
-`````````````
-
-.. list-table::
- :widths: 23 50 100
- :header-rows: 1
-
- * - ID
- - Requirement Name
- - Description
- * - RNF-2
- - Extendibility: frontend
- - The compiler design and implementations should provide possibility to add new features to front-end: new NN models format
- * - RNF-3
- - Extendibility: backend
- - The compiler design and implementations should provide possibility to add new features to backend (new targets)
-
-
-Testability
-```````````
-
-
-.. list-table::
- :widths: 23 50 100
- :header-rows: 1
-
- * - ID
- - Requirement Name
- - Description
- * - RNF-4
- - Testability: environment
- - The test environment should be built in order to verify compiler functionality, product build status, artefact build/execution status, artefact calculation result and calculation memory footprint and performance
-
-
-Portability
-```````````
-
-.. list-table::
- :widths: 23 50 100
- :header-rows: 1
-
- * - ID
- - Requirement Name
- - Description
- * - RNF-5
- - Portability: Linux
- - The compiler should be portable with Linux-based OS
-
-Scalability
-```````````
-
-No special requirements
-
-Expandability
-`````````````
-
-No special requirements
-
-Configurability
-```````````````
-
-.. list-table::
- :widths: 23 50 100
- :header-rows: 1
-
- * - ID
- - Requirement Name
- - Description
- * - RNF-6
- - Configurability: command line
- - The compiler should support applying configuration through command line options.
- * - RNF-7
- - Configurability: file (Optional)
- - The compiler should support applying configuration through configuration file.
- * - RNF-8
- - Configurability: environment (Optional)
- - The compiler should support applying configuration through environment variables.
-
-The compiled artefact
----------------------
-
-No special requirements
-
-The source code
----------------
-
-.. list-table::
- :widths: 23 50 100
- :header-rows: 1
-
- * - ID
- - Requirement Name
- - Description
- * - RNF-9
- - Legislation
- - All source code files should follows its original license and general project license without any conflicts
- * - RNF-10
- - Legitimacy
- - The project should have its own general license
- * - RNF-11
- - Coding style
- - Each source code file should follow the one defined for the project coding style
- * - RNF-12
- - Contrib
- - RNF-9, RNF-10, RNF-11 are applicable only for the final release version of source code. These requirements are not applicable to the source code placed in development branch or any folder which is used as temporary storage for the source code under development.
-
-
-SW Interface Requirements
-=========================
-
-The compiler interface
-----------------------
-
-
-User Interface
-``````````````
-
-.. list-table::
- :widths: 23 50 100
- :header-rows: 1
-
- * - ID
- - Requirement Name
- - Description
- * - RIF-1
- - Compiler UI: no interaction
- - The compiler should not require any user interation during compilation (completed compilations, fatal exit)
- * - RIF-2
- - Compiler UI: CLI
- - The compiler is considering as a command line tool which proceed parameters from command line and/or config file, environment variables
- * - RIF-3
- - Compiler UI: input
- - The compiler should provide the facility to specify NN model to be compiled
- * - RIF-4
- - Compiler UI: target device
- - The compiler should provide the facility to specify result target device (CPU or GPU)
- * - RIF-5
- - Compiler UI: target platform
- - The compiler should provide the facility to specify result target SW platform
- * - RIF-6
- - Compiler UI: output
- - The compiler should provide the facility to specify result target name
- * - RIF-7
- - Compiler UI: target type
- - The compiler should provide the facility to specify result target type: shared or static library
-
-
-Hardware Interface
-``````````````````
-
-.. list-table::
- :widths: 23 50 100
- :header-rows: 1
-
- * - ID
- - Requirement Name
- - Description
- * - RIF-8
- - Compiler HWI: x86_64 executable
- - The solution should provide executables to run on x86_64-compatible system
-
-Software Interface
-``````````````````
-
-.. list-table::
- :widths: 23 50 100
- :header-rows: 1
-
- * - ID
- - Requirement Name
- - Description
- * - RIF-9
- - Compiler SWI: frontend plugin
- - The compiler should provide the SW interface in order to add support of the new NN model formats
- * - RIF-10
- - Compiler SWI: backend plugin (HW)
- - The compiler should provide the SW interface in order to add support of the new HW
- * - RIF-11
- - Compiler SWI: backend plugin (SW Platform)
- - The compiler should provide the SW interface in order to add support of the new SW Platform
-
-Communication Interface
-```````````````````````
-
-No requirements for communication interface.
-
-
-The compiled Artefact interface
--------------------------------
-
-
-User Interface
-``````````````
-
-.. list-table::
- :widths: 23 50 100
- :header-rows: 1
-
- * - ID
- - Requirement Name
- - Description
- * - RIF-12
- - Artefact UI: no GUI
- - Command line UI in text is suitable
-
-Hardware Interface
-``````````````````
-
-.. list-table::
- :widths: 23 50 100
- :header-rows: 1
-
- * - ID
- - Requirement Name
- - Description
- * - RIF-13
- - Artefact HWI: CPU
- - The artefact should use ARM CPU instruction set when it was built for ARM CPU
- * - RIF-14
- - Artefact HWI: GPU
- - The artefact should use ARM GPU instruction set when it was build for ARM GPU
-
-Software Interface
-``````````````````
-
-.. list-table::
- :widths: 23 50 100
- :header-rows: 1
-
- * - ID
- - Requirement Name
- - Description
- * - RIF-15
- - Artefact SWI: GPU driver
- - The artefact should use ARM GPU driver to invoke calculations when it was built for ARM GPU
- * - RIF-16
- - Artefact SWI: C/C++ header
- - The artefact should provide C/C++ interface in order to use it in other applications
- * - RIF-17
- - Artefact SWI: shared type
- - The compiled artefact should be a shared library in order to share it between several executables when it was specified before compilation
- * - RIF-18
- - Artefact SWI: static type
- - The compiled artefact should be a static library in order to be built-in to an executable when it was specified before compilation
- * - RIF-19
- - Artefact SWI: Info
- - The artefact should provide SW interface in order to get the actual status of calculation process (progress, errors, final result)
-
-
-Communication Interface
-```````````````````````
-
-No requirements for communication interface.
-
-
-
diff --git a/compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_STD.rst b/compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_STD.rst
deleted file mode 100644
index 97aaa711e..000000000
--- a/compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_STD.rst
+++ /dev/null
@@ -1,1696 +0,0 @@
-=======================
-SW System Test Document
-=======================
-
-
-.. contents::
-
-.. section-numbering::
-
-.. raw:: pdf
-
- PageBreak oneColumn
-
-**Revision history**
-
-+-------+-------------+------------------------------------------+--------------------------+---------------------+
-| Ver. | Date | Contents | Author | Approver |
-+=======+=============+==========================================+==========================+=====================+
-| 0.1 | 2018.04.12 | Initial version | Vostokov Sergey | Sung-Jae Lee |
-+-------+-------------+------------------------------------------+--------------------------+---------------------+
-| 0.2 | 2018.04.13 | SE member in-charge review | Aleksei Kondrashov | |
-+-------+-------------+------------------------------------------+--------------------------+---------------------+
-| 1.0 | 2018.04.17 | Final Execution DR version | Vostokov Sergey | Sung-Jae Lee |
-+-------+-------------+------------------------------------------+--------------------------+---------------------+
-| 1.1 | 2018.06.20 | DR1 version (Test Round 1) | Vostokov Sergey | Sung-Jae Lee |
-+-------+-------------+------------------------------------------+--------------------------+---------------------+
-| 2.0 | 2018.09.24 | DR2 version (Test Round 2) | Vostokov Sergey | Sung-Jae Lee |
-+-------+-------------+------------------------------------------+--------------------------+---------------------+
-| 2.1 | 2018.10.23 | NN Importer Tech Item (Test Round 2.1) | Roman Rusyaev | Sung-Jae Lee |
-+-------+-------------+------------------------------------------+--------------------------+---------------------+
-| 3.0 | 2018.11.30 | Code Emitter Tech Item (Test Round 3) | Roman Rusyaev | Sung-Jae Lee |
-+-------+-------------+------------------------------------------+--------------------------+---------------------+
-| 3.1 | 2018.12.07 | Minor updates for Completion DR | Roman Rusyaev | Sung-Jae Lee |
-+-------+-------------+------------------------------------------+--------------------------+---------------------+
-| 3.2 | 2018.12.07 | SE member in-charge review | Aleksei Kondrashov | |
-+-------+-------------+------------------------------------------+--------------------------+---------------------+
-| 3.3 | 2018.12.07 | Final Completion DR version | Roman Rusyaev | Sung-Jae Lee |
-+-------+-------------+------------------------------------------+--------------------------+---------------------+
-
-|
-
-**Terminology and Abbreviation**
-
-.. list-table::
- :widths: 10 30
- :header-rows: 0
-
- * - OS
- - Operating System
- * - OS API
- - Application interface of OS
- * - HW
- - Hardware
- * - SW
- - Software
- * - NN
- - Neural Network
- * - NN model
- - Neural network model (Instance of NN built with ML framework)
- * - NN compiler
- - The compiler for neural network
- * - ML framework
- - The machine learning framework
- * - TF/TF Lite
- - Tensorflow/Tensorflow Lite ML framework
- * - IR
- - Intermediate representation
- * - CI/CI system
- - Continuous integration system
- * - UI
- - The user interface
- * - GUI
- - The graphical user interface
- * - CLI
- - The command-line interface
- * - CG
- - Computational Graph
-
-
-|
-
-**References**
-
-[1] Vostokov Sergey, `SW Requirements Specification <requirements_specification.md>`_
-
-
-SW System Test Overview
-=======================
-
-Purpose
--------
-
-Software testing is an investigation to provide the quality of the product under test and to reduce risk of its failure
-to users or customers. Purpose of testing is to detect software failures so that defects may be discovered and corrected.
-
-Software system test procedure is a collection of processes and methods used to ensure quality. An additional goal is to make sure that the product
-follows regulations and meets the quality standards expected by the customer.
-
-Scope
------
-
-As the number of possible tests for every software is practically infinite, we use some strategy to select tests that
-are feasible for the available time and resources.
-
-Software system tests attempt to cover requirements listed in the `SW Requirement Specification <https://github.sec.samsung.net/STAR/nncc/doc/project/requirements_specification.md>`_.
-
-Since the project outcome is a compiler then its testing are in different domain than many other kinds of application or
-system testing. They are dedicated to find all possible issues that cause the following bugs:
-
-- Compiler crashes (also known as an ICE or Internal Compiler Error)
-- Compiler hangs (kind of infinite loop in the compiler)
-- Bad code generation (a result of incorrect compiler output):
-
- - Bad code generation that leads to a crash in the application
- - “Silent” bad code generation
-
-- Compiler throughput issues (Issues that affect the amount of time the compiler takes to compile code )
-- Code quality issues (Issues that affect the performance of the compiled application)
-- Compiler feature correctness issues (This class of bugs involves the compiler generating correct code, but not doing what a particular feature specifies should be done)
-
-
-SW System Test Items
-====================
-
-Functions to be tested
-----------------------
-
-**Table 2-1. Test Item**
-
-.. list-table::
- :widths: 20 10 50
- :header-rows: 1
-
- * - Feature
- - Test Item ID
- - Test Item description
-
- * - RF-1, RIF-3 - RIF-7
- - TST-1
- - Test suite checks NN ops import from Tensorflow Lite format by loading NN model that consists of a single NN op. One test for each NN op.
-
- * - RF-2, RIF-3 - RIF-7
- - TST-2
- - Test suite checks NN ops import from Caffe format by loading NN model that consists of a single NN op. One test for each NN op.
-
- * - RF-3, RIF-3 - RIF-7
- - TST-3
- - Test suite checks NN ops import from Caffe2 format by loading NN model that consists of a single NN op. One test for each NN op.
-
- * - RF-5, RIF-3 - RIF-7
- - TST-4
- - The test should verify successful loading the Inception V3 NN model
-
- * - RF-6, RIF-3 - RIF-7
- - TST-5
- - The test should verify successful loading the MobileNet NN model
-
- * - RF-4
- - TST-6
- - The test suite should automatically verify the completeness of information that was read from the raw data by comparing it with serialized raw data from Model IR
-
- * - RF-7, RF-18, RIF-13
- - TST-7
- - The test should automatically verify successful execution of binary on ARM CPU
-
- * - RF-8, RF-17, RIF-14, RIF-15
- - TST-8
- - The unit test should automatically verify successful execution of calculation on GPU
-
- * - RF-9, RNF-1, RIF-17, RIF-18
- - TST-9
- - Unit test should verify the existence and format of binary (shared or static) in accordance to specified options
-
- * - RF-10
- - TST-10
- - Unit test should verify that compiler produces a compiled artefact for the Inception V3 NN model (Validity of compiled artefact is checked by other tests)
-
- * - RF-11
- - TST-11
- - Unit test should verify that compiler produces a compiled artefact for the MobileNet NN model (Validity of compiled artefact is checked by other tests)
-
- * - RF-12, RF-13, RF-14, RNF-6, RNF-7, RNF-8
- - TST-12
- - The test suite should verify correctness of configuration object by unit testing
-
- * - RF-15, RNF-1
- - TST-13
- - The test suite is to verify the correctness of calculations by comparing the result of original NN model and the result of compiled artefact on the same input data
-
- * - RF-16
- - TST-14
- - Unit test should verify that the incorrect input data is processed with an error message without unexpected termination of the application
-
- * - RNF-4, RNF-5, RIF-8
- - TST-15
- - A Linux-based OS should be used while the test environment are built.
-
- * - RIF-16
- - TST-16
- - The unit test should verify the existence and validity of generated C/C++ header for compiled artefact
-
-
-**The following requirements can be tested only manually:**
-
-* Non-functional requirements: RNF-2, RNF-3 (They would be tested during development)
-* Interface requirements: RIF-1, RIF-2, RIF-9 - RIF-12, RIF-19
-
-
-Functions not to be tested
---------------------------
-
-The following requirements cannot be tested:
-
-* The source code requirements (RNF-9. RNF-10. RNF-11)
-
-
-SW System Test Procedure
-========================
-
-Test approaches
----------------
-
-While implementation of the project deliverables several kinds of testing are used. All of them are performed automatically by continuous integration system since it is developed. CI system subscribes on source code modification in the version control system. The configuration does not allow any changes to be merged into the main line if these changes do not pass merge mandatory tests.
-
-- **Code style check** (Merge mandatory test): to verify consistency of coding style
-
-- **Build test** (Merge mandatory test): to verify the current build
-
-- **Unit tests**: to verify SW system consistency. All new implemented features, code refactoring, optimizations must not cause unit test failure. Each unit test reflect the exact logic of testing component, thus, it should be adopted any time when program logic changes.
-
-- **System tests**: to verify the feature quality as well as compliance with its specified requirements.
-
-- **Manual-based UI testing approach**: for interface requirements, which cannot be automated
-
-
-Test Pass/Fail Criteria
------------------------
-
-All tests (unit/system) must be executed without any issues at any time for newly implemented, refactored, or changed code.
-
-Test Start/Suspension/Resumption criteria
------------------------------------------
-
-Two mandatory tests (code style check and build test) are performed for every pool request (PR) before it is merged. The configuration of continuous integration system (CI) does not allow to merge the changes into devel branch if they does not pass the tests.
-
-Unit and feature testing are performed for the devel branch automatically. The merge to master branch (release) are possible when all these tests passed.
-
-Regression Test strategy
-------------------------
-
-If a new issue is detected and it is not covered by an existing
-test then a new test will be developed. In other case the issue should be resolved.
-
-Test tools
-----------
-
-**Table 3-1. Test Tools**
-
-.. list-table::
- :widths: 20 30
- :stub-columns: 1
-
- * - Source code static verification
- - SVACE
-
- * - Source code dynamic analysis
- - Valgrind/AdressSanitizer/LeakSanitizer/MemorySanitizer
-
- * - Test execution
- - CMake
-
- * - Defect management
- - Samsung Research GitHub
-
- * - Continuous Integration system
- - HQ CI (CODE)
-
-
-SW System Test Schedule Plan
-============================
-
-Test task & schedule
---------------------
-
-**Table 4-1. Test Tasks and Schedule**
-
-.. list-table::
- :widths: 20 20 20 30
- :stub-columns: 1
-
- * - Task
- - Schedule
- - Responsibility
- - Detailed Task
-
- * - Unit testing
- - 01.04.2018 - 31.12.2018
- - All
- - All unit tests should be carried out
-
- * - System testing
- - 01.04.2018 - 31.12.2018
- - All
- - All system tests should be carried out
-
-
-Test Resource organization plan
--------------------------------
-
-Test environment
-````````````````
-
-**Table 4-2. Hardware / Operating System**
-
-.. list-table::
- :widths: 10 15 30
- :header-rows: 1
-
- * - Type/Model
- - Operating System
- - Usage
-
- * - PC/x86
- - Ubuntu GNU/Linux version >=14.04
- - Build system with unit tests. System tests are performed too.
-
- * - Tizen TM2
- - Tizen
- - Unit and system testing
-
- * - Odroid XU4
- - Tizen
- - Unit and system testing
-
-**Table 4-3. Software**
-
-.. list-table::
- :widths: 10 15 30
- :header-rows: 1
-
- * - Type
- - Spec
- - Usage
-
- * - Library
- - Google test
- - Organize test code and provide utility methods
-
- * - VCS
- - Samsung github
- - The source code version controlling system
-
- * - CI
- - CODE
- - The HQ CI system
-
- * - Build system
- - CMake
- - Run test and check status
-
- * - Device connectivity
- - sdb
- - Send tools to the device and provide shell to run it
-
- * - Management tool
- - The CODE (Collaborative Open Development Environment)
- - Source code version control, code review, issue tracker, Continuous Integration
-
-Risk management plan
---------------------
-
-**Table 4-5. Risk Management**
-
-.. list-table::
- :widths: 20 35 11 19
- :header-rows: 1
-
- * - Risk
- - Description
- - Probability
- - Countermeasures
-
- * - SmartMachine OS SDK toolchain is not available
- - In order to support compilation for SmartMachine OS the SDK is required. The compiler would have dependency of a SmartMachine OS SDK toolchain.
- - High
- - Suspend support of SmartMachine OS, and make plans when SmartMachine OS SDK is released
-
- * - SmartMachine OS targets are not available
- - To perform testing of executables for SmartMachine OS the specified targets are required.
- - High
- - Request targets or SW emulator when SmartMachine OS is released
-
- * - HQ CI does not support target testing
- - Some tests required the target devices to be run on it. The provided CI system may not support such type of testing.
- - High
- - Set CI environment on site
-
- * - Targets for testing/development are not available
- - Full automatic testing may take a long time. It also required target devices to execute the binaries.
- - Medium
- - Request/Buy enough amount of devices
-
-SW configuration management plan
---------------------------------
-
-SW Configuration items identification
-`````````````````````````````````````
-
-**Table 4-6. SW Configuration Items List**
-
-.. list-table::
- :widths: 5 30 25 30
- :header-rows: 1
-
- * - No
- - Document number
- - SW configuration Item
- - File name
-
- * - 1
- - SRR-RAJ0118ZZ-BWRF-STD001
- - System Test Document (test cases design, round 1)
- - 18 NN compiler and Optimizer (STD) v1.0.pdf
-
- * - 2
- - SRR-RAJ0118ZZ-BWRF-STD002
- - System Test Document (Round 2, 2.1)
- - 18 NN compiler and Optimizer (STD) v2.0.pdf
-
- * - 3
- - SRR-RAJ0118ZZ-BWRF-UTR001
- - Unit Test Report (Round 1)
- - 18 NN compiler and Optimizer (UTR) v1.0.pdf
-
- * - 4
- - SRR-RAJ0118ZZ-BWRF-STD003
- - System Test Document (Round 3)
- - 18 NN compiler and Optimizer (STD) v3.0.pdf
-
- * - 5
- - SRR-RAJ0118ZZ-BWRF-UTR002
- - Unit Test Report (Round 2)
- - 18 NN compiler and Optimizer (UTR) v2.0.pdf
-
- * - 6
- - SRR-RAJ0118ZZ-BWRF-UTR003
- - Unit Test Report (Round 3)
- - 18 NN compiler and Optimizer (UTR) v3.0.pdf
-
-
-
-Directory Structure
-```````````````````
-
-**Table 4-7. Directory Structure**
-
-.. list-table::
- :widths: 20 60
- :header-rows: 1
-
- * - Directory
- - Description
-
- * - /
- - source codes of the build system, main README file
-
- * - /contrib
- - Incubating projects
-
- * - /contrib/nnc
- - Contains the source code of nnc (NN Compiler)
-
- * - /doc
- - Contains the documentation of the project
-
- * - /doc/project
- - Contains project management documents (SRS, SDD, STD, HLD, DLD, etc)
-
- * - /libs
- - Contains the source of the libraries which are used by the nncc
-
- * - /libs/core
- - Contains the source code of the core library of nncc
-
- * - /libs/frontend
- - Contains the source code of supported frontend's plugins
-
- * - /libs/frontend/caffe
- - The source code for the Caffe frontend
-
- * - /libs/frontend/caffe2
- - The source code for the Caffe2 frontend
-
- * - /libs/frontend/tflite
- - The source code for the Tensorflow Lite frontend
-
- * - /libs/backend
- - Contains the source code of supported backend plugins
-
- * - /libs/backend/cpu
- - Contains the source code of CPU backend
-
- * - /libs/backend/gpu
- - Contains the source code of GPU backend
-
- * - /libs/backend/3rd_party
- - Contains the source code of backend to utilize 3rd party libraries
-
- * - /scripts
- - Various scripts for building and testing the nncc
-
- * - /tools
- - The source code of the executables
-
-
-Baseline
-````````
-
-**Table 4-8. Baselines**
-
-.. list-table::
- :widths: 15 40 40 15
- :header-rows: 1
-
- * - Test Round
- - Baseline Name
- - Configuration Item
- - Schedule
-
- * - Round 1
- - The nncc v0.3 (Commit: **f1d2cca0fa9d22292e47adc82b10d6bcdd9c2097**)
- - SRR-RAJ0118ZZ-BWRF-STD001, SRR-RAJ0118ZZ-BWRF-UTR001
- - 09.2018
-
- * - Round 2
- - The nncc v0.7 (Commit: **84e16c9d06903ab9fb2094ea40914942e430d20a**)
- - SRR-RAJ0118ZZ-BWRF-STD002, SRR-RAJ0118ZZ-BWRF-UTR002
- - 11.2018
-
- * - Round 2.1
- - The nncc v0.9 (Commit: **2d7efd2d0bc8a1ba135cb4996902284078e14eff**)
- - SRR-RAJ0118ZZ-BWRF-STD002, SRR-RAJ0118ZZ-BWRF-UTR002
- - 11.2018
-
- * - Round 3
- - The nncc v1.0 (Commit: **85708532881069c6c7b2d26cd1537bf8685a81a7**)
- - SRR-RAJ0118ZZ-BWRF-STD003, SRR-RAJ0118ZZ-BWRF-UTR003
- - 12.2018
-
-
-
-SW System Test Case
-===================
-
-
-The following tests are not supported because they are unavailable in ML Frameworks
-
-**Table 5-1. Unsupported Tests**
-
-+--------------+----------------------------------------------------+
-| ML Framework | Unsupported Tests |
-+==============+====================================================+
-| TFLite | TST-1-9, TST-1-14, TST-1-15, TST-1-23, TST-1-26, |
-| | TST-1-27, TST-1-28, TST-1-29, TST-1-30, TST-1-31, |
-| | TST-1-32, TST-1-34, TST-1-43, TST-1-45, TST-1-51 |
-+--------------+----------------------------------------------------+
-| Caffe | TST-2-15 |
-+--------------+----------------------------------------------------+
-
-**Table 5-2. System Test case**
-
-.. list-table::
- :widths: 10 12 50 20
- :header-rows: 1
-
- * - TestItem ID
- - Testcase ID
- - Test Procedures
- - Expected Results
-
- * - TST-1
- - TST-1-1
- - Import a NN consisting of a single Tensorflow Lite ADD operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-2
- - Import a NN consisting of a single Tensorflow Lite AVERAGE_POOL_2D operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-3
- - Import a NN consisting of a single Tensorflow Lite CONCATENATION operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-4
- - Import a NN consisting of a single Tensorflow Lite CONV_2D operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-5
- - Import a NN consisting of a single Tensorflow Lite DEPTHWISE_CONV_2D operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-6
- - Import a NN consisting of a single Tensorflow Lite DEQUANTIZE operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-7
- - Import a NN consisting of a single Tensorflow Lite EMBEDDING_LOOKUP operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-8
- - Import a NN consisting of a single Tensorflow Lite FULLY_CONNECTED operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-9
- - Import a NN consisting of a single Tensorflow Lite HASHTABLE_LOOKUP operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-10
- - Import a NN consisting of a single Tensorflow Lite L2_NORMALIZATION operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-11
- - Import a NN consisting of a single Tensorflow Lite L2_POOL_2D operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-12
- - Import a NN consisting of a single Tensorflow Lite LOCAL_RESPONSE_NORMALIZATION operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-13
- - Import a NN consisting of a single Tensorflow Lite LOGISTIC operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-14
- - Import a NN consisting of a single Tensorflow Lite LSH_PROJECTION operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-15
- - Import a NN consisting of a single Tensorflow Lite LSTM operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-16
- - Import a NN consisting of a single Tensorflow Lite MAX_POOL_2D operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-17
- - Import a NN consisting of a single Tensorflow Lite MUL operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-18
- - Import a NN consisting of a single Tensorflow Lite RELU operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-19
- - Import a NN consisting of a single Tensorflow Lite RELU_N1_TO_1 operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-20
- - Import a NN consisting of a single Tensorflow Lite RELU6 operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-21
- - Import a NN consisting of a single Tensorflow Lite RESHAPE operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-22
- - Import a NN consisting of a single Tensorflow Lite RESIZE_BILINEAR operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-23
- - Import a NN consisting of a single Tensorflow Lite RNN operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-24
- - Import a NN consisting of a single Tensorflow Lite SOFTMAX operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-25
- - Import a NN consisting of a single Tensorflow Lite SPACE_TO_DEPTH operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-26
- - Import a NN consisting of a single Tensorflow Lite SVDF operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-27
- - Import a NN consisting of a single Tensorflow Lite TANH operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-28
- - Import a NN consisting of a single Tensorflow Lite CONCAT_EMBEDDINGS operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-29
- - Import a NN consisting of a single Tensorflow Lite SKIP_GRAM operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-30
- - Import a NN consisting of a single Tensorflow Lite CALL operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-31
- - Import a NN consisting of a single Tensorflow Lite CUSTOM operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-32
- - Import a NN consisting of a single Tensorflow Lite EMBEDDING_LOOKUP_SPARSE operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-33
- - Import a NN consisting of a single Tensorflow Lite PAD operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-34
- - Import a NN consisting of a single Tensorflow Lite UNIDIRECTIONAL_SEQUENCE_RNN operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-35
- - Import a NN consisting of a single Tensorflow Lite GATHER operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-36
- - Import a NN consisting of a single Tensorflow Lite BATCH_TO_SPACE_ND operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-37
- - Import a NN consisting of a single Tensorflow Lite SPACE_TO_BATCH_ND operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-38
- - Import a NN consisting of a single Tensorflow Lite TRANSPOSE operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-39
- - Import a NN consisting of a single Tensorflow Lite MEAN operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-40
- - Import a NN consisting of a single Tensorflow Lite SUB operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-41
- - Import a NN consisting of a single Tensorflow Lite DIV operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-42
- - Import a NN consisting of a single Tensorflow Lite SQUEEZE operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-43
- - Import a NN consisting of a single Tensorflow Lite UNIDIRECTIONAL_SEQUENCE_LSTM operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-44
- - Import a NN consisting of a single Tensorflow Lite STRIDED_SLICE operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-45
- - Import a NN consisting of a single Tensorflow Lite BIDIRECTIONAL_SEQUENCE_RNN operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-46
- - Import a NN consisting of a single Tensorflow Lite EXP operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-47
- - Import a NN consisting of a single Tensorflow Lite TOPK_V2 operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-48
- - Import a NN consisting of a single Tensorflow Lite SPLIT operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-49
- - Import a NN consisting of a single Tensorflow Lite LOG_SOFTMAX operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-50
- - Import a NN consisting of a single Tensorflow Lite DELEGATE operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-51
- - Import a NN consisting of a single Tensorflow Lite BIDIRECTIONAL_SEQUENCE_LSTM operation
- - During import no crashes or error messages occurred
-
- * - TST-1
- - TST-1-52
- - Import a NN consisting of a single Tensorflow Lite CAST operation
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-1
- - Import a NN consisting of Caffe ImageData layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-2
- - Import a NN consisting of Caffe Data layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-3
- - Import a NN consisting of Caffe HDF5Input layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-4
- - Import a NN consisting of two Caffe layers - Input layer and HDF5Output layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-5
- - Import a NN consisting of Caffe Input layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-6
- - Import a NN consisting of Caffe WindowData layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-7
- - Import a NN consisting of Caffe MemoryData layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-8
- - Import a NN consisting of Caffe DummyData layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-9
- - Import a NN consisting of two Caffe layers - Input layer and Convolution layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-10
- - Import a NN consisting of two Caffe layers - Input layer and Pooling layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-11
- - Import a NN consisting of two Caffe layers - Input layer and SPP layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-12
- - Import a NN consisting of two Caffe layers - Input layer and Crop layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-13
- - Import a NN consisting of two Caffe layers - Input layer and Deconvolution layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-14
- - Import a NN consisting of two Caffe layers - Input layer and Im2Col layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-15
- - Import a NN consisting of two Caffe layers - Input layer and Recurrent layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-16
- - Import a NN consisting of two Caffe layers - Input layer and RNN layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-17
- - Import a NN consisting of two Caffe layers - Input layer and LSTM layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-18
- - Import a NN consisting of two Caffe layers - Input layer and InnerProduct layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-19
- - Import a NN consisting of two Caffe layers - Input layer and Dropout layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-20
- - Import a NN consisting of two Caffe layers - Input layer and Embed layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-21
- - Import a NN consisting of two Caffe layers - Input layer and LRN layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-22
- - Import a NN consisting of two Caffe layers - Input layer and MVN layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-23
- - Import a NN consisting of two Caffe layers - Input layer and BatchNorm layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-24
- - Import a NN consisting of two Caffe layers - Input layer and ReLU layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-25
- - Import a NN consisting of two Caffe layers - Input layer and PReLU layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-26
- - Import a NN consisting of two Caffe layers - Input layer and ELU layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-27
- - Import a NN consisting of two Caffe layers - Input layer and Sigmoid layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-28
- - Import a NN consisting of two Caffe layers - Input layer and TanH layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-29
- - Import a NN consisting of two Caffe layers - Input layer and AbsVal layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-30
- - Import a NN consisting of two Caffe layers - Input layer and Power layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-31
- - Import a NN consisting of two Caffe layers - Input layer and Exp layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-32
- - Import a NN consisting of two Caffe layers - Input layer and Log layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-33
- - Import a NN consisting of two Caffe layers - Input layer and BNLL layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-34
- - Import a NN consisting of two Caffe layers - Input layer and Threshold layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-35
- - Import a NN consisting of two Caffe layers - Input layer and Bias layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-36
- - Import a NN consisting of two Caffe layers - Input layer and Scale layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-37
- - Import a NN consisting of two Caffe layers - Input layer and Flatten layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-38
- - Import a NN consisting of two Caffe layers - Input layer and Reshape layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-39
- - Import a NN consisting of two Caffe layers - Input layer and BatchReindex layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-40
- - Import a NN consisting of two Caffe layers - Input layer and Split layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-41
- - Import a NN consisting of two Caffe layers - Input layer and Concat layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-42
- - Import a NN consisting of two Caffe layers - Input layer and Slice layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-43
- - Import a NN consisting of two Caffe layers - Input layer and Eltwise layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-44
- - Import a NN consisting of two Caffe layers - Input layer and Filter layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-45
- - Import a NN consisting of two Caffe layers - Input layer and Parameter layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-46
- - Import a NN consisting of two Caffe layers - Input layer and Reduction layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-47
- - Import a NN consisting of two Caffe layers - Input layer and Silence layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-48
- - Import a NN consisting of two Caffe layers - Input layer and ArgMax layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-49
- - Import a NN consisting of two Caffe layers - Input layer and Softmax layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-50
- - Import a NN consisting of two Caffe layers - Input layer and Python layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-51
- - Import a NN consisting of two Caffe layers - Input layer and MultinomialLogisticLoss layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-52
- - Import a NN consisting of two Caffe layers - Input layer and Infogain layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-53
- - Import a NN consisting of two Caffe layers - Input layer and SoftmaxWithLoss layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-54
- - Import a NN consisting of two Caffe layers - Input layer and EuclideanLoss layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-55
- - Import a NN consisting of two Caffe layers - Input layer and HingeLoss layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-56
- - Import a NN consisting of two Caffe layers - Input layer and SigmoidCrossEntropyLoss layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-57
- - Import a NN consisting of two Caffe layers - Input layer and Accuracy layer
- - During import no crashes or error messages occurred
-
- * - TST-2
- - TST-2-58
- - Import a NN consisting of two Caffe layers - Input layer and ContrastiveLoss layer
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-1
- - Import a NN consisting of a single Caffe2 Add operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-2
- - Import a NN consisting of a single Caffe2 AveragePool2D operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-3
- - Import a NN consisting of a single Caffe2 Concat operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-4
- - Import a NN consisting of a single Caffe2 Conv2D operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-5
- - Import a NN consisting of a single Caffe2 FC operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-6
- - Import a NN consisting of a single Caffe2 LRN operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-7
- - Import a NN consisting of a single Caffe2 Sigmoid operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-8
- - Import a NN consisting of a single Caffe2 MaxPool2D operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-9
- - Import a NN consisting of a single Caffe2 Mul operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-10
- - Import a NN consisting of a single Caffe2 Relu operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-11
- - Import a NN consisting of a single Caffe2 Reshape operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-12
- - Import a NN consisting of a single Caffe2 Softmax operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-13
- - Import a NN consisting of a single Caffe2 Tanh operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-14
- - Import a NN consisting of a single Caffe2 PadImage operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-15
- - Import a NN consisting of a single Caffe2 BatchToSpace operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-16
- - Import a NN consisting of a single Caffe2 SpaceToBatch operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-17
- - Import a NN consisting of a single Caffe2 Transpose operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-18
- - Import a NN consisting of a single Caffe2 Mean operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-19
- - Import a NN consisting of a single Caffe2 Sub operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-20
- - Import a NN consisting of a single Caffe2 Div operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-21
- - Import a NN consisting of a single Caffe2 Squeeze operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-22
- - Import a NN consisting of a single Caffe2 Exp operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-23
- - Import a NN consisting of a single Caffe2 TopK operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-24
- - Import a NN consisting of a single Caffe2 Split operation
- - During import no crashes or error messages occurred
-
- * - TST-3
- - TST-3-25
- - Import a NN consisting of a single Caffe2 Cast operation
- - During import no crashes or error messages occurred
-
- * - TST-4
- - TST-4-1
- - Import Inception V3 NN model
- - During import no crashes or error messages occurred
-
- * - TST-5
- - TST-5-1
- - Import MobileNet NN model
- - During import no crashes or error messages occurred
-
- * - TST-6
- - TST-6-1
- - Import Inception V3 NN model, serialize all model weights, compare serialized data with the initial NN model
- - Test executed successfully, serialized weights are equal to initial model weights
-
- * - TST-6
- - TST-6-2
- - Import MobileNet NN model, serialize all model weigths, compare serialized data with the initial NN model
- - Test executed successfully, serialized weights are equal to initial model weights
-
- * - TST-7
- - TST-7-1
- - Generate binary for the Inception V3 NN model and run its inference on ARM CPU with Ubuntu
- - Test executed successfully, no crashes occurred, inference result was output, amount and format of the outputs corresponds to the expected NN model outputs
-
- * - TST-7
- - TST-7-2
- - Generate binary for the MobileNet NN model and run its inference on ARM CPU with Ubuntu
- - Test executed successfully, no crashes occurred, inference result was output, amount and format of the outputs corresponds to the expected NN model outputs
-
- * - TST-7
- - TST-7-3
- - Generate binary for the Inception V3 NN model and run its inference on ARM CPU with Tizen
- - Test executed successfully, no crashes occurred, inference result was output, amount and format of the outputs corresponds to the expected NN model outputs
-
- * - TST-7
- - TST-7-4
- - Generate binary for the MobileNet NN model and run its inference on ARM CPU with Tizen
- - Test executed successfully, no crashes occurred, inference result was output, amount and format of the outputs corresponds to the expected NN model outputs
-
- * - TST-7
- - TST-7-5
- - Generate binary for the Inception V3 NN model and run its inference on ARM CPU with SmartMachine OS
- - Test executed successfully, no crashes occurred, inference result was output, amount and format of the outputs corresponds to the expected NN model outputs
-
- * - TST-7
- - TST-7-6
- - Generate binary for the MobileNet NN model and run its inference on ARM CPU with SmartMachine OS
- - Test executed successfully, no crashes occurred, inference result was output, amount and format of the outputs corresponds to the expected NN model outputs
-
- * - TST-8
- - TST-8-1
- - Generate binary for the Inception V3 NN model and run its inference on a GPU-enabled device
- - Test executed successfully, no crashes occurred, inference result was output, amount and format of the outputs corresponds to the expected NN model outputs
-
- * - TST-8
- - TST-8-2
- - Generate binary for the MobileNet V3 NN model and run its inference on a GPU-enabled device
- - Test executed successfully, no crashes occurred, inference result was output, amount and format of the outputs corresponds to the expected NN model outputs
-
- * - TST-9
- - TST-9-1
- - Provide correct NN model, compile it as a static library, then check that corresponding binary exists and it is a static library
- - Test executed successfully
-
- * - TST-9
- - TST-9-2
- - Provide correct NN model, compile it as a shared library, then check that corresponding binary exists and it is a shared library
- - Test executed successfully
-
- * - TST-9
- - TST-9-3
- - Provide incorrect model, compile it as a static library, then check that no compiled artifact is produced
- - Test executed successfully
-
- * - TST-9
- - TST-9-4
- - Provide incorrect model, compile it as a shared library, then check that no compiled artifact is produced
- - Test executed successfully
-
- * - TST-10
- - TST-10-1
- - Check that a static library is provided after compiling Inception V3 as a static library
- - Test executed successfully
-
- * - TST-10
- - TST-10-2
- - Check that a shared library is provided after compiling Inception V3 as a shared library
- - Test executed successfully
-
- * - TST-11
- - TST-11-1
- - Check that a static library is provided after compiling MobileNet as a static library
- - Test executed successfully
-
- * - TST-11
- - TST-11-2
- - Check that a shared library is provided after compiling MobileNet as a shared library
- - Test executed successfully
-
- * - TST-12
- - TST-12-1
- - Check that configuration object is constructed correctly when getting configuration parameters from command line
- - Test executed successfully
-
- * - TST-12
- - TST-12-2
- - Check that configuration object is constructed correctly when getting configuration parameters from config file
- - Test executed successfully
-
- * - TST-12
- - TST-12-3
- - Check that configuration object is constructed correctly when getting configuration parameters from environment variables
- - Test executed successfully
-
- * - TST-13
- - TST-13-1
- - Compile Inception V3 as static library for CPU, provide it and the original model with same correct input data, then compare the result from original model with the result from compiled artifact
- - Test executed successfully, results are comparable
-
- * - TST-13
- - TST-13-2
- - Compile Inception V3 as shared library for CPU, provide it and the original model with same correct input data, then compare the result from original model with the result from compiled artifact
- - Test executed successfully, results are comparable
-
- * - TST-13
- - TST-13-3
- - Compile Inception V3 as static library for GPU, provide it and the original model with same correct input data, then compare the result from original model with the result from compiled artifact
- - Test executed successfully, results are comparable
-
- * - TST-13
- - TST-13-4
- - Compile Inception V3 as shared library for GPU, provide it and the original model with same correct input data, then compare the result from original model with the result from compiled artifact
- - Test executed successfully, results are comparable
-
- * - TST-13
- - TST-13-5
- - Compile MobileNet as static library for CPU, provide it and the original model with same correct input data, then compare the result from original model with the result from compiled artifact
- - Test executed successfully, results are comparable
-
- * - TST-13
- - TST-13-6
- - Compile MobileNet as shared library for CPU, provide it and the original model with same correct input data, then compare the result from original model with the result from compiled artifact
- - Test executed successfully, results are comparable
-
- * - TST-13
- - TST-13-7
- - Compile MobileNet as static library for GPU, provide it and the original model with same correct input data, then compare the result from original model with the result from compiled artifact
- - Test executed successfully, results are comparable
-
- * - TST-13
- - TST-13-8
- - Compile MobileNet as shared library for GPU, provide it and the original model with same correct input data, then compare the result from original model with the result from compiled artifact
- - Test executed successfully, results are comparable
-
- * - TST-14
- - TST-14-1
- - Provide compiled Inception V3 artifact with invalid input, check that no unexpected termination occurs
- - Test executed successfully
-
- * - TST-14
- - TST-14-2
- - Provide compiled Inception V3 artifact with invalid input, check that an error message is provided
- - Test executed successfully
-
- * - TST-14
- - TST-14-3
- - Provide compiled MobileNet artifact with invalid input, check that no unexpected termination occurs
- - Test executed successfully
-
- * - TST-14
- - TST-14-4
- - Provide compiled MobileNet artifact with invalid input, check that an error message is provided
- - Test executed successfully
-
- * - TST-15
- - TST-15-1
- - Check that the OS used during test environment build is Linux-based
- - Test executed successfully
-
- * - TST-16
- - TST-16-1
- - Compile a valid NN model, then check that C/C++ header corresponding to compiled artifact exists
- - Test executed successfully
-
- * - TST-16
- - TST-16-2
- - Compile a valid NN model, then if C/C++ header corresponding to compiled artifact exists, verify its validity
- - Test executed successfully
-
-
-SW System Test Result
-=====================
-
-Test Schedule
--------------
-
-**Table 6-1. Test Period**
-
-.. list-table::
- :widths: 40 20 20
- :header-rows: 1
-
- * -
- - Plan
- - Actual
-
- * - Entire test period
- - 04/2018 - 12/2018
- - 06/2018 - 12/2018
-
- * - Reason for change period
- -
- -
-
-|
-
-**Table 6-2. Test plan vs Actual**
-
-+------------+---------------------+--------------------+-----------------+
-| Test round | Plan | Actual | Remarks |
-| +-----------+---------+-----------+--------+ |
-| | Schedule | MM | Schedule | MM | |
-+============+===========+=========+===========+========+=================+
-| R.1 | 09/2018 | 1 | 09/2018 | 1 | nncc v0.3 |
-+------------+-----------+---------+-----------+--------+-----------------+
-| R.2 | 11/2018 | 1 | 10/2018 | 1 | nncc v0.7 |
-+------------+-----------+---------+-----------+--------+-----------------+
-| R.2.1 | 11/2018 | 1 | 11/2018 | 1 | nncc v0.9 |
-+------------+-----------+---------+-----------+--------+-----------------+
-| R.3 | 12/2018 | 1 | 12/2018 | 1 | nncc v1.0 |
-+------------+-----------+---------+-----------+--------+-----------------+
-
-Test Output
------------
-
-Test Round 1
-````````````
-
-::
-
- Discovering tests...
- Total tests discovered: 80
- Log folder: /home/ivan/GIT/nnc_doc_test/2018_09_25_14_30_35
- Threads quantity: 8
-
- =================================
- Tests: 80/80, successes: 80, failures: 0, errors: 0, other: 0
- =================================
- Result:
- Total tests: 80
- Successes: 80
- Errors: 0
- Failures: 0
- Skipped: 0
- Expected failures: 0
- Unexpected successes: 0
-
-Test Round 2
-````````````
-
-::
-
- Discovering tests...
- Total tests discovered: 118
- Log folder: /home/ivan/GIT/nnc_doc_test/2018_10_23_16_33_19
- Threads quantity: 8
-
- =================================
- Tests: 118/118, successes: 118, failures: 0, errors: 0, other: 0
- =================================
- Result:
- Total tests: 118
- Successes: 118
- Errors: 0
- Failures: 0
- Skipped: 0
- Expected failures: 0
- Unexpected successes: 0
-
-Test Round 2.1
-``````````````
-
-::
-
- Discovering tests...
- Total tests discovered: 122
- Log folder: /home/ivan/GIT/nnc_doc_test/2018_11_09_21_46_03
- Threads quantity: 1
-
- =================================
- Tests: 122/122, successes: 122, failures: 0, errors: 0, other: 0
- =================================
- Result:
- Total tests: 122
- Successes: 122
- Errors: 0
- Failures: 0
- Skipped: 0
- Expected failures: 0
- Unexpected successes: 0
-
-Test Round 3
-````````````
-
-::
-
- Discovering tests...
- Total tests discovered: 128
- Log folder: /home/ivan/GIT/nnc_doc_test/2018_12_03_22_10_26
- Threads quantity: 1
-
- =================================
- Tests: 128/128, successes: 128, failures: 0, errors: 0, other: 0
- =================================
- Result:
- Total tests: 128
- Successes: 128
- Errors: 0
- Failures: 0
- Skipped: 0
- Expected failures: 0
- Unexpected successes: 0
-
-
-Test Result
------------
-
-
-**Table 6-3. System Test Result**
-
-+--------------------+------------------------------------+------------+----------------+
-| Official release | The number of test case | Pass rate | Result |
-| +-------+-------+------+-------------+ | |
-| | Total | Pass | Fail | Unsupported | Pass/Total | |
-+====================+=======+=======+======+=============+============+================+
-| v0.3 | 80 | 80 | 0 | 1 | 1.0 |**Tests passed**|
-+--------------------+-------+-------+------+-------------+------------+----------------+
-| v0.7 | 118 | 118 | 0 | 15 | 1.0 |**Tests passed**|
-+--------------------+-------+-------+------+-------------+------------+----------------+
-| v0.9 | 122 | 122 | 0 | 15 | 1.0 |**Tests passed**|
-+--------------------+-------+-------+------+-------------+------------+----------------+
-| v1.0 | 128 | 128 | 0 | 15 | 1.0 |**Tests passed**|
-+--------------------+-------+-------+------+-------------+------------+----------------+
-
-|
-
-**Table 6-4. Test Problem Result**
-
-+---------+---------------------------+---------------------------+
-| Status | Unresolved | Resolved |
-+---------+----------+--------+-------+----------+--------+-------+
-| Round | Critical | Major | Minor | Critical | Major | Minor |
-+=========+==========+========+=======+==========+========+=======+
-| R.1 | 0 | 0 | 0 | 0 | 0 | 0 |
-+---------+----------+--------+-------+----------+--------+-------+
-| R.2 | 0 | 0 | 0 | 0 | 0 | 0 |
-+---------+----------+--------+-------+----------+--------+-------+
-| R.2.1 | 0 | 0 | 0 | 0 | 0 | 0 |
-+---------+----------+--------+-------+----------+--------+-------+
-| R.3 | 0 | 0 | 0 | 0 | 0 | 0 |
-+---------+----------+--------+-------+----------+--------+-------+
-
-|
-
-**Table 6-5. Round Test Item**
-
-+---------+----------------------------------------------------------------------------------------------+
-| Round | Test Item |
-+=========+==============================================================================================+
-| R.1 | TST-2-1, TST-2-2, TST-2-3, TST-2-4, TST-2-5, TST-2-6, TST-2-7, TST-2-8, TST-2-9, |
-| | TST-2-10, TST-2-11, TST-2-12, TST-2-13, TST-2-14, TST-2-16, TST-2-17, TST-2-18, TST-2-19, |
-| | TST-2-20, TST-2-21, TST-2-22, TST-2-23, TST-2-24, TST-2-25, TST-2-26, TST-2-27, TST-2-28, |
-| | TST-2-29, TST-2-30, TST-2-31, TST-2-32, TST-2-33, TST-2-34, TST-2-35, TST-2-36, TST-2-37, |
-| | TST-2-38, TST-2-39, TST-2-40, TST-2-41, TST-2-42, TST-2-43, TST-2-44, TST-2-45, TST-2-46, |
-| | TST-2-47, TST-2-48, TST-2-49, TST-2-50, TST-2-51, TST-2-52, TST-2-53, TST-2-54, TST-2-55, |
-| | TST-2-56, TST-2-57, TST-2-58, TST-4-1, TST-5-1, TST-7-1, TST-7-2, TST-9-1, TST-9-2, TST-9-3, |
-| | TST-9-4, TST-10-1, TST-10-2, TST-11-1, TST-11-2, TST-13-1, TST-13-2, TST-13-5, TST-13-6, |
-| | TST-14-1, TST-14-2, TST-14-3, TST-14-4, TST-15-1, TST-16-1, TST-16-2 |
-+---------+----------------------------------------------------------------------------------------------+
-| R.2 | TST-2-1, TST-2-2, TST-2-3, TST-2-4, TST-2-5, TST-2-6, TST-2-7, TST-2-8, TST-2-9, |
-| | TST-2-10, TST-2-11, TST-2-12, TST-2-13, TST-2-14, TST-2-16, TST-2-17, TST-2-18, TST-2-19, |
-| | TST-2-20, TST-2-21, TST-2-22, TST-2-23, TST-2-24, TST-2-25, TST-2-26, TST-2-27, TST-2-28, |
-| | TST-2-29, TST-2-30, TST-2-31, TST-2-32, TST-2-33, TST-2-34, TST-2-35, TST-2-36, TST-2-37, |
-| | TST-2-38, TST-2-39, TST-2-40, TST-2-41, TST-2-42, TST-2-43, TST-2-44, TST-2-45, TST-2-46, |
-| | TST-2-47, TST-2-48, TST-2-49, TST-2-50, TST-2-51, TST-2-52, TST-2-53, TST-2-54, TST-2-55, |
-| | TST-2-56, TST-2-57, TST-2-58, TST-4-1, TST-5-1, TST-7-1, TST-7-2, TST-9-1, TST-9-2, TST-9-3, |
-| | TST-9-4, TST-10-1, TST-10-2, TST-11-1, TST-11-2, TST-13-1, TST-13-2, TST-13-5, TST-13-6, |
-| | TST-14-1, TST-14-2, TST-14-3, TST-14-4, TST-15-1, TST-16-1, TST-16-2, TST-1-11, TST-1-25, |
-| | TST-1-36, TST-1-37, TST-1-10, TST-1-12, TST-1-13, TST-1-16, TST-1-17, TST-1-18, TST-1-19, |
-| | TST-1-1, TST-1-20, TST-1-21, TST-1-22, TST-1-24, TST-1-2, TST-1-33, TST-1-35, TST-1-38, |
-| | TST-1-39, TST-1-3, TST-1-40, TST-1-41, TST-1-42, TST-1-44, TST-1-46, TST-1-47, TST-1-48, |
-| | TST-1-49, TST-1-4, TST-1-50, TST-1-52, TST-1-5, TST-1-6, TST-1-7, TST-1-8, TST-12-1 |
-+---------+----------------------------------------------------------------------------------------------+
-| R.2.1 | TST-2-1, TST-2-2, TST-2-3, TST-2-4, TST-2-5, TST-2-6, TST-2-7, TST-2-8, TST-2-9, |
-| | TST-2-10, TST-2-11, TST-2-12, TST-2-13, TST-2-14, TST-2-16, TST-2-17, TST-2-18, TST-2-19, |
-| | TST-2-20, TST-2-21, TST-2-22, TST-2-23, TST-2-24, TST-2-25, TST-2-26, TST-2-27, TST-2-28, |
-| | TST-2-29, TST-2-30, TST-2-31, TST-2-32, TST-2-33, TST-2-34, TST-2-35, TST-2-36, TST-2-37, |
-| | TST-2-38, TST-2-39, TST-2-40, TST-2-41, TST-2-42, TST-2-43, TST-2-44, TST-2-45, TST-2-46, |
-| | TST-2-47, TST-2-48, TST-2-49, TST-2-50, TST-2-51, TST-2-52, TST-2-53, TST-2-54, TST-2-55, |
-| | TST-2-56, TST-2-57, TST-2-58, TST-4-1, TST-5-1, TST-7-1, TST-7-2, TST-7-3, TST-7-4, TST-7-5, |
-| | TST-7-6, TST-9-1, TST-9-2, TST-9-3, TST-9-4, TST-10-1, TST-10-2, TST-11-1, TST-11-2, |
-| | TST-13-1, TST-13-2, TST-13-5, TST-13-6, TST-14-1, TST-14-2, TST-14-3, TST-14-4, TST-15-1, |
-| | TST-16-1, TST-16-2, TST-1-11, TST-1-25, TST-1-36, TST-1-37, TST-1-10, TST-1-12, TST-1-13, |
-| | TST-1-16, TST-1-17, TST-1-18, TST-1-19, TST-1-1, TST-1-20, TST-1-21, TST-1-22, TST-1-24, |
-| | TST-1-2, TST-1-33, TST-1-35, TST-1-38, TST-1-39, TST-1-3, TST-1-40, TST-1-41, TST-1-42, |
-| | TST-1-44, TST-1-46, TST-1-47, TST-1-48, TST-1-49, TST-1-4, TST-1-50, TST-1-52, TST-1-5, |
-| | TST-1-6, TST-1-7, TST-1-8, TST-12-1 |
-+---------+----------------------------------------------------------------------------------------------+
-| R.3 | TST-2-1, TST-2-2, TST-2-3, TST-2-4, TST-2-5, TST-2-6, TST-2-7, TST-2-8, TST-2-9, |
-| | TST-2-10, TST-2-11, TST-2-12, TST-2-13, TST-2-14, TST-2-16, TST-2-17, TST-2-18, TST-2-19, |
-| | TST-2-20, TST-2-21, TST-2-22, TST-2-23, TST-2-24, TST-2-25, TST-2-26, TST-2-27, TST-2-28, |
-| | TST-2-29, TST-2-30, TST-2-31, TST-2-32, TST-2-33, TST-2-34, TST-2-35, TST-2-36, TST-2-37, |
-| | TST-2-38, TST-2-39, TST-2-40, TST-2-41, TST-2-42, TST-2-43, TST-2-44, TST-2-45, TST-2-46, |
-| | TST-2-47, TST-2-48, TST-2-49, TST-2-50, TST-2-51, TST-2-52, TST-2-53, TST-2-54, TST-2-55, |
-| | TST-2-56, TST-2-57, TST-2-58, TST-4-1, TST-5-1, TST-7-1, TST-7-2, TST-7-3, TST-7-4, TST-7-5, |
-| | TST-7-6, TST-9-1, TST-9-2, TST-9-3, TST-9-4, TST-10-1, TST-10-2, TST-11-1, TST-11-2, |
-| | TST-13-1, TST-13-2, TST-13-5, TST-13-6, TST-14-1, TST-14-2, TST-14-3, TST-14-4, TST-15-1, |
-| | TST-16-1, TST-16-2, TST-1-11, TST-1-25, TST-1-36, TST-1-37, TST-1-10, TST-1-12, TST-1-13, |
-| | TST-1-16, TST-1-17, TST-1-18, TST-1-19, TST-1-1, TST-1-20, TST-1-21, TST-1-22, TST-1-24, |
-| | TST-1-2, TST-1-33, TST-1-35, TST-1-38, TST-1-39, TST-1-3, TST-1-40, TST-1-41, TST-1-42, |
-| | TST-1-44, TST-1-46, TST-1-47, TST-1-48, TST-1-49, TST-1-4, TST-1-50, TST-1-52, TST-1-5, |
-| | TST-1-6, TST-1-7, TST-1-8, TST-12-1, TST-8-1, TST-8-2, TST-13-3, TST-13-4, TST-13-7, |
-| | TST-13-8 |
-+---------+----------------------------------------------------------------------------------------------+
-
-Defect rates
-````````````
-No defects are found
-
-
-Main problem contents
----------------------
-
-No problems were found
-
-
-Total Result
-============
-
-128 of 128 system tests are passed. 15 tests currently unsupported by nncc v1.0.
diff --git a/compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_UTR.rst b/compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_UTR.rst
deleted file mode 100644
index 9f06ca30f..000000000
--- a/compiler/nnc/doc/project/18_NN_Compiler_and_Optimizer_UTR.rst
+++ /dev/null
@@ -1,767 +0,0 @@
-===================
-SW Unit Test Report
-===================
-
-.. contents::
-
-.. section-numbering::
-
-.. raw:: pdf
-
- PageBreak oneColumn
-
-
-**Revision history**
-
-+--------+---------------+----------------------------+---------------------------------------+---------------------+
-| Ver. | Date | Contents | Authors | Approver |
-+========+===============+============================+=======================================+=====================+
-| 1.0 | 2018.09.24 | Initial version | Vostokov Sergey | Sung-Jae Lee |
-+--------+---------------+----------------------------+---------------------------------------+---------------------+
-| 1.1 | 2018.10.23 | Test Round 2 | Roman Rusyaev | Sung-Jae Lee |
-+--------+---------------+----------------------------+---------------------------------------+---------------------+
-| 2.0 | 2018.12.10 | Test Round 3 | Roman Rusyaev | Sung-Jae Lee |
-+--------+---------------+----------------------------+---------------------------------------+---------------------+
-
-
-Test Status
-===========
-
-Summary
--------
-
-.. list-table::
- :class: longtable
- :widths: 4 10 10 10 5 5 5 5 10 12 12
- :header-rows: 1
-
- * - N
- - SW Version
- - Start date
- - End date
- - Total
- - Pass
- - Fail
- - N/A
- - Pass Rate
- - Characteristic of TC
- - Remarks
-
- * - 1
- - 0.3
- - 09/24/2018
- - 09/24/2018
- - 35
- - 35
- - 0
- - 0
- - 100%
- - tests for CLI, soft backend, compiler driver and IR
- - commit: f1d2cca0fa9d22292e47adc82b10d6bcdd9c2097
-
- * - 2
- - 0.7
- - 10/23/2018
- - 10/23/2018
- - 43
- - 43
- - 0
- - 0
- - 100%
- - tests for CLI, frontend, soft backend, compiler driver and IR
- - commit: 84e16c9d06903ab9fb2094ea40914942e430d20a
-
- * - 3
- - 1.0
- - 12/10/2018
- - 12/10/2018
- - 74
- - 74
- - 0
- - 0
- - 100%
- - tests for CLI, frontend, soft backend for CPU and GPU, compiler driver and IR
- - commit: 7974192249ce1dd46c785ba8d25506eddb5b24ad
-
-
-Details
--------
-
-NNCC Version 0.3 (09.2018)
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-These unit tests are from commit **f1d2cca0fa9d22292e47adc82b10d6bcdd9c2097**.
-
-Tests description
-#################
-
-``NNC`` unit tests coverage is 67%. For more information about what functions are covered see ``nncc/contrib/nnc/unittests``.
-
-|
-
-.. list-table::
- :class: longtable
- :widths: 20 10 30
- :header-rows: 1
-
- * - Test Name
- - Number of Tests
- - Tests Scope
-
- * - nnc_soft_backend_test
- - 15
- - C++ CPU soft backend functionality
-
- * - nnc_support_commandline_test
- - 1
- - CLI: parser and configuration system
-
- * - nnc_core_test
- - 17
- - model IR: serializer, shape inference etc
-
- * - nnc_pass_test
- - 2
- - pass manager, pass_data class
-
-Tests output
-############
-
-::
-
-[==========] Running 15 tests from 4 test cases.
-[----------] Global test environment set-up.
-[----------] 11 tests from cpp_operations_test
-[ RUN ] cpp_operations_test.bias
-[ OK ] cpp_operations_test.bias (0 ms)
-[ RUN ] cpp_operations_test.scale
-[ OK ] cpp_operations_test.scale (1 ms)
-[ RUN ] cpp_operations_test.capped_relu
-[ OK ] cpp_operations_test.capped_relu (0 ms)
-[ RUN ] cpp_operations_test.concat
-[ OK ] cpp_operations_test.concat (10 ms)
-[ RUN ] cpp_operations_test.conv2d
-[ OK ] cpp_operations_test.conv2d (211 ms)
-[ RUN ] cpp_operations_test.fully_connected
-[ OK ] cpp_operations_test.fully_connected (0 ms)
-[ RUN ] cpp_operations_test.maxpool
-[ OK ] cpp_operations_test.maxpool (30 ms)
-[ RUN ] cpp_operations_test.avgpool
-[ OK ] cpp_operations_test.avgpool (48 ms)
-[ RUN ] cpp_operations_test.relu
-[ OK ] cpp_operations_test.relu (0 ms)
-[ RUN ] cpp_operations_test.softmax
-[ OK ] cpp_operations_test.softmax (1 ms)
-[ RUN ] cpp_operations_test.reshape
-[ OK ] cpp_operations_test.reshape (0 ms)
-[----------] 11 tests from cpp_operations_test (301 ms total)
-[----------] 1 test from cpp_operations_tests
-[ RUN ] cpp_operations_tests.depthwise_conv
-[ OK ] cpp_operations_tests.depthwise_conv (157 ms)
-[----------] 1 test from cpp_operations_tests (157 ms total)
-[----------] 2 tests from SOFT_BACKEND
-[ RUN ] SOFT_BACKEND.shape_and_index
-[ OK ] SOFT_BACKEND.shape_and_index (83 ms)
-[ RUN ] SOFT_BACKEND.tensor
-[ OK ] SOFT_BACKEND.tensor (0 ms)
-[----------] 2 tests from SOFT_BACKEND (83 ms total)
-[----------] 1 test from Generator
-[ RUN ] Generator.check_generator_call
-[ OK ] Generator.check_generator_call (6 ms)
-[----------] 1 test from Generator (6 ms total)
-[----------] Global test environment tear-down
-[==========] 15 tests from 4 test cases ran. (547 ms total)
-[ PASSED ] 15 tests.
-
-::
-
-[==========] Running 1 test from 1 test case.
-[----------] Global test environment set-up.
-[----------] 1 test from SUPPORT_NNC
-[ RUN ] SUPPORT_NNC.verify_cl_options
-[ OK ] SUPPORT_NNC.verify_cl_options (0 ms)
-[----------] 1 test from SUPPORT_NNC (0 ms total)
-[----------] Global test environment tear-down
-[==========] 1 test from 1 test case ran. (0 ms total)
-[ PASSED ] 1 test.
-
-::
-
-[==========] Running 17 tests from 6 test cases.
-[----------] Global test environment set-up.
-[----------] 4 tests from Serializer
-[ RUN ] Serializer.ShapeSerializationTest
-[ OK ] Serializer.ShapeSerializationTest (0 ms)
-[ RUN ] Serializer.IntTensorSerializationTest
-[ OK ] Serializer.IntTensorSerializationTest (0 ms)
-[ RUN ] Serializer.FloatTensorSerializationTest
-[ OK ] Serializer.FloatTensorSerializationTest (0 ms)
-[ RUN ] Serializer.DoubleTensorSerializationTest
-[ OK ] Serializer.DoubleTensorSerializationTest (0 ms)
-[----------] 4 tests from Serializer (0 ms total)
-[----------] 2 tests from ShapeInferenceTest
-[ RUN ] ShapeInferenceTest.ReshapeAutoDimension
-[ OK ] ShapeInferenceTest.ReshapeAutoDimension (0 ms)
-[ RUN ] ShapeInferenceTest.ReshapeAutoDimensionVaryRank
-[ OK ] ShapeInferenceTest.ReshapeAutoDimensionVaryRank (0 ms)
-[----------] 2 tests from ShapeInferenceTest (0 ms total)
-[----------] 3 tests from TensorVariant
-[ RUN ] TensorVariant.BasicTest
-[ OK ] TensorVariant.BasicTest (0 ms)
-[ RUN ] TensorVariant.ElementSizeDeductionTest
-[ OK ] TensorVariant.ElementSizeDeductionTest (0 ms)
-[ RUN ] TensorVariant.DeletionTest
-[ OK ] TensorVariant.DeletionTest (0 ms)
-[----------] 3 tests from TensorVariant (0 ms total)
-[----------] 1 test from IRNode
-[ RUN ] IRNode.ConnectionTest
-[ OK ] IRNode.ConnectionTest (0 ms)
-[----------] 1 test from IRNode (0 ms total)
-[----------] 3 tests from OpDescription
-[ RUN ] OpDescription.InputOutputShapeTest
-[ OK ] OpDescription.InputOutputShapeTest (0 ms)
-[ RUN ] OpDescription.SoftmaxAxisTest
-[ OK ] OpDescription.SoftmaxAxisTest (0 ms)
-[ RUN ] OpDescription.ConcatAxisTest
-[ OK ] OpDescription.ConcatAxisTest (0 ms)
-[----------] 3 tests from OpDescription (0 ms total)
-[----------] 4 tests from SimpleInput/ShapeIteratorTest
-[ RUN ] SimpleInput/ShapeIteratorTest.ElementCount/0
-[ OK ] SimpleInput/ShapeIteratorTest.ElementCount/0 (0 ms)
-[ RUN ] SimpleInput/ShapeIteratorTest.ElementCount/1
-[ OK ] SimpleInput/ShapeIteratorTest.ElementCount/1 (0 ms)
-[ RUN ] SimpleInput/ShapeIteratorTest.ElementCount/2
-[ OK ] SimpleInput/ShapeIteratorTest.ElementCount/2 (0 ms)
-[ RUN ] SimpleInput/ShapeIteratorTest.ElementCount/3
-[ OK ] SimpleInput/ShapeIteratorTest.ElementCount/3 (0 ms)
-[----------] 4 tests from SimpleInput/ShapeIteratorTest (1 ms total)
-[----------] Global test environment tear-down
-[==========] 17 tests from 6 test cases ran. (1 ms total)
-[ PASSED ] 17 tests.
-
-::
-
-[==========] Running 2 tests from 1 test case.
-[----------] Global test environment set-up.
-[----------] 2 tests from CONTRIB_PASS
-[ RUN ] CONTRIB_PASS.PassException
-[ OK ] CONTRIB_PASS.PassException (1 ms)
-[ RUN ] CONTRIB_PASS.PassManager
-[ OK ] CONTRIB_PASS.PassManager (0 ms)
-[----------] 2 tests from CONTRIB_PASS (1 ms total)
-[----------] Global test environment tear-down
-[==========] 2 tests from 1 test case ran. (1 ms total)
-[ PASSED ] 2 tests.
-
-
-NNCC Version 0.7 (11.2018)
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-These unit tests are from commit **84e16c9d06903ab9fb2094ea40914942e430d20a**.
-
-Tests description
-#################
-
-``NNC`` unit tests coverage is 60.4%. For more information about what functions are covered see ``nncc/contrib/nnc/unittests``.
-
-|
-
-.. list-table::
- :class: longtable
- :widths: 20 10 30
- :header-rows: 1
-
- * - Test Name
- - Number of Tests
- - Tests Scope
-
- * - nnc_soft_backend_test
- - 15
- - C++ CPU soft backend functionality
-
- * - nnc_caffe_frontend_test
- - 1
- - Importer of Caffe NN models
-
- * - nnc_support_commandline_test
- - 1
- - CLI: parser and configuration system
-
- * - nnc_core_test
- - 24
- - model IR: serializer, shape inference etc
-
- * - nnc_pass_test
- - 2
- - pass manager, pass_data class
-
-Tests output
-############
-
-::
-
-[==========] Running 15 tests from 4 test cases.
-[----------] Global test environment set-up.
-[----------] 11 tests from cpp_operations_test
-[ RUN ] cpp_operations_test.bias
-[ OK ] cpp_operations_test.bias (0 ms)
-[ RUN ] cpp_operations_test.scale
-[ OK ] cpp_operations_test.scale (1 ms)
-[ RUN ] cpp_operations_test.capped_relu
-[ OK ] cpp_operations_test.capped_relu (0 ms)
-[ RUN ] cpp_operations_test.concat
-[ OK ] cpp_operations_test.concat (10 ms)
-[ RUN ] cpp_operations_test.conv2d
-[ OK ] cpp_operations_test.conv2d (211 ms)
-[ RUN ] cpp_operations_test.fully_connected
-[ OK ] cpp_operations_test.fully_connected (0 ms)
-[ RUN ] cpp_operations_test.maxpool
-[ OK ] cpp_operations_test.maxpool (30 ms)
-[ RUN ] cpp_operations_test.avgpool
-[ OK ] cpp_operations_test.avgpool (48 ms)
-[ RUN ] cpp_operations_test.relu
-[ OK ] cpp_operations_test.relu (0 ms)
-[ RUN ] cpp_operations_test.softmax
-[ OK ] cpp_operations_test.softmax (1 ms)
-[ RUN ] cpp_operations_test.reshape
-[ OK ] cpp_operations_test.reshape (0 ms)
-[----------] 11 tests from cpp_operations_test (301 ms total)
-[----------] 1 test from cpp_operations_tests
-[ RUN ] cpp_operations_tests.depthwise_conv
-[ OK ] cpp_operations_tests.depthwise_conv (157 ms)
-[----------] 1 test from cpp_operations_tests (157 ms total)
-[----------] 2 tests from SOFT_BACKEND
-[ RUN ] SOFT_BACKEND.shape_and_index
-[ OK ] SOFT_BACKEND.shape_and_index (83 ms)
-[ RUN ] SOFT_BACKEND.tensor
-[ OK ] SOFT_BACKEND.tensor (0 ms)
-[----------] 2 tests from SOFT_BACKEND (83 ms total)
-[----------] 1 test from Generator
-[ RUN ] Generator.check_generator_call
-[ OK ] Generator.check_generator_call (6 ms)
-[----------] 1 test from Generator (6 ms total)
-[----------] Global test environment tear-down
-[==========] 15 tests from 4 test cases ran. (547 ms total)
-[ PASSED ] 15 tests.
-
-::
-
-[==========] Running 1 test from 1 test case.
-[----------] Global test environment set-up.
-[----------] 1 test from CAFFE_IMPORT_UNSUPPORTED
-[ RUN ] CAFFE_IMPORT_UNSUPPORTED.ImportAModelWithUnsupportedLayers
-[ OK ] CAFFE_IMPORT_UNSUPPORTED.ImportAModelWithUnsupportedLayers (1 ms)
-[----------] 1 test from CAFFE_IMPORT_UNSUPPORTED (1 ms total)
-[----------] Global test environment tear-down
-[==========] 1 test from 1 test case ran. (1 ms total)
-[ PASSED ] 1 test.
-
-::
-
-[==========] Running 1 test from 1 test case.
-[----------] Global test environment set-up.
-[----------] 1 test from SUPPORT_NNC
-[ RUN ] SUPPORT_NNC.verify_cl_options
-[ OK ] SUPPORT_NNC.verify_cl_options (0 ms)
-[----------] 1 test from SUPPORT_NNC (0 ms total)
-[----------] Global test environment tear-down
-[==========] 1 test from 1 test case ran. (0 ms total)
-[ PASSED ] 1 test.
-
-::
-
-[==========] Running 24 tests from 10 test cases.
-[----------] Global test environment set-up.
-[----------] 1 test from IRNode
-[ RUN ] IRNode.ConnectionTest
-[ OK ] IRNode.ConnectionTest (0 ms)
-[----------] 1 test from IRNode (0 ms total)
-[----------] 3 tests from OpDescription
-[ RUN ] OpDescription.InputOutputShapeTest
-[ OK ] OpDescription.InputOutputShapeTest (0 ms)
-[ RUN ] OpDescription.SoftmaxAxisTest
-[ OK ] OpDescription.SoftmaxAxisTest (0 ms)
-[ RUN ] OpDescription.ConcatAxisTest
-[ OK ] OpDescription.ConcatAxisTest (0 ms)
-[----------] 3 tests from OpDescription (0 ms total)
-[----------] 1 test from Shape
-[ RUN ] Shape.Base
-[ OK ] Shape.Base (0 ms)
-[----------] 1 test from Shape (0 ms total)
-[----------] 1 test from Index
-[ RUN ] Index.Base
-[ OK ] Index.Base (0 ms)
-[----------] 1 test from Index (0 ms total)
-[----------] 2 tests from ShapeInferenceTest
-[ RUN ] ShapeInferenceTest.ReshapeAutoDimension
-[ OK ] ShapeInferenceTest.ReshapeAutoDimension (0 ms)
-[ RUN ] ShapeInferenceTest.ReshapeAutoDimensionVaryRank
-[ OK ] ShapeInferenceTest.ReshapeAutoDimensionVaryRank (0 ms)
-[----------] 2 tests from ShapeInferenceTest (0 ms total)
-[----------] 1 test from ShapeRange
-[ RUN ] ShapeRange.Contains
-[ OK ] ShapeRange.Contains (0 ms)
-[----------] 1 test from ShapeRange (0 ms total)
-[----------] 3 tests from TensorVariant
-[ RUN ] TensorVariant.BasicTest
-[ OK ] TensorVariant.BasicTest (0 ms)
-[ RUN ] TensorVariant.ElementSizeDeductionTest
-[ OK ] TensorVariant.ElementSizeDeductionTest (0 ms)
-[ RUN ] TensorVariant.DeletionTest
-[ OK ] TensorVariant.DeletionTest (0 ms)
-[----------] 3 tests from TensorVariant (0 ms total)
-[----------] 4 tests from Serializer
-[ RUN ] Serializer.ShapeSerializationTest
-[ OK ] Serializer.ShapeSerializationTest (0 ms)
-[ RUN ] Serializer.IntTensorSerializationTest
-[ OK ] Serializer.IntTensorSerializationTest (0 ms)
-[ RUN ] Serializer.FloatTensorSerializationTest
-[ OK ] Serializer.FloatTensorSerializationTest (0 ms)
-[ RUN ] Serializer.DoubleTensorSerializationTest
-[ OK ] Serializer.DoubleTensorSerializationTest (0 ms)
-[----------] 4 tests from Serializer (0 ms total)
-[----------] 4 tests from Deserializer
-[ RUN ] Deserializer.ShapeDeserializationTest
-[ OK ] Deserializer.ShapeDeserializationTest (0 ms)
-[ RUN ] Deserializer.IntTensorDeserializationTest
-[ OK ] Deserializer.IntTensorDeserializationTest (1 ms)
-[ RUN ] Deserializer.FloatTensorDeserializationTest
-[ OK ] Deserializer.FloatTensorDeserializationTest (0 ms)
-[ RUN ] Deserializer.DoubleTensorDeserializationTest
-[ OK ] Deserializer.DoubleTensorDeserializationTest (0 ms)
-[----------] 4 tests from Deserializer (1 ms total)
-[----------] 4 tests from SimpleInput/ShapeIteratorTest
-[ RUN ] SimpleInput/ShapeIteratorTest.ElementCount/0
-[ OK ] SimpleInput/ShapeIteratorTest.ElementCount/0 (0 ms)
-[ RUN ] SimpleInput/ShapeIteratorTest.ElementCount/1
-[ OK ] SimpleInput/ShapeIteratorTest.ElementCount/1 (0 ms)
-[ RUN ] SimpleInput/ShapeIteratorTest.ElementCount/2
-[ OK ] SimpleInput/ShapeIteratorTest.ElementCount/2 (0 ms)
-[ RUN ] SimpleInput/ShapeIteratorTest.ElementCount/3
-[ OK ] SimpleInput/ShapeIteratorTest.ElementCount/3 (0 ms)
-[----------] 4 tests from SimpleInput/ShapeIteratorTest (0 ms total)
-[----------] Global test environment tear-down
-[==========] 24 tests from 10 test cases ran. (1 ms total)
-[ PASSED ] 24 tests.
-
-::
-
-[==========] Running 2 tests from 1 test case.
-[----------] Global test environment set-up.
-[----------] 2 tests from CONTRIB_PASS
-[ RUN ] CONTRIB_PASS.PassException
-[ OK ] CONTRIB_PASS.PassException (1 ms)
-[ RUN ] CONTRIB_PASS.PassManager
-[ OK ] CONTRIB_PASS.PassManager (0 ms)
-[----------] 2 tests from CONTRIB_PASS (1 ms total)
-[----------] Global test environment tear-down
-[==========] 2 tests from 1 test case ran. (1 ms total)
-[ PASSED ] 2 tests.
-
-
-NNCC Version 1.0 (12.2018)
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-These unit tests are from commit **7974192249ce1dd46c785ba8d25506eddb5b24ad**.
-
-Tests description
-#################
-
-``NNC`` unit tests coverage is 63.3%. For more information about what functions are covered see ``nncc/contrib/nnc/unittests``.
-
-|
-
-.. list-table::
- :class: longtable
- :widths: 20 10 30
- :header-rows: 1
-
- * - Test Name
- - Number of Tests
- - Tests Scope
-
- * - nnc_cpu_cpp_backend_ops_test
- - 20
- - C++ CPU soft backend functionality
-
- * - nnc_cpu_cpp_backend_general_test
- - 4
- - C++ CPU soft backend functionality
-
- * - nnc_acl_cpp_backend_test
- - 11
- - C++ GPU soft backend functionality
-
- * - nnc_caffe_frontend_test
- - 1
- - Importer of Caffe NN models
-
- * - nnc_support_commandline_test
- - 1
- - CLI: parser and configuration system
-
- * - nnc_core_test
- - 35
- - model IR: serializer, shape inference etc
-
- * - nnc_pass_test
- - 2
- - pass manager, pass_data class
-
-Tests output
-############
-
-::
-
-[==========] Running 2 tests from 1 test case.
-[----------] Global test environment set-up.
-[----------] 2 tests from CONTRIB_PASS
-[ RUN ] CONTRIB_PASS.PassException
-[ OK ] CONTRIB_PASS.PassException (0 ms)
-[ RUN ] CONTRIB_PASS.PassManager
-[ OK ] CONTRIB_PASS.PassManager (0 ms)
-[----------] 2 tests from CONTRIB_PASS (0 ms total)
-[----------] Global test environment tear-down
-[==========] 2 tests from 1 test case ran. (0 ms total)
-[ PASSED ] 2 tests.
-
-::
-
-[==========] Running 35 tests from 11 test cases.
-[----------] Global test environment set-up.
-[----------] 4 tests from Operation
-[ RUN ] Operation.ConnectionTest
-[ OK ] Operation.ConnectionTest (0 ms)
-[ RUN ] Operation.InputOutputShapeTest
-[ OK ] Operation.InputOutputShapeTest (0 ms)
-[ RUN ] Operation.SoftmaxAxisTest
-[ OK ] Operation.SoftmaxAxisTest (0 ms)
-[ RUN ] Operation.ConcatAxisTest
-[ OK ] Operation.ConcatAxisTest (0 ms)
-[----------] 4 tests from Operation (0 ms total)
-[----------] 1 test from Shape
-[ RUN ] Shape.Base
-[ OK ] Shape.Base (0 ms)
-[----------] 1 test from Shape (0 ms total)
-[----------] 1 test from Index
-[ RUN ] Index.Base
-[ OK ] Index.Base (0 ms)
-[----------] 1 test from Index (0 ms total)
-[----------] 9 tests from ShapeInferenceTest
-[ RUN ] ShapeInferenceTest.ReshapeAutoDimension
-[ OK ] ShapeInferenceTest.ReshapeAutoDimension (0 ms)
-[ RUN ] ShapeInferenceTest.ResizeWithShape
-[ OK ] ShapeInferenceTest.ResizeWithShape (0 ms)
-[ RUN ] ShapeInferenceTest.ResizeWithScale
-[ OK ] ShapeInferenceTest.ResizeWithScale (0 ms)
-[ RUN ] ShapeInferenceTest.ReduceChangeRank
-[ OK ] ShapeInferenceTest.ReduceChangeRank (0 ms)
-[ RUN ] ShapeInferenceTest.ReshapeAutoDimensionShrink
-[ OK ] ShapeInferenceTest.ReshapeAutoDimensionShrink (0 ms)
-[ RUN ] ShapeInferenceTest.ReshapeAutoDimensionExpand
-[ OK ] ShapeInferenceTest.ReshapeAutoDimensionExpand (0 ms)
-[ RUN ] ShapeInferenceTest.SqueezeTestAllDims
-[ OK ] ShapeInferenceTest.SqueezeTestAllDims (0 ms)
-[ RUN ] ShapeInferenceTest.SqueezeTestSpecificDims
-[ OK ] ShapeInferenceTest.SqueezeTestSpecificDims (0 ms)
-[ RUN ] ShapeInferenceTest.SqueezeTestScalarResult
-[ OK ] ShapeInferenceTest.SqueezeTestScalarResult (0 ms)
-[----------] 9 tests from ShapeInferenceTest (1 ms total)
-[----------] 1 test from ShapeRange
-[ RUN ] ShapeRange.Contains
-[ OK ] ShapeRange.Contains (0 ms)
-[----------] 1 test from ShapeRange (0 ms total)
-[----------] 3 tests from TensorVariant
-[ RUN ] TensorVariant.BasicTest
-[ OK ] TensorVariant.BasicTest (0 ms)
-[ RUN ] TensorVariant.ElementSizeDeductionTest
-[ OK ] TensorVariant.ElementSizeDeductionTest (0 ms)
-[ RUN ] TensorVariant.DeletionTest
-[ OK ] TensorVariant.DeletionTest (0 ms)
-[----------] 3 tests from TensorVariant (0 ms total)
-[----------] 1 test from NodeMutatorTest
-[ RUN ] NodeMutatorTest.SimpleChainTest
-[ OK ] NodeMutatorTest.SimpleChainTest (0 ms)
-[----------] 1 test from NodeMutatorTest (0 ms total)
-[----------] 3 tests from Graph
-[ RUN ] Graph.ReplaceInputs
-[ OK ] Graph.ReplaceInputs (0 ms)
-[ RUN ] Graph.ReplaceOutputs
-[ OK ] Graph.ReplaceOutputs (0 ms)
-[ RUN ] Graph.ReplaceOutputNodeWithInput
-[ OK ] Graph.ReplaceOutputNodeWithInput (0 ms)
-[----------] 3 tests from Graph (0 ms total)
-[----------] 4 tests from Serializer
-[ RUN ] Serializer.ShapeSerializationTest
-[ OK ] Serializer.ShapeSerializationTest (0 ms)
-[ RUN ] Serializer.IntTensorSerializationTest
-[ OK ] Serializer.IntTensorSerializationTest (0 ms)
-[ RUN ] Serializer.FloatTensorSerializationTest
-[ OK ] Serializer.FloatTensorSerializationTest (0 ms)
-[ RUN ] Serializer.DoubleTensorSerializationTest
-[ OK ] Serializer.DoubleTensorSerializationTest (0 ms)
-[----------] 4 tests from Serializer (0 ms total)
-[----------] 4 tests from Deserializer
-[ RUN ] Deserializer.ShapeDeserializationTest
-[ OK ] Deserializer.ShapeDeserializationTest (1 ms)
-[ RUN ] Deserializer.IntTensorDeserializationTest
-[ OK ] Deserializer.IntTensorDeserializationTest (0 ms)
-[ RUN ] Deserializer.FloatTensorDeserializationTest
-[ OK ] Deserializer.FloatTensorDeserializationTest (0 ms)
-[ RUN ] Deserializer.DoubleTensorDeserializationTest
-[ OK ] Deserializer.DoubleTensorDeserializationTest (0 ms)
-[----------] 4 tests from Deserializer (1 ms total)
-[----------] 4 tests from SimpleInput/ShapeIteratorTest
-[ RUN ] SimpleInput/ShapeIteratorTest.ElementCount/0
-[ OK ] SimpleInput/ShapeIteratorTest.ElementCount/0 (0 ms)
-[ RUN ] SimpleInput/ShapeIteratorTest.ElementCount/1
-[ OK ] SimpleInput/ShapeIteratorTest.ElementCount/1 (0 ms)
-[ RUN ] SimpleInput/ShapeIteratorTest.ElementCount/2
-[ OK ] SimpleInput/ShapeIteratorTest.ElementCount/2 (0 ms)
-[ RUN ] SimpleInput/ShapeIteratorTest.ElementCount/3
-[ OK ] SimpleInput/ShapeIteratorTest.ElementCount/3 (0 ms)
-[----------] 4 tests from SimpleInput/ShapeIteratorTest (0 ms total)
-[----------] Global test environment tear-down
-[==========] 35 tests from 11 test cases ran. (2 ms total)
-[ PASSED ] 35 tests.
-
-::
-
-[==========] Running 20 tests from 2 test cases.
-[----------] Global test environment set-up.
-[----------] 19 tests from cpp_operations_test
-[ RUN ] cpp_operations_test.bias
-[ OK ] cpp_operations_test.bias (1 ms)
-[ RUN ] cpp_operations_test.scale
-[ OK ] cpp_operations_test.scale (0 ms)
-[ RUN ] cpp_operations_test.capped_relu
-[ OK ] cpp_operations_test.capped_relu (0 ms)
-[ RUN ] cpp_operations_test.concat
-[ OK ] cpp_operations_test.concat (9 ms)
-[ RUN ] cpp_operations_test.add2
-[ OK ] cpp_operations_test.add2 (1 ms)
-[ RUN ] cpp_operations_test.mul3
-[ OK ] cpp_operations_test.mul3 (1 ms)
-[ RUN ] cpp_operations_test.max4
-[ OK ] cpp_operations_test.max4 (2 ms)
-[ RUN ] cpp_operations_test.convTransposed2d
-[ OK ] cpp_operations_test.convTransposed2d (3173 ms)
-[ RUN ] cpp_operations_test.conv2d
-[ OK ] cpp_operations_test.conv2d (179 ms)
-[ RUN ] cpp_operations_test.fully_connected
-[ OK ] cpp_operations_test.fully_connected (0 ms)
-[ RUN ] cpp_operations_test.maxpool
-[ OK ] cpp_operations_test.maxpool (30 ms)
-[ RUN ] cpp_operations_test.avgpool
-[ OK ] cpp_operations_test.avgpool (51 ms)
-[ RUN ] cpp_operations_test.relu
-[ OK ] cpp_operations_test.relu (0 ms)
-[ RUN ] cpp_operations_test.elu
-[ OK ] cpp_operations_test.elu (0 ms)
-[ RUN ] cpp_operations_test.tanh
-[ OK ] cpp_operations_test.tanh (1 ms)
-[ RUN ] cpp_operations_test.reduceMeanTst
-[ OK ] cpp_operations_test.reduceMeanTst (3 ms)
-[ RUN ] cpp_operations_test.softmax
-[ OK ] cpp_operations_test.softmax (1 ms)
-[ RUN ] cpp_operations_test.reshape
-[ OK ] cpp_operations_test.reshape (0 ms)
-[ RUN ] cpp_operations_test.pad
-[ OK ] cpp_operations_test.pad (1 ms)
-[----------] 19 tests from cpp_operations_test (3453 ms total)
-[----------] 1 test from cpp_operations_tests
-[ RUN ] cpp_operations_tests.depthwise_conv
-[ OK ] cpp_operations_tests.depthwise_conv (126 ms)
-[----------] 1 test from cpp_operations_tests (126 ms total)
-[----------] Global test environment tear-down
-[==========] 20 tests from 2 test cases ran. (3579 ms total)
-[ PASSED ] 20 tests.
-
-::
-
-[==========] Running 4 tests from 3 test cases.
-[----------] Global test environment set-up.
-[----------] 1 test from Generator
-[ RUN ] Generator.check_generator_call
-[ OK ] Generator.check_generator_call (7 ms)
-[----------] 1 test from Generator (7 ms total)
-[----------] 2 tests from SOFT_BACKEND
-[ RUN ] SOFT_BACKEND.shape_and_index
-[ OK ] SOFT_BACKEND.shape_and_index (90 ms)
-[ RUN ] SOFT_BACKEND.tensor
-[ OK ] SOFT_BACKEND.tensor (0 ms)
-[----------] 2 tests from SOFT_BACKEND (90 ms total)
-[----------] 1 test from ModelAnalyzer
-[ RUN ] ModelAnalyzer.linearization
-[ OK ] ModelAnalyzer.linearization (0 ms)
-[----------] 1 test from ModelAnalyzer (0 ms total)
-[----------] Global test environment tear-down
-[==========] 4 tests from 3 test cases ran. (97 ms total)
-[ PASSED ] 4 tests.
-
-::
-
-[==========] Running 11 tests from 2 test cases.
-[----------] Global test environment set-up.
-[----------] 1 test from acl_backend_dom_to_text
-[ RUN ] acl_backend_dom_to_text.ArtifactLiteral
-[ OK ] acl_backend_dom_to_text.ArtifactLiteral (0 ms)
-[----------] 1 test from acl_backend_dom_to_text (0 ms total)
-[----------] 10 tests from acl_backend_mir_to_dom
-[ RUN ] acl_backend_mir_to_dom.bias
-[ OK ] acl_backend_mir_to_dom.bias (2 ms)
-[ RUN ] acl_backend_mir_to_dom.scale
-[ OK ] acl_backend_mir_to_dom.scale (1 ms)
-[ RUN ] acl_backend_mir_to_dom.concat
-[ OK ] acl_backend_mir_to_dom.concat (0 ms)
-[ RUN ] acl_backend_mir_to_dom.conv2d
-[ OK ] acl_backend_mir_to_dom.conv2d (1 ms)
-[ RUN ] acl_backend_mir_to_dom.depthwise_conv
-[ OK ] acl_backend_mir_to_dom.depthwise_conv (0 ms)
-[ RUN ] acl_backend_mir_to_dom.fully_connected
-[ OK ] acl_backend_mir_to_dom.fully_connected (1 ms)
-[ RUN ] acl_backend_mir_to_dom.maxpool
-[ OK ] acl_backend_mir_to_dom.maxpool (0 ms)
-[ RUN ] acl_backend_mir_to_dom.relu
-[ OK ] acl_backend_mir_to_dom.relu (0 ms)
-[ RUN ] acl_backend_mir_to_dom.softmax
-[ OK ] acl_backend_mir_to_dom.softmax (1 ms)
-[ RUN ] acl_backend_mir_to_dom.reshape
-[ OK ] acl_backend_mir_to_dom.reshape (0 ms)
-[----------] 10 tests from acl_backend_mir_to_dom (6 ms total)
-[----------] Global test environment tear-down
-[==========] 11 tests from 2 test cases ran. (6 ms total)
-[ PASSED ] 11 tests.
-
-::
-
-[==========] Running 1 test from 1 test case.
-[----------] Global test environment set-up.
-[----------] 1 test from SUPPORT_NNC
-[ RUN ] SUPPORT_NNC.verify_cl_options
-[ OK ] SUPPORT_NNC.verify_cl_options (1 ms)
-[----------] 1 test from SUPPORT_NNC (1 ms total)
-[----------] Global test environment tear-down
-[==========] 1 test from 1 test case ran. (1 ms total)
-[ PASSED ] 1 test.
-
-::
-
-[==========] Running 1 test from 1 test case.
-[----------] Global test environment set-up.
-[----------] 1 test from CAFFE_IMPORT_UNSUPPORTED
-[ RUN ] CAFFE_IMPORT_UNSUPPORTED.ImportAModelWithUnsupportedLayers
-[ OK ] CAFFE_IMPORT_UNSUPPORTED.ImportAModelWithUnsupportedLayers (3 ms)
-[----------] 1 test from CAFFE_IMPORT_UNSUPPORTED (3 ms total)
-[----------] Global test environment tear-down
-[==========] 1 test from 1 test case ran. (3 ms total)
-[ PASSED ] 1 test.
-
-
-Defect Status
-=============
-
-Summary
--------
-
-No defects are detected
diff --git a/compiler/nnc/doc/project/images/nncc_components.png b/compiler/nnc/doc/project/images/nncc_components.png
deleted file mode 100644
index becd63d14..000000000
--- a/compiler/nnc/doc/project/images/nncc_components.png
+++ /dev/null
Binary files differ
diff --git a/compiler/nnc/doc/project/images/nncc_idef0_a0.png b/compiler/nnc/doc/project/images/nncc_idef0_a0.png
deleted file mode 100644
index 9ba09681f..000000000
--- a/compiler/nnc/doc/project/images/nncc_idef0_a0.png
+++ /dev/null
Binary files differ
diff --git a/compiler/nnc/doc/project/images/nncc_idef0_a1.png b/compiler/nnc/doc/project/images/nncc_idef0_a1.png
deleted file mode 100644
index c5ebec5d9..000000000
--- a/compiler/nnc/doc/project/images/nncc_idef0_a1.png
+++ /dev/null
Binary files differ
diff --git a/compiler/nnc/doc/project/images/nncc_idef0_a12.png b/compiler/nnc/doc/project/images/nncc_idef0_a12.png
deleted file mode 100644
index dabcad718..000000000
--- a/compiler/nnc/doc/project/images/nncc_idef0_a12.png
+++ /dev/null
Binary files differ
diff --git a/compiler/nnc/driver/Driver.cpp b/compiler/nnc/driver/Driver.cpp
index d002ef2a0..995fa9bad 100644
--- a/compiler/nnc/driver/Driver.cpp
+++ b/compiler/nnc/driver/Driver.cpp
@@ -17,6 +17,7 @@
#include "pass/PassData.h"
#include "passes/transformations/DataFormatSwitcher.h"
+#include "passes/transformations/LowerConv2D.h"
#include "backends/interpreter/InterpreterBackend.h"
#include "backends/soft_backend/CPPGenerator.h"
@@ -25,14 +26,14 @@
#include "passes/optimizations/CombineTransposes.h"
#include "passes/optimizations/ConstantFoldTranspose.h"
-#include "passes/optimizations/RemoveDeadEnds.h"
+#include "passes/optimizations/DeadCodeElimination.h"
#include "passes/optimizations/FuseArithmeticOps.h"
#include "passes/optimizations/SinkRelu.h"
#include "passes/optimizations/SinkTranspose.h"
#include "support/CommandLine.h"
#include "Definitions.h"
-#include "option/Options.h"
+#include "Options.h"
#include "Driver.h"
#ifdef NNC_FRONTEND_CAFFE2_ENABLED
@@ -48,6 +49,8 @@
#include <ONNXImporterImpl.h>
#endif // NNC_FRONTEND_ONNX_ENABLED
+#include <memory>
+
namespace nnc
{
@@ -81,8 +84,7 @@ static std::unique_ptr<mir::Graph> importModel()
if (cli::caffeFrontend)
{
#ifdef NNC_FRONTEND_CAFFE_ENABLED
- mir_caffe::CaffeImporter importer(cli::inputFile.getRawValue());
- return importer.importModel();
+ return mir_caffe::loadModel(cli::inputFile.getRawValue());
#endif // NNC_FRONTEND_CAFFE_ENABLED
}
else if (cli::caffe2Frontend)
@@ -90,23 +92,20 @@ static std::unique_ptr<mir::Graph> importModel()
#ifdef NNC_FRONTEND_CAFFE2_ENABLED
// FIXME: caffe2 input shapes are not provided by model and must be set from cli
// current 'inputShapes' could provide only one shape, while model could has several inputs
- mir_caffe2::Caffe2Importer importer(cli::inputFile.getRawValue(), cli::initNet.getRawValue(),
- {cli::inputShapes.getRawValue()});
- return importer.importModel();
+ return mir_caffe2::loadModel(cli::inputFile.getRawValue(), cli::initNet.getRawValue(),
+ {cli::inputShapes.getRawValue()});
#endif // NNC_FRONTEND_CAFFE2_ENABLED
}
else if (cli::onnxFrontend)
{
#ifdef NNC_FRONTEND_ONNX_ENABLED
- mir_onnx::ONNXImporterImpl importer(cli::inputFile.getRawValue());
- return importer.importModel();
+ return mir_onnx::loadModel(cli::inputFile.getRawValue());
#endif // NNC_FRONTEND_ONNX_ENABLED
}
else if (cli::tflFrontend)
{
#ifdef NNC_FRONTEND_TFLITE_ENABLED
- mir_tflite::TfliteImporter importer(cli::inputFile.getRawValue());
- return importer.importModel();
+ return mir_tflite::loadModel(cli::inputFile.getRawValue());
#endif // NNC_FRONTEND_TFLITE_ENABLED
}
@@ -118,15 +117,15 @@ static void backend(mir::Graph *graph)
{
if (cli::target == NNC_TARGET_ARM_CPP || cli::target == NNC_TARGET_X86_CPP)
{
- CPPCodeGenerator().run(graph);
+ CPPCodeGenerator(cli::artifactDir, cli::artifactName).run(graph);
}
else if (cli::target == NNC_TARGET_ARM_GPU_CPP)
{
- AclCppCodeGenerator().run(graph);
+ AclCppCodeGenerator(cli::artifactDir, cli::artifactName).run(graph);
}
else if (cli::target == NNC_TARGET_INTERPRETER)
{
- InterpreterBackend().run(graph);
+ InterpreterBackend(cli::interInputDataDir, cli::artifactDir).run(graph);
}
else
{
@@ -170,31 +169,28 @@ void Driver::registerBackendSpecificPasses()
if (cli::target == NNC_TARGET_ARM_CPP || cli::target == NNC_TARGET_X86_CPP)
{
- data_format_pass = std::unique_ptr<Pass>(new DataFormatSwitcher(mir::DataFormat::NHWC));
+ _passManager.registerPass(std::make_unique<LowerConv2D>());
+ _passManager.registerPass(std::make_unique<DataFormatSwitcher>(mir::DataFormat::NHWC));
}
else if (cli::target == NNC_TARGET_ARM_GPU_CPP)
{
+ _passManager.registerPass(std::make_unique<LowerConv2D>());
+ _passManager.registerPass(std::make_unique<ConstantFoldTranspose>());
// TODO Change to DataFormat::NCHW when fix it in ACL
- data_format_pass = std::unique_ptr<Pass>(new DataFormatSwitcher(mir::DataFormat::NHWC));
+ _passManager.registerPass(std::make_unique<DataFormatSwitcher>(mir::DataFormat::NHWC));
}
else if (cli::target == NNC_TARGET_INTERPRETER)
{
- data_format_pass = std::unique_ptr<Pass>(new DataFormatSwitcher(mir::DataFormat::NHWC));
+ _passManager.registerPass(std::make_unique<DataFormatSwitcher>(mir::DataFormat::NHWC));
}
else
{
assert(false && "invalid option value");
}
-
- _passManager.registerPass(std::move(data_format_pass));
}
void Driver::registerOptimizationPass()
{
- // TODO For now this optimization is mandatory. Do it optional when ACL backend is able to handle
- // all transposes that come from importers.
- _passManager.registerPass(std::unique_ptr<Pass>(new ConstantFoldTranspose()));
-
if (cli::doOptimizationPass)
{
// TODO: maybe we should start managing the optimizations more intelligently?
@@ -205,7 +201,7 @@ void Driver::registerOptimizationPass()
// TODO Support broadcasting.
_passManager.registerPass(std::unique_ptr<Pass>(new FuseArithmeticOps()));
#endif
- _passManager.registerPass(std::unique_ptr<Pass>(new RemoveDeadEnds()));
+ _passManager.registerPass(std::unique_ptr<Pass>(new DeadCodeElimination()));
}
} // registerOptimizationPass
diff --git a/compiler/nnc/driver/Options.cpp b/compiler/nnc/driver/Options.cpp
index dcf0a2613..e22d01847 100644
--- a/compiler/nnc/driver/Options.cpp
+++ b/compiler/nnc/driver/Options.cpp
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-#include <string>
-
-#include "support/CommandLine.h"
-#include "option/Options.h"
+#include "Options.h"
#include "Definitions.h"
+#include <string>
+
namespace nnc
{
namespace cli
diff --git a/compiler/nnc/include/option/Options.h b/compiler/nnc/driver/Options.h
index 06994a4d5..06994a4d5 100644
--- a/compiler/nnc/include/option/Options.h
+++ b/compiler/nnc/driver/Options.h
diff --git a/compiler/nnc/include/backends/acl_soft_backend/AclCppGenerator.h b/compiler/nnc/include/backends/acl_soft_backend/AclCppGenerator.h
index cb17deae7..f8e51ab74 100644
--- a/compiler/nnc/include/backends/acl_soft_backend/AclCppGenerator.h
+++ b/compiler/nnc/include/backends/acl_soft_backend/AclCppGenerator.h
@@ -19,6 +19,8 @@
#include "mir/Graph.h"
+#include <string>
+
namespace nnc
{
@@ -29,12 +31,18 @@ namespace nnc
class AclCppCodeGenerator final
{
public:
+ AclCppCodeGenerator(std::string output_dir, std::string artifact_name);
+
/**
* @brief Method represents the generation sequence: analysis, serialization,
* header/code generation, etc
* @param graph MIR graph
*/
void run(mir::Graph *graph);
+
+private:
+ std::string _output_dir;
+ std::string _artifact_name;
};
} // namespace nnc
diff --git a/compiler/nnc/include/backends/interpreter/Interpreter.h b/compiler/nnc/include/backends/interpreter/Interpreter.h
deleted file mode 100644
index e80cc5fc0..000000000
--- a/compiler/nnc/include/backends/interpreter/Interpreter.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NNC_BACKEND_INTERPRETER_CORE_INTERPRETER_
-#define _NNC_BACKEND_INTERPRETER_CORE_INTERPRETER_
-
-#include "mir/Visitor.h"
-#include "mir/Operation.h"
-#include "mir/TensorVariant.h"
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-namespace nnc
-{
-
-class NNInterpreter : public mir::Visitor
-{
-public:
- explicit NNInterpreter() = default;
-
- ~NNInterpreter() override = default;
-
- void setInput(const std::string &name, const mir::TensorVariant &data);
-
- mir::TensorVariant getResult(const mir::Operation::Output *tensor);
-
- void visit(mir::ops::AddOp &op) override;
- void visit(mir::ops::AvgPool2DOp &op) override;
- void visit(mir::ops::CappedReluOp &op) override;
- void visit(mir::ops::ConcatOp &op) override;
- void visit(mir::ops::ConstantOp &op) override;
- void visit(mir::ops::Conv2DOp &op) override;
- void visit(mir::ops::DeConv2DOp &op) override;
- void visit(mir::ops::DepthwiseConv2DOp &op) override;
- void visit(mir::ops::DivOp &op) override;
- void visit(mir::ops::EluOp &op) override;
- void visit(mir::ops::FullyConnectedOp &op) override;
- void visit(mir::ops::GatherOp &op) override;
- void visit(mir::ops::InputOp &op) override;
- void visit(mir::ops::LeakyReluOp &op) override;
- void visit(mir::ops::MaxOp &op) override;
- void visit(mir::ops::MaxPool2DOp &op) override;
- void visit(mir::ops::MulOp &op) override;
- void visit(mir::ops::OutputOp &op) override;
- void visit(mir::ops::PadOp &op) override;
- void visit(mir::ops::ReduceMeanOp &op) override;
- void visit(mir::ops::ReluOp &op) override;
- void visit(mir::ops::ReshapeOp &op) override;
- void visit(mir::ops::ResizeOp &op) override;
- void visit(mir::ops::SigmoidOp &op) override;
- void visit(mir::ops::SliceOp &op) override;
- void visit(mir::ops::SoftmaxOp &op) override;
- void visit(mir::ops::SqrtOp &op) override;
- void visit(mir::ops::SqueezeOp &op) override;
- void visit(mir::ops::SubOp &op) override;
- void visit(mir::ops::TanhOp &op) override;
- void visit(mir::ops::TransposeOp &op) override;
-
-protected:
- void visit_fallback(mir::Operation &op) override;
-
-private:
- /// @brief Gets the computed inputs for the operation.
- std::vector<std::reference_wrapper<const mir::TensorVariant>>
- getInputTensors(const mir::Operation &op);
-
- /// @brief Saves the computed outputs for the operation.
- void setOutputTensors(const mir::Operation &op, std::vector<mir::TensorVariant> &&outputs);
-
- /// @brief Mapping of graph named inputs to their values.
- std::unordered_map<std::string, mir::TensorVariant> _inputTensors;
-
- /// @brief Mapping of operations to their computed results.
- std::unordered_map<const mir::Operation *, std::vector<mir::TensorVariant>> _opResults;
-};
-
-} // namespace nnc
-
-#endif //_NNC_BACKEND_INTERPRETER_CORE_INTERPRETER_
diff --git a/compiler/nnc/include/backends/interpreter/InterpreterBackend.h b/compiler/nnc/include/backends/interpreter/InterpreterBackend.h
index 1f4ec161a..caa0b34e0 100644
--- a/compiler/nnc/include/backends/interpreter/InterpreterBackend.h
+++ b/compiler/nnc/include/backends/interpreter/InterpreterBackend.h
@@ -19,13 +19,21 @@
#include "mir/Graph.h"
+#include <string>
+
namespace nnc
{
class InterpreterBackend final
{
public:
+ InterpreterBackend(std::string input_dir, std::string output_dir);
+
void run(mir::Graph *data);
+
+private:
+ std::string _input_dir;
+ std::string _output_dir;
};
} // namespace nnc
diff --git a/compiler/nnc/include/backends/soft_backend/CPPGenerator.h b/compiler/nnc/include/backends/soft_backend/CPPGenerator.h
index 8bb707c25..d21168aef 100644
--- a/compiler/nnc/include/backends/soft_backend/CPPGenerator.h
+++ b/compiler/nnc/include/backends/soft_backend/CPPGenerator.h
@@ -47,6 +47,8 @@ struct DestroyTmp;
class CPPCodeGenerator final
{
public:
+ CPPCodeGenerator(std::string output_dir, std::string artifact_name);
+
/**
* @brief Method represents base generation sequence: analysis, serialization, header/code
* generation, etc
@@ -150,6 +152,8 @@ private:
*/
void materializeModelParams(std::ostream &out, const Serializer &s);
+ std::string _output_dir;
+ std::string _artifact_name;
std::vector<std::string> _formattedTensors;
};
diff --git a/compiler/nnc/include/passes/optimizations/DeadCodeElimination.h b/compiler/nnc/include/passes/optimizations/DeadCodeElimination.h
new file mode 100644
index 000000000..600c3b5ab
--- /dev/null
+++ b/compiler/nnc/include/passes/optimizations/DeadCodeElimination.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NNCC_DEADCODEELIMINATION_H
+#define NNCC_DEADCODEELIMINATION_H
+
+#include "pass/Pass.h"
+#include "pass/PassData.h"
+
+namespace nnc
+{
+
+/**
+ * @brief This pass removes operations without uses.
+ * Importers currently only generate `sConstantOp`s without uses.
+ */
+class DeadCodeElimination : public Pass
+{
+public:
+ PassData run(PassData data) override;
+
+ std::string getName() override { return "RemoveDeadEnds"; };
+};
+
+} // namespace nnc
+
+#endif // NNCC_DEADCODEELIMINATION_H
diff --git a/compiler/nnc/include/passes/optimizations/RemoveDeadEnds.h b/compiler/nnc/include/passes/optimizations/RemoveDeadEnds.h
deleted file mode 100644
index 6225f6a9f..000000000
--- a/compiler/nnc/include/passes/optimizations/RemoveDeadEnds.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef NNCC_REMOVEDEADENDS_H
-#define NNCC_REMOVEDEADENDS_H
-
-#include "pass/Pass.h"
-#include "pass/PassData.h"
-
-namespace nnc
-{
-
-/**
- * @brief This pass removes operations without uses.
- * Importers currently only generate `sConstantOp`s without uses.
- */
-class RemoveDeadEnds : public Pass
-{
-public:
- PassData run(PassData data) override;
-
- std::string getName() override { return "RemoveDeadEnds"; };
-};
-
-} // namespace nnc
-
-#endif // NNCC_REMOVEDEADENDS_H
diff --git a/compiler/nnc/include/passes/transformations/LowerConv2D.h b/compiler/nnc/include/passes/transformations/LowerConv2D.h
new file mode 100644
index 000000000..1177f4b7c
--- /dev/null
+++ b/compiler/nnc/include/passes/transformations/LowerConv2D.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NNC_LOWER_CONV2D_H
+#define NNC_LOWER_CONV2D_H
+
+#include "pass/Pass.h"
+
+namespace nnc
+{
+
+// Transforms grouped Conv2D into DepthwiseConv2D when possible. This pass is supposed to be used
+// with backends which do not support grouped Conv2D operation.
+class LowerConv2D : public Pass
+{
+public:
+ LowerConv2D();
+
+ PassData run(PassData data) override;
+
+ void cleanup() override;
+
+ std::string getName() override { return "LowerConv2D"; }
+};
+
+} // namespace nnc
+
+#endif // NNC_LOWER_CONV2D_H
diff --git a/compiler/nnc/passes/dot_dumper/CMakeLists.txt b/compiler/nnc/passes/dot_dumper/CMakeLists.txt
index 6ed343c4a..55117ee4d 100644
--- a/compiler/nnc/passes/dot_dumper/CMakeLists.txt
+++ b/compiler/nnc/passes/dot_dumper/CMakeLists.txt
@@ -1,6 +1,6 @@
file(GLOB_RECURSE DUMPER_SRC ./*.cpp ./*.h)
nnc_add_library(nnc_dumper SHARED ${DUMPER_SRC})
-target_link_libraries(nnc_dumper PRIVATE mir nnc_support)
+target_link_libraries(nnc_dumper PRIVATE mir)
# install dumper library
nnc_install_library(nnc_dumper)
diff --git a/compiler/nnc/passes/optimizations/CMakeLists.txt b/compiler/nnc/passes/optimizations/CMakeLists.txt
index 00799ac41..006c92888 100644
--- a/compiler/nnc/passes/optimizations/CMakeLists.txt
+++ b/compiler/nnc/passes/optimizations/CMakeLists.txt
@@ -1,12 +1,13 @@
-set(OPTIMIZATIONS_SRC CombineTransposes.cpp
- ConstantFoldTranspose.cpp
- FuseArithmeticOps.cpp
- RemoveDeadEnds.cpp
- SinkRelu.cpp
- SinkTranspose.cpp
- OptimizationUtils.cpp)
+set(OPTIMIZATIONS_SRC
+ CombineTransposes.cpp
+ ConstantFoldTranspose.cpp
+ FuseArithmeticOps.cpp
+ DeadCodeElimination.cpp
+ SinkRelu.cpp
+ SinkTranspose.cpp
+ OptimizationUtils.cpp)
nnc_add_library(nnc_optimizations SHARED ${OPTIMIZATIONS_SRC})
-target_link_libraries(nnc_optimizations PRIVATE mir nnc_support)
+target_link_libraries(nnc_optimizations PRIVATE mir)
# install optimizations library
nnc_install_library(nnc_optimizations)
diff --git a/compiler/nnc/passes/optimizations/CombineTransposes.cpp b/compiler/nnc/passes/optimizations/CombineTransposes.cpp
index 12aab9443..e381a9cae 100644
--- a/compiler/nnc/passes/optimizations/CombineTransposes.cpp
+++ b/compiler/nnc/passes/optimizations/CombineTransposes.cpp
@@ -76,19 +76,19 @@ nnc::PassData nnc::CombineTransposes::run(nnc::PassData data)
if (!isIdentityTranspose(combined_axis_order))
{
- auto new_tr_op = g->create<mir::ops::TransposeOp>(top_transpose->getInput(0)->getProducer(),
- combined_axis_order);
+ auto new_tr_op =
+ g->create<mir::ops::TransposeOp>(top_transpose->getInput(0), combined_axis_order);
g->replaceNode(bottom_transpose, new_tr_op);
}
else
{
// Connect top input to all outputs of bottom
- Operation *top = top_transpose->getInput(0)->getProducer()->getNode();
+ Operation *top = top_transpose->getInput(0)->getNode();
g->replaceNode(bottom_transpose, top);
}
deleted_nodes.emplace(bottom_transpose);
- if (top_transpose->getOutput(0)->getConsumers().empty())
+ if (top_transpose->getOutput(0)->getUses().empty())
{
g->removeNode(top_transpose);
deleted_nodes.emplace(top_transpose);
diff --git a/compiler/nnc/passes/optimizations/ConstantFoldTranspose.cpp b/compiler/nnc/passes/optimizations/ConstantFoldTranspose.cpp
index f23d5b2ce..47a3147a5 100644
--- a/compiler/nnc/passes/optimizations/ConstantFoldTranspose.cpp
+++ b/compiler/nnc/passes/optimizations/ConstantFoldTranspose.cpp
@@ -18,35 +18,34 @@
#include "passes/optimizations/OptimizationUtils.h"
#include "mir/GraphPatternMatcher.h"
#include "mir/ShapeRange.h"
-#include "mir/Tensor.h"
#include "mir/ops/ConstantOp.h"
#include "mir/ops/TransposeOp.h"
+#include <cstring>
+
using namespace nnc;
using namespace mir;
// Copy & paste from interpreter backend.
// TODO Extract this to a common place and use in both interpreter and optimizations.
-static void transpose(const TensorVariant &arg, TensorVariant &res,
+static void transpose(const TensorVariant &input, TensorVariant &res,
const std::vector<std::size_t> &axis_order)
{
- Tensor<float> arg_accessor(arg);
- Tensor<float> res_accessor(res);
-
- const auto &input_shape = arg.getShape();
+ const auto &input_shape = input.getShape();
const int num_axes = static_cast<int>(axis_order.size());
assert(num_axes == input_shape.rank());
ShapeRange in_range(input_shape);
Index out_index(input_shape.rank());
+ const size_t elem_size = input.getElementSize();
+
for (const auto &in_index : in_range)
{
for (int i = 0; i < num_axes; ++i)
- {
out_index.at(i) = in_index.at(axis_order[i]);
- }
- res_accessor.at(out_index) = arg_accessor.at(in_index);
+
+ std::memcpy(res.at(out_index), input.at(in_index), elem_size);
}
}
@@ -68,8 +67,13 @@ PassData ConstantFoldTranspose::run(PassData data)
auto constant_op = dynamic_cast<ops::ConstantOp *>(match.first);
auto transpose_op = dynamic_cast<ops::TransposeOp *>(match.second);
- // FIXME Revise this when we've got type information in operations.
- TensorVariant res(DataType::FLOAT32, transpose_op->getOutputShape(0));
+ const auto elem_type = constant_op->getValue().getElementType();
+ const auto &out_shape = transpose_op->getOutputShape(0);
+ TensorType res_type(elem_type, out_shape);
+ if (constant_op->getOutput(0)->getType().isQuantized())
+ res_type.setQuantization(constant_op->getOutput(0)->getType().getQuantization());
+
+ TensorVariant res(res_type);
transpose(constant_op->getValue(), res, transpose_op->getAxisOrder());
auto new_op = graph->create<ops::ConstantOp>(res);
diff --git a/compiler/nnc/passes/optimizations/DeadCodeElimination.cpp b/compiler/nnc/passes/optimizations/DeadCodeElimination.cpp
new file mode 100644
index 000000000..b89dca1b7
--- /dev/null
+++ b/compiler/nnc/passes/optimizations/DeadCodeElimination.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "passes/optimizations/DeadCodeElimination.h"
+#include "mir/Graph.h"
+
+#include <algorithm>
+
+using namespace mir;
+
+nnc::PassData nnc::DeadCodeElimination::run(PassData data)
+{
+ auto graph = static_cast<Graph *>(data);
+ assert(graph);
+
+ std::vector<Operation *> sorted_nodes = getSortedNodes(graph);
+
+ auto remove_if_unused = [graph](Operation *op) {
+ if (op->getType() == Operation::Type::input || op->getType() == Operation::Type::output)
+ return;
+
+ bool has_no_uses =
+ std::all_of(op->getOutputs().cbegin(), op->getOutputs().cend(),
+ [](const Operation::Output &output) { return output.getUses().empty(); });
+
+ if (has_no_uses)
+ {
+ graph->removeNode(op);
+ }
+ };
+
+ std::for_each(sorted_nodes.rbegin(), sorted_nodes.rend(), remove_if_unused);
+
+ return graph;
+}
diff --git a/compiler/nnc/passes/optimizations/FuseArithmeticOps.cpp b/compiler/nnc/passes/optimizations/FuseArithmeticOps.cpp
index f601d0562..91686ef74 100644
--- a/compiler/nnc/passes/optimizations/FuseArithmeticOps.cpp
+++ b/compiler/nnc/passes/optimizations/FuseArithmeticOps.cpp
@@ -45,11 +45,11 @@ using Edge = pair<Operation *, Operation *>;
* This function used to get 'ConstantOp' with weights of 'AddOp', 'MulOp' or 'Conv2DOp'
* For each of these ops weights stored in second input node
*/
-ops::ConstantOp *getSecondInputAsConst(const Operation *op)
+ops::ConstantOp *getSecondInputAsConst(Operation *op)
{
assert(op->getType() == OpType::add || op->getType() == OpType::mul ||
op->getType() == OpType::conv2D);
- return dynamic_cast<ops::ConstantOp *>(op->getInput(1)->getProducer()->getNode());
+ return dynamic_cast<ops::ConstantOp *>(op->getInput(1)->getNode());
}
// This function finds successive operations of given types, with ConstantOp as second input
@@ -64,9 +64,9 @@ vector<Edge> findSuccessiveOpsWithConstWeights(Graph *g, OpType first_op_type,
{
for (auto &out : first_op->getOutputs())
{
- for (auto *consumer : out.getConsumers())
+ for (Operation::Use use : out.getUses())
{
- Operation *second_op = consumer->getNode();
+ Operation *second_op = use.getNode();
if (second_op->getType() == second_op_type && getSecondInputAsConst(second_op))
{
/**
@@ -174,7 +174,7 @@ bool fuseSuccessiveOps(Graph *g)
// Create new constant operation and copy first successive operation
auto new_const_op = mergeConstantOps(g, const1_op, const2_op, edge.second->getType());
- auto first_op_input = edge.first->getInput(0)->getProducer();
+ auto first_op_input = edge.first->getInput(0);
auto new_op = g->copyOpWithInputs(edge.first, {first_op_input, new_const_op->getOutput(0)});
// Replace second successive operation with new one and remove old nodes
@@ -213,7 +213,7 @@ bool sinkAddThroughMul(Graph *g)
assert(old_add_const_op && ols_mul_const_op);
// Create new operations
- auto old_add_input = old_add_op->getInput(0)->getProducer();
+ auto old_add_input = old_add_op->getInput(0);
auto new_mul_op =
g->copyOpWithInputs(old_mul_op, {old_add_input, ols_mul_const_op->getOutput(0)});
auto new_add_const_op = mergeConstantOps(g, old_add_const_op, ols_mul_const_op, OpType::mul);
diff --git a/compiler/nnc/passes/optimizations/OptimizationUtils.cpp b/compiler/nnc/passes/optimizations/OptimizationUtils.cpp
index 706ddca7b..27e52cdaf 100644
--- a/compiler/nnc/passes/optimizations/OptimizationUtils.cpp
+++ b/compiler/nnc/passes/optimizations/OptimizationUtils.cpp
@@ -25,12 +25,12 @@ void swapAdjacent(mir::Graph *g, mir::Operation *top, mir::Operation *bottom)
assert(top->getNumInputs() == bottom->getNumInputs() && top->getNumInputs() == 1 &&
top->getNumInputs() == top->getNumOutputs() &&
top->getNumInputs() == bottom->getNumOutputs() && "incompatible ops");
- auto &ins = top->getInputs();
+ const auto &ins = top->getInputs();
std::vector<mir::Operation::Output *> prods;
prods.reserve(top->getNumInputs());
- for (auto &in : ins)
+ for (mir::Operation::Output *in : ins)
{
- prods.emplace_back(in.getProducer());
+ prods.emplace_back(in);
}
mir::Operation *new_bottom = g->copyOpWithInputs(bottom, prods);
prods.clear();
@@ -47,7 +47,7 @@ void swapAdjacent(mir::Graph *g, mir::Operation *top, mir::Operation *bottom)
// TODO: this function and it's usages should be removed, after DCE optimization will be implemented
void removeNodeIfUnused(mir::Graph *g, mir::Operation *op)
{
- if (op->getOutput(0)->getConsumers().empty())
+ if (op->getOutput(0)->getUses().empty())
g->removeNode(op);
}
diff --git a/compiler/nnc/passes/optimizations/RemoveDeadEnds.cpp b/compiler/nnc/passes/optimizations/RemoveDeadEnds.cpp
deleted file mode 100644
index 0d942742f..000000000
--- a/compiler/nnc/passes/optimizations/RemoveDeadEnds.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "passes/optimizations/RemoveDeadEnds.h"
-#include "mir/Graph.h"
-
-using namespace mir;
-
-nnc::PassData nnc::RemoveDeadEnds::run(PassData data)
-{
- auto g = static_cast<Graph *>(data);
- assert(g);
- for (auto op : g->getNodes())
- {
- if (op->getOutput(0)->getConsumers().empty() && op->getType() == mir::Operation::Type::constant)
- {
- g->removeNode(op);
- }
- }
- return g;
-}
diff --git a/compiler/nnc/passes/optimizations/SinkRelu.cpp b/compiler/nnc/passes/optimizations/SinkRelu.cpp
index 09bdc0290..1307c6254 100644
--- a/compiler/nnc/passes/optimizations/SinkRelu.cpp
+++ b/compiler/nnc/passes/optimizations/SinkRelu.cpp
@@ -58,7 +58,7 @@ PassData SinkRelu::run(PassData data)
pre_relu.reserve(relus.size());
for (auto *r : relus)
{
- pre_relu.emplace_back(r->getInput(0)->getProducer());
+ pre_relu.emplace_back(r->getInput(0));
}
// create replacement nodes
auto new_concat = g->create<ops::ConcatOp>(pre_relu, concat->getAxis());
diff --git a/compiler/nnc/passes/optimizations/SinkTranspose.cpp b/compiler/nnc/passes/optimizations/SinkTranspose.cpp
index b1c7a0dad..eb5ea2f49 100644
--- a/compiler/nnc/passes/optimizations/SinkTranspose.cpp
+++ b/compiler/nnc/passes/optimizations/SinkTranspose.cpp
@@ -63,7 +63,7 @@ PassData SinkTranspose::run(PassData data)
prev_trans.reserve(trs.size());
for (auto transpose : trs)
{
- prev_trans.emplace_back(transpose->getInput(0)->getProducer());
+ prev_trans.emplace_back(transpose->getInput(0));
}
auto new_concat = g->create<ops::ConcatOp>(prev_trans, axis_order[concat->getAxis()]);
auto new_transpose = g->create<ops::TransposeOp>(new_concat->getOutput(0), axis_order);
diff --git a/compiler/nnc/passes/transformations/CMakeLists.txt b/compiler/nnc/passes/transformations/CMakeLists.txt
index 3a12b1519..08517204d 100644
--- a/compiler/nnc/passes/transformations/CMakeLists.txt
+++ b/compiler/nnc/passes/transformations/CMakeLists.txt
@@ -1,6 +1,7 @@
set(TRANSFORMATIONS_SRC
- DataFormatSwitcher.cpp)
+ DataFormatSwitcher.cpp
+ LowerConv2D.cpp)
nnc_add_library(nnc_transformations STATIC ${TRANSFORMATIONS_SRC})
set_target_properties(nnc_transformations PROPERTIES POSITION_INDEPENDENT_CODE ON)
-target_link_libraries(nnc_transformations PRIVATE mir nnc_support)
+target_link_libraries(nnc_transformations PRIVATE mir)
diff --git a/compiler/nnc/passes/transformations/DataFormatSwitcher.cpp b/compiler/nnc/passes/transformations/DataFormatSwitcher.cpp
index bf0f19e2a..8ff842660 100644
--- a/compiler/nnc/passes/transformations/DataFormatSwitcher.cpp
+++ b/compiler/nnc/passes/transformations/DataFormatSwitcher.cpp
@@ -16,6 +16,7 @@
#include "passes/transformations/DataFormatSwitcher.h"
+#include "mir/TensorUtil.h"
#include "mir/ops/AvgPool2DOp.h"
#include "mir/ops/Conv2DOp.h"
#include "mir/ops/Deconv2DOp.h"
@@ -85,22 +86,30 @@ void DataFormatSwitcher::cleanup() { _candidates_for_switch.clear(); }
mir::Operation::Output *DataFormatSwitcher::insertTransposeBefore(mir::Operation::Output *out)
{
+ mir::Operation::Output *new_out;
if (_target_format == mir::DataFormat::NHWC)
- return _graph->create<mir::ops::TransposeOp>(out, std::vector<std::size_t>{0, 2, 3, 1})
- ->getOutput(0); // NCHW -> NHWC
+ new_out = _graph->create<mir::ops::TransposeOp>(out, std::vector<std::size_t>{0, 2, 3, 1})
+ ->getOutput(0); // NCHW -> NHWC
else
- return _graph->create<mir::ops::TransposeOp>(out, std::vector<std::size_t>{0, 3, 1, 2})
- ->getOutput(0); // NHWC -> NCHW
+ new_out = _graph->create<mir::ops::TransposeOp>(out, std::vector<std::size_t>{0, 3, 1, 2})
+ ->getOutput(0); // NHWC -> NCHW
+ if (out->getType().isQuantized())
+ new_out->setQuantization(out->getType().getQuantization());
+ return new_out;
}
mir::Operation::Output *DataFormatSwitcher::insertTransposeAfter(mir::Operation::Output *out)
{
+ mir::Operation::Output *new_out;
if (_target_format == mir::DataFormat::NHWC)
- return _graph->create<mir::ops::TransposeOp>(out, std::vector<std::size_t>{0, 3, 1, 2})
- ->getOutput(0); // NHWC -> NCHW
+ new_out = _graph->create<mir::ops::TransposeOp>(out, std::vector<std::size_t>{0, 3, 1, 2})
+ ->getOutput(0); // NHWC -> NCHW
else
- return _graph->create<mir::ops::TransposeOp>(out, std::vector<std::size_t>{0, 2, 3, 1})
- ->getOutput(0); // NCHW -> NHWC
+ new_out = _graph->create<mir::ops::TransposeOp>(out, std::vector<std::size_t>{0, 2, 3, 1})
+ ->getOutput(0); // NCHW -> NHWC
+ if (out->getType().isQuantized())
+ new_out->setQuantization(out->getType().getQuantization());
+ return new_out;
}
void DataFormatSwitcher::switchAvgPool2D(mir::ops::AvgPool2DOp *op)
@@ -108,18 +117,14 @@ void DataFormatSwitcher::switchAvgPool2D(mir::ops::AvgPool2DOp *op)
if (op->getDataFormat() == _target_format)
return;
- auto *input = op->getInput(0)->getProducer();
+ auto *input = op->getInput(0);
- const auto &window_size = op->getWindowSize();
- const auto &strides = op->getStrides();
- const auto &padding_before = op->getPaddingBefore();
- const auto &padding_after = op->getPaddingAfter();
- const auto include_pad = op->getIncludePad();
+ mir::AvgPool2DOpAttributes attributes(op->getAttributes());
+ attributes.data_format = _target_format;
auto *trans_in = insertTransposeBefore(input);
- auto new_pool = _graph->create<mir::ops::AvgPool2DOp>(
- trans_in, window_size, strides, padding_before, padding_after, include_pad, _target_format);
+ auto new_pool = _graph->create<mir::ops::AvgPool2DOp>(trans_in, attributes);
auto *trans_out = insertTransposeAfter(new_pool->getOutput(0));
@@ -131,20 +136,28 @@ void DataFormatSwitcher::switchConv2D(mir::ops::Conv2DOp *op)
if (op->getDataFormat() == _target_format)
return;
- assert(op->getNumInputs() == 2);
- auto *input = op->getInput(0)->getProducer();
- auto *kernel = op->getInput(1)->getProducer();
+ assert(op->getNumInputs() >= 2);
+ auto *input = op->getInput(0);
+ auto *kernel = op->getInput(1);
- const auto &strides = op->getStrides();
- const auto &padding_before = op->getPaddingBefore();
- const auto &padding_after = op->getPaddingAfter();
+ mir::Conv2DOpAttributes attributes(op->getAttributes());
+ attributes.data_format = _target_format;
auto *trans_in = insertTransposeBefore(input);
- auto new_dw_conv = _graph->create<mir::ops::Conv2DOp>(trans_in, kernel, strides, padding_before,
- padding_after, _target_format);
+ mir::Operation *new_conv;
+ if (op->getNumInputs() == 2)
+ new_conv = _graph->create<mir::ops::Conv2DOp>(trans_in, kernel, attributes);
+ else
+ {
+ auto bias = op->getInput(2);
+ new_conv = _graph->create<mir::ops::Conv2DOp>(trans_in, kernel, bias, attributes);
+ }
+
+ if (op->getOutput(0)->getType().isQuantized())
+ new_conv->getOutput(0)->setQuantization(op->getOutput(0)->getType().getQuantization());
- auto *trans_out = insertTransposeAfter(new_dw_conv->getOutput(0));
+ auto *trans_out = insertTransposeAfter(new_conv->getOutput(0));
_graph->replaceNode(op, trans_out->getNode());
}
@@ -155,23 +168,27 @@ void DataFormatSwitcher::switchDeConv2D(mir::ops::DeConv2DOp *op)
return;
assert(op->getNumInputs() == 2);
- auto *input = op->getInput(0)->getProducer();
- auto *kernel = op->getInput(1)->getProducer();
-
- const auto &strides = op->getStrides();
- const auto &padding_before = op->getPaddingBefore();
- const auto &padding_after = op->getPaddingAfter();
- const auto padding_type = op->getPaddingType();
+ auto *input = op->getInput(0);
+ auto *kernel = op->getInput(1);
auto *trans_in = insertTransposeBefore(input);
mir::Operation *new_deconv;
- if (padding_type == mir::ops::PaddingType::Explicit)
- new_deconv = _graph->create<mir::ops::DeConv2DOp>(trans_in, kernel, strides, padding_before,
- padding_after, _target_format);
+ mir::Deconv2DOpAttributes attributes(op->getAttributes());
+ attributes.data_format = _target_format;
+ if (attributes.padding_type == mir::ops::PaddingType::Explicit)
+ {
+ new_deconv = _graph->create<mir::ops::DeConv2DOp>(trans_in, kernel, attributes);
+ }
else
- new_deconv = _graph->create<mir::ops::DeConv2DOp>(trans_in, kernel, strides, padding_type,
- op->getOutputShape(0), _target_format);
+ {
+ mir::Shape output_shape = op->getOutputShape(0);
+ if (_target_format == mir::DataFormat::NHWC)
+ output_shape = mir::transposeShape<0, 2, 3, 1>(output_shape);
+ else
+ output_shape = mir::transposeShape<0, 3, 1, 2>(output_shape);
+ new_deconv = _graph->create<mir::ops::DeConv2DOp>(trans_in, kernel, attributes, output_shape);
+ }
auto *trans_out = insertTransposeAfter(new_deconv->getOutput(0));
@@ -183,18 +200,26 @@ void DataFormatSwitcher::switchDepthwiseConv2D(mir::ops::DepthwiseConv2DOp *op)
if (op->getDataFormat() == _target_format)
return;
- assert(op->getNumInputs() == 2);
- auto *input = op->getInput(0)->getProducer();
- auto *kernel = op->getInput(1)->getProducer();
+ assert(op->getNumInputs() >= 2);
+ auto *input = op->getInput(0);
+ auto *kernel = op->getInput(1);
- const auto &strides = op->getStrides();
- const auto &padding_before = op->getPaddingBefore();
- const auto &padding_after = op->getPaddingAfter();
+ mir::Conv2DOpAttributes attributes(op->getAttributes());
+ attributes.data_format = _target_format;
auto *trans_in = insertTransposeBefore(input);
- auto new_dw_conv = _graph->create<mir::ops::DepthwiseConv2DOp>(
- trans_in, kernel, strides, padding_before, padding_after, _target_format);
+ mir::Operation *new_dw_conv;
+ if (op->getNumInputs() == 2)
+ new_dw_conv = _graph->create<mir::ops::DepthwiseConv2DOp>(trans_in, kernel, attributes);
+ else
+ {
+ auto bias = op->getInput(2);
+ new_dw_conv = _graph->create<mir::ops::DepthwiseConv2DOp>(trans_in, kernel, bias, attributes);
+ }
+
+ if (op->getOutput(0)->getType().isQuantized())
+ new_dw_conv->getOutput(0)->setQuantization(op->getOutput(0)->getType().getQuantization());
auto *trans_out = insertTransposeAfter(new_dw_conv->getOutput(0));
@@ -206,17 +231,14 @@ void DataFormatSwitcher::switchMaxPool2D(mir::ops::MaxPool2DOp *op)
if (op->getDataFormat() == _target_format)
return;
- auto *input = op->getInput(0)->getProducer();
+ auto *input = op->getInput(0);
- const auto &window_size = op->getWindowSize();
- const auto &strides = op->getStrides();
- const auto &padding_before = op->getPaddingBefore();
- const auto &padding_after = op->getPaddingAfter();
+ mir::MaxPool2DOpAttributes attributes(op->getAttributes());
+ attributes.data_format = _target_format;
auto *trans_in = insertTransposeBefore(input);
- auto new_pool = _graph->create<mir::ops::MaxPool2DOp>(
- trans_in, window_size, strides, padding_before, padding_after, _target_format);
+ auto new_pool = _graph->create<mir::ops::MaxPool2DOp>(trans_in, attributes);
auto *trans_out = insertTransposeAfter(new_pool->getOutput(0));
diff --git a/compiler/nnc/passes/transformations/LowerConv2D.cpp b/compiler/nnc/passes/transformations/LowerConv2D.cpp
new file mode 100644
index 000000000..9e32978bc
--- /dev/null
+++ b/compiler/nnc/passes/transformations/LowerConv2D.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "passes/transformations/LowerConv2D.h"
+
+#include "mir/ops/Conv2DOp.h"
+#include "mir/ops/DepthwiseConv2DOp.h"
+#include "mir/ops/TransposeOp.h"
+
+namespace nnc
+{
+
+static void lowerConv2D(mir::Graph *graph, mir::ops::Conv2DOp *op)
+{
+ mir::Operation::Output *input = op->getInput(0);
+ mir::Operation::Output *kernel = op->getInput(1);
+
+ const std::int32_t in_group_size = kernel->getShape().dim(3);
+ const std::int32_t out_group_size = kernel->getShape().dim(0) / op->getNumGroups();
+
+ if (in_group_size == 1 && out_group_size == 1)
+ {
+ // [O, H, W, I / M] == [M, H, W, 1] -> [H, W, M, 1]
+ std::vector<std::size_t> perm{1, 2, 0, 3};
+ mir::Operation::Output *new_kernel =
+ graph->create<mir::ops::TransposeOp>(kernel, perm)->getOutput(0);
+ mir::Conv2DOpAttributes attributes = op->getAttributes();
+ attributes.num_groups = 1;
+ mir::Operation::Output *new_result =
+ graph->create<mir::ops::DepthwiseConv2DOp>(input, new_kernel, attributes)->getOutput(0);
+ graph->replaceNode(op, new_result->getNode());
+ }
+}
+
+LowerConv2D::LowerConv2D() = default;
+
+PassData LowerConv2D::run(PassData data)
+{
+ auto *graph = static_cast<mir::Graph *>(data);
+
+ // Collect candidate ops before actual transformation because the graph will be changed.
+ std::vector<mir::ops::Conv2DOp *> group_conv_ops;
+ for (mir::Operation *op : graph->getNodes())
+ {
+ auto *conv_op = dynamic_cast<mir::ops::Conv2DOp *>(op);
+ if (conv_op != nullptr && conv_op->getNumGroups() != 1)
+ {
+ group_conv_ops.push_back(conv_op);
+ }
+ }
+
+ for (mir::ops::Conv2DOp *op : group_conv_ops)
+ {
+ lowerConv2D(graph, op);
+ }
+
+ return graph;
+}
+
+void LowerConv2D::cleanup() {}
+
+} // namespace nnc
diff --git a/compiler/nnc/requires.cmake b/compiler/nnc/requires.cmake
index b6584f859..2fcaea4d5 100644
--- a/compiler/nnc/requires.cmake
+++ b/compiler/nnc/requires.cmake
@@ -1,5 +1,3 @@
require("adtidas")
-require("mir-caffe2-importer")
-require("mir-caffe-importer")
-require("mir-onnx-importer")
-require("mir-tflite-importer")
+require("mir")
+require("mir-interpreter")
diff --git a/compiler/nnc/support/CLOptionChecker.cpp b/compiler/nnc/support/CLOptionChecker.cpp
index 3a00cfc8a..1ec1e876a 100644
--- a/compiler/nnc/support/CLOptionChecker.cpp
+++ b/compiler/nnc/support/CLOptionChecker.cpp
@@ -14,14 +14,10 @@
* limitations under the License.
*/
-#include "option/Options.h"
#include "support/CommandLine.h"
#include <dirent.h>
#include <cstring>
-#include <fstream>
-#include <sys/types.h>
-#include <unistd.h>
namespace nnc
{
diff --git a/compiler/nnc/support/CommandLine.cpp b/compiler/nnc/support/CommandLine.cpp
index 27078be51..3ab28ff37 100644
--- a/compiler/nnc/support/CommandLine.cpp
+++ b/compiler/nnc/support/CommandLine.cpp
@@ -634,4 +634,4 @@ template <> void Option<uint32_t>::setValue(const std::string &val)
}
} // namespace cli
-} // namespace nnc \ No newline at end of file
+} // namespace nnc
diff --git a/compiler/nnc/tests/acl_soft_backend/AclCppOperations.cpp b/compiler/nnc/tests/acl_soft_backend/AclCppOperations.cpp
index 8cbc715d3..4ae020355 100644
--- a/compiler/nnc/tests/acl_soft_backend/AclCppOperations.cpp
+++ b/compiler/nnc/tests/acl_soft_backend/AclCppOperations.cpp
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#include "gtest/gtest.h"
#include <sstream>
#include <thread>
diff --git a/compiler/nnc/tests/acl_soft_backend/BuildInfo.h.in b/compiler/nnc/tests/acl_soft_backend/BuildInfo.h.in
index c229a4f74..b390e8e4f 100644
--- a/compiler/nnc/tests/acl_soft_backend/BuildInfo.h.in
+++ b/compiler/nnc/tests/acl_soft_backend/BuildInfo.h.in
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#ifndef _NNC_BUILD_INFO_H_IN_H_
#define _NNC_BUILD_INFO_H_IN_H_
diff --git a/compiler/nnc/tests/acl_soft_backend/CMakeLists.txt b/compiler/nnc/tests/acl_soft_backend/CMakeLists.txt
index 435c398d9..69a3be882 100644
--- a/compiler/nnc/tests/acl_soft_backend/CMakeLists.txt
+++ b/compiler/nnc/tests/acl_soft_backend/CMakeLists.txt
@@ -29,8 +29,9 @@ if(NOT DEFINED ENV{ODROID_H5_DIR})
return()
endif()
-find_package(HDF5 COMPONENTS CXX REQUIRED)
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(HDF5 QUIET)
+
+nnas_find_package(GTest REQUIRED)
# Provide the test suite with the information where to locate executalbes to run etc.
configure_file(BuildInfo.h.in BuildInfo.h)
diff --git a/compiler/nnc/tests/acl_soft_backend/artifact_cmake/CMakeLists.txt b/compiler/nnc/tests/acl_soft_backend/artifact_cmake/CMakeLists.txt
index ceecded9e..b0a805a30 100644
--- a/compiler/nnc/tests/acl_soft_backend/artifact_cmake/CMakeLists.txt
+++ b/compiler/nnc/tests/acl_soft_backend/artifact_cmake/CMakeLists.txt
@@ -11,7 +11,8 @@ set(ODROID_ACL_BUILD_DIR ${ODROID_ACL_DIR}/build)
find_library(OPEN_CL OpenCL /usr/lib/arm-linux-gnueabihf)
find_library(ARM_COMPUTE arm_compute PATHS ${ODROID_ACL_BUILD_DIR})
find_library(ARM_COMPUTE_CORE arm_compute_core PATHS ${ODROID_ACL_BUILD_DIR})
-find_package(HDF5 COMPONENTS CXX REQUIRED)
+nnas_find_package(HDF5 QUIET)
+
add_executable(nnc_test main.cpp AclArtifact.cpp)
diff --git a/compiler/nnc/tests/acl_soft_backend/artifact_cmake/main.cpp b/compiler/nnc/tests/acl_soft_backend/artifact_cmake/main.cpp
index 5dec0f380..c326b390b 100644
--- a/compiler/nnc/tests/acl_soft_backend/artifact_cmake/main.cpp
+++ b/compiler/nnc/tests/acl_soft_backend/artifact_cmake/main.cpp
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#include "AclArtifact.h"
#include <iostream>
#include <memory>
diff --git a/compiler/nnc/tests/import/CMakeLists.txt b/compiler/nnc/tests/import/CMakeLists.txt
index 259915019..0aa07c5e5 100644
--- a/compiler/nnc/tests/import/CMakeLists.txt
+++ b/compiler/nnc/tests/import/CMakeLists.txt
@@ -10,11 +10,11 @@
# how to store large files (in this case, files with models), and how to run all system tests.
# As soon as it is decided, model files should be added, as well as the code that runs the tests.
if(NNC_FRONTEND_TFLITE_ENABLED)
- add_executable(system_test_import_tflite tflite.cpp ${OPTIONS_SRC})
+ add_executable(system_test_import_tflite tflite.cpp)
target_link_libraries(system_test_import_tflite PRIVATE nnc_support mir_tflite_importer)
endif()
if(NNC_FRONTEND_CAFFE_ENABLED)
- add_executable(system_test_import_caffe caffe.cpp ${OPTIONS_SRC})
+ add_executable(system_test_import_caffe caffe.cpp)
target_link_libraries(system_test_import_caffe PRIVATE nnc_support mir_caffe_importer)
endif()
diff --git a/compiler/nnc/tests/import/caffe.cpp b/compiler/nnc/tests/import/caffe.cpp
index 956742754..a3abafccb 100644
--- a/compiler/nnc/tests/import/caffe.cpp
+++ b/compiler/nnc/tests/import/caffe.cpp
@@ -15,7 +15,6 @@
*/
#include "support/CommandLine.h"
-#include "option/Options.h"
#include <caffe_importer.h>
@@ -25,19 +24,16 @@ using namespace nnc;
int main(int argc, const char **argv)
{
- if (argc != 2)
- return 1;
-
+ cli::Option<std::string> model_path(cli::optname("--model"), cli::overview("Path to the model"));
cli::CommandLine::getParser()->parseCommandLine(argc, argv);
try
{
- mir_caffe::CaffeImporter importer{cli::inputFile};
- importer.importModel();
+ mir_caffe::loadModel(model_path);
}
catch (...)
{
- std::cout << "Could not create IR for model \"" << cli::inputFile << "\"" << std::endl;
+ std::cout << "Could not create IR for model \"" << model_path << "\"" << std::endl;
return 1;
}
diff --git a/compiler/nnc/tests/import/tflite.cpp b/compiler/nnc/tests/import/tflite.cpp
index 2743dfb6e..1c11e02c1 100644
--- a/compiler/nnc/tests/import/tflite.cpp
+++ b/compiler/nnc/tests/import/tflite.cpp
@@ -15,7 +15,6 @@
*/
#include "support/CommandLine.h"
-#include "option/Options.h"
#include <tflite_importer.h>
@@ -25,21 +24,16 @@ using namespace nnc;
int main(int argc, const char **argv)
{
- if (argc != 2)
- {
- return 1;
- }
-
+ cli::Option<std::string> model_path(cli::optname("--model"), cli::overview("Path to the model"));
cli::CommandLine::getParser()->parseCommandLine(argc, argv);
try
{
- mir_tflite::TfliteImporter importer{cli::inputFile};
- importer.importModel();
+ mir_tflite::loadModel(model_path);
}
catch (...)
{
- std::cout << "Could not create IR for model \"" << cli::inputFile << "\"" << std::endl;
+ std::cout << "Could not create IR for model \"" << model_path << "\"" << std::endl;
return 1;
}
diff --git a/compiler/nnc/tests/soft_backend/CMakeLists.txt b/compiler/nnc/tests/soft_backend/CMakeLists.txt
index 8587da3e4..5526327ba 100644
--- a/compiler/nnc/tests/soft_backend/CMakeLists.txt
+++ b/compiler/nnc/tests/soft_backend/CMakeLists.txt
@@ -2,8 +2,8 @@ file(GLOB_RECURSE SOFT_TEST_DEF_SOURCES *.def)
nnc_make_generated_sources("${SOFT_TEST_DEF_SOURCES}" ${CMAKE_CURRENT_BINARY_DIR} SOFT_TEST_GENERATED_SOURCES)
-add_executable(nnc_system_soft_backend_cpp_compile CompileCPP.cpp ${OPTIONS_SRC} ${SOFT_TEST_GENERATED_SOURCES})
-target_link_libraries(nnc_system_soft_backend_cpp_compile PRIVATE nnc_support soft_backend_cpp mir)
+add_executable(nnc_system_soft_backend_cpp_compile CompileCPP.cpp ${SOFT_TEST_GENERATED_SOURCES})
+target_link_libraries(nnc_system_soft_backend_cpp_compile PRIVATE soft_backend_cpp mir)
target_include_directories(nnc_system_soft_backend_cpp_compile PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${NNC_SOFT_BACKEND_DIR})
-add_test(NAME nnc_system_test_soft_backend_cpp_compile COMMAND nnc_system_soft_backend_cpp_compile -d test_output --target=x86-c++ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+add_test(nnc_system_test_soft_backend_cpp_compile nnc_system_soft_backend_cpp_compile)
diff --git a/compiler/nnc/tests/soft_backend/CompileCPP.cpp b/compiler/nnc/tests/soft_backend/CompileCPP.cpp
index ec3c14879..63aeb4a1b 100644
--- a/compiler/nnc/tests/soft_backend/CompileCPP.cpp
+++ b/compiler/nnc/tests/soft_backend/CompileCPP.cpp
@@ -19,9 +19,6 @@
* This test is not intended to check correctness of generated artifact
*/
-#include "support/CommandLine.h"
-#include "option/Options.h"
-
#include "mir/Graph.h"
#include "mir/Shape.h"
#include "mir/ops/InputOp.h"
@@ -48,7 +45,8 @@ using namespace mir;
static void fillGraph(Graph &g)
{
Shape input_shape{1, 2, 3};
- Operation *input_op = g.create<ops::InputOp>(input_shape);
+ mir::TensorType input_type{mir::DataType::FLOAT32, input_shape};
+ Operation *input_op = g.create<ops::InputOp>(input_type);
Operation *relu_op = g.create<ops::ReluOp>(input_op->getOutput(0));
Operation *output_op = g.create<ops::OutputOp>(relu_op->getOutput(0));
input_op->getOutput(0)->setName("in");
@@ -77,16 +75,15 @@ static void createMain(const string &path, const string &header_path)
out.write(test_main, sizeof(test_main));
}
-int main(int argc, const char *argv[])
+int main()
{
- cli::CommandLine::getParser()->parseCommandLine(argc, argv, false);
- std::string output_dir = cli::artifactDir;
- std::string artifact_name = cli::artifactName;
+ std::string output_dir = "test_output";
+ std::string artifact_name = "nnmodel";
Graph g;
fillGraph(g);
- nnc::CPPCodeGenerator cpp_code_generator;
+ nnc::CPPCodeGenerator cpp_code_generator(output_dir, artifact_name);
cpp_code_generator.run(&g);
string base_path = output_dir + "/" + artifact_name;
diff --git a/compiler/nnc/tests/soft_backend/test_main.def b/compiler/nnc/tests/soft_backend/test_main.def
index 75d219f75..6a464f862 100644
--- a/compiler/nnc/tests/soft_backend/test_main.def
+++ b/compiler/nnc/tests/soft_backend/test_main.def
@@ -7,4 +7,4 @@ int main()
model.doInference();
std::shared_ptr<Tensor> out_t = model.get_out();
return 0;
-} \ No newline at end of file
+}
diff --git a/compiler/nnc/unittests/CMakeLists.txt b/compiler/nnc/unittests/CMakeLists.txt
index 87b3de3b5..bbb6c7e67 100644
--- a/compiler/nnc/unittests/CMakeLists.txt
+++ b/compiler/nnc/unittests/CMakeLists.txt
@@ -8,12 +8,3 @@ add_subdirectory(acl_backend)
add_subdirectory(support)
add_subdirectory(optimizations)
add_subdirectory(transformations)
-if(NNC_FRONTEND_CAFFE_ENABLED)
- add_subdirectory(caffe_frontend)
-endif()
-if(NNC_FRONTEND_CAFFE2_ENABLED)
- add_subdirectory(caffe2_frontend)
-endif()
-if(NNC_FRONTEND_TFLITE_ENABLED)
- add_subdirectory(tflite_frontend)
-endif()
diff --git a/compiler/nnc/unittests/acl_backend/CMakeLists.txt b/compiler/nnc/unittests/acl_backend/CMakeLists.txt
index acfa456e3..b89e75498 100644
--- a/compiler/nnc/unittests/acl_backend/CMakeLists.txt
+++ b/compiler/nnc/unittests/acl_backend/CMakeLists.txt
@@ -3,7 +3,7 @@ set(ACL_CPP_BACKEND_UTEST_SOURCES DOMToText.cpp MIRToDOM.cpp)
file(GLOB_RECURSE ACL_IN_SOURCES "${NNC_ACL_BACKEND_DIR}/*.in")
nnc_make_generated_sources("${ACL_IN_SOURCES}" ${CMAKE_CURRENT_BINARY_DIR} ACL_GENERATED_SOURCES)
-nnc_add_unit_test(nnc_acl_cpp_backend_test ${ACL_CPP_BACKEND_UTEST_SOURCES} ${OPTIONS_SRC} ${ACL_GENERATED_SOURCES})
+nnc_add_unit_test(nnc_acl_cpp_backend_test ${ACL_CPP_BACKEND_UTEST_SOURCES} ${ACL_GENERATED_SOURCES})
-optional_target_link_libraries(nnc_acl_cpp_backend_test mir nnc_support acl_soft_backend_cpp)
+optional_target_link_libraries(nnc_acl_cpp_backend_test mir acl_soft_backend_cpp)
target_include_directories(nnc_acl_cpp_backend_test PRIVATE ${NNC_ACL_BACKEND_DIR} ${CMAKE_CURRENT_BINARY_DIR})
diff --git a/compiler/nnc/unittests/acl_backend/MIRToDOM.cpp b/compiler/nnc/unittests/acl_backend/MIRToDOM.cpp
index d4f5ac35e..a9b36a145 100644
--- a/compiler/nnc/unittests/acl_backend/MIRToDOM.cpp
+++ b/compiler/nnc/unittests/acl_backend/MIRToDOM.cpp
@@ -74,7 +74,8 @@ void fillGraph(Graph &g, const OpConstructor &op_constr, const vector<Shape> &in
vector<mir::Operation::Output *> inputs;
for (std::size_t i = 0; i < input_shapes.size(); ++i)
{
- auto input = g.create<ops::InputOp>(input_shapes[i])->getOutput(0);
+ mir::TensorType input_type{mir::DataType::FLOAT32, input_shapes[i]};
+ auto input = g.create<ops::InputOp>(input_type)->getOutput(0);
input->setName("x" + to_string(i));
inputs.push_back(input);
}
@@ -287,11 +288,8 @@ TEST(acl_backend_mir_to_dom, conv2d)
Graph g;
OpConstructor op_generator =
[kernel_tensor](mir::Graph &g, const std::vector<mir::Operation::Output *> &inputs) {
- vector<int32_t> strides{1, 1};
- vector<int32_t> padding{0, 0};
auto kernel = g.create<mir::ops::ConstantOp>(kernel_tensor)->getOutput(0);
- return g.create<mir::ops::Conv2DOp>(inputs[0], kernel, strides, padding, padding,
- mir::DataFormat::NHWC);
+ return g.create<mir::ops::Conv2DOp>(inputs[0], kernel, mir::Conv2DOpAttributes());
};
vector<Shape> input_shapes{{1, 10, 10, channels}};
@@ -315,11 +313,9 @@ TEST(acl_backend_mir_to_dom, depthwise_conv)
Graph g;
OpConstructor op_generator =
[kernel_tensor](mir::Graph &g, const std::vector<mir::Operation::Output *> &inputs) {
- vector<int32_t> strides{1, 1};
- vector<int32_t> padding{0, 0};
+ Conv2DOpAttributes attributes;
auto kernel = g.create<mir::ops::ConstantOp>(kernel_tensor)->getOutput(0);
- return g.create<mir::ops::DepthwiseConv2DOp>(inputs[0], kernel, strides, padding, padding,
- mir::DataFormat::NHWC);
+ return g.create<mir::ops::DepthwiseConv2DOp>(inputs[0], kernel, attributes);
};
vector<Shape> input_shapes{{1, 10, 10, channels}};
@@ -367,16 +363,14 @@ TEST(acl_backend_mir_to_dom, fully_connected)
TEST(acl_backend_mir_to_dom, maxpool)
{
- vector<int32_t> window_size{3, 3}; // Height, Width
- vector<int32_t> strides{1, 1};
+ mir::MaxPool2DOpAttributes attributes;
+ attributes.window = {3, 3};
Graph g;
- OpConstructor op_generator =
- [window_size, strides](mir::Graph &g, const std::vector<mir::Operation::Output *> &inputs) {
- std::vector<int32_t> padding{0, 0};
- return g.create<mir::ops::MaxPool2DOp>(inputs[0], window_size, strides, padding, padding,
- mir::DataFormat::NHWC);
- };
+ OpConstructor op_generator = [&attributes](mir::Graph &g,
+ const std::vector<mir::Operation::Output *> &inputs) {
+ return g.create<mir::ops::MaxPool2DOp>(inputs[0], attributes);
+ };
vector<Shape> input_shapes{{1, 10, 10, 3}};
diff --git a/compiler/nnc/unittests/caffe2_frontend/CMakeLists.txt b/compiler/nnc/unittests/caffe2_frontend/CMakeLists.txt
deleted file mode 100644
index 53b9363ff..000000000
--- a/compiler/nnc/unittests/caffe2_frontend/CMakeLists.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-file(GLOB_RECURSE TESTS "*.cpp")
-
-execute_process(
- COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_data/gen_model.py ${CMAKE_CURRENT_BINARY_DIR}
- OUTPUT_VARIABLE outp
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
- RESULT_VARIABLE test_create_failed
-)
-
-if (NOT ${test_create_failed})
- add_definitions(-DTEST_DIR="${CMAKE_CURRENT_BINARY_DIR}")
- nnc_add_unit_test(nnc_caffe2_frontend_test ${TESTS} ${OPTIONS_SRC})
- optional_target_link_libraries(nnc_caffe2_frontend_test mir_caffe2_importer nnc_support mir)
-endif()
diff --git a/compiler/nnc/unittests/caffe2_frontend/test_data/gen_model.py b/compiler/nnc/unittests/caffe2_frontend/test_data/gen_model.py
deleted file mode 100755
index 52f799731..000000000
--- a/compiler/nnc/unittests/caffe2_frontend/test_data/gen_model.py
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/python3
-
-import os
-import sys
-
-try:
- from caffe2.python import workspace, model_helper
- from caffe2.python.predictor import mobile_exporter
-except ImportError:
- print("!! Caffe2 not installed, caffe2 frontend test not generated", file=sys.stderr)
- exit(1)
-
-
-def save_net(init_net_pb, predict_net_pb, model):
- init_net, predict_net = mobile_exporter.Export(workspace, model.net, m.params)
- with open(predict_net_pb, 'wb') as f:
- f.write(model.net._net.SerializeToString())
- with open(init_net_pb, 'wb') as f:
- f.write(init_net.SerializeToString())
-
-
-resDir = sys.argv[1]
-
-m = model_helper.ModelHelper(name='unsupported_net')
-m.net.GivenTensorFill([], 'input_data', values=(1., ), shape=(1, ))
-m.net.Sin(['input_data'], 'result')
-save_net(os.path.join(resDir, 'init_net.pb'), os.path.join(resDir, 'predict_net.pb'), m)
diff --git a/compiler/nnc/unittests/caffe2_frontend/unsupported_caffe2_model.cpp b/compiler/nnc/unittests/caffe2_frontend/unsupported_caffe2_model.cpp
deleted file mode 100644
index 79c5c21e1..000000000
--- a/compiler/nnc/unittests/caffe2_frontend/unsupported_caffe2_model.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <caffe2_importer.h>
-#include <gtest/gtest.h>
-
-#include <exception>
-#include <string>
-
-const char *ErrorMsg = "NNC can't load model. Detected problems:\n"
- " * Sin: unknown layer";
-
-// When adding support for new layers, change the model, not the test
-TEST(CAFFE_IMPORT_UNSUPPORTED, ImportAModelWithUnsupportedLayers)
-{
-
- std::string predict_net = std::string(TEST_DIR) + "/predict_net.pb";
- std::string init_net = std::string(TEST_DIR) + "/init_net.pb";
-
- try
- {
- mir_caffe2::Caffe2Importer importer{predict_net, init_net, {{1}}};
- importer.importModel();
- }
- catch (std::exception &e)
- {
- ASSERT_EQ(std::string(ErrorMsg), e.what());
- return;
- }
-
- FAIL();
-}
diff --git a/compiler/nnc/unittests/caffe_frontend/CMakeLists.txt b/compiler/nnc/unittests/caffe_frontend/CMakeLists.txt
deleted file mode 100644
index 8aae7bcaf..000000000
--- a/compiler/nnc/unittests/caffe_frontend/CMakeLists.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-file(GLOB_RECURSE TESTS "*.cpp")
-
-add_definitions(-DTEST_DIR="${CMAKE_CURRENT_SOURCE_DIR}/test_data/")
-nnc_add_unit_test(nnc_caffe_frontend_test ${TESTS} ${OPTIONS_SRC})
-optional_target_link_libraries(nnc_caffe_frontend_test mir_caffe_importer nnc_support mir)
diff --git a/compiler/nnc/unittests/caffe_frontend/unsupported_caffe_model.cpp b/compiler/nnc/unittests/caffe_frontend/unsupported_caffe_model.cpp
deleted file mode 100644
index cc984193d..000000000
--- a/compiler/nnc/unittests/caffe_frontend/unsupported_caffe_model.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <caffe_importer.h>
-#include <gtest/gtest.h>
-
-#include <exception>
-#include <string>
-
-const char *ErrorMsg = "NNC can't load model. Detected problems:\n"
- " * DummyData: unsupported layer\n"
- " * LSTM: parameter 'expose_hidden' has unsupported value: 1\n"
- " * UnexcitingLayerType: unknown layer";
-
-// When adding support for new layers, change the model, not the test
-TEST(CAFFE_IMPORT_UNSUPPORTED, ImportAModelWithUnsupportedLayers)
-{
- std::string filename = std::string(TEST_DIR) + "unsupported.caffemodel";
-
- try
- {
- mir_caffe::CaffeImporter importer{filename};
- importer.importModel();
- }
- catch (std::exception &e)
- {
- ASSERT_EQ(std::string(ErrorMsg), e.what());
- return;
- }
-
- FAIL();
-}
diff --git a/compiler/nnc/unittests/optimizations/CMakeLists.txt b/compiler/nnc/unittests/optimizations/CMakeLists.txt
index 40d37198d..a3ec3e6af 100644
--- a/compiler/nnc/unittests/optimizations/CMakeLists.txt
+++ b/compiler/nnc/unittests/optimizations/CMakeLists.txt
@@ -1,6 +1,7 @@
-set(TESTS_OPTIMIZATIONS_SRC CombineTransposes.cpp
- SinkTest.cpp
- RemoveDeadEnds.cpp
- FuseArithmeticOps.cpp)
+set(TESTS_OPTIMIZATIONS_SRC
+ CombineTransposes.cpp
+ SinkTest.cpp
+ DeadCodeElimination.cpp
+ FuseArithmeticOps.cpp)
nnc_add_unit_test(tests_for_optimizations ${TESTS} ${TESTS_OPTIMIZATIONS_SRC})
-optional_target_link_libraries(tests_for_optimizations nnc_optimizations nnc_support mir)
+optional_target_link_libraries(tests_for_optimizations nnc_optimizations mir)
diff --git a/compiler/nnc/unittests/optimizations/CombineTransposes.cpp b/compiler/nnc/unittests/optimizations/CombineTransposes.cpp
index 517755fdb..8d90bd20a 100644
--- a/compiler/nnc/unittests/optimizations/CombineTransposes.cpp
+++ b/compiler/nnc/unittests/optimizations/CombineTransposes.cpp
@@ -40,7 +40,8 @@ TEST(OptPass, eliminateTransposesLinear)
* ||
* [relu]
*/
- Operation *input = g.create<ops::InputOp>(Shape{1, 2, 3});
+ mir::TensorType input_type{mir::DataType::FLOAT32, Shape{1, 2, 3}};
+ Operation *input = g.create<ops::InputOp>(input_type);
Operation *tr1 = g.create<ops::TransposeOp>(input->getOutput(0), vector<size_t>{1, 0, 2});
Operation *tr15 = g.create<ops::TransposeOp>(tr1->getOutput(0), vector<size_t>{1, 0, 2});
Operation *tr2 = g.create<ops::TransposeOp>(tr15->getOutput(0), vector<size_t>{1, 0, 2});
@@ -68,7 +69,8 @@ TEST(OptPass, combineTransposesLinear)
* ||
* [relu]
*/
- Operation *input = g.create<ops::InputOp>(Shape{1, 2, 3});
+ mir::TensorType input_type{mir::DataType::FLOAT32, Shape{1, 2, 3}};
+ Operation *input = g.create<ops::InputOp>(input_type);
Operation *tr1 = g.create<ops::TransposeOp>(input->getOutput(0), vector<size_t>{1, 0, 2});
Operation *tr2 = g.create<ops::TransposeOp>(tr1->getOutput(0), vector<size_t>{0, 2, 1});
Operation *relu = g.create<ops::ReluOp>(tr2->getOutput(0));
@@ -81,9 +83,8 @@ TEST(OptPass, combineTransposesLinear)
// Assert transposes are combined
ASSERT_EQ("i_0.t_4.r_3.", ss.str());
- auto ax_ord_actual = dynamic_cast<ops::TransposeOp *>(
- (*(g.getInputs()[0]->getOutput(0)->getConsumers().begin()))->getNode())
- ->getAxisOrder();
+ Operation::Use use = g.getInputs()[0]->getOutput(0)->getUses()[0];
+ auto ax_ord_actual = dynamic_cast<ops::TransposeOp *>(use.getNode())->getAxisOrder();
auto ax_ord_true = vector<size_t>{1, 2, 0};
ASSERT_TRUE(ax_ord_actual == ax_ord_true);
}
@@ -100,7 +101,8 @@ TEST(OptPass, combineTransposesBush)
* \\ //
* [Add]
*/
- Operation *input = g.create<ops::InputOp>(Shape{1, 2, 3, 2});
+ mir::TensorType input_type{mir::DataType::FLOAT32, Shape{1, 2, 3, 2}};
+ Operation *input = g.create<ops::InputOp>(input_type);
Operation *tr1 = g.create<ops::TransposeOp>(input->getOutput(0), vector<size_t>{1, 0, 2, 3});
Operation *tr2 = g.create<ops::TransposeOp>(tr1->getOutput(0), vector<size_t>{1, 0, 2, 3});
Operation *tr3 = g.create<ops::TransposeOp>(tr1->getOutput(0), vector<size_t>{1, 0, 2, 3});
@@ -111,8 +113,8 @@ TEST(OptPass, combineTransposesBush)
pass.run(&g);
g.accept(&d);
ASSERT_EQ("i_0.b_4.", ss.str());
- ASSERT_EQ(elw->getInput(0)->getProducer()->getNode()->getType(), mir::Operation::Type::input);
- ASSERT_EQ(elw->getInput(1)->getProducer()->getNode()->getType(), mir::Operation::Type::input);
+ ASSERT_EQ(elw->getInput(0)->getNode()->getType(), mir::Operation::Type::input);
+ ASSERT_EQ(elw->getInput(1)->getNode()->getType(), mir::Operation::Type::input);
}
TEST(OptPass, combineTransposesOpOrder)
@@ -127,20 +129,20 @@ TEST(OptPass, combineTransposesOpOrder)
* \\ //
* [Add]
*/
- Operation *in1 = g.create<ops::InputOp>(Shape{1, 2, 3});
- Operation *in2 = g.create<ops::InputOp>(Shape{1, 2, 3});
+ mir::TensorType input_type{mir::DataType::FLOAT32, {1, 2, 3}};
+ Operation *in1 = g.create<ops::InputOp>(input_type);
+ Operation *in2 = g.create<ops::InputOp>(input_type);
Operation *tr0 = g.create<ops::TransposeOp>(in1->getOutput(0), vector<size_t>{1, 0, 2});
Operation *tr1 = g.create<ops::TransposeOp>(in2->getOutput(0), vector<size_t>{2, 1, 0});
Operation *tr2 = g.create<ops::TransposeOp>(tr0->getOutput(0), vector<size_t>{1, 0, 2});
Operation *tr3 = g.create<ops::TransposeOp>(tr1->getOutput(0), vector<size_t>{2, 1, 0});
Operation *elw = g.create<ops::AddOp>(tr2->getOutput(0), tr3->getOutput(0));
g.create<ops::OutputOp>(elw->getOutput(0));
- int n1 = elw->getInput(0)->getNode()->getInput(0)->getNode()->getInput(0)->getNode()->getId();
- int n2 = elw->getInput(1)->getNode()->getInput(0)->getNode()->getInput(0)->getNode()->getId();
+ int n1 = in1->getId();
+ int n2 = in2->getId();
CombineTransposes pass;
pass.run(&g);
- ASSERT_EQ(g.getOutputs()[0]->getInput(0)->getProducer()->getNode()->getType(),
- mir::Operation::Type::add);
+ ASSERT_EQ(g.getOutputs()[0]->getInput(0)->getNode()->getType(), mir::Operation::Type::add);
// Order is preserved
ASSERT_EQ(n1, elw->getInput(0)->getNode()->getId());
ASSERT_EQ(n2, elw->getInput(1)->getNode()->getId());
diff --git a/compiler/nnc/unittests/optimizations/DeadCodeElimination.cpp b/compiler/nnc/unittests/optimizations/DeadCodeElimination.cpp
new file mode 100644
index 000000000..057dd710b
--- /dev/null
+++ b/compiler/nnc/unittests/optimizations/DeadCodeElimination.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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 "passes/optimizations/DeadCodeElimination.h"
+#include "mir/ops/AddOp.h"
+#include "mir/ops/ConstantOp.h"
+#include "mir/ops/InputOp.h"
+#include "mir/ops/OutputOp.h"
+
+#include <gtest/gtest.h>
+
+namespace
+{
+using namespace nnc;
+using namespace mir;
+
+TEST(DeadCodeEliminationTest, RemovesSingleNodes)
+{
+ Graph graph;
+ graph.create<ops::ConstantOp>(TensorVariant{DataType::FLOAT32, {}});
+ graph.create<ops::ConstantOp>(TensorVariant{DataType::FLOAT32, {}});
+
+ DeadCodeElimination pass;
+ pass.run(&graph);
+ ASSERT_EQ(graph.getNodes().size(), 0);
+}
+
+TEST(DeadCodeEliminationTest, RemovesChainedNodes)
+{
+ Graph graph;
+ auto c1 = graph.create<ops::ConstantOp>(TensorVariant{DataType::FLOAT32, {}})->getOutput(0);
+ auto c2 = graph.create<ops::ConstantOp>(TensorVariant{DataType::FLOAT32, {}})->getOutput(0);
+ auto sum = graph.create<ops::AddOp>(c1, c2)->getOutput(0);
+ graph.create<ops::AddOp>(sum, sum);
+
+ DeadCodeElimination pass;
+ pass.run(&graph);
+ ASSERT_EQ(graph.getNodes().size(), 0);
+}
+
+TEST(DeadCodeEliminationTest, PreservesInputNode)
+{
+ Graph graph;
+ graph.create<ops::InputOp>(TensorType{DataType::FLOAT32, {}});
+
+ DeadCodeElimination pass;
+ pass.run(&graph);
+ ASSERT_EQ(graph.getNodes().size(), 1);
+}
+
+TEST(DeadCodeEliminationTest, PreservesOutputNode)
+{
+ Graph graph;
+ auto c = graph.create<ops::ConstantOp>(TensorVariant{DataType::FLOAT32, {}})->getOutput(0);
+ graph.create<ops::OutputOp>(c);
+
+ DeadCodeElimination pass;
+ pass.run(&graph);
+ ASSERT_EQ(graph.getNodes().size(), 2);
+}
+
+TEST(DeadCodeEliminationTest, PreservesUsedNodes)
+{
+ Graph graph;
+ auto c1 = graph.create<ops::ConstantOp>(TensorVariant{DataType::FLOAT32, {}})->getOutput(0);
+ auto c2 = graph.create<ops::ConstantOp>(TensorVariant{DataType::FLOAT32, {}})->getOutput(0);
+ graph.create<ops::AddOp>(c1, c2);
+ graph.create<ops::OutputOp>(c1);
+ graph.create<ops::OutputOp>(c2);
+
+ DeadCodeElimination pass;
+ pass.run(&graph);
+ ASSERT_EQ(graph.getNodes().size(), 4);
+}
+
+} // unnamed namespace
diff --git a/compiler/nnc/unittests/optimizations/FuseArithmeticOps.cpp b/compiler/nnc/unittests/optimizations/FuseArithmeticOps.cpp
index 69180ac07..85a2fee76 100644
--- a/compiler/nnc/unittests/optimizations/FuseArithmeticOps.cpp
+++ b/compiler/nnc/unittests/optimizations/FuseArithmeticOps.cpp
@@ -36,12 +36,11 @@ TEST(OptPass, fuseConvBiasScaleScaleBias)
mir::Graph g;
// Create graph: 'input->conv->bias->scale->scale->bias'
- auto input = g.create<ops::InputOp>(Shape{1, 299, 299, 3});
+ mir::TensorType input_type(mir::DataType::FLOAT32, Shape{1, 299, 299, 3});
+ auto input = g.create<ops::InputOp>(input_type);
auto conv_const = g.create<ops::ConstantOp>(TensorVariant(DataType::FLOAT32, {10, 3, 3, 3}));
- const std::vector<std::int32_t> strides{1, 1};
- const std::vector<std::int32_t> padding{0, 0};
- auto conv = g.create<ops::Conv2DOp>(input->getOutput(0), conv_const->getOutput(0), strides,
- padding, padding, mir::DataFormat::NHWC);
+ auto conv = g.create<ops::Conv2DOp>(input->getOutput(0), conv_const->getOutput(0),
+ mir::Conv2DOpAttributes());
auto bias1_const = g.create<ops::ConstantOp>(TensorVariant(DataType::FLOAT32, {10}));
auto bias1 = g.create<ops::AddOp>(conv->getOutput(0), bias1_const->getOutput(0));
auto scale1_const = g.create<ops::ConstantOp>(TensorVariant(DataType::FLOAT32, {10}));
diff --git a/compiler/nnc/unittests/optimizations/RemoveDeadEnds.cpp b/compiler/nnc/unittests/optimizations/RemoveDeadEnds.cpp
deleted file mode 100644
index afed25bfb..000000000
--- a/compiler/nnc/unittests/optimizations/RemoveDeadEnds.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "passes/optimizations/RemoveDeadEnds.h"
-#include "mir/ops/ReluOp.h"
-#include "mir/ops/ConstantOp.h"
-
-#include <gtest/gtest.h>
-
-namespace
-{
-using namespace std;
-using namespace nnc;
-using namespace mir;
-
-TEST(OptPass, removeDeadEndConstants)
-{
- mir::Graph g;
- /* Create graph: (with useless constants)
- * [input]
- * ||
- * [relu]
- */
- Operation *C0 = g.create<ops::ConstantOp>(TensorVariant(DataType::FLOAT32, {2, 2}));
- Operation *input = g.create<ops::InputOp>(Shape{1, 2, 3});
- Operation *C1 = g.create<ops::ConstantOp>(TensorVariant(DataType::FLOAT32, {2, 2}));
- Operation *C2 = g.create<ops::ConstantOp>(TensorVariant(DataType::FLOAT32, {2, 2}));
- Operation *relu = g.create<ops::ReluOp>(input->getOutput(0));
-
- std::stringstream ss;
- RemoveDeadEnds pass;
- pass.run(&g);
- ASSERT_EQ(2, g.getNodes().size());
-}
-} // unnamed namespace
diff --git a/compiler/nnc/unittests/optimizations/SinkTest.cpp b/compiler/nnc/unittests/optimizations/SinkTest.cpp
index e5559074e..8c5b2767e 100644
--- a/compiler/nnc/unittests/optimizations/SinkTest.cpp
+++ b/compiler/nnc/unittests/optimizations/SinkTest.cpp
@@ -37,13 +37,14 @@ namespace
Operation *getPrev(Operation *op)
{
assert(op->getNumInputs() == 1);
- return op->getInput(0)->getProducer()->getNode();
+ return op->getInput(0)->getNode();
}
Operation *getNext(Operation *op)
{
- assert(op->getNumOutputs() == 1 && (op->getOutput(0)->getConsumers().size() == 1));
- return (*op->getOutput(0)->getConsumers().begin())->getNode();
+ assert(op->getNumOutputs() == 1 && (op->getOutput(0)->getUses().size() == 1));
+ Operation::Use use = op->getOutput(0)->getUses()[0];
+ return use.getNode();
}
/* This tests swapping relu and transpose */
@@ -60,7 +61,8 @@ TEST(OptPass, sinkTrReLU)
* ||
* [tanh]
*/
- Operation *input = g.create<ops::InputOp>(Shape{1, 2, 3});
+ mir::TensorType input_type{mir::DataType::FLOAT32, Shape{1, 2, 3}};
+ Operation *input = g.create<ops::InputOp>(input_type);
Operation *tr1 = g.create<ops::TransposeOp>(input->getOutput(0), vector<size_t>{1, 0, 2});
Operation *relu = g.create<ops::ReluOp>(tr1->getOutput(0));
Operation *tanh = g.create<ops::TanhOp>(relu->getOutput(0));
@@ -92,8 +94,12 @@ TEST(OptPass, sinkTrConcat)
* ||
* [TanH]
*/
- Operation *in1 = g.create<ops::InputOp>(Shape{1, 1, 2, 3});
- Operation *in2 = g.create<ops::InputOp>(Shape{1, 1, 2, 3});
+
+ mir::TensorType in1_type{mir::DataType::FLOAT32, Shape{1, 1, 2, 3}};
+ Operation *in1 = g.create<ops::InputOp>(in1_type);
+
+ mir::TensorType in2_type{mir::DataType::FLOAT32, Shape{1, 1, 2, 3}};
+ Operation *in2 = g.create<ops::InputOp>(in2_type);
Operation *tr1 = g.create<ops::TransposeOp>(in1->getOutput(0), vector<size_t>{0, 3, 1, 2});
Operation *tr2 = g.create<ops::TransposeOp>(in2->getOutput(0), vector<size_t>{0, 3, 1, 2});
Operation *conc =
@@ -127,8 +133,11 @@ TEST(OptPass, sinkReluConcat)
* ||
* [TanH]
*/
- Operation *in1 = g.create<ops::InputOp>(Shape{1, 1, 2, 3});
- Operation *in2 = g.create<ops::InputOp>(Shape{1, 1, 2, 3});
+ mir::TensorType in1_type{mir::DataType::FLOAT32, Shape{1, 1, 2, 3}};
+ Operation *in1 = g.create<ops::InputOp>(in1_type);
+
+ mir::TensorType in2_type{mir::DataType::FLOAT32, Shape{1, 1, 2, 3}};
+ Operation *in2 = g.create<ops::InputOp>(in2_type);
Operation *relu1 = g.create<ops::ReluOp>(in1->getOutput(0));
Operation *relu2 = g.create<ops::ReluOp>(in2->getOutput(0));
Operation *conc = g.create<ops::ConcatOp>(
@@ -161,11 +170,13 @@ TEST(OptPass, sinkPoolReLU)
* ||
* [tanh]
*/
- Operation *input = g.create<ops::InputOp>(Shape{1, 4, 4, 3});
+ mir::TensorType input_type{mir::DataType::FLOAT32, Shape{1, 4, 4, 3}};
+ Operation *input = g.create<ops::InputOp>(input_type);
Operation *relu = g.create<ops::ReluOp>(input->getOutput(0));
- Operation *mp =
- g.create<ops::MaxPool2DOp>(relu->getOutput(0), vector<int32_t>{2, 2}, vector<int32_t>{2, 2},
- vector<int32_t>{0, 0}, vector<int32_t>{0, 0}, DataFormat::NHWC);
+ mir::MaxPool2DOpAttributes attributes;
+ attributes.window = {2, 2};
+ attributes.strides = {2, 2};
+ Operation *mp = g.create<ops::MaxPool2DOp>(relu->getOutput(0), attributes);
Operation *tanh = g.create<ops::TanhOp>(mp->getOutput(0));
Operation *out = g.create<ops::OutputOp>(tanh->getOutput(0));
(void)out;
diff --git a/compiler/nnc/unittests/pass/CMakeLists.txt b/compiler/nnc/unittests/pass/CMakeLists.txt
index bafeab597..778d500d9 100644
--- a/compiler/nnc/unittests/pass/CMakeLists.txt
+++ b/compiler/nnc/unittests/pass/CMakeLists.txt
@@ -1,4 +1,4 @@
file(GLOB_RECURSE TEST_SOURCES "*.cpp")
-nnc_add_unit_test(nnc_pass_test ${TEST_SOURCES} ${OPTIONS_SRC})
-optional_target_link_libraries(nnc_pass_test nnc_support mir)
+nnc_add_unit_test(nnc_pass_test ${TEST_SOURCES})
+optional_target_link_libraries(nnc_pass_test mir)
diff --git a/compiler/nnc/unittests/pass/PassExceptionTest.cpp b/compiler/nnc/unittests/pass/PassExceptionTest.cpp
index c55b76f52..ea9d7a2d5 100644
--- a/compiler/nnc/unittests/pass/PassExceptionTest.cpp
+++ b/compiler/nnc/unittests/pass/PassExceptionTest.cpp
@@ -55,4 +55,4 @@ TEST(CONTRIB_PASS, PassException)
FAIL();
}
-} // unnamed namespace \ No newline at end of file
+} // unnamed namespace
diff --git a/compiler/nnc/unittests/pass/PassManagerTest.cpp b/compiler/nnc/unittests/pass/PassManagerTest.cpp
index 952aeedda..a0ee3140d 100644
--- a/compiler/nnc/unittests/pass/PassManagerTest.cpp
+++ b/compiler/nnc/unittests/pass/PassManagerTest.cpp
@@ -17,7 +17,6 @@
#include <dlfcn.h>
#include "mir/Graph.h"
-#include "support/CommandLine.h"
#include "pass/Pass.h"
#include "pass/PassData.h"
#include "pass/PassException.h"
diff --git a/compiler/nnc/unittests/soft_backend/CMakeLists.txt b/compiler/nnc/unittests/soft_backend/CMakeLists.txt
index e51d5038b..f1cd30b0f 100644
--- a/compiler/nnc/unittests/soft_backend/CMakeLists.txt
+++ b/compiler/nnc/unittests/soft_backend/CMakeLists.txt
@@ -1,16 +1,7 @@
-file(GLOB_RECURSE CPU_CPP_DEF_SOURCES "${NNC_SOFT_BACKEND_DIR}/*.def")
-nnc_make_generated_sources("${CPU_CPP_DEF_SOURCES}" ${CMAKE_CURRENT_BINARY_DIR} CPU_CPP_GENERATED_SOURCES)
+nnc_add_unit_test(nnc_cpu_cpp_backend_ops_test CPPOperations.cpp)
+optional_target_link_libraries(nnc_cpu_cpp_backend_ops_test mir_interpreter mir soft_backend_cpp)
+target_include_directories(nnc_cpu_cpp_backend_ops_test PRIVATE ${NNC_SOFT_BACKEND_DIR})
-set(CPU_CPP_BACKEND_OP_SOURCES CPPOperations.cpp)
-set(CPU_CPP_BACKEND_GN_SOURCES ${NNC_SOFT_BACKEND_DIR}/CPPGenerator.cpp Generator.cpp ${CPU_CPP_GENERATED_SOURCES})
-set(CPU_CPP_BACKEND_MA_SOURCES ModelAnalyzer.cpp)
-set(CPU_CPP_BACKEND_HT_SOURCES CPPHeaderTypes.cpp)
-
-nnc_add_unit_test(nnc_cpu_cpp_backend_ops_test ${CPU_CPP_BACKEND_OP_SOURCES} ${CPU_BACKEND_CPP_OP_SOURCES} ${OPTIONS_SRC} ${SOFT_DEF_SOURCES})
-optional_target_link_libraries(nnc_cpu_cpp_backend_ops_test nnc_support nnc_interpreter mir soft_backend_cpp)
-target_include_directories(nnc_cpu_cpp_backend_ops_test PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${NNC_SOFT_BACKEND_DIR})
-
-nnc_add_unit_test(nnc_cpu_cpp_backend_general_test ${CPU_CPP_BACKEND_GN_SOURCES} ${CPU_CPP_BACKEND_HT_SOURCES}
- ${CPU_CPP_BACKEND_MA_SOURCES} ${OPTIONS_SRC} ${SOFT_DEF_SOURCES})
-optional_target_link_libraries(nnc_cpu_cpp_backend_general_test nnc_support mir soft_backend_cpp)
-target_include_directories(nnc_cpu_cpp_backend_general_test PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${NNC_SOFT_BACKEND_DIR})
+nnc_add_unit_test(nnc_cpu_cpp_backend_general_test Generator.cpp CPPHeaderTypes.cpp ModelAnalyzer.cpp)
+optional_target_link_libraries(nnc_cpu_cpp_backend_general_test mir soft_backend_cpp)
+target_include_directories(nnc_cpu_cpp_backend_general_test PRIVATE ${NNC_SOFT_BACKEND_DIR})
diff --git a/compiler/nnc/unittests/soft_backend/CPPOperations.cpp b/compiler/nnc/unittests/soft_backend/CPPOperations.cpp
index b28801511..508ee954d 100644
--- a/compiler/nnc/unittests/soft_backend/CPPOperations.cpp
+++ b/compiler/nnc/unittests/soft_backend/CPPOperations.cpp
@@ -26,6 +26,7 @@
#include "code_snippets/cpp_header_types.def"
#include "code_snippets/cpp_common_funcs.def"
+#include "code_snippets/cpp_broadcast.def"
#include "code_snippets/cpp_capped_relu.def"
#include "code_snippets/cpp_concat.def"
#include "code_snippets/cpp_conv.def"
@@ -56,22 +57,23 @@
#include "SBSerializer.h"
// operations part
+#include "mir/ops/AbsOp.h"
#include "mir/ops/AddOp.h"
#include "mir/ops/AvgPool2DOp.h"
+#include "mir/ops/BroadcastOp.h"
#include "mir/ops/CappedReluOp.h"
#include "mir/ops/ConcatOp.h"
+#include "mir/ops/ConstantOp.h"
#include "mir/ops/Conv2DOp.h"
#include "mir/ops/Deconv2DOp.h"
#include "mir/ops/DepthwiseConv2DOp.h"
#include "mir/ops/DivOp.h"
#include "mir/ops/EluOp.h"
#include "mir/ops/FullyConnectedOp.h"
-#include "mir/ops/InputOp.h"
#include "mir/ops/LeakyReluOp.h"
#include "mir/ops/MaxOp.h"
#include "mir/ops/MaxPool2DOp.h"
#include "mir/ops/MulOp.h"
-#include "mir/ops/OutputOp.h"
#include "mir/ops/PadOp.h"
#include "mir/ops/ReduceMeanOp.h"
#include "mir/ops/ReluOp.h"
@@ -91,7 +93,7 @@
#include "mir/Graph.h"
#include "mir/ShapeRange.h"
-#include "backends/interpreter/Interpreter.h"
+#include "MirInterpreter.h"
#include "gtest/gtest.h"
@@ -124,23 +126,15 @@ fillGraph(mir::Graph &g,
&op_gen,
const vector<unique_ptr<mir::TensorVariant>> &input_ntensors)
{
- // Create graph inputs.
- std::vector<mir::Operation::Output *> inputs;
- for (std::size_t i = 0; i < input_ntensors.size(); ++i)
+ // Create operation inputs.
+ vector<mir::Operation::Output *> inputs;
+ for (const unique_ptr<mir::TensorVariant> &ntensor : input_ntensors)
{
- auto input_op = g.create<mir::ops::InputOp>(input_ntensors[i]->getShape());
- input_op->getOutput(0)->setName("x" + std::to_string(i));
- inputs.push_back(input_op->getOutput(0));
+ auto input = g.create<mir::ops::ConstantOp>(*ntensor)->getOutput(0);
+ inputs.push_back(input);
}
- // Create the operation.
- mir::Operation *op = op_gen(g, inputs);
-
- // Create graph outputs.
- assert(op->getNumOutputs() == 1);
- g.create<mir::ops::OutputOp>(op->getOutput(0));
-
- return op;
+ return op_gen(g, inputs);
}
/**
@@ -223,16 +217,13 @@ void fillTensors(unique_ptr<mir::TensorVariant> &ntensor, Tensor &atensor, const
/**
* @brief Run interpreter to get reference output data
*/
-mir::TensorVariant getReferenceTensor(mir::Graph &g,
- const vector<unique_ptr<mir::TensorVariant>> &input_ntensors)
+mir::TensorVariant getReferenceTensor(mir::Graph &g, mir::Operation *op)
{
- NNInterpreter interpreter;
- for (int i = 0; i < static_cast<int>(input_ntensors.size()); ++i)
- interpreter.setInput("x" + to_string(i), *input_ntensors[i]);
+ mir_interpreter::MIRInterpreter interpreter;
g.accept(&interpreter);
- const auto *output_op = g.getOutputs()[0];
- return interpreter.getResult(output_op->getInput(0)->getProducer());
-};
+ assert(op->getNumOutputs() == 1);
+ return interpreter.getTensor(op->getOutput(0));
+}
/**
* @brief Run selected operation, used to make code in tests more compact and fit getReferenceTensor
@@ -332,7 +323,7 @@ void createAndRunTestGraph(
serializer.serialize(inference_sequence);
assert(static_cast<sir::CallFunction *>(inference_sequence.front().get())->paramStartOffset == 0);
- mir::TensorVariant reference_output = getReferenceTensor(g, input_ntensors);
+ mir::TensorVariant reference_output = getReferenceTensor(g, actual_operation);
Tensor test_output;
artifactOperation(test_output, serializer.getBuffer().data(), input_atensors...);
@@ -400,8 +391,8 @@ TEST(cpp_operations_test, addbc)
return g.create<mir::ops::AddOp>(inputs[0], inputs[1]);
};
- createAndRunTestGraph(op_generator, ElementWise<Add, Tensor, Tensor>, input_ntensors,
- input_atensors[0], input_atensors[1]);
+ createAndRunTestGraph(op_generator, ElementWise<Add>, input_ntensors, input_atensors[0],
+ input_atensors[1]);
}
}
@@ -422,8 +413,8 @@ TEST(cpp_operations_test, mulbc)
return g.create<mir::ops::MulOp>(inputs[0], inputs[1]);
};
- createAndRunTestGraph(opGenerator, ElementWise<Mul, Tensor, Tensor>, input_ntensors,
- input_atensors[0], input_atensors[1]);
+ createAndRunTestGraph(opGenerator, ElementWise<Mul>, input_ntensors, input_atensors[0],
+ input_atensors[1]);
}
}
@@ -444,8 +435,8 @@ TEST(cpp_operations_test, divbc)
return g.create<mir::ops::DivOp>(inputs[0], inputs[1]);
};
- createAndRunTestGraph(opGenerator, ElementWise<Div, Tensor, Tensor>, input_ntensors,
- input_atensors[0], input_atensors[1]);
+ createAndRunTestGraph(opGenerator, ElementWise<Div>, input_ntensors, input_atensors[0],
+ input_atensors[1]);
}
}
@@ -464,8 +455,8 @@ TEST(cpp_operations_test, add)
return g.create<mir::ops::AddOp>(inputs[0], inputs[1]);
};
- createAndRunTestGraph(op_generator, ElementWise<Add, Tensor, Tensor>, input_ntensors,
- input_atensors[0], input_atensors[1]);
+ createAndRunTestGraph(op_generator, ElementWise<Add>, input_ntensors, input_atensors[0],
+ input_atensors[1]);
}
}
@@ -484,8 +475,8 @@ TEST(cpp_operations_test, sub)
return g.create<mir::ops::SubOp>(inputs[0], inputs[1]);
};
- createAndRunTestGraph(opGenerator, ElementWise<Sub, Tensor, Tensor>, input_n_tensors,
- input_atensors[0], input_atensors[1]);
+ createAndRunTestGraph(opGenerator, ElementWise<Sub>, input_n_tensors, input_atensors[0],
+ input_atensors[1]);
}
}
@@ -504,8 +495,8 @@ TEST(cpp_operations_test, mul)
return g.create<mir::ops::MulOp>(inputs[0], inputs[1]);
};
- createAndRunTestGraph(op_generator, ElementWise<Mul, Tensor, Tensor>, input_ntensors,
- input_atensors[0], input_atensors[1]);
+ createAndRunTestGraph(op_generator, ElementWise<Mul>, input_ntensors, input_atensors[0],
+ input_atensors[1]);
}
}
@@ -524,8 +515,8 @@ TEST(cpp_operations_test, max)
return g.create<mir::ops::MaxOp>(inputs[0], inputs[1]);
};
- createAndRunTestGraph(op_generator, ElementWise<Max, Tensor, Tensor>, input_ntensors,
- input_atensors[0], input_atensors[1]);
+ createAndRunTestGraph(op_generator, ElementWise<Max>, input_ntensors, input_atensors[0],
+ input_atensors[1]);
}
}
@@ -555,9 +546,9 @@ TEST(cpp_operations_test, convTransposed2d)
fillTensors(input_ntensors[1], input_atensor1, kernel_shape_data, 1.0f);
auto op_generator = [&strides](mir::Graph &g,
const std::vector<mir::Operation::Output *> &inputs) {
- std::vector<int32_t> padding{0, 0};
- return g.create<mir::ops::DeConv2DOp>(inputs[0], inputs[1], strides, padding,
- padding, mir::DataFormat::NHWC);
+ mir::Deconv2DOpAttributes attributes;
+ attributes.strides = strides;
+ return g.create<mir::ops::DeConv2DOp>(inputs[0], inputs[1], attributes);
};
createAndRunTestGraph(op_generator, convTransposed2d, input_ntensors, input_atensor0,
@@ -591,9 +582,9 @@ TEST(cpp_operations_test, conv2d)
fillTensors(input_ntensors[1], input_atensor1, kernel_shape_data, 1.0f);
auto op_generator = [&strides](mir::Graph &g,
const std::vector<mir::Operation::Output *> &inputs) {
- std::vector<int32_t> padding{0, 0};
- return g.create<mir::ops::Conv2DOp>(inputs[0], inputs[1], strides, padding, padding,
- mir::DataFormat::NHWC);
+ mir::Conv2DOpAttributes attributes;
+ attributes.strides = strides;
+ return g.create<mir::ops::Conv2DOp>(inputs[0], inputs[1], attributes);
};
createAndRunTestGraph(op_generator, conv2d, input_ntensors, input_atensor0,
@@ -627,9 +618,9 @@ TEST(cpp_operations_test, depthwise_conv)
fillTensors(input_ntensors[1], input_atensor1, kernel_shape_data, 1.0f);
auto op_generator = [&strides](mir::Graph &g,
const std::vector<mir::Operation::Output *> &inputs) {
- std::vector<int32_t> padding{0, 0};
- return g.create<mir::ops::DepthwiseConv2DOp>(inputs[0], inputs[1], strides, padding,
- padding, mir::DataFormat::NHWC);
+ mir::Conv2DOpAttributes attributes;
+ attributes.strides = strides;
+ return g.create<mir::ops::DepthwiseConv2DOp>(inputs[0], inputs[1], attributes);
};
createAndRunTestGraph(op_generator, depthwiseConv2d, input_ntensors, input_atensor0,
@@ -714,13 +705,15 @@ TEST(cpp_operations_test, avgpool)
vector<unique_ptr<mir::TensorVariant>> input_ntensors(1);
fillTensors(input_ntensors[0], input_atensor, shape_data, 1.0f);
+ mir::AvgPool2DOpAttributes attributes;
+ attributes.window = window_size;
+ attributes.strides = strides;
for (const auto include_pad : {false, true})
{
- auto op_generator = [&window_size, &strides, include_pad](
+ attributes.include_pad = include_pad;
+ auto op_generator = [&attributes](
mir::Graph &g, const std::vector<mir::Operation::Output *> &inputs) {
- std::vector<int32_t> padding{0, 0};
- return g.create<mir::ops::AvgPool2DOp>(inputs[0], window_size, strides, padding,
- padding, include_pad, mir::DataFormat::NHWC);
+ return g.create<mir::ops::AvgPool2DOp>(inputs[0], attributes);
};
createAndRunTestGraph(op_generator, avgPool, input_ntensors, input_atensor);
@@ -751,9 +744,10 @@ TEST(cpp_operations_test, maxpool)
auto op_generator = [&window_size, &strides](
mir::Graph &g, const std::vector<mir::Operation::Output *> &inputs) {
- std::vector<int32_t> padding{0, 0};
- return g.create<mir::ops::MaxPool2DOp>(inputs[0], window_size, strides, padding,
- padding, mir::DataFormat::NHWC);
+ mir::MaxPool2DOpAttributes attributes;
+ attributes.window = window_size;
+ attributes.strides = strides;
+ return g.create<mir::ops::MaxPool2DOp>(inputs[0], attributes);
};
createAndRunTestGraph(op_generator, maxPool, input_ntensors, input_atensor);
@@ -914,7 +908,20 @@ TEST(cpp_operations_test, reshape)
createAndRunTestGraph(op_generator, reshape, input_ntensors, input_atensor);
}
-TEST(cpp_operations_test, sqrtTest)
+TEST(cpp_operations_test, abs)
+{
+ // test prerequisites
+ vector<int> shape_data{2, 3, 4, 5};
+ Tensor input_atensor;
+ vector<unique_ptr<mir::TensorVariant>> input_ntensor(1);
+ fillTensors(input_ntensor[0], input_atensor, shape_data, 1.0f);
+ auto op_generator = [](mir::Graph &g, const std::vector<mir::Operation::Output *> &inputs) {
+ return g.create<mir::ops::AbsOp>(inputs[0]);
+ };
+ createAndRunTestGraph(op_generator, absFN, input_ntensor, input_atensor);
+}
+
+TEST(cpp_operations_test, sqrt)
{
// test prerequisites
vector<int> shape_data{2, 3, 4, 5};
@@ -936,14 +943,13 @@ TEST(cpp_operations_test, pad)
vector<unique_ptr<mir::TensorVariant>> input_ntensor(1);
fillTensors(input_ntensor[0], input_atensor, input_shape, 1.0f);
// PadOp params
- std::vector<std::int32_t> padding_before{1, 2};
- std::vector<std::int32_t> padding_after{1, 2};
-
- const float padding_value = 0.0;
+ mir::PadOpAttributes attributes;
+ attributes.padding_before = {1, 2};
+ attributes.padding_after = {1, 2};
- auto op_generator = [&padding_before, &padding_after, padding_value](
- mir::Graph &g, const std::vector<mir::Operation::Output *> &inputs) {
- return g.create<mir::ops::PadOp>(inputs[0], padding_before, padding_after, padding_value);
+ auto op_generator = [&attributes](mir::Graph &g,
+ const std::vector<mir::Operation::Output *> &inputs) {
+ return g.create<mir::ops::PadOp>(inputs[0], attributes);
};
createAndRunTestGraph(op_generator, pad, input_ntensor, input_atensor);
@@ -982,3 +988,20 @@ TEST(cpp_operations_test, transpose)
createAndRunTestGraph(op_generator, transpose, input_ntensor_3d, input_atensor_3d);
}
}
+
+TEST(cpp_operation_test, broadcast)
+{
+ const mir::Shape target_shapes[] = {{6}, {2, 3}, {2, 3, 1}, {1, 2, 1, 3}};
+ for (const mir::Shape &target_shape : target_shapes)
+ {
+ vector<int> input_shape_data{};
+ vector<unique_ptr<mir::TensorVariant>> input_ntensors(1);
+ Tensor input_atensor;
+ fillTensors(input_ntensors[0], input_atensor, input_shape_data, 1.0f);
+ auto op_generator = [&target_shape](mir::Graph &g,
+ const std::vector<mir::Operation::Output *> &inputs) {
+ return g.create<mir::ops::BroadcastOp>(inputs[0], target_shape);
+ };
+ createAndRunTestGraph(op_generator, broadcast, input_ntensors, input_atensor);
+ }
+}
diff --git a/compiler/nnc/unittests/soft_backend/Generator.cpp b/compiler/nnc/unittests/soft_backend/Generator.cpp
index b96df887a..dce700348 100644
--- a/compiler/nnc/unittests/soft_backend/Generator.cpp
+++ b/compiler/nnc/unittests/soft_backend/Generator.cpp
@@ -24,7 +24,6 @@
#include <unistd.h>
#include <cstdio>
#include <ftw.h>
-#include "support/CommandLine.h"
using namespace std;
@@ -74,13 +73,10 @@ TEST(Generator, check_generator_call)
#define TEST_DIR "output_dir"
#define TEST_NAME "someName"
#define BASE_NAME TEST_DIR "/" TEST_NAME
- const char *argv[] = {"soft_backend_test", "-d", TEST_DIR, "-o", TEST_NAME, nullptr};
- int argc = (sizeof(argv) / sizeof(argv[0])) - 1;
-
- cli::CommandLine::getParser()->parseCommandLine(argc, argv, false);
mir::Graph g;
- Operation::Output *input = g.create<ops::InputOp>(Shape{1, 2, 3, 4})->getOutput(0);
+ mir::TensorType input_type{mir::DataType::FLOAT32, Shape{1, 2, 3, 4}};
+ Operation::Output *input = g.create<ops::InputOp>(input_type)->getOutput(0);
input->setName("input");
Operation *output = g.create<ops::ReluOp>(input);
@@ -88,7 +84,7 @@ TEST(Generator, check_generator_call)
if (isFileExists(TEST_DIR))
deleteDir(TEST_DIR);
assert(!isFileExists(TEST_DIR) && "remove output dir");
- CPPCodeGenerator cpp_code_generator;
+ CPPCodeGenerator cpp_code_generator(TEST_DIR, TEST_NAME);
cpp_code_generator.run(&g);
checkOutputExists(BASE_NAME);
diff --git a/compiler/nnc/unittests/soft_backend/ModelAnalyzer.cpp b/compiler/nnc/unittests/soft_backend/ModelAnalyzer.cpp
index 9718a1eeb..d38385e91 100644
--- a/compiler/nnc/unittests/soft_backend/ModelAnalyzer.cpp
+++ b/compiler/nnc/unittests/soft_backend/ModelAnalyzer.cpp
@@ -52,12 +52,13 @@ TEST(ModelAnalyzer, linearization)
* \ /
* [join]
*/
- Operation *input = g.create<ops::InputOp>(Shape{1, 2, 3});
+ mir::TensorType input_type{mir::DataType::FLOAT32, Shape{1, 2, 3}};
+ Operation *input = g.create<ops::InputOp>(input_type);
Operation *head1 = g.create<ops::ReluOp>(input->getOutput(0));
Operation *head2 = g.create<ops::ReluOp>(input->getOutput(0));
Operation *tail1 = g.create<ops::ReluOp>(head1->getOutput(0));
Operation *tail2 = g.create<ops::ReluOp>(head2->getOutput(0));
- std::vector<mir::Operation::Output *> concat_inputs{tail1->getOutput(0), tail2->getOutput(0)};
+ vector<mir::Operation::Output *> concat_inputs{tail1->getOutput(0), tail2->getOutput(0)};
Operation *join = g.create<ops::ConcatOp>(concat_inputs, 0);
input->getOutput(0)->setName("input");
head1->getOutput(0)->setName("head1");
@@ -71,11 +72,12 @@ TEST(ModelAnalyzer, linearization)
ma.analyze(&g);
const auto &seq = ma.getInferenceSequence();
ASSERT_EQ(seq.size(), 6u);
- auto it = seq.begin();
- ASSERT_EQ(getCall(*(it++))->mirOp, input);
- ASSERT_EQ(getCall(*(it++))->mirOp, head1);
- ASSERT_EQ(getCall(*(it++))->mirOp, tail1);
- ASSERT_EQ(getCall(*(it++))->mirOp, head2);
- ASSERT_EQ(getCall(*(it++))->mirOp, tail2);
- ASSERT_EQ(getCall(*(it++))->mirOp, join);
+
+ vector<Operation *> op_seq(seq.size());
+ transform(seq.cbegin(), seq.cend(), op_seq.begin(),
+ [](const unique_ptr<sir::Action> &action) { return getCall(action)->mirOp; });
+
+ vector<Operation *> valid_seq1{input, head1, tail1, head2, tail2, join};
+ vector<Operation *> valid_seq2{input, head2, tail2, head1, tail1, join};
+ ASSERT_TRUE(op_seq == valid_seq1 || op_seq == valid_seq2);
}
diff --git a/compiler/nnc/unittests/tflite_frontend/CMakeLists.txt b/compiler/nnc/unittests/tflite_frontend/CMakeLists.txt
deleted file mode 100644
index e20737a8a..000000000
--- a/compiler/nnc/unittests/tflite_frontend/CMakeLists.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-file(GLOB_RECURSE TESTS "*.cpp")
-
-#Feature detect:
-execute_process(
- COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_data/gen_test.py ${CMAKE_CURRENT_BINARY_DIR}
- OUTPUT_VARIABLE outp
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
- RESULT_VARIABLE test_create_failed
-)
-
-if (NOT ${test_create_failed})
- add_definitions(-DTFLITE_TEST_DIR="${CMAKE_CURRENT_BINARY_DIR}/")
- nnc_add_unit_test(nnc_tflite_frontend_test ${TESTS} ${OPTIONS_SRC})
- optional_target_link_libraries(nnc_tflite_frontend_test mir_tflite_importer nnc_support mir)
-endif()
diff --git a/compiler/nnc/unittests/tflite_frontend/test_data/gen_test.py b/compiler/nnc/unittests/tflite_frontend/test_data/gen_test.py
deleted file mode 100755
index 74a566d9b..000000000
--- a/compiler/nnc/unittests/tflite_frontend/test_data/gen_test.py
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/python3
-import sys
-from distutils.version import LooseVersion, StrictVersion
-try:
- import numpy as np
-except ImportError:
- print(
- "!! NumPy is not installed, tflite frontend test not generated", file=sys.stderr)
- exit(1)
-try:
- import tensorflow as tf
- if (LooseVersion(tf.VERSION) < LooseVersion("1.11.0")):
- raise (Exception("Wrong Version"))
-except:
- print(
- "!! Tensorflow v 1.11 not installed, tflite frontend test not generated",
- file=sys.stderr)
- exit(1)
-
-resDir = sys.argv[1]
-if resDir[-1] != "/": resDir += "/"
-
-output_shape = [1, 28, 28, 1]
-strides = [1, 1, 1, 1]
-W = tf.constant(np.ones([7, 7, 1, 1]).astype(np.float32), name="ker_d")
-
-# Create the graph.
-X = tf.placeholder(shape=[1, 28, 28, 1], name='input', dtype=tf.float32)
-Y = tf.sin(X)
-
-out0 = tf.identity(Y, name="out")
-# Filter the input image.
-with tf.Session() as sess:
- out = sess.run(
- out0, feed_dict={"input:0": np.ones((1, 28, 28, 1)).astype(np.float32)})
- frozen_graphdef = tf.graph_util.convert_variables_to_constants(
- sess, sess.graph_def, ["out"])
- converter = tf.contrib.lite.TocoConverter.from_session(sess, [X], [out0])
- tflite_model = converter.convert()
-
- open(resDir + "unsupported.tflite", "wb").write(tflite_model)
diff --git a/compiler/nnc/unittests/tflite_frontend/unsupported_tflite_model.cpp b/compiler/nnc/unittests/tflite_frontend/unsupported_tflite_model.cpp
deleted file mode 100644
index 024a5c1d6..000000000
--- a/compiler/nnc/unittests/tflite_frontend/unsupported_tflite_model.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <tflite_importer.h>
-#include <gtest/gtest.h>
-
-#include <exception>
-#include <string>
-
-const char *ErrorMsg = "NNC can't load model. Detected problems:\n"
- " * SIN: unsupported operator";
-
-// When adding support for new layers, change the model, not the test
-TEST(TFLITE_IMPORT_UNSUPPORTED, ImportModelWithUnsupportedLayers)
-{
- std::string filename = std::string(TFLITE_TEST_DIR) + "unsupported.tflite";
- std::cout << filename << "\n";
-
- try
- {
- mir_tflite::TfliteImporter importer{filename};
- importer.importModel();
- }
- catch (std::exception &e)
- {
- ASSERT_EQ(std::string(ErrorMsg), e.what());
- return;
- }
-
- FAIL();
-}
diff --git a/compiler/nnc/unittests/transformations/CMakeLists.txt b/compiler/nnc/unittests/transformations/CMakeLists.txt
index 4308dfc74..eb3303a98 100644
--- a/compiler/nnc/unittests/transformations/CMakeLists.txt
+++ b/compiler/nnc/unittests/transformations/CMakeLists.txt
@@ -1,4 +1,4 @@
set(TESTS_TRANSFORMATIONS_SRC Switcher.cpp)
nnc_add_unit_test(nnc_transformations_test ${TESTS} ${TESTS_TRANSFORMATIONS_SRC})
-optional_target_link_libraries(nnc_transformations_test nnc_transformations nnc_support mir)
+optional_target_link_libraries(nnc_transformations_test nnc_transformations mir)
diff --git a/compiler/nnc/unittests/transformations/Switcher.cpp b/compiler/nnc/unittests/transformations/Switcher.cpp
index fae943962..049ac44cd 100644
--- a/compiler/nnc/unittests/transformations/Switcher.cpp
+++ b/compiler/nnc/unittests/transformations/Switcher.cpp
@@ -28,12 +28,18 @@
TEST(TRANSFORMATIONS, Switcher_Conv2D_NCHW2NHWC)
{
mir::Graph g;
- auto *input = g.create<mir::ops::InputOp>(mir::Shape{1, 3, 299, 299});
- auto *kernel = g.create<mir::ops::InputOp>(mir::Shape{3, 32, 3, 3});
+ mir::TensorType input_type{mir::DataType::FLOAT32, {1, 3, 299, 299}};
+ auto *input = g.create<mir::ops::InputOp>(input_type);
+
+ mir::TensorType kernel_type{mir::DataType::FLOAT32, {3, 32, 3, 3}};
+ auto *kernel = g.create<mir::ops::InputOp>(kernel_type);
// Conv2DOp
- auto *conv = g.create<mir::ops::Conv2DOp>(input->getOutput(0), kernel->getOutput(0),
- std::vector<int32_t>{2, 5}, std::vector<int32_t>{8, 1},
- std::vector<int32_t>{7, 9}, mir::DataFormat::NCHW);
+ mir::Conv2DOpAttributes attributes;
+ attributes.strides = {2, 5};
+ attributes.padding_before = {8, 1};
+ attributes.padding_after = {7, 9};
+ attributes.data_format = mir::DataFormat::NCHW;
+ auto *conv = g.create<mir::ops::Conv2DOp>(input->getOutput(0), kernel->getOutput(0), attributes);
auto *output = g.create<mir::ops::OutputOp>(conv->getOutput(0));
@@ -41,10 +47,10 @@ TEST(TRANSFORMATIONS, Switcher_Conv2D_NCHW2NHWC)
switcher.run(&g);
- auto *trans_out = output->getInput(0)->getProducer()->getNode();
- auto *conv_ = trans_out->getInput(0)->getProducer()->getNode();
- auto *trans_in = conv_->getInput(0)->getProducer()->getNode();
- auto *input_ = trans_in->getInput(0)->getProducer()->getNode();
+ auto *trans_out = output->getInput(0)->getNode();
+ auto *conv_ = trans_out->getInput(0)->getNode();
+ auto *trans_in = conv_->getInput(0)->getNode();
+ auto *input_ = trans_in->getInput(0)->getNode();
ASSERT_EQ(trans_out->getType(), mir::Operation::Type::transpose);
ASSERT_NE(conv_, conv);
@@ -70,12 +76,19 @@ TEST(TRANSFORMATIONS, Switcher_Conv2D_NCHW2NHWC)
TEST(TRANSFORMATIONS, Switcher_DWConv2D_NHWC2NCHW)
{
mir::Graph g;
- auto *input = g.create<mir::ops::InputOp>(mir::Shape{1, 112, 112, 32});
- auto *kernel = g.create<mir::ops::InputOp>(mir::Shape{3, 3, 32, 3});
+
+ mir::TensorType input_type{mir::DataType::FLOAT32, {1, 112, 112, 32}};
+ auto *input = g.create<mir::ops::InputOp>(input_type);
+
+ mir::TensorType kernel_type{mir::DataType::FLOAT32, {3, 3, 32, 3}};
+ auto *kernel = g.create<mir::ops::InputOp>(kernel_type);
// DepthwiseConv2DOp
- auto *dw_conv = g.create<mir::ops::DepthwiseConv2DOp>(
- input->getOutput(0), kernel->getOutput(0), std::vector<int32_t>{3, 25},
- std::vector<int32_t>{67, 123}, std::vector<int32_t>{32, 356}, mir::DataFormat::NHWC);
+ mir::Conv2DOpAttributes attributes;
+ attributes.strides = {3, 25};
+ attributes.padding_before = {67, 123};
+ attributes.padding_after = {32, 356};
+ auto *dw_conv =
+ g.create<mir::ops::DepthwiseConv2DOp>(input->getOutput(0), kernel->getOutput(0), attributes);
auto *output = g.create<mir::ops::OutputOp>(dw_conv->getOutput(0));
@@ -83,10 +96,10 @@ TEST(TRANSFORMATIONS, Switcher_DWConv2D_NHWC2NCHW)
switcher.run(&g);
- auto *trans_out = output->getInput(0)->getProducer()->getNode();
- auto *dw_conv_ = trans_out->getInput(0)->getProducer()->getNode();
- auto *trans_in = dw_conv_->getInput(0)->getProducer()->getNode();
- auto *input_ = trans_in->getInput(0)->getProducer()->getNode();
+ auto *trans_out = output->getInput(0)->getNode();
+ auto *dw_conv_ = trans_out->getInput(0)->getNode();
+ auto *trans_in = dw_conv_->getInput(0)->getNode();
+ auto *input_ = trans_in->getInput(0)->getNode();
ASSERT_EQ(trans_out->getType(), mir::Operation::Type::transpose);
ASSERT_NE(dw_conv_, dw_conv);
@@ -112,12 +125,20 @@ TEST(TRANSFORMATIONS, Switcher_DWConv2D_NHWC2NCHW)
TEST(TRANSFORMATIONS, Switcher_DeConv2D_NHWC2NCHW)
{
mir::Graph g;
- auto *input = g.create<mir::ops::InputOp>(mir::Shape{1, 112, 112, 32});
- auto *kernel = g.create<mir::ops::InputOp>(mir::Shape{3, 3, 3, 32});
+
+ mir::TensorType input_type{mir::DataType::FLOAT32, {1, 112, 112, 32}};
+ auto *input = g.create<mir::ops::InputOp>(input_type);
+
+ mir::TensorType kernel_type{mir::DataType::FLOAT32, {3, 3, 3, 32}};
+ auto *kernel = g.create<mir::ops::InputOp>(kernel_type);
// DeConv2DOp
- auto *deconv = g.create<mir::ops::DeConv2DOp>(
- input->getOutput(0), kernel->getOutput(0), std::vector<int32_t>{255, 54},
- std::vector<int32_t>{31, 72}, std::vector<int32_t>{32, 71}, mir::DataFormat::NHWC);
+
+ mir::Deconv2DOpAttributes attributes;
+ attributes.strides = {255, 54};
+ attributes.padding_before = {31, 72};
+ attributes.padding_after = {32, 71};
+ auto *deconv =
+ g.create<mir::ops::DeConv2DOp>(input->getOutput(0), kernel->getOutput(0), attributes);
auto *output = g.create<mir::ops::OutputOp>(deconv->getOutput(0));
@@ -125,10 +146,10 @@ TEST(TRANSFORMATIONS, Switcher_DeConv2D_NHWC2NCHW)
switcher.run(&g);
- auto *trans_out = output->getInput(0)->getProducer()->getNode();
- auto *deconv_ = trans_out->getInput(0)->getProducer()->getNode();
- auto *trans_in = deconv_->getInput(0)->getProducer()->getNode();
- auto *input_ = trans_in->getInput(0)->getProducer()->getNode();
+ auto *trans_out = output->getInput(0)->getNode();
+ auto *deconv_ = trans_out->getInput(0)->getNode();
+ auto *trans_in = deconv_->getInput(0)->getNode();
+ auto *input_ = trans_in->getInput(0)->getNode();
ASSERT_EQ(trans_out->getType(), mir::Operation::Type::transpose);
ASSERT_NE(deconv_, deconv);
@@ -154,11 +175,16 @@ TEST(TRANSFORMATIONS, Switcher_DeConv2D_NHWC2NCHW)
TEST(TRANSFORMATIONS, Switcher_AvgPool2D_NHWC2NCHW)
{
mir::Graph g;
- auto *input = g.create<mir::ops::InputOp>(mir::Shape{1, 112, 112, 32});
+
+ mir::TensorType input_type{mir::DataType::FLOAT32, {1, 112, 112, 32}};
+ auto *input = g.create<mir::ops::InputOp>(input_type);
// AvgPool2DOp
- auto *avg_pool = g.create<mir::ops::AvgPool2DOp>(
- input->getOutput(0), std::vector<int32_t>{41, 54}, std::vector<int32_t>{22, 53},
- std::vector<int32_t>{11, 36}, std::vector<int32_t>{38, 45}, true, mir::DataFormat::NHWC);
+ mir::AvgPool2DOpAttributes attributes;
+ attributes.window = {41, 54};
+ attributes.strides = {22, 53};
+ attributes.padding_before = {11, 36};
+ attributes.padding_after = {38, 45};
+ auto *avg_pool = g.create<mir::ops::AvgPool2DOp>(input->getOutput(0), attributes);
auto *output = g.create<mir::ops::OutputOp>(avg_pool->getOutput(0));
@@ -166,10 +192,10 @@ TEST(TRANSFORMATIONS, Switcher_AvgPool2D_NHWC2NCHW)
switcher.run(&g);
- auto *trans_out = output->getInput(0)->getProducer()->getNode();
- auto *avg_pool_ = trans_out->getInput(0)->getProducer()->getNode();
- auto *trans_in = avg_pool_->getInput(0)->getProducer()->getNode();
- auto *input_ = trans_in->getInput(0)->getProducer()->getNode();
+ auto *trans_out = output->getInput(0)->getNode();
+ auto *avg_pool_ = trans_out->getInput(0)->getNode();
+ auto *trans_in = avg_pool_->getInput(0)->getNode();
+ auto *input_ = trans_in->getInput(0)->getNode();
ASSERT_EQ(trans_out->getType(), mir::Operation::Type::transpose);
ASSERT_NE(avg_pool_, avg_pool);
@@ -197,12 +223,20 @@ TEST(TRANSFORMATIONS, Switcher_AvgPool2D_NHWC2NCHW)
TEST(TRANSFORMATIONS, Switcher_MaxPool2D_NCHW2NHWC)
{
mir::Graph g;
- auto *input = g.create<mir::ops::InputOp>(mir::Shape{1, 3, 299, 299});
- auto *kernel = g.create<mir::ops::InputOp>(mir::Shape{3, 32, 3, 3});
+
+ mir::TensorType input_type{mir::DataType::FLOAT32, {1, 3, 299, 299}};
+ auto *input = g.create<mir::ops::InputOp>(input_type);
+
+ mir::TensorType kernel_type{mir::DataType::FLOAT32, {3, 32, 3, 3}};
+ auto *kernel = g.create<mir::ops::InputOp>(kernel_type);
// MaxPool2DOp
- auto *max_pool = g.create<mir::ops::MaxPool2DOp>(
- input->getOutput(0), std::vector<int32_t>{41, 54}, std::vector<int32_t>{22, 53},
- std::vector<int32_t>{11, 36}, std::vector<int32_t>{38, 45}, mir::DataFormat::NCHW);
+ mir::MaxPool2DOpAttributes attributes;
+ attributes.window = {41, 54};
+ attributes.strides = {22, 53};
+ attributes.padding_before = {11, 36};
+ attributes.padding_after = {38, 45};
+ attributes.data_format = mir::DataFormat::NCHW;
+ auto *max_pool = g.create<mir::ops::MaxPool2DOp>(input->getOutput(0), attributes);
auto *output = g.create<mir::ops::OutputOp>(max_pool->getOutput(0));
@@ -210,10 +244,10 @@ TEST(TRANSFORMATIONS, Switcher_MaxPool2D_NCHW2NHWC)
switcher.run(&g);
- auto *trans_out = output->getInput(0)->getProducer()->getNode();
- auto *max_pool_ = trans_out->getInput(0)->getProducer()->getNode();
- auto *trans_in = max_pool_->getInput(0)->getProducer()->getNode();
- auto *input_ = trans_in->getInput(0)->getProducer()->getNode();
+ auto *trans_out = output->getInput(0)->getNode();
+ auto *max_pool_ = trans_out->getInput(0)->getNode();
+ auto *trans_in = max_pool_->getInput(0)->getNode();
+ auto *input_ = trans_in->getInput(0)->getNode();
ASSERT_EQ(trans_out->getType(), mir::Operation::Type::transpose);
ASSERT_NE(max_pool_, max_pool);
diff --git a/compiler/nnc/utils/CMakeLists.txt b/compiler/nnc/utils/CMakeLists.txt
index 32988669f..e5d96d005 100644
--- a/compiler/nnc/utils/CMakeLists.txt
+++ b/compiler/nnc/utils/CMakeLists.txt
@@ -1,9 +1,3 @@
-
-#
-# "tflite_model_generator" is a generator random TFLite models.
-#
-add_subdirectory(tflite_model_generator)
-
# dumpers of NN models
add_subdirectory(tflite_dot_dumper)
add_subdirectory(caffe_dot_dumper)
diff --git a/compiler/nnc/utils/caffe2_dot_dumper/CMakeLists.txt b/compiler/nnc/utils/caffe2_dot_dumper/CMakeLists.txt
index 881bab83a..0bde55394 100644
--- a/compiler/nnc/utils/caffe2_dot_dumper/CMakeLists.txt
+++ b/compiler/nnc/utils/caffe2_dot_dumper/CMakeLists.txt
@@ -2,5 +2,5 @@ if (NOT TARGET mir_caffe2_importer)
return()
endif()
-add_executable(caffe2_model_dumper ${OPTIONS_SRC} model_dump.cpp)
+add_executable(caffe2_model_dumper model_dump.cpp)
target_link_libraries(caffe2_model_dumper PRIVATE nnc_support mir_caffe2_importer)
diff --git a/compiler/nnc/utils/caffe2_dot_dumper/model_dump.cpp b/compiler/nnc/utils/caffe2_dot_dumper/model_dump.cpp
index 579e0fe19..13b5cfb37 100644
--- a/compiler/nnc/utils/caffe2_dot_dumper/model_dump.cpp
+++ b/compiler/nnc/utils/caffe2_dot_dumper/model_dump.cpp
@@ -15,7 +15,6 @@
*/
#include "support/CommandLine.h"
-#include "option/Options.h"
#include "mir/IrDotDumper.h"
#include <caffe2_importer.h>
@@ -28,13 +27,18 @@ using namespace mir;
int main(int argc, const char **argv)
{
- cli::CommandLine::getParser()->parseCommandLine(argc, argv, false);
+ cli::Option<std::string> predict_net(cli::optname("--predict-net"),
+ cli::overview("Path to the model"));
+ cli::Option<std::string> init_net(cli::optname("--init-net"),
+ cli::overview("Path to the weights"));
+ cli::Option<std::vector<int>> input_shape(cli::optname("--input-shape"),
+ cli::overview("Shape of the input"));
+ cli::CommandLine::getParser()->parseCommandLine(argc, argv);
try
{
// FIXME: caffe2 input shapes are not provided by model and must be set from cli
- mir_caffe2::Caffe2Importer importer{cli::inputFile, cli::initNet, {cli::inputShapes}};
- auto graph = importer.importModel();
+ auto graph = mir_caffe2::loadModel(predict_net, init_net, {input_shape});
dumpGraph(graph.get(), std::cout);
}
catch (std::exception &e)
diff --git a/compiler/nnc/utils/caffe_dot_dumper/CMakeLists.txt b/compiler/nnc/utils/caffe_dot_dumper/CMakeLists.txt
index fa33eec80..780dece20 100644
--- a/compiler/nnc/utils/caffe_dot_dumper/CMakeLists.txt
+++ b/compiler/nnc/utils/caffe_dot_dumper/CMakeLists.txt
@@ -2,5 +2,5 @@ if (NOT TARGET mir_caffe_importer)
return ()
endif()
-add_executable(caffe_model_dumper ${OPTIONS_SRC} model_dump.cpp)
+add_executable(caffe_model_dumper model_dump.cpp)
target_link_libraries(caffe_model_dumper PRIVATE nnc_support mir_caffe_importer)
diff --git a/compiler/nnc/utils/caffe_dot_dumper/model_dump.cpp b/compiler/nnc/utils/caffe_dot_dumper/model_dump.cpp
index f297690b7..6dab885f8 100644
--- a/compiler/nnc/utils/caffe_dot_dumper/model_dump.cpp
+++ b/compiler/nnc/utils/caffe_dot_dumper/model_dump.cpp
@@ -15,7 +15,6 @@
*/
#include "support/CommandLine.h"
-#include "option/Options.h"
#include "mir/IrDotDumper.h"
#include <caffe_importer.h>
@@ -28,12 +27,12 @@ using namespace mir;
int main(int argc, const char **argv)
{
- cli::CommandLine::getParser()->parseCommandLine(argc, argv, false);
+ cli::Option<std::string> model_path(cli::optname("--model"), cli::overview("Path to the model"));
+ cli::CommandLine::getParser()->parseCommandLine(argc, argv);
try
{
- mir_caffe::CaffeImporter importer{cli::inputFile};
- auto graph = importer.importModel();
+ auto graph = mir_caffe::loadModel(model_path);
dumpGraph(graph.get(), std::cout);
}
catch (std::exception &e)
diff --git a/compiler/nnc/utils/caffe_model_maker/AllFill.sh b/compiler/nnc/utils/caffe_model_maker/AllFill.sh
deleted file mode 100755
index 93e38d1d7..000000000
--- a/compiler/nnc/utils/caffe_model_maker/AllFill.sh
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/bin/sh
-: '
-Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-'
-
-
-#Fills all models and writes errors
-usage () {
- echo "Filler.sh should be in the working directory\nusage:
- no args - assumes current directory
- -d=<dir> fills models in <dir>
- Example:
- $(basename $0) -d='./foobar/'"
-}
-
-DIR="./"
-for i in "$@"
-do
- case $i in
- -h|--help|help)
- usage
- exit 1
- ;;
- -d=*)
- DIR=${i#*=}
- ;;
- esac
- shift
-done
-echo $DIR
-if [ $# -eq 0 ]; then
- echo "Assume working directory"
-fi
-for a in `ls $DIR*.prototxt`; do
- ./Filler.sh $a
-done 2>error.log
diff --git a/compiler/nnc/utils/caffe_model_maker/Filler.sh b/compiler/nnc/utils/caffe_model_maker/Filler.sh
deleted file mode 100755
index 963edbfb3..000000000
--- a/compiler/nnc/utils/caffe_model_maker/Filler.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/sh
-: '
-Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-'
-
-#Fills $1 with random weights
-if [ $# -eq 0 ]
- then
- echo "usage:\n $(basename $0) foo.prototxt"
- exit 1
-fi
-FN=$1
-NOEXT=${FN%%.*} # filename without the extension
-mkdir $NOEXT
-caffegen init < $FN > $NOEXT/filled.prototxt
-caffegen encode < $NOEXT/filled.prototxt > $NOEXT/model.caffemodel
diff --git a/compiler/nnc/utils/caffe_model_maker/GenerateCaffeModels.py b/compiler/nnc/utils/caffe_model_maker/GenerateCaffeModels.py
deleted file mode 100644
index ca8b3776a..000000000
--- a/compiler/nnc/utils/caffe_model_maker/GenerateCaffeModels.py
+++ /dev/null
@@ -1,722 +0,0 @@
-#!/usr/bin/python3
-"""
-Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-import caffe
-import numpy as np
-import sys
-import h5py
-from itertools import chain
-from caffe import layers as L
-import random
-import lmdb
-from collections import Counter, OrderedDict
-
-if (len(sys.argv) < 2):
- dest_folder = ''
- print('Using current directory as destination folder')
-else:
- dest_folder = sys.argv[1] + '/'
-
-
-class PH:
- """
- PlaceHolder value
- """
-
- def __init__(self, type, param):
- self.type = type
- self.param = param
-
-
-# Bookkeeping
-LS = 224
-# bynaryProto file for Infogain
-H = np.eye(3, dtype='f4')
-blob = caffe.io.array_to_blobproto(H.reshape((1, 1, 3, 3)))
-with open(dest_folder + 'infogainH.binaryproto', 'wb+') as f:
- f.write(blob.SerializeToString())
-
-# List of hdf5 files
-with open(dest_folder + "in", 'w+') as f:
- f.write('in.hdf5')
-
-#Window File
-with open(dest_folder + "in_winds", 'w+') as f:
- f.write("""# 1
-in.jpg
-3
-224
-224
-2
-1 0.1 50 50 60 70
-1 0.9 30 30 50 50
-# 2
-in.jpg
-3
-224
-224
-2
-1 0.1 50 50 70 70
-1 0.9 30 30 50 50
-""")
-
-# HDF5 file for HDF5DataSet
-h5f = h5py.File(dest_folder + "in.hdf5", "w")
-h5f.create_dataset("data", data=np.random.rand(1, 3, LS, LS))
-h5f.close()
-
-# LMDB file
-env = lmdb.open(dest_folder + 'test-lmdb')
-with env.begin(write=True) as txn:
- img_data = np.random.rand(3, LS, LS)
- datum = caffe.io.array_to_datum(img_data, label=1)
- txn.put('{:0>10d}'.format(1).encode('ascii'), datum.SerializeToString())
-env.close()
-
-# recurring parameters
-losspara = {'ignore_label': True, 'normalization': 1, 'normalize': True}
-softmaxpara = {'engine': 0, 'axis': 1}
-gdfil = {'type': 'gaussian', 'std': 0.001}
-cofil = {'type': 'constant', 'value': 0}
-rp = {
- 'num_output': 1,
- 'weight_filler': gdfil,
- 'bias_filler': cofil,
- 'expose_hidden': True
-}
-
-filler_par = {
- 'type': 'constant',
- 'value': 0,
- 'min': 0,
- 'max': 1,
- 'mean': 0,
- 'std': 1,
- 'sparse': -1, # -1 means no sparsification
- 'variance_norm': 0
-} # 0 = FAN_IN, 1 = FAN_OUT, 2 = AVERAGE
-
-OPS = [
- ('Parameter', {
- 'shape': {
- 'dim': [1]
- },
- "is_data": True
- }), # ok
- (
- 'Data',
- {
- 'source': 'test-lmdb', # FIXME: unknown DB backend
- 'batch_size': 1,
- 'rand_skip': 0,
- 'backend': 1, # 0 = LEVELDB, 1 = LMDB
- 'scale': 1.0, # deprecated in favor of TransformationParameter
- 'mean_file': 'wtf.is_that',
- 'crop_size': 0,
- 'mirror': False,
- 'force_encoded_color': False,
- 'prefetch': 4,
- "is_data": True
- }),
- (
- 'DummyData',
- {
- 'data_filler': cofil, # ok
- #'num' : [1,1,1], # deprecated shape specification
- #'channels' : [2,2,2],
- #'height' : [3,3,3],
- #'width' : [4,4,4]},
- 'shape': {
- 'dim': [1, 3, LS, LS]
- },
- "is_data": True
- }),
- (
- 'ImageData',
- {
- 'source': 'in_imgs', # file with list of imgs
- 'top': 'op2',
- 'batch_size': 1,
- 'rand_skip': 0,
- 'shuffle': False,
- 'new_height': 0,
- 'new_width': 0,
- 'is_color': True,
- 'root_folder': '',
- 'scale': 1.0, # deprecated in favor of TransformationParameter
- 'mirror': False,
- "is_data": True
- }),
- (
- 'WindowData',
- {
- 'source': 'in_winds',
- 'top': 'op2',
- 'batch_size': 1,
- 'mean_file': 'in.jpg',
- 'transform_param': {
- 'scale': 0.8,
- 'crop_size': 24,
- 'mirror': False,
- #'fg_treshold' : 0.5,
- #'bg_treshold' : 0.5,
- #'fg_fraction' : 0.25,
- },
- 'context_pad': 1,
- 'crop_mode': 'warp',
- 'cache_images': True,
- 'root_folder': './',
- "is_data": True
- }),
- (
- 'HDF5Data',
- {
- 'source': 'in', # This is the name of the file WITH HDF5 FILENAMES 0_0
- # Top should have the same name as the dataset in the hdf5 file
- # FIXME Requires Caffegen to be built with Caffe that supports LMDB
- 'batch_size': 1,
- 'shuffle': False,
- "is_data": True
- }),
- ('Input', {
- 'shape': {
- 'dim': [1, 2, 3, 4]
- },
- "is_data": True
- }), # ok
- (
- 'MemoryData',
- {
- 'batch_size': 1, # ok
- 'channels': 2,
- 'height': 3,
- 'width': 4,
- 'top': "foo",
- "is_data": True
- }),
-
- ## Regular OPS
- (
- "Convolution",
- {
- 'num_output': 64, # ok
- 'kernel_size': 9,
- 'stride': 1,
- 'pad': 0,
- 'weight_filler': gdfil,
- 'param': [{
- 'lr_mult': 1
- }, {
- 'lr_mult': 0.1
- }],
- 'bias_filler': cofil
- }),
-
- # Depthvise conv
- (
- "Convolution",
- {
- 'num_output': 12, # ok
- 'kernel_size': 9,
- 'stride': 1,
- 'dilation': 2,
- 'group': 3,
- 'pad': 0,
- 'weight_filler': gdfil,
- 'param': [{
- 'lr_mult': 1
- }, {
- 'lr_mult': 0.1
- }],
- 'bias_filler': cofil
- }),
- (
- "Deconvolution",
- {
- 'convolution_param': # ok
- {
- 'num_output': 4,
- 'kernel_size': 9,
- 'stride': 1,
- 'pad': 0,
- 'weight_filler': gdfil,
- 'bias_filler': cofil
- }
- }),
- # Depthvise deconv
- (
- "Deconvolution",
- {
- 'convolution_param': # ok
- {
- 'num_output': 12,
- 'kernel_size': 9,
- 'stride': 1,
- 'dilation': 2,
- 'group': 3,
- 'pad': 0,
- 'weight_filler': gdfil,
- 'bias_filler': cofil
- }
- }),
- (
- 'BatchNorm',
- {
- 'eps': 1e-5, # ok
- 'moving_average_fraction': 0.999
- }),
- (
- 'LRN',
- {
- 'alpha': 1., # ok
- 'beta': 0.75,
- 'norm_region': 1,
- 'local_size': 5,
- 'k': 1,
- 'engine': 0
- }),
- # local_size[default 5]: the number of channels to sum over
- # alpha[default 1]: the scaling paramete
- # beta[default5]: the exponent
- # norm_region[default ACROSS_CHANNLS]: whether to sum over adjacent channels(ACROSS_CHANNLS) or nearby
- # spatial locations(WITHIN_CHANNLS)
- # `input / (1 + (\alpha/n) \sum_i x_i^2)^\beta`
- (
- "MVN",
- {
- 'normalize_variance': True, # ok
- 'across_channels': False,
- 'eps': 1e-9
- }),
- (
- 'Im2col',
- {
- 'convolution_param': # ok
- {
- 'num_output': 64,
- 'kernel_size': 9,
- 'stride': 1,
- 'pad': 0,
- 'weight_filler': gdfil,
- # 'param' : [{'lr_mult':1},{'lr_mult':0.1}],
- 'bias_filler': cofil
- }
- }),
- ('Dropout', {
- 'dropout_ratio': 0.5
- }), # ok
- ('Split', {}), # ok
- ('Concat', {
- 'axis': 1
- }), # ok
- (
- 'Tile',
- {
- 'axis': 1, # ok
- 'tiles': 2
- }),
- ('Slice', {
- 'axis': 1,
- 'top': 'op2',
- 'slice_point': 1
- }),
- (
- 'Reshape',
- {
- 'shape': {
- 'dim': [1, 0, -1]
- }, # ok
- 'axis': 0,
- 'num_axes': -1
- }),
- # reshapes only [axis, axis + num_axes] if those aren't 0 and -1; axis can be negative
- # 0 in shape means retaining dim size, -1 means auto size
- (
- 'Flatten',
- {
- 'axis': 1, # ok
- 'end_axis': -1
- }),
- (
- 'Pooling',
- {
- 'pool': 0, # ok # pool: 0 = MAX, 1 = AVE, 2 = STOCHASTIC
- 'pad': 0, # can be replaced with pad_w, pad_h
- 'kernel_size': 3, # can be replaced with kernel_w, kernel_h
- 'stride': 1, # can be replaced with stride_w, stride_h
- 'engine': 0,
- 'global_pooling': False
- }),
- # 'round_mode' : 0}), # 0 = CELS, 1 = FLOOR
- (
- 'Reduction',
- {
- 'operation': 1, # ok # 1 = SUM, 2 = ASUM, 3 = SUMSQ, 4 = MEAN # ok
- 'axis': 0,
- 'coeff': 1.0
- }),
- (
- 'SPP',
- {
- 'pyramid_height': 1, # ok
- 'pool': 0,
- 'engine': 0
- }),
- (
- 'InnerProduct',
- {
- 'num_output': 2, # ok
- 'bias_term': True,
- 'weight_filler': filler_par,
- 'bias_filler': filler_par,
- 'axis': 1,
- 'transpose': False
- }),
- (
- 'Embed',
- {
- 'num_output': 2, # ok
- 'input_dim': 1,
- 'bias_term': True,
- 'weight_filler': filler_par,
- 'bias_filler': filler_par
- }),
- (
- 'ArgMax',
- {
- 'out_max_val': False, # ok # if True, outputs pairs (argmax, maxval) # ok
- 'top_k': 1,
- 'axis': -1
- }),
- (
- 'Softmax',
- {
- 'engine': 0, # ok
- 'axis': 1
- }),
- (
- 'ReLU',
- {
- 'negative_slope': 0, # ok
- 'engine': 0
- }),
- (
- 'PReLU',
- {
- 'filler': filler_par, # ok
- 'channel_shared': False
- }),
- ('ELU', {
- 'alpha': 1
- }), # ok
- ('Sigmoid', {
- 'engine': 0
- }), # ok
- ('BNLL', {}), # ok
- ('TanH', {
- 'engine': 0
- }), # ok
- ('Threshold', {
- 'threshold': 0
- }), # ok
- (
- 'Bias',
- {
- 'axis': 0, # ok
- 'num_axes': -1,
- 'filler': filler_par
- }),
- (
- 'Scale',
- {
- 'axis': 0, # ok
- 'num_axes': -1,
- 'filler': filler_par,
- 'bias_term': False,
- 'bias_filler': filler_par
- }),
- ('AbsVal', {}), # ok
- (
- 'Log',
- {
- 'base': -1.0, # ok
- 'scale': 1.0,
- 'shift': PH(float, (2.0, 10.0)),
- 'how_many': 10
- }), # y = ln(shift + scale * x) (log_base() for base > 0)
- (
- 'Power',
- {
- 'power': -1.0, # ok
- 'scale': 1.0,
- 'shift': 0.0
- }), # y = (shift + scale * x) ^ power
- (
- 'Exp',
- {
- 'base': -1.0, # ok
- 'scale': 1.0,
- 'shift': 0.0
- }),
-
- ## TWO INPUTS
- (
- 'Crop',
- {
- 'axis': 2, # ok
- 'offset': [0],
- "inputs": 2
- }), # if one offset - for all dims, more - specifies
- (
- "Eltwise",
- {
- 'operation': 1, # ok
- 'coeff': [3, 3],
- 'stable_prod_grad': True,
- "inputs": 2
- }),
- ("EuclideanLoss", {
- "inputs": 2
- }), # ok
- ("HingeLoss", {
- 'norm': 1,
- "inputs": 2
- }), # L1 = 1; L2 = 2; # ok
- ("SigmoidCrossEntropyLoss", {
- 'loss_param': losspara,
- "inputs": 2
- }), # ok
-
- ## TWO Inputs, special shape
- (
- "Accuracy",
- {
- 'top_k': 1, # FIXME: different bottom shapes needed
- 'axis': 0,
- 'ignore_label': 0,
- "inputs": 2,
- "special_shape": [1, 3, 1, 1]
- }),
- (
- "SoftmaxWithLoss",
- {
- 'loss_param': losspara, # FIXME: different bottom shapes needed
- 'softmax_param': softmaxpara,
- "inputs": 2,
- "special_shape": [1, 1, 1, 1]
- }),
- ("MultinomialLogisticLoss", {
- 'loss_param': losspara,
- "inputs": 2,
- "special_shape": [1, 1, 1, 1]
- }), # FIXME: different bottom shapes needed
- ("Filter", {
- "inputs": 2,
- "special_shape": [1, 1, 1, 1]
- }), # FIXME: different bottom shapes needed
- ('BatchReindex', {
- "inputs": 2,
- "special_shape": [2]
- }), # takes indices as second blob
- ("InfogainLoss", {
- 'source': 'infogainH.binaryproto',
- 'axis': 1,
- "inputs": 2,
- "special_shape": [1, 1, 1, 1]
- }),
- (
- 'Python',
- {
- 'python_param': # Custom Loss layer
- {
- 'module': 'Pyloss', # the module name -- usually the filename -- that needs to be in $PYTHONPATH
- 'layer': 'EuclideanLossLayer', # the layer name -- the class name in the module
- 'share_in_parallel': False
- },
- # set loss weight so Caffe knows this is a loss layer.
- # since PythonLayer inherits directly from Layer, this isn't automatically
- # known to Caffe
- 'loss_weight': 1,
- "inputs": 2,
- "special_shape": [1, 3, 1, 1]
- },
- ),
-
- ## NOTOP OPS
- ('HDF5Output', {
- 'file_name': 'out.hdf5',
- "inputs": 2,
- "is_notop": True
- }), # ok
- ('Silence', {
- "inputs": 2,
- "is_notop": True
- }), # ok, need to remove tops
-
- ## THREE INPUTS
- ("RNN", {
- 'recurrent_param': rp,
- 'top': "out2",
- "inputs": 3
- }), # ok
- ("Recurrent", {
- 'recurrent_param': rp,
- 'top': "out2",
- "inputs": 3
- }), # ok
-
- ## FOUR INPUTS
- ("LSTM", {
- 'recurrent_param': rp,
- 'top': ["out2", "out3"],
- "inputs": 4
- }), # ok
-
- ## Handled explicitly (special case)
- ("ContrastiveLoss", {
- 'margin': 1.0,
- 'legacy_version': False
- }),
-]
-
-#Helper functions
-
-
-def traverse(obj, callback=None):
- """
- walks a nested dict/list recursively
- :param obj:
- :param callback:
- :return:
- """
- if isinstance(obj, dict):
- value = {k: traverse(v, callback) for k, v in obj.items()}
- elif isinstance(obj, list):
- value = [traverse(elem, callback) for elem in obj]
- else:
- value = obj
-
- if callback is None:
- return value
- else:
- return callback(value)
-
-
-def mock(inp):
- if not (isinstance(inp, PH)): return inp
- if inp.type == int:
- return random.randint(*inp.param)
- if inp.type == float:
- return random.uniform(*inp.param)
-
-
-EXTRA_SHAPES = \
- [(), # alredy defined
- [1, 3],
- [1, 3, 1],
- [1, 3, 1]]
-
-
-class Layer:
- """
- Represents a caffe layer
- """
-
- def __init__(self, name, params):
- self.name = name
- self.args = params
- if self.args == None: self.args = dict()
- self.num_inp = self.args.pop("inputs", 1)
- self.num_out = self.args.pop("outputs", 1)
- self.special_shape = self.args.pop("special_shape",
- False) # 2nd input has special shape
- self.is_data = self.args.pop("is_data", False)
- self.is_notop = self.args.pop("is_notop", False)
-
- def make_net(self):
- """
- Creates a protobuf network
- :return:
- """
- net = caffe.NetSpec()
-
- if self.is_data:
- net.data = getattr(L, self.name)(**self.args)
-
- # Very special,
- elif self.name == "ContrastiveLoss":
- net.data = L.Input(shape={'dim': [1, 4]})
- net.data1 = L.DummyData(data_filler=cofil, shape={'dim': [1, 4]})
- net.data2 = L.DummyData(data_filler=cofil, shape={'dim': [1, 1]})
-
- net.op = getattr(L, self.name)(net.data, net.data1, net.data2, **self.args)
-
- # this covers most cases
- else:
- net.data = L.Input(shape={'dim': [1, 3, LS, LS]})
- if self.num_inp == 2:
- net.data1 = L.DummyData(data_filler=cofil, shape={'dim': [1, 3, LS, LS]})
- elif self.num_inp > 2:
- for i in range(1, self.num_inp):
- setattr(
- net, "data" + str(i),
- L.DummyData(data_filler=cofil, shape={'dim': EXTRA_SHAPES[i]}))
- if self.special_shape:
- net.data = L.Input(shape={'dim': [1, 3, 1, 1]})
- net.data1 = L.DummyData(
- data_filler=cofil, shape={'dim': self.special_shape})
-
- net.op = getattr(L, self.name)(
- net.data,
- *[getattr(net, "data" + str(i))
- for i in range(1, self.num_inp)], **self.args)
-
- if self.is_notop:
- net.op.fn.tops = OrderedDict()
- net.op.fn.ntop = 0 # the messing about in question
-
- return net
-
-
-class LayerMaker:
- """
- Factory class for Layer
- """
-
- def __init__(self, params):
- self.name, self.args = params
- self.how_many = self.args.pop("how_many", 1)
-
- def make(self):
- return [Layer(self.name, traverse(self.args, mock)) for i in range(self.how_many)]
-
-
-layer_gen = chain(*map(lambda para: LayerMaker(para).make(), OPS))
-
-filename = dest_folder + '{}_{}.prototxt'
-
-counter = Counter()
-for layer in layer_gen:
- n = layer.make_net()
- counter[layer.name] += 1
-
- with open(filename.format(layer.name, counter[layer.name] - 1), 'w+') as ptxt_file:
- print(n.to_proto(), file=ptxt_file)
-
- if layer.name == "Python": # Special case for python layer
- with open("Python_0.caffemodel", 'wb+') as caffemodelFile:
- caffemodelFile.write(n.to_proto().SerializeToString())
diff --git a/compiler/nnc/utils/caffe_model_maker/Pyloss.py b/compiler/nnc/utils/caffe_model_maker/Pyloss.py
deleted file mode 100644
index e3f781759..000000000
--- a/compiler/nnc/utils/caffe_model_maker/Pyloss.py
+++ /dev/null
@@ -1,83 +0,0 @@
-"""
-COPYRIGHT
-
-All contributions by the University of California:
-Copyright (c) 2014-2017 The Regents of the University of California (Regents)
-All rights reserved.
-
-All other contributions:
-Copyright (c) 2014-2017, the respective contributors
-All rights reserved.
-
-Caffe uses a shared copyright model: each contributor holds copyright over
-their contributions to Caffe. The project versioning records all such
-contribution and copyright details. If a contributor wants to further mark
-their specific copyright on a particular contribution, they should indicate
-their copyright solely in the commit message of the change when it is
-committed.
-
-LICENSE
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-CONTRIBUTION AGREEMENT
-
-By contributing to the BVLC/caffe repository through pull-request, comment,
-or otherwise, the contributor releases their content to the
-license and copyright terms herein.
-"""
-import caffe
-import numpy as np
-
-
-class EuclideanLossLayer(caffe.Layer):
- """
- Compute the Euclidean Loss in the same manner as the C++ EuclideanLossLayer
- to demonstrate the class interface for developing layers in Python.
- """
-
- def setup(self, bottom, top):
- # check input pair
- if len(bottom) != 2:
- raise Exception("Need two inputs to compute distance.")
-
- def reshape(self, bottom, top):
- # check input dimensions match
- if bottom[0].count != bottom[1].count:
- raise Exception("Inputs must have the same dimension.")
- # difference is shape of inputs
- self.diff = np.zeros_like(bottom[0].data, dtype=np.float32)
- # loss output is scalar
- top[0].reshape(1)
-
- def forward(self, bottom, top):
- self.diff[...] = bottom[0].data - bottom[1].data
- top[0].data[...] = np.sum(self.diff**2) / bottom[0].num / 2.
-
- def backward(self, top, propagate_down, bottom):
- for i in range(2):
- if not propagate_down[i]:
- continue
- if i == 0:
- sign = 1
- else:
- sign = -1
- bottom[i].diff[...] = sign * self.diff / bottom[i].num
diff --git a/compiler/nnc/utils/caffe_model_maker/README.md b/compiler/nnc/utils/caffe_model_maker/README.md
deleted file mode 100644
index e34a769a0..000000000
--- a/compiler/nnc/utils/caffe_model_maker/README.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# Utils
-Caffe model generation helpers
-
-REQUIRES:
-
-* caffe
-* h5py
-* lmdb
-* numpy
-* caffegen in `$PATH`
-
-`GenerateCaffeModels.py` creates `*.prototxt` files for 1 and 2 layer caffe models
-The generator can create multiple examples of any layer, assuming you add a
-`how_many` field into the layer's dict. You will also need to replace the constants in said dict with `PH(type, param)` values, where `type` is the type of the placeholder variable
-and `params` is a list (or tuple) of paramenters for generating the mock.
-
-For an example of generating multiple instances of a layer see the `Log` layer.
-
-`Filler.sh` fills a single model with random weights by using `caffegen` and creates a dir with a filled `prototxt` and a `caffemodel` binary file. The result directory is located in the same directory as the `prototxt` file
-
-`AllFill.sh` fills all `*.prototxt` files in the current directory or in provided directory
-(-d)
diff --git a/compiler/nnc/utils/caffe_model_maker/in.jpg b/compiler/nnc/utils/caffe_model_maker/in.jpg
deleted file mode 100644
index 9eeef5ddb..000000000
--- a/compiler/nnc/utils/caffe_model_maker/in.jpg
+++ /dev/null
Binary files differ
diff --git a/compiler/nnc/utils/infer_tests/README.md b/compiler/nnc/utils/infer_tests/README.md
index 1f5d3f62c..aa6dd5f58 100644
--- a/compiler/nnc/utils/infer_tests/README.md
+++ b/compiler/nnc/utils/infer_tests/README.md
@@ -6,4 +6,4 @@ infer_testcases.py: run inference with `nnkit` on testcases
res2bin.py: used by infer_testcases.py to convert resulting hdf5 to binary format
'testcases' folder structure:
-At the moment we use the following structure: a folder for a model contains 'models' and 'testcases' subfolders. The 'models' subfolder contains model that we run inference on, 'testcases' subfolder contains a 'testcase*' folder for each different testcase. Each of those folders in turn contain 'input' with a '.JPEG' file (and '.hdf5' and '.dat' files after running `jpeg2hdf5` script), and 'output' folder where inference results are stored. \ No newline at end of file
+At the moment we use the following structure: a folder for a model contains 'models' and 'testcases' subfolders. The 'models' subfolder contains model that we run inference on, 'testcases' subfolder contains a 'testcase*' folder for each different testcase. Each of those folders in turn contain 'input' with a '.JPEG' file (and '.hdf5' and '.dat' files after running `jpeg2hdf5` script), and 'output' folder where inference results are stored.
diff --git a/compiler/nnc/utils/infer_tests/infer_testcases.py b/compiler/nnc/utils/infer_tests/infer_testcases.py
index fab887c1a..fab887c1a 100644..100755
--- a/compiler/nnc/utils/infer_tests/infer_testcases.py
+++ b/compiler/nnc/utils/infer_tests/infer_testcases.py
diff --git a/compiler/nnc/utils/infer_tests/res2bin.py b/compiler/nnc/utils/infer_tests/res2bin.py
index 0c21848d9..0c21848d9 100644..100755
--- a/compiler/nnc/utils/infer_tests/res2bin.py
+++ b/compiler/nnc/utils/infer_tests/res2bin.py
diff --git a/compiler/nnc/utils/input_gen/tensor_gen.cpp b/compiler/nnc/utils/input_gen/tensor_gen.cpp
index a9a2f5d5c..04798c8fb 100644
--- a/compiler/nnc/utils/input_gen/tensor_gen.cpp
+++ b/compiler/nnc/utils/input_gen/tensor_gen.cpp
@@ -145,8 +145,6 @@ static void dumpTensor(Tensor &tensor)
iterate(tensor, on_loop);
}
-static vector<hsize_t> dimensions;
-
static void writeTensorToDatFile(const string &file_name, Tensor &tensor)
{
ofstream of(file_name + ".dat", ios_base::binary);
@@ -157,8 +155,8 @@ static void writeTensorToDatFile(const string &file_name, Tensor &tensor)
of.write(reinterpret_cast<char *>(tensor.data()), tensor.numElems() * sizeof(float));
}
-static void writeTensorToHDF5File(const string &tensor_name, const string &file_name,
- Tensor &tensor)
+static void writeTensorToHDF5File(const vector<hsize_t> &dimensions, const string &tensor_name,
+ const string &file_name, Tensor &tensor)
{
H5::H5File h5File(file_name + ".hdf5", H5F_ACC_TRUNC);
H5::DataSpace dataspace(dimensions.size(), &dimensions[0]);
@@ -176,6 +174,8 @@ int main(int argc, char *argv[])
return 1;
}
+ vector<hsize_t> dimensions;
+
for (int i = 3; i < argc; ++i)
{
try
@@ -205,7 +205,7 @@ int main(int argc, char *argv[])
Tensor caffe_tensor(dimensions);
fillTensor(caffe_tensor);
- writeTensorToHDF5File(argv[1], "in_" + string(argv[2]) + "_caffe", caffe_tensor);
+ writeTensorToHDF5File(dimensions, argv[1], "in_" + string(argv[2]) + "_caffe", caffe_tensor);
vector<hsize_t> tf_reshape{0, 2, 3, 1};
Tensor tf_tensor = caffe_tensor.transpose(tf_reshape);
diff --git a/compiler/nnc/utils/model_runner/common_place.py b/compiler/nnc/utils/model_runner/common_place.py
index eb8953455..eb8953455 100644..100755
--- a/compiler/nnc/utils/model_runner/common_place.py
+++ b/compiler/nnc/utils/model_runner/common_place.py
diff --git a/compiler/nnc/utils/model_runner/model_runner_caffe.py b/compiler/nnc/utils/model_runner/model_runner_caffe.py
index a2e94272c..a2e94272c 100644..100755
--- a/compiler/nnc/utils/model_runner/model_runner_caffe.py
+++ b/compiler/nnc/utils/model_runner/model_runner_caffe.py
diff --git a/compiler/nnc/utils/model_runner/model_runner_caffe2.py b/compiler/nnc/utils/model_runner/model_runner_caffe2.py
index 0c8feca92..0c8feca92 100644..100755
--- a/compiler/nnc/utils/model_runner/model_runner_caffe2.py
+++ b/compiler/nnc/utils/model_runner/model_runner_caffe2.py
diff --git a/compiler/nnc/utils/model_runner/model_runner_onnx.py b/compiler/nnc/utils/model_runner/model_runner_onnx.py
index 6e0e8a657..6e0e8a657 100644..100755
--- a/compiler/nnc/utils/model_runner/model_runner_onnx.py
+++ b/compiler/nnc/utils/model_runner/model_runner_onnx.py
diff --git a/compiler/nnc/utils/model_runner/model_runner_tflite.py b/compiler/nnc/utils/model_runner/model_runner_tflite.py
index 80847b7df..80847b7df 100644..100755
--- a/compiler/nnc/utils/model_runner/model_runner_tflite.py
+++ b/compiler/nnc/utils/model_runner/model_runner_tflite.py
diff --git a/compiler/nnc/utils/prepare_inputs/README.md b/compiler/nnc/utils/prepare_inputs/README.md
index dcd5899a1..c11759ee3 100644
--- a/compiler/nnc/utils/prepare_inputs/README.md
+++ b/compiler/nnc/utils/prepare_inputs/README.md
@@ -5,4 +5,4 @@ Note that these scripts are just development artifacts and are not supposed to g
jpeg2hdf5.py: prepare '.hdf5' files from '.JPEG' to be used by nnkit. Can also convert those '.JPEG's to binary format along the way.
'testcases' folder structure:
-At the moment we use the following structure: a folder for a model contains 'models' and 'testcases' subfolders. The 'models' subfolder contains model that we run inference on, 'testcases' subfolder contains a 'testcase*' folder for each different testcase. Each of those folders in turn contain 'input' with a '.JPEG' file (and '.hdf5' and '.dat' files after running `jpeg2hdf5` script), and 'output' folder where inference results are stored. \ No newline at end of file
+At the moment we use the following structure: a folder for a model contains 'models' and 'testcases' subfolders. The 'models' subfolder contains model that we run inference on, 'testcases' subfolder contains a 'testcase*' folder for each different testcase. Each of those folders in turn contain 'input' with a '.JPEG' file (and '.hdf5' and '.dat' files after running `jpeg2hdf5` script), and 'output' folder where inference results are stored.
diff --git a/compiler/nnc/utils/prepare_inputs/jpeg2hdf5.py b/compiler/nnc/utils/prepare_inputs/jpeg2hdf5.py
index 54f12b062..54f12b062 100644..100755
--- a/compiler/nnc/utils/prepare_inputs/jpeg2hdf5.py
+++ b/compiler/nnc/utils/prepare_inputs/jpeg2hdf5.py
diff --git a/compiler/nnc/utils/tflite_dot_dumper/CMakeLists.txt b/compiler/nnc/utils/tflite_dot_dumper/CMakeLists.txt
index 13667a4c9..f5e894e67 100644
--- a/compiler/nnc/utils/tflite_dot_dumper/CMakeLists.txt
+++ b/compiler/nnc/utils/tflite_dot_dumper/CMakeLists.txt
@@ -2,6 +2,6 @@ if (NOT TARGET mir_tflite_importer)
return ()
endif()
-add_executable(tflite_model_dumper model_dump.cpp ${OPTIONS_SRC})
+add_executable(tflite_model_dumper model_dump.cpp)
target_link_libraries(tflite_model_dumper PRIVATE nnc_support mir_tflite_importer)
diff --git a/compiler/nnc/utils/tflite_dot_dumper/model_dump.cpp b/compiler/nnc/utils/tflite_dot_dumper/model_dump.cpp
index 971bddd6e..ab264c82f 100644
--- a/compiler/nnc/utils/tflite_dot_dumper/model_dump.cpp
+++ b/compiler/nnc/utils/tflite_dot_dumper/model_dump.cpp
@@ -15,7 +15,6 @@
*/
#include "support/CommandLine.h"
-#include "option/Options.h"
#include "mir/IrDotDumper.h"
#include <tflite_importer.h>
@@ -28,12 +27,12 @@ using namespace mir;
int main(int argc, const char **argv)
{
- cli::CommandLine::getParser()->parseCommandLine(argc, argv, false);
+ cli::Option<std::string> model_path(cli::optname("--model"), cli::overview("Path to the model"));
+ cli::CommandLine::getParser()->parseCommandLine(argc, argv);
try
{
- mir_tflite::TfliteImporter importer{cli::inputFile};
- auto graph = importer.importModel();
+ auto graph = mir_tflite::loadModel(model_path);
dumpGraph(graph.get(), std::cout);
}
catch (std::exception &e)
diff --git a/compiler/nnc/utils/tflite_model_generator/CMakeLists.txt b/compiler/nnc/utils/tflite_model_generator/CMakeLists.txt
deleted file mode 100644
index 934f9fb34..000000000
--- a/compiler/nnc/utils/tflite_model_generator/CMakeLists.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-find_package(HDF5 COMPONENTS CXX QUIET)
-if(NOT HDF5_FOUND)
- message(WARNING "HDF5 not found, tflite_model_generator not available")
- return()
-endif(NOT HDF5_FOUND)
-
-nncc_find_package(FlatBuffers QUIET)
-if(NOT FlatBuffers_FOUND)
- message(WARNING "FlatBuffers not found, tflite_model_generator not available")
- return()
-endif(NOT FlatBuffers_FOUND)
-
-if (NOT TARGET tflite_schema)
- message(WARNING "tflite_schema not found, tflite_model_generator not available")
- return()
-endif (NOT TARGET tflite_schema)
-
-set(TFLITE_MODEL_GENERATOR_SOURCES "ModelGenerator.cpp" "TFLiteRandomModelBuilder.cpp" "Tree.cpp")
-
-add_executable(tflite_model_generator ${TFLITE_MODEL_GENERATOR_SOURCES})
-
-target_link_libraries(tflite_model_generator PRIVATE flatbuffers)
-target_link_libraries(tflite_model_generator PRIVATE tflite_schema)
-target_link_libraries(tflite_model_generator PRIVATE ${HDF5_CXX_LIBRARIES})
-
-target_include_directories(tflite_model_generator PRIVATE ${HDF5_INCLUDE_DIRS})
diff --git a/compiler/nnc/utils/tflite_model_generator/ModelGenerator.cpp b/compiler/nnc/utils/tflite_model_generator/ModelGenerator.cpp
deleted file mode 100644
index fc42d793f..000000000
--- a/compiler/nnc/utils/tflite_model_generator/ModelGenerator.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <iostream>
-#include <string>
-#include <memory>
-
-#include "Tree.h"
-
-#include "RandomModelBuilder.h"
-#include "TFLiteRandomModelBuilder.h"
-
-using namespace modelgen;
-using namespace treebuilder;
-
-int main(int argc, const char *argv[])
-{
- std::unique_ptr<TreeBuilder> tree_builder(new TreeBuilder);
- auto tree = tree_builder->buildTree();
-
- std::unique_ptr<RandomModelBuilder> builder(new TFLiteRandomModelBuilder);
- builder->convertTreeToModel(tree.get());
-
- std::unique_ptr<ModelSaver> saver = builder->createModelSaver();
- saver->saveModel();
-
- return 0;
-}
diff --git a/compiler/nnc/utils/tflite_model_generator/Operator.def b/compiler/nnc/utils/tflite_model_generator/Operator.def
deleted file mode 100644
index 4a3c373e9..000000000
--- a/compiler/nnc/utils/tflite_model_generator/Operator.def
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * @Name -- 'char *' string with name of operator.
- * @OpCode -- operator ID form enum.
- * @CreatorFunc -- method of the RandomModelBuilder class for creation this operator.
- * DEF_OPERATOR(Name, OpCode, CreatorFunc)
- *
- * Comments in this file is operations which are recommended for implementation.
- * @todo: support not supported among the following set.
- *
- */
-
-#ifndef DEF_OPERATOR
-#define DEF_OPERATOR(Name, OpCode, TFLiteOpCode, CreatorFunc)
-#endif
-/*
-DEF_OPERATOR("ADD", Op_ADD, tflite::BuiltinOperator::BuiltinOperator_ADD, createLayerADD )
-*/
-DEF_OPERATOR("CONV_2D", OpConv2d, tflite::BuiltinOperator::BuiltinOperator_CONV_2D, createLayerCONV_2D )
-DEF_OPERATOR("CONCATENATION", OpConcatenation, tflite::BuiltinOperator::BuiltinOperator_CONCATENATION, createLayerCONCATENATION )
-DEF_OPERATOR("DEPTHWISE_CONV_2D", OpDepthwiseConv2d, tflite::BuiltinOperator::BuiltinOperator_DEPTHWISE_CONV_2D, createLayerDEPTHWISE_CONV_2D )
-DEF_OPERATOR("MAX_POOL_2D", OpMaxPool2d, tflite::BuiltinOperator::BuiltinOperator_MAX_POOL_2D, createLayerX_POOL_2D )
-DEF_OPERATOR("AVERAGE_POOL_2D", OpAveragePool2d, tflite::BuiltinOperator::BuiltinOperator_AVERAGE_POOL_2D, createLayerX_POOL_2D )
-DEF_OPERATOR("SOFTMAX", OpSoftmax, tflite::BuiltinOperator::BuiltinOperator_SOFTMAX, createLayerSOFTMAX )
-DEF_OPERATOR("FULLY_CONNECTED", OpFullyConnected, tflite::BuiltinOperator::BuiltinOperator_FULLY_CONNECTED, createLayerFULLY_CONNECTED )
-/*
-DEF_OPERATOR("DEQUANTIZE", Op_DEQUANTIZE, tflite::BuiltinOperator::BuiltinOperator_DEQUANTIZE, createLayerDEQUANTIZE )
-DEF_OPERATOR("EMBEDDING_LOOKUP", Op_EMBEDDING_LOOKUP, tflite::BuiltinOperator::BuiltinOperator_EMBEDDING_LOOKUP, createLayerEMBEDDING_LOOKUP )
-DEF_OPERATOR("FLOOR", Op_FLOOR, tflite::BuiltinOperator::BuiltinOperator_FLOOR, createLayerFLOOR )
-DEF_OPERATOR("HASHTABLE_LOOKUP", Op_HASHTABLE_LOOKUP, tflite::BuiltinOperator::BuiltinOperator_HASHTABLE_LOOKUP, createLayerHASHTABLE_LOOKUP )
-DEF_OPERATOR("L2_NORMALIZATION", Op_L2_NORMALIZATION, tflite::BuiltinOperator::BuiltinOperator_L2_NORMALIZATION, createLayerL2_NORMALIZATION )
-DEF_OPERATOR("L2_POOL_2D", Op_L2_POOL_2D, tflite::BuiltinOperator::BuiltinOperator_L2_POOL_2D, createLayerL2_POOL_2D )
-DEF_OPERATOR("LOCAL_RESPONSE_NORMALIZATION", Op_LOCAL_RESPONSE_NORMALIZATION,tflite::BuiltinOperator::BuiltinOperator_LOCAL_RESPONSE_NORMALIZATION, createLayerLOCAL_RESPONSE_NORMALIZATION)
-DEF_OPERATOR("LOGISTIC", Op_LOGISTIC, tflite::BuiltinOperator::BuiltinOperator_LOGISTIC, createLayerLOGISTIC )
-DEF_OPERATOR("LSH_PROJECTION", Op_LSH_PROJECTION, tflite::BuiltinOperator::BuiltinOperator_LSH_PROJECTION, createLayerLSH_PROJECTION )
-DEF_OPERATOR("LSTM", Op_LSTM, tflite::BuiltinOperator::BuiltinOperator_LSTM, createLayerLSTM )
-DEF_OPERATOR("MUL", Op_MUL, tflite::BuiltinOperator::BuiltinOperator_MUL, createLayerMUL )
-DEF_OPERATOR("RELU", Op_RELU, tflite::BuiltinOperator::BuiltinOperator_RELU, createLayerRELU )
-DEF_OPERATOR("RELU_N1_TO_1", Op_RELU_N1_TO_1, tflite::BuiltinOperator::BuiltinOperator_RELU_N1_TO_1, createLayerRELU_N1_TO_1 )
-DEF_OPERATOR("RELU6", Op_RELU6, tflite::BuiltinOperator::BuiltinOperator_RELU6, createLayerRELU6 )
-DEF_OPERATOR("RESHAPE", Op_RESHAPE, tflite::BuiltinOperator::BuiltinOperator_RESHAPE, createLayerRESHAPE )
-DEF_OPERATOR("RESIZE_BILINEAR", Op_RESIZE_BILINEAR, tflite::BuiltinOperator::BuiltinOperator_RESIZE_BILINEAR, createLayerRESIZE_BILINEAR )
-DEF_OPERATOR("RNN", Op_RNN, tflite::BuiltinOperator::BuiltinOperator_RNN, createLayerRNN )
-DEF_OPERATOR("SPACE_TO_DEPTH", Op_SPACE_TO_DEPTH, tflite::BuiltinOperator::BuiltinOperator_SPACE_TO_DEPTH, createLayerSPACE_TO_DEPTH )
-DEF_OPERATOR("SVDF", Op_SVDF, tflite::BuiltinOperator::BuiltinOperator_SVDF, createLayerSVDF )
-DEF_OPERATOR("TANH", Op_TANH, tflite::BuiltinOperator::BuiltinOperator_TANH, createLayerTANH )
-DEF_OPERATOR("CONCAT_EMBEDDINGS", Op_CONCAT_EMBEDDINGS, tflite::BuiltinOperator::BuiltinOperator_CONCAT_EMBEDDINGS, createLayerCONCAT_EMBEDDINGS )
-DEF_OPERATOR("SKIP_GRAM", Op_SKIP_GRAM, tflite::BuiltinOperator::BuiltinOperator_SKIP_GRAM, createLayerSKIP_GRAM )
-DEF_OPERATOR("CALL", Op_CALL, tflite::BuiltinOperator::BuiltinOperator_CALL, createLayerCALL )
-DEF_OPERATOR("CUSTOM", Op_CUSTOM, tflite::BuiltinOperator::BuiltinOperator_CUSTOM, createLayerCUSTOM )
-DEF_OPERATOR("EMBEDDING_LOOKUP_SPARSE", Op_EMBEDDING_LOOKUP_SPARSE, tflite::BuiltinOperator::BuiltinOperator_EMBEDDING_LOOKUP_SPARSE, createLayerEMBEDDING_LOOKUP_SPARSE )
-DEF_OPERATOR("PAD", Op_PAD, tflite::BuiltinOperator::BuiltinOperator_PAD, createLayerPAD )
-DEF_OPERATOR("UNIDIRECTIONAL_SEQUENCE_RNN", Op_UNIDIRECTIONAL_SEQUENCE_RNN, tflite::BuiltinOperator::BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_RNN, createLayerUNIDIRECTIONAL_SEQUENCE_RNN )
-DEF_OPERATOR("GATHER", Op_GATHER, tflite::BuiltinOperator::BuiltinOperator_GATHER, createLayerGATHER )
-DEF_OPERATOR("BATCH_TO_SPACE_ND", Op_BATCH_TO_SPACE_ND, tflite::BuiltinOperator::BuiltinOperator_BATCH_TO_SPACE_ND, createLayerBATCH_TO_SPACE_ND )
-DEF_OPERATOR("SPACE_TO_BATCH_ND", Op_SPACE_TO_BATCH_ND, tflite::BuiltinOperator::BuiltinOperator_SPACE_TO_BATCH_ND, createLayerSPACE_TO_BATCH_ND )
-DEF_OPERATOR("TRANSPOSE", Op_TRANSPOSE, tflite::BuiltinOperator::BuiltinOperator_TRANSPOSE, createLayerTRANSPOSE )
-DEF_OPERATOR("MEAN", Op_MEAN, tflite::BuiltinOperator::BuiltinOperator_MEAN, createLayerMEAN )
-DEF_OPERATOR("SUB", Op_SUB, tflite::BuiltinOperator::BuiltinOperator_SUB, createLayerSUB )
-DEF_OPERATOR("DIV", Op_DIV, tflite::BuiltinOperator::BuiltinOperator_DIV, createLayerDIV )
-DEF_OPERATOR("SQUEEZE", Op_SQUEEZE, tflite::BuiltinOperator::BuiltinOperator_SQUEEZE, createLayerSQUEEZE )
-DEF_OPERATOR("UNIDIRECTIONAL_SEQUENCE_LSTM", Op_UNIDIRECTIONAL_SEQUENCE_LSTM,tflite::BuiltinOperator::BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM, createLayerUNIDIRECTIONAL_SEQUENCE_LSTM)
-DEF_OPERATOR("STRIDED_SLICE", Op_STRIDED_SLICE, tflite::BuiltinOperator::BuiltinOperator_STRIDED_SLICE, createLayerSTRIDED_SLICE )
-DEF_OPERATOR("BIDIRECTIONAL_SEQUENCE_RNN", Op_BIDIRECTIONAL_SEQUENCE_RNN, tflite::BuiltinOperator::BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN, createLayerBIDIRECTIONAL_SEQUENCE_RNN )
-DEF_OPERATOR("EXP", Op_EXP, tflite::BuiltinOperator::BuiltinOperator_EXP, createLayerEXP )
-DEF_OPERATOR("TOPK_V2", Op_TOPK_V2, tflite::BuiltinOperator::BuiltinOperator_TOPK_V2, createLayerTOPK_V2 )
-DEF_OPERATOR("SPLIT", Op_SPLIT, tflite::BuiltinOperator::BuiltinOperator_SPLIT, createLayerSPLIT )
-DEF_OPERATOR("LOG_SOFTMAX", Op_LOG_SOFTMAX, tflite::BuiltinOperator::BuiltinOperator_LOG_SOFTMAX, createLayerLOG_SOFTMAX )
-DEF_OPERATOR("DELEGATE", Op_DELEGATE, tflite::BuiltinOperator::BuiltinOperator_DELEGATE, createLayerDELEGATE )
-DEF_OPERATOR("BIDIRECTIONAL_SEQUENCE_LSTM", Op_BIDIRECTIONAL_SEQUENCE_LSTM, tflite::BuiltinOperator::BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM, createLayerBIDIRECTIONAL_SEQUENCE_LSTM )
-DEF_OPERATOR("CAST", Op_CAST, tflite::BuiltinOperator::BuiltinOperator_CAST, createLayerCAST )
-DEF_OPERATOR("PRELU", Op_PRELU, tflite::BuiltinOperator::BuiltinOperator_PRELU, createLayerPRELU )
-DEF_OPERATOR("MAXIMUM", Op_MAXIMUM, tflite::BuiltinOperator::BuiltinOperator_MAXIMUM, createLayerMAXIMUM )
-DEF_OPERATOR("ARG_MAX", Op_ARG_MAX, tflite::BuiltinOperator::BuiltinOperator_ARG_MAX, createLayerARG_MAX )
-DEF_OPERATOR("MINIMUM", Op_MINIMUM, tflite::BuiltinOperator::BuiltinOperator_MINIMUM, createLayerMINIMUM )
-DEF_OPERATOR("LESS", Op_LESS, tflite::BuiltinOperator::BuiltinOperator_LESS, createLayerLESS )
-DEF_OPERATOR("NEG", Op_NEG, tflite::BuiltinOperator::BuiltinOperator_NEG, createLayerNEG )
-DEF_OPERATOR("PADV2", Op_PADV2, tflite::BuiltinOperator::BuiltinOperator_PADV2, createLayerPADV2 )
-DEF_OPERATOR("GREATER", Op_GREATER, tflite::BuiltinOperator::BuiltinOperator_GREATER, createLayerGREATER )
-DEF_OPERATOR("GREATER_EQUAL", Op_GREATER_EQUAL, tflite::BuiltinOperator::BuiltinOperator_GREATER_EQUAL, createLayerGREATER_EQUAL )
-DEF_OPERATOR("LESS_EQUAL", Op_LESS_EQUAL, tflite::BuiltinOperator::BuiltinOperator_LESS_EQUAL, createLayerLESS_EQUAL )
-DEF_OPERATOR("SELECT", Op_SELECT, tflite::BuiltinOperator::BuiltinOperator_SELECT, createLayerSELECT )
-DEF_OPERATOR("SLICE", Op_SLICE, tflite::BuiltinOperator::BuiltinOperator_SLICE, createLayerSLICE )
-DEF_OPERATOR("SIN", Op_SIN, tflite::BuiltinOperator::BuiltinOperator_SIN, createLayerSIN )
-DEF_OPERATOR("TRANSPOSE_CONV", Op_TRANSPOSE_CONV, tflite::BuiltinOperator::BuiltinOperator_TRANSPOSE_CONV, createLayerTRANSPOSE_CONV )
-DEF_OPERATOR("SPARSE_TO_DENSE", Op_SPARSE_TO_DENSE, tflite::BuiltinOperator::BuiltinOperator_SPARSE_TO_DENSE, createLayerSPARSE_TO_DENSE )
-DEF_OPERATOR("TILE", Op_TILE, tflite::BuiltinOperator::BuiltinOperator_TILE, createLayerTILE )
-DEF_OPERATOR("EXPAND_DIMS", Op_EXPAND_DIMS, tflite::BuiltinOperator::BuiltinOperator_EXPAND_DIMS, createLayerEXPAND_DIMS )
-DEF_OPERATOR("EQUAL", Op_EQUAL, tflite::BuiltinOperator::BuiltinOperator_EQUAL, createLayerEQUAL )
-DEF_OPERATOR("NOT_EQUAL", Op_NOT_EQUAL, tflite::BuiltinOperator::BuiltinOperator_NOT_EQUAL, createLayerNOT_EQUAL )
-DEF_OPERATOR("LOG", Op_LOG, tflite::BuiltinOperator::BuiltinOperator_LOG, createLayerLOG )
-DEF_OPERATOR("SUM", Op_SUM, tflite::BuiltinOperator::BuiltinOperator_SUM, createLayerSUM )
-DEF_OPERATOR("SQRT", Op_SQRT, tflite::BuiltinOperator::BuiltinOperator_SQRT, createLayerSQRT )
-DEF_OPERATOR("RSQRT", Op_RSQRT, tflite::BuiltinOperator::BuiltinOperator_RSQRT, createLayerRSQRT )
-DEF_OPERATOR("SHAPE", Op_SHAPE, tflite::BuiltinOperator::BuiltinOperator_SHAPE, createLayerSHAPE )
-DEF_OPERATOR("POW", Op_POW, tflite::BuiltinOperator::BuiltinOperator_POW, createLayerPOW )
-DEF_OPERATOR("ARG_MIN", Op_ARG_MIN, tflite::BuiltinOperator::BuiltinOperator_ARG_MIN, createLayerARG_MIN )
-DEF_OPERATOR("FAKE_QUANT", Op_FAKE_QUANT, tflite::BuiltinOperator::BuiltinOperator_FAKE_QUANT, createLayerFAKE_QUANT )
-DEF_OPERATOR("REDUCE_PROD", Op_REDUCE_PROD, tflite::BuiltinOperator::BuiltinOperator_REDUCE_PROD, createLayerREDUCE_PROD )
-DEF_OPERATOR("REDUCE_MAX", Op_REDUCE_MAX, tflite::BuiltinOperator::BuiltinOperator_REDUCE_MAX, createLayerREDUCE_MAX )
-DEF_OPERATOR("PACK", Op_PACK, tflite::BuiltinOperator::BuiltinOperator_PACK, createLayerPACK )
-DEF_OPERATOR("LOGICAL_OR", Op_LOGICAL_OR, tflite::BuiltinOperator::BuiltinOperator_LOGICAL_OR, createLayerLOGICAL_OR )
-DEF_OPERATOR("ONE_HOT", Op_ONE_HOT, tflite::BuiltinOperator::BuiltinOperator_ONE_HOT, createLayerONE_HOT )
-DEF_OPERATOR("LOGICAL_AND", Op_LOGICAL_AND, tflite::BuiltinOperator::BuiltinOperator_LOGICAL_AND, createLayerLOGICAL_AND )
-DEF_OPERATOR("LOGICAL_NOT", Op_LOGICAL_NOT, tflite::BuiltinOperator::BuiltinOperator_LOGICAL_NOT, createLayerLOGICAL_NOT )
-DEF_OPERATOR("UNPACK", Op_UNPACK, tflite::BuiltinOperator::BuiltinOperator_UNPACK, createLayerUNPACK )
-DEF_OPERATOR("REDUCE_MIN", Op_REDUCE_MIN, tflite::BuiltinOperator::BuiltinOperator_REDUCE_MIN, createLayerREDUCE_MIN )
-DEF_OPERATOR("FLOOR_DIV", Op_FLOOR_DIV, tflite::BuiltinOperator::BuiltinOperator_FLOOR_DIV, createLayerFLOOR_DIV )
-DEF_OPERATOR("REDUCE_ANY", Op_REDUCE_ANY, tflite::BuiltinOperator::BuiltinOperator_REDUCE_ANY, createLayerREDUCE_ANY )
-DEF_OPERATOR("SQUARE", Op_SQUARE, tflite::BuiltinOperator::BuiltinOperator_SQUARE, createLayerSQUARE )
-DEF_OPERATOR("ZEROS_LIKE", Op_ZEROS_LIKE, tflite::BuiltinOperator::BuiltinOperator_ZEROS_LIKE, createLayerZEROS_LIKE )
-DEF_OPERATOR("FILL", Op_FILL, tflite::BuiltinOperator::BuiltinOperator_FILL, createLayerFILL )
-*/
diff --git a/compiler/nnc/utils/tflite_model_generator/RandomModelBuilder.h b/compiler/nnc/utils/tflite_model_generator/RandomModelBuilder.h
deleted file mode 100644
index b6659a01c..000000000
--- a/compiler/nnc/utils/tflite_model_generator/RandomModelBuilder.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef RANDOM_MODELBUILDER_H
-#define RANDOM_MODELBUILDER_H
-
-#include <iostream>
-#include <memory>
-#include <functional>
-#include <random>
-
-#include "Tree.h"
-
-namespace modelgen
-{
-/**
- * @brief ModelSaver this class describes a common interface for saving model.
- */
-class ModelSaver
-{
-public:
- ModelSaver() = default;
- virtual ~ModelSaver() = default;
-
- /**
- * @brief saveModel does export a owned model.
- */
- virtual void saveModel() = 0;
-};
-
-/**
- * @brief RandomModelBuilder this class describe a common interface for building
- * random neural network models.
- */
-class RandomModelBuilder
-{
-public:
- /**
- * @brief constructor for RandomModelBuilder.
- * @details Be careful, opCreators's initializer list should save the order
- * from the OpCode enum.
- */
- RandomModelBuilder()
- : _operatorCounts{0}, _gen(_rd()),
- _floatRand(std::numeric_limits<float>::min(), std::numeric_limits<float>::max()),
- _intRand(static_cast<int32_t>(OpCodes::opFirst), static_cast<int32_t>(OpCodes::opLast))
- {
- _opCreators[static_cast<int>(OpCodes::opConv2d)] =
- [this](treebuilder::Tree *t, treebuilder::Operation *op) { createLayerCONV_2D(t, op); };
- _opCreators[static_cast<int>(OpCodes::opConcatenation)] = [this](
- treebuilder::Tree *t, treebuilder::Operation *op) { createLayerCONCATENATION(t, op); };
- _opCreators[static_cast<int>(OpCodes::opDepthwiseConv2d)] = [this](
- treebuilder::Tree *t, treebuilder::Operation *op) { createLayerDEPTHWISE_CONV_2D(t, op); };
- _opCreators[static_cast<int>(OpCodes::opOpMaxPool2d)] = [this](treebuilder::Tree *t,
- treebuilder::Operation *op) {
- createLayerX_POOL_2D(t, op, OpCodes::opOpMaxPool2d);
- };
- _opCreators[static_cast<int>(OpCodes::opAveragePool2d)] = [this](treebuilder::Tree *t,
- treebuilder::Operation *op) {
- createLayerX_POOL_2D(t, op, OpCodes::opAveragePool2d);
- };
- _opCreators[static_cast<int>(OpCodes::opSoftmax)] =
- [this](treebuilder::Tree *t, treebuilder::Operation *op) { createLayerSOFTMAX(t, op); };
- _opCreators[static_cast<int>(OpCodes::opFullyConnected)] = [this](
- treebuilder::Tree *t, treebuilder::Operation *op) { createLayerFULLY_CONNECTED(t, op); };
- };
-
- virtual ~RandomModelBuilder() = default;
-
- virtual void convertTreeToModel(treebuilder::Tree *t) = 0;
-
- /**
- * @brief getModelSaver does create unique_ptr to ModelSaver.
- * @return unique_ptr to ModelSaver.
- */
- virtual std::unique_ptr<ModelSaver> createModelSaver() = 0;
-
-protected:
- /**
- * @Brief createInput does add input tensor to model.
- */
- virtual void createInput(treebuilder::Tree *t) = 0;
- /**
- * @brief addOperator does add a new layer to model.
- */
- virtual void addOperator(treebuilder::Tree *t, treebuilder::Operation *op) = 0;
-
- /**
- * @brief createLayerXXX are creator for operators.
- * @param input_tensor_id is id of input tensor.
- */
- virtual void createLayerCONV_2D(treebuilder::Tree *, treebuilder::Operation *) = 0;
- virtual void createLayerCONCATENATION(treebuilder::Tree *t, treebuilder::Operation *op) = 0;
- virtual void createLayerDEPTHWISE_CONV_2D(treebuilder::Tree *t, treebuilder::Operation *op) = 0;
- virtual void createLayerX_POOL_2D(treebuilder::Tree *t, treebuilder::Operation *op,
- OpCodes opcode) = 0;
- virtual void createLayerSOFTMAX(treebuilder::Tree *t, treebuilder::Operation *op) = 0;
- virtual void createLayerFULLY_CONNECTED(treebuilder::Tree *t, treebuilder::Operation *op) = 0;
-
- /**
- * @brief opCreators this array contains a lambda with call of method
- * for building specified operator.
- * @details This array is used for convenient creation random operators,
- * like follow: opCreators[OpCodes::opCount]
- * For example: opCreators[OpCodes::opConv2d](0) -- will lead to call createLayerCONV_2D method.
- */
- std::function<void(treebuilder::Tree *, treebuilder::Operation *)>
- _opCreators[static_cast<int32_t>(OpCodes::opCount)];
- /**
- * @brief operatorCounts this array contains amount of used operators in generated model.
- * @details For example: operatorCounts[Op_CONV_2D] -- amount of used 2D convolution operators.
- */
- int _operatorCounts[static_cast<int32_t>(OpCodes::opCount)];
-
- std::random_device _rd;
- std::mt19937 _gen;
- std::uniform_real_distribution<float> _floatRand;
- std::uniform_int_distribution<int> _intRand;
-};
-} // namespace modelgen
-
-#endif // RANDOM_MODELBUILDER_H
diff --git a/compiler/nnc/utils/tflite_model_generator/TFLiteRandomModelBuilder.cpp b/compiler/nnc/utils/tflite_model_generator/TFLiteRandomModelBuilder.cpp
deleted file mode 100644
index 4347d2cfa..000000000
--- a/compiler/nnc/utils/tflite_model_generator/TFLiteRandomModelBuilder.cpp
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <memory>
-#include <random>
-
-#include "TFLiteRandomModelBuilder.h"
-
-namespace modelgen
-{
-static const char *modelFileOut = "model.tflite";
-static const int notInitialized = -1;
-
-static const char *const opNames[] = {
-#define DEF_OPERATOR(Name, OpCode, TFLiteOpCode, CreatorFunc) Name,
-#include "Operator.def"
-#undef DEF_OPERATOR
-};
-
-static const tflite::BuiltinOperator internalOpCode2TFLiteOpCode[]{
-#define DEF_OPERATOR(Name, OpCode, TFLiteOpCode, CreatorFunc) TFLiteOpCode,
-#include "Operator.def"
-#undef DEF_OPERATOR
-};
-
-void TFLiteModelSaver::saveModel()
-{
- auto m = tflite::Model::Pack(_flatBufferBuilder, _model.get());
- tflite::FinishModelBuffer(_flatBufferBuilder, m);
-
- auto model_data = _flatBufferBuilder.GetBufferPointer();
- auto size = _flatBufferBuilder.GetSize();
-
- std::string fileName(modelFileOut);
- auto f = fopen(fileName.c_str(), "wb");
- assert(f != nullptr);
- auto rlen = fwrite(model_data, size, 1, f);
- assert(rlen == 1);
- fclose(f);
-}
-
-TFLiteRandomModelBuilder::TFLiteRandomModelBuilder() : RandomModelBuilder(), _model(new ModelT())
-{
- std::fill_n(_mapOperatorCode, static_cast<long>(OpCodes::opCount), notInitialized);
-}
-
-void TFLiteRandomModelBuilder::convertTreeToModel(treebuilder::Tree *t)
-{
- createInput(t);
-
- for (auto &op : t->opList)
- {
- addOperator(t, op.get());
- }
-}
-
-std::unique_ptr<ModelSaver> TFLiteRandomModelBuilder::createModelSaver()
-{
- return std::unique_ptr<ModelSaver>(new TFLiteModelSaver(std::move(_model)));
-}
-
-/**
- * @todo Add support several inputs.
- */
-void TFLiteRandomModelBuilder::createInput(treebuilder::Tree *t)
-{
- assert(_model->subgraphs.empty() && "Subgraph is already created");
-
- std::unique_ptr<SubGraphT> subgraph(new SubGraphT);
- subgraph->inputs.push_back(0);
- subgraph->outputs.push_back(0);
-
- subgraph->tensors.push_back(createEmptyTensor(t->inputShapeTree, "input"));
- _operandTree2tensor.push_back(0); // it is same as: push_pack(subgraph->tensors.size() - 1);
-
- _model->subgraphs.push_back(std::move(subgraph));
- _model->description = "Random tflite model";
- _model->version = 3;
-}
-
-void TFLiteRandomModelBuilder::addOperator(treebuilder::Tree *t, treebuilder::Operation *op)
-{
- assert(!_model->subgraphs.empty() && "Subgraph is not created");
-
- std::cout << "Add operator [" << opNames[static_cast<int32_t>(op->opcode)] << "] on the level [ "
- << op->levelOwner << " ]" << std::endl;
- _opCreators[static_cast<int32_t>(op->opcode)](t, op);
- _model->subgraphs[0]->outputs[0] = (*_model->subgraphs[0]->operators.rbegin())->outputs[0];
-}
-
-void TFLiteRandomModelBuilder::createLayerCONV_2D(treebuilder::Tree *t, treebuilder::Operation *op)
-{
- std::string output_name(opNames[static_cast<int32_t>(OpCodes::opConv2d)]);
- output_name += "_" + std::to_string(_operatorCounts[static_cast<int32_t>(OpCodes::opConv2d)]);
- auto operator_ptr = createEmptyOperator(op);
-
- auto out_tensor_ptr = createEmptyTensor(op->outputShape, output_name.c_str());
- auto kernel_ptr = createTensorWthBuffer(op->kernelShape, "Kernel");
- auto bias_ptr = createTensorWthBuffer({op->outputShape[3]}, "bias");
-
- auto input_tensor_id = op->levelOwner == 0 ? _operandTree2tensor[0]
- : _operandTree2tensor[t->inputCnt + op->inputs[0]];
-
- operator_ptr->inputs.push_back(input_tensor_id);
- operator_ptr->inputs.push_back(static_cast<int32_t>(_model->subgraphs[0]->tensors.size()));
- _model->subgraphs[0]->tensors.push_back(std::move(kernel_ptr));
- operator_ptr->inputs.push_back(static_cast<int32_t>(_model->subgraphs[0]->tensors.size()));
- _model->subgraphs[0]->tensors.push_back(std::move(bias_ptr));
-
- auto output_tensor_id = static_cast<int32_t>(_model->subgraphs[0]->tensors.size());
- _operandTree2tensor.push_back(output_tensor_id);
- operator_ptr->outputs.push_back(output_tensor_id);
- _model->subgraphs[0]->tensors.push_back(std::move(out_tensor_ptr));
-
- operator_ptr->builtin_options.Set(tflite::Conv2DOptionsT());
- auto conv2D_opt = operator_ptr->builtin_options.AsConv2DOptions();
- conv2D_opt->stride_w = conv2D_opt->stride_h = 1;
- conv2D_opt->fused_activation_function =
- tflite::ActivationFunctionType::ActivationFunctionType_RELU6;
-
- _model->subgraphs[0]->operators.push_back(std::move(operator_ptr));
-}
-
-void TFLiteRandomModelBuilder::createLayerCONCATENATION(treebuilder::Tree *t,
- treebuilder::Operation *op)
-{
- std::string output_name(opNames[static_cast<int32_t>(OpCodes::opConcatenation)]);
- output_name +=
- "_" + std::to_string(_operatorCounts[static_cast<int32_t>(OpCodes::opConcatenation)]);
- auto operator_ptr = createEmptyOperator(op);
-
- auto out_tensor_ptr = createEmptyTensor(op->outputShape, output_name.c_str());
-
- std::cout << "Concatination inputs [ ";
- for (auto it : op->inputs)
- {
- std::cout << it << "/";
- auto input_tensor_id =
- op->levelOwner == 0 ? _operandTree2tensor[0] : _operandTree2tensor[t->inputCnt + it];
- std::cout << input_tensor_id << " ";
- operator_ptr->inputs.push_back(input_tensor_id);
- }
- std::cout << "]" << std::endl;
-
- auto output_tensor_id = static_cast<int32_t>(_model->subgraphs[0]->tensors.size());
- _operandTree2tensor.push_back(output_tensor_id);
- operator_ptr->outputs.push_back(output_tensor_id);
- _model->subgraphs[0]->tensors.push_back(std::move(out_tensor_ptr));
-
- operator_ptr->builtin_options.Set(tflite::ConcatenationOptionsT());
- auto concat_opt = operator_ptr->builtin_options.AsConcatenationOptions();
- concat_opt->fused_activation_function =
- tflite::ActivationFunctionType::ActivationFunctionType_RELU6;
- concat_opt->axis = 0;
-
- for (auto it : op->inputShape)
- {
- if (it == -1)
- break;
- concat_opt->axis++;
- }
-
- _model->subgraphs[0]->operators.push_back(std::move(operator_ptr));
-}
-
-void TFLiteRandomModelBuilder::createLayerDEPTHWISE_CONV_2D(treebuilder::Tree *t,
- treebuilder::Operation *op)
-{
- std::string output_name(opNames[static_cast<int32_t>(OpCodes::opDepthwiseConv2d)]);
- output_name +=
- "_" + std::to_string(_operatorCounts[static_cast<int32_t>(OpCodes::opDepthwiseConv2d)]);
- auto operator_ptr = createEmptyOperator(op);
-
- auto out_tensor_ptr = createEmptyTensor(op->outputShape, output_name.c_str());
- auto kernel_ptr = createTensorWthBuffer(op->kernelShape, "Kernel");
- auto bias_ptr = createTensorWthBuffer({op->outputShape[3]}, "bias");
-
- auto input_tensor_id = op->levelOwner == 0 ? _operandTree2tensor[0]
- : _operandTree2tensor[t->inputCnt + op->inputs[0]];
-
- operator_ptr->inputs.push_back(input_tensor_id);
- operator_ptr->inputs.push_back(static_cast<int32_t>(_model->subgraphs[0]->tensors.size()));
- _model->subgraphs[0]->tensors.push_back(std::move(kernel_ptr));
- operator_ptr->inputs.push_back(static_cast<int32_t>(_model->subgraphs[0]->tensors.size()));
- _model->subgraphs[0]->tensors.push_back(std::move(bias_ptr));
-
- auto output_tensor_id = static_cast<int32_t>(_model->subgraphs[0]->tensors.size());
- _operandTree2tensor.push_back(output_tensor_id);
- operator_ptr->outputs.push_back(output_tensor_id);
- _model->subgraphs[0]->tensors.push_back(std::move(out_tensor_ptr));
-
- operator_ptr->builtin_options.Set(tflite::DepthwiseConv2DOptionsT());
- auto depthwise_conv2d_opt = operator_ptr->builtin_options.AsDepthwiseConv2DOptions();
- depthwise_conv2d_opt->stride_w = depthwise_conv2d_opt->stride_h = 1;
- depthwise_conv2d_opt->depth_multiplier = 1;
- depthwise_conv2d_opt->fused_activation_function =
- tflite::ActivationFunctionType::ActivationFunctionType_RELU6;
-
- _model->subgraphs[0]->operators.push_back(std::move(operator_ptr));
-}
-
-void TFLiteRandomModelBuilder::createLayerX_POOL_2D(treebuilder::Tree *t,
- treebuilder::Operation *op, OpCodes opcode)
-{
- std::string output_name(opNames[static_cast<int32_t>(opcode)]);
- output_name += "_" + std::to_string(_operatorCounts[static_cast<int32_t>(opcode)]);
- auto operator_ptr = createEmptyOperator(op);
-
- auto out_tensor_ptr = createEmptyTensor(op->outputShape, output_name.c_str());
-
- auto input_tensor_id = op->levelOwner == 0 ? _operandTree2tensor[0]
- : _operandTree2tensor[t->inputCnt + op->inputs[0]];
- operator_ptr->inputs.push_back(input_tensor_id);
-
- auto output_tensor_id = static_cast<int32_t>(_model->subgraphs[0]->tensors.size());
- _operandTree2tensor.push_back(output_tensor_id);
- operator_ptr->outputs.push_back(output_tensor_id);
- _model->subgraphs[0]->tensors.push_back(std::move(out_tensor_ptr));
-
- /**
- * @todo generate random filter width/height.
- */
- operator_ptr->builtin_options.Set(tflite::Pool2DOptionsT());
- auto pool2d_opt = operator_ptr->builtin_options.AsPool2DOptions();
- pool2d_opt->stride_w = pool2d_opt->stride_h = 1;
- pool2d_opt->filter_width = pool2d_opt->filter_height = 3;
-
- _model->subgraphs[0]->operators.push_back(std::move(operator_ptr));
-}
-
-void TFLiteRandomModelBuilder::createLayerSOFTMAX(treebuilder::Tree *t, treebuilder::Operation *op)
-{
- std::string output_name(opNames[static_cast<int32_t>(OpCodes::opSoftmax)]);
- output_name += "_" + std::to_string(_operatorCounts[static_cast<int32_t>(OpCodes::opSoftmax)]);
- auto operator_ptr = createEmptyOperator(op);
-
- auto out_tensor_ptr = createEmptyTensor(op->outputShape, output_name.c_str());
-
- auto input_tensor_id = op->levelOwner == 0 ? _operandTree2tensor[0]
- : _operandTree2tensor[t->inputCnt + op->inputs[0]];
- operator_ptr->inputs.push_back(input_tensor_id);
-
- auto output_tensor_id = static_cast<int32_t>(_model->subgraphs[0]->tensors.size());
- _operandTree2tensor.push_back(output_tensor_id);
- operator_ptr->outputs.push_back(output_tensor_id);
- _model->subgraphs[0]->tensors.push_back(std::move(out_tensor_ptr));
-
- operator_ptr->builtin_options.Set(tflite::SoftmaxOptionsT());
- auto softmax_opt = operator_ptr->builtin_options.AsSoftmaxOptions();
- softmax_opt->beta = _floatRand(_gen);
-
- _model->subgraphs[0]->operators.push_back(std::move(operator_ptr));
-}
-
-void TFLiteRandomModelBuilder::createLayerFULLY_CONNECTED(treebuilder::Tree *t,
- treebuilder::Operation *op)
-{
- std::string output_name(opNames[static_cast<int32_t>(OpCodes::opFullyConnected)]);
- output_name +=
- "_" + std::to_string(_operatorCounts[static_cast<int32_t>(OpCodes::opFullyConnected)]);
- auto operator_ptr = createEmptyOperator(op);
-
- auto out_tensor_ptr = createEmptyTensor(op->outputShape, output_name.c_str());
- auto kernel_ptr = createTensorWthBuffer(op->kernelShape, "Kernel");
- auto bias_ptr = createTensorWthBuffer({op->outputShape[3]}, "bias");
-
- auto input_tensor_id = op->levelOwner == 0 ? _operandTree2tensor[0]
- : _operandTree2tensor[t->inputCnt + op->inputs[0]];
-
- operator_ptr->inputs.push_back(input_tensor_id);
- operator_ptr->inputs.push_back(static_cast<int32_t>(_model->subgraphs[0]->tensors.size()));
- _model->subgraphs[0]->tensors.push_back(std::move(kernel_ptr));
- operator_ptr->inputs.push_back(static_cast<int32_t>(_model->subgraphs[0]->tensors.size()));
- _model->subgraphs[0]->tensors.push_back(std::move(bias_ptr));
-
- auto output_tensor_id = static_cast<int32_t>(_model->subgraphs[0]->tensors.size());
- _operandTree2tensor.push_back(output_tensor_id);
- operator_ptr->outputs.push_back(output_tensor_id);
- _model->subgraphs[0]->tensors.push_back(std::move(out_tensor_ptr));
-
- operator_ptr->builtin_options.Set(tflite::FullyConnectedOptionsT());
- auto fullcon_opt = operator_ptr->builtin_options.AsFullyConnectedOptions();
- fullcon_opt->fused_activation_function =
- tflite::ActivationFunctionType::ActivationFunctionType_RELU6;
-
- _model->subgraphs[0]->operators.push_back(std::move(operator_ptr));
-}
-
-std::unique_ptr<TensorT>
-TFLiteRandomModelBuilder::createEmptyTensor(const std::vector<int32_t> &shape, const char *name)
-{
- auto tensor_ptr = std::unique_ptr<TensorT>(new TensorT);
-
- tensor_ptr->type = tflite::TensorType_FLOAT32;
- tensor_ptr->name = name;
- tensor_ptr->shape = shape;
- tensor_ptr->buffer = static_cast<uint32_t>(_model->buffers.size());
- _model->buffers.push_back(std::unique_ptr<BufferT>(new BufferT));
-
- return tensor_ptr;
-}
-
-std::unique_ptr<TensorT>
-TFLiteRandomModelBuilder::createTensorWthBuffer(const std::vector<int32_t> &shape, const char *name)
-{
- auto tensor_ptr = createEmptyTensor(shape, name);
-
- size_t buffer_size = 1;
- for (auto s : shape)
- {
- buffer_size *= s;
- }
- buffer_size *= sizeof(float);
-
- _model->buffers[tensor_ptr->buffer]->data.resize(buffer_size);
-
- for (size_t i = 0; i < buffer_size; i += sizeof(float))
- {
- float val = _floatRand(_gen);
- memcpy(_model->buffers[tensor_ptr->buffer]->data.data() + i, &val, sizeof(float));
- }
- return tensor_ptr;
-}
-
-std::unique_ptr<OperatorT> TFLiteRandomModelBuilder::createEmptyOperator(treebuilder::Operation *op)
-{
- auto operator_ptr = std::unique_ptr<OperatorT>(new OperatorT);
- auto opcode_id = _mapOperatorCode[static_cast<int32_t>(op->opcode)];
- auto tflite_opcode = internalOpCode2TFLiteOpCode[static_cast<int32_t>(op->opcode)];
-
- if (opcode_id == notInitialized)
- {
- auto opcode_ptr = std::unique_ptr<OperatorCodeT>(new OperatorCodeT);
- opcode_ptr->builtin_code = tflite_opcode;
- opcode_ptr->custom_code = tflite::EnumNamesBuiltinOperator()[tflite_opcode];
- opcode_id = static_cast<int32_t>(_model->operator_codes.size());
- _model->operator_codes.push_back(std::move(opcode_ptr));
- _mapOperatorCode[static_cast<int32_t>(op->opcode)] = opcode_id;
- }
- operator_ptr->opcode_index = static_cast<uint32_t>(opcode_id);
- _operatorCounts[static_cast<int32_t>(op->opcode)]++;
-
- return operator_ptr;
-}
-
-} // namespace modelgen
diff --git a/compiler/nnc/utils/tflite_model_generator/TFLiteRandomModelBuilder.h b/compiler/nnc/utils/tflite_model_generator/TFLiteRandomModelBuilder.h
deleted file mode 100644
index 38ff7bdb3..000000000
--- a/compiler/nnc/utils/tflite_model_generator/TFLiteRandomModelBuilder.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TFLITEBUILDER_H
-#define TFLITEBUILDER_H
-
-#include <vector>
-#include <memory>
-
-#include "Tree.h"
-#include "RandomModelBuilder.h"
-#include "schema_generated.h"
-
-namespace modelgen
-{
-using namespace tflite;
-
-/**
- * @brief TFLiteModelSaver contains the unique_ptr to model and does convertation of model
- * to FlatBuffer format and it's saving.
- */
-class TFLiteModelSaver : public ModelSaver
-{
-public:
- TFLiteModelSaver() = default;
- ~TFLiteModelSaver() override = default;
-
- TFLiteModelSaver(std::unique_ptr<ModelT> &&m) : ModelSaver(), _model(std::move(m)) {}
-
- void saveModel() override;
-
-private:
- flatbuffers::FlatBufferBuilder _flatBufferBuilder;
- std::unique_ptr<ModelT> _model;
-};
-
-/**
- * @brief TFLiteRandomModelBuilder does build random TFLiteModel
- * and gives unique_ptr to ModelSaver for export it to file.
- */
-class TFLiteRandomModelBuilder : public RandomModelBuilder
-{
-public:
- TFLiteRandomModelBuilder();
- ~TFLiteRandomModelBuilder() override = default;
-
- void convertTreeToModel(treebuilder::Tree *t) override;
-
- std::unique_ptr<ModelSaver> createModelSaver() override;
-
-protected:
- void createInput(treebuilder::Tree *t) override;
- void addOperator(treebuilder::Tree *t, treebuilder::Operation *op) override;
- /**
- * Operations:
- */
- void createLayerCONV_2D(treebuilder::Tree *t, treebuilder::Operation *op) override;
- void createLayerCONCATENATION(treebuilder::Tree *t, treebuilder::Operation *op) override;
- void createLayerDEPTHWISE_CONV_2D(treebuilder::Tree *t, treebuilder::Operation *op) override;
- void createLayerX_POOL_2D(treebuilder::Tree *t, treebuilder::Operation *op,
- OpCodes opcode) override;
- void createLayerSOFTMAX(treebuilder::Tree *t, treebuilder::Operation *op) override;
- void createLayerFULLY_CONNECTED(treebuilder::Tree *t, treebuilder::Operation *op) override;
-
-private:
- /**
- * @brief createEmptyTensor does create tensor without buffer
- * and add it to tensors array of SubGraphT.
- * @param shape is a shape of tensor.
- * @param name is a name of tensor.
- * @return unique_ptr to created tensor.
- */
- std::unique_ptr<TensorT> createEmptyTensor(const std::vector<int32_t> &shape, const char *name);
- /**
- * @brief createTensorWthBuffer does create tensor with buffer
- * and add it to tensors array of SubGraphT.
- * @param shape is a shape of tensor.
- * @param name is a name of tensor.
- * @return unique_ptr to created tensor.
- */
- std::unique_ptr<TensorT> createTensorWthBuffer(const std::vector<int32_t> &shape,
- const char *name);
- /**
- * @brief createEmptyOperator does create operator without tensors and
- * add operator to operators array of SubGraphT
- * @param opcode is operator's code.
- * @return unique_ptr to created operator.
- */
- std::unique_ptr<OperatorT> createEmptyOperator(treebuilder::Operation *op);
-
- std::unique_ptr<ModelT> _model;
- /**
- * @details This vector contains a index of tensor (in subgraph tflite vector)
- * for output operand of tree's node `i`.
- */
- std::vector<int32_t> _operandTree2tensor;
-
- /**
- * @brief mapOperatorCode contains indexes to operator_codes array in ModelT.
- */
- int32_t _mapOperatorCode[static_cast<int32_t>(OpCodes::opCount)];
-};
-} // namespace modelgen
-
-#endif // TFLITEBUILDER_H
diff --git a/compiler/nnc/utils/tflite_model_generator/Tree.cpp b/compiler/nnc/utils/tflite_model_generator/Tree.cpp
deleted file mode 100644
index 79620baa7..000000000
--- a/compiler/nnc/utils/tflite_model_generator/Tree.cpp
+++ /dev/null
@@ -1,389 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <assert.h>
-#include <algorithm>
-
-#include "Tree.h"
-
-namespace modelgen
-{
-namespace treebuilder
-{
-
-static constexpr int levelsMin = 1;
-static constexpr int levelsMax = 15;
-static constexpr int widthMin = 1;
-static constexpr int widthMax = 10;
-static constexpr int shapeMin = 1;
-static constexpr int shapeMax = 64;
-static constexpr int concatCntInputsMin = 4;
-static constexpr int concatCntInputsMax = 8;
-static constexpr int depthwiseConv2dMultiply = 4;
-static constexpr int fullyConnectedMaxWeight = 8;
-static constexpr int fullyConnectedKernelDim = 2;
-
-TreeBuilder::TreeBuilder() : _gen(_rd()) {}
-
-std::unique_ptr<Tree> TreeBuilder::buildTree()
-{
- std::uniform_int_distribution<int> int_rand(levelsMin, levelsMax);
- auto t = std::unique_ptr<Tree>(new Tree);
- t->inputCnt = 1;
- t->hTree = int_rand(_gen);
- initTree(t.get());
-
- std::cout << "Build " << t->hTree << " levels in tree" << std::endl;
- for (int i = 1; i < t->hTree; i++)
- {
- buildLevel(t.get());
- }
-
- std::cout << "operations count " << t->opList.end() - t->opList.begin() << std::endl;
- std::cout << "levels count " << t->widthLevel.end() - t->widthLevel.begin() << std::endl;
-
- return t;
-}
-
-void TreeBuilder::buildLevel(Tree *t)
-{
- std::uniform_int_distribution<int32_t> int_rand(widthMin, widthMax);
- auto levelId = t->widthLevel.size();
- auto levelWidth = int_rand(_gen);
- t->widthLevel.push_back(static_cast<int32_t>(levelWidth));
- t->beginLevel.push_back(static_cast<int32_t>(t->opList.size()));
- t->endLevel.push_back(static_cast<int32_t>(t->opList.size() + levelWidth - 1));
-
- for (int32_t i = 0; i < levelWidth; i++)
- {
- auto op = std::unique_ptr<Operation>(new Operation);
- op->levelOwner = levelId;
- /**
- * If the operation was not created, then repeat the creation.
- */
- if (!buildSketchOperation(t, op.get()))
- {
- i--;
- continue;
- }
- t->opList.push_back(std::move(op));
- }
-}
-
-void TreeBuilder::initTree(Tree *t)
-{
- std::uniform_int_distribution<int32_t> int_rand(shapeMin, shapeMax);
-
- std::cout << "Build first level in tree" << std::endl;
-
- t->widthLevel.push_back(int_rand(_gen));
- t->beginLevel.push_back(0);
- t->endLevel.push_back(0);
- int32_t x = int_rand(_gen) * 2, y = int_rand(_gen) * 2, z = int_rand(_gen) % 8 + 1;
- t->inputShapeTree = {1, x, y, z};
-
- std::cout << "Initialize first level with width = [ " << t->widthLevel[0] << " ]"
- << " and shape [ 1"
- << " " << x << " " << y << " " << z << " ]" << std::endl;
- for (int32_t i = 0; i < t->widthLevel[0]; i++)
- {
- auto op = std::unique_ptr<Operation>(new Operation);
- op->levelOwner = 0;
- /**
- * If the operation was not created, then repeat the creation.
- */
- if (!buildSketchOperation(t, op.get()))
- {
- i--;
- continue;
- }
- t->opList.push_back(std::move(op));
- }
-}
-
-bool TreeBuilder::buildSketchOperation(Tree *t, Operation *op)
-{
- std::uniform_int_distribution<int32_t> opcode_rand(static_cast<int32_t>(OpCodes::opFirst),
- static_cast<int32_t>(OpCodes::opLast));
-
- op->opcode = static_cast<OpCodes>(opcode_rand(_gen));
- switch (op->opcode)
- {
- case OpCodes::opConv2d:
- buildSketchConv2D(t, op);
- break;
- case OpCodes::opConcatenation:
- buildSketchConcat(t, op);
- break;
- case OpCodes::opDepthwiseConv2d:
- buildSketchDepthwiseConv2D(t, op);
- break;
- case OpCodes::opOpMaxPool2d:
- case OpCodes::opAveragePool2d:
- buildSketchPooling(t, op);
- break;
- case OpCodes::opSoftmax:
- buildSketchSoftmax(t, op);
- break;
- case OpCodes::opFullyConnected:
- /**
- * Currently, we can create fullyconnected operation only on last level.
- * @todo fix it.
- */
- if (t->beginLevel.size() != static_cast<size_t>(t->hTree))
- {
- return false;
- }
-
- buildSketchFullyConnected(t, op);
- break;
- default:
- assert(false && "TreeBuilder: Unsupported operation");
- }
-
- return true;
-}
-
-void TreeBuilder::buildSketchConv2D(Tree *t, Operation *op)
-{
- std::uniform_int_distribution<int32_t> int_rand(0, INT32_MAX);
-
- if (t->beginLevel.size() == 1)
- {
- op->inputShape = t->inputShapeTree;
- buildSketchConv2DForShape(op->inputShape, op);
- return;
- }
-
- auto levelId = int_rand(_gen) % (t->beginLevel.size() - 1);
- auto opId = t->beginLevel[levelId] + (int_rand(_gen) % t->widthLevel[levelId]);
-
- std::cout << "input level [ " << levelId << " ] operation id [ " << opId << " ]" << std::endl;
-
- op->inputs.push_back(opId);
- op->levelOwner = t->beginLevel.size() - 1;
- op->inputShape = t->opList[opId]->outputShape;
- buildSketchConv2DForShape(op->inputShape, op);
-}
-
-void TreeBuilder::buildSketchConcat(Tree *t, Operation *op)
-{
- std::uniform_int_distribution<int32_t> int_rand(2, INT32_MAX);
- auto axis = 1 + (int_rand(_gen) + 3) % 3;
- auto input_cnt = concatCntInputsMin + int_rand(_gen) % concatCntInputsMax;
-
- /* Special case if there are only one level (input to neural network) */
- if (t->beginLevel.size() == 1)
- {
- op->inputShape = t->inputShapeTree;
- op->outputShape = op->inputShape;
- for (int i = 0; i < input_cnt; i++)
- {
- op->inputs.push_back(-1); /* -1 means that it is needed to specify amount inputs
- * on the first level where input tensor for operation
- * is a input tensor for neural network. */
- addConcatInput(op->inputShape, axis, op);
- }
- op->inputShape[axis] = -1; /* specify a dimension for concatenation. */
- return;
- }
-
- /* Select the first operand */
- auto levelId = int_rand(_gen) % (t->beginLevel.size() - 1);
- auto opId = t->beginLevel[levelId] + (int_rand(_gen) % t->widthLevel[levelId]);
- std::cout << "input level [ " << levelId << " ] operation id [ " << opId << " ]" << std::endl;
-
- op->inputs.push_back(opId);
- op->levelOwner = t->beginLevel.size() - 1;
- op->inputShape = t->opList[opId]->outputShape;
- op->outputShape = op->inputShape;
- std::vector<int32_t> shape = op->inputShape;
- shape[axis] = -1;
- for (int i = 0; i < input_cnt; i++)
- {
- opId = lookupConsistentOutput(t, op, shape, t->beginLevel.size() - 1);
- op->inputs.push_back(opId);
- addConcatInput(t->opList[opId]->outputShape, axis, op);
- }
-
- op->inputShape[axis] = -1; /* specify a dimension for concatenation. */
-}
-
-void TreeBuilder::buildSketchDepthwiseConv2D(Tree *t, Operation *op)
-{
- std::uniform_int_distribution<int32_t> int_rand(1, INT32_MAX);
- /**
- * Currently, on the stage of building arbitrary tree it is enough
- * build DepthwiseConv2D node as Conv2D.
- * @todo: maby there is sense to specifically create OpDepthwiseConv2d.
- */
- buildSketchConv2D(t, op);
-
- /**
- * Then change the kernel's shape.
- */
- op->kernelShape[0] = int_rand(_gen) % depthwiseConv2dMultiply + 1; /* channel multiplier */
- op->kernelShape[1] = op->inputShape[3]; /* filter height */
- op->kernelShape[2] = op->inputShape[2]; /* filter width */
- op->kernelShape[3] = op->inputShape[3]; /* input channels */
- op->outputShape[3] = op->kernelShape[0] * op->kernelShape[3];
-}
-
-void TreeBuilder::buildSketchPooling(Tree *t, Operation *op)
-{
- std::uniform_int_distribution<int32_t> int_rand(2, INT32_MAX);
-
- if (t->beginLevel.size() == 1)
- {
- op->inputShape = t->inputShapeTree;
- op->outputShape = op->inputShape;
- return;
- }
-
- auto levelId = int_rand(_gen) % (t->beginLevel.size() - 1);
- auto opId = t->beginLevel[levelId] + (int_rand(_gen) % t->widthLevel[levelId]);
-
- std::cout << "input level [ " << levelId << " ] operation id [ " << opId << " ]" << std::endl;
-
- op->inputs.push_back(opId);
- op->levelOwner = t->beginLevel.size() - 1;
- op->inputShape = t->opList[opId]->outputShape;
- op->outputShape = op->inputShape;
-}
-
-void TreeBuilder::buildSketchSoftmax(Tree *t, Operation *op)
-{
- /**
- * We need only select input node, the output shape will be same as input.
- * That is why we use pooling's builder.
- */
- buildSketchPooling(t, op);
-}
-
-void TreeBuilder::buildSketchFullyConnected(Tree *t, Operation *op)
-{
- std::uniform_int_distribution<int32_t> int_rand(2, fullyConnectedMaxWeight);
- /**
- * 1. Select a input form previous nodes by means of buildSketchPooling
- */
- buildSketchPooling(t, op);
-
- /**
- * 2. Create a weights for fully connected layer.
- */
- op->kernelShape.resize(fullyConnectedKernelDim);
- op->kernelShape[0] = int_rand(_gen);
- op->kernelShape[1] =
- op->inputShape[0] * op->inputShape[1] * op->inputShape[2] * op->inputShape[3];
-
- op->outputShape.resize(2);
- op->outputShape[0] = op->kernelShape[0];
- op->outputShape[1] = op->kernelShape[1];
-}
-
-// =========== private ===========
-
-int32_t TreeBuilder::lookupConsistentOutput(Tree *t, Operation *op, std::vector<int32_t> &shape,
- int32_t until_level)
-{
- for (int i = 0, j = 0; i < t->beginLevel[until_level]; i++)
- {
- for (j = 0; j < 4; j++)
- {
- if (shape[j] != t->opList[i]->outputShape[j] && shape[j] != -1)
- {
- j = 0;
- break;
- }
- }
- if (j == 3 && std::find(op->inputs.begin(), op->inputs.end(), i) == op->inputs.end())
- return i;
- }
-
- /*
- * Help to code below (initialization new_op):
- * Z = y->inputs
- * / \
- * Y new_op
- * \ /
- * op
- */
- const Operation *y = t->opList[op->inputs[0]].get();
- std::unique_ptr<Operation> new_op = std::unique_ptr<Operation>(new Operation(*y));
-
- /*
- * reindex operations
- */
- auto inser_pos = t->beginLevel[y->levelOwner];
- for (auto &in : op->inputs)
- {
- if (in >= inser_pos)
- in++;
- }
- for (int i = inser_pos; i < static_cast<int32_t>(t->opList.size()); i++)
- {
- for (auto &in : t->opList[i]->inputs)
- {
- if (in >= inser_pos)
- {
- in++;
- }
- }
- }
-
- t->endLevel[y->levelOwner]++;
- t->widthLevel[y->levelOwner]++;
- for (int i = y->levelOwner + 1; i < static_cast<int32_t>(t->beginLevel.size()); i++)
- {
- t->beginLevel[i]++;
- t->endLevel[i]++;
- }
- t->opList.insert(t->opList.begin() + inser_pos, std::move(new_op));
-
- return inser_pos;
-}
-
-void TreeBuilder::buildSketchConv2DForShape(std::vector<int32_t> &input_shape, Operation *op)
-{
- std::uniform_int_distribution<int32_t> int_rand(shapeMin, shapeMax);
-
- /* out channels */
- op->kernelShape.push_back(input_shape[3]);
- /* filter height */
- op->kernelShape.push_back(int_rand(_gen));
- /* filter width */
- op->kernelShape.push_back(int_rand(_gen));
- /* in channels */
- op->kernelShape.push_back(input_shape[3]);
- op->outputShape = input_shape;
- std::cout << "\t with kernel [ ";
- for (auto i : op->kernelShape)
- std::cout << " " << i;
- std::cout << " ]" << std::endl;
-}
-
-void TreeBuilder::addConcatInput(std::vector<int32_t> &input_shape, int32_t axis, Operation *op)
-{
- for (int i = 0; i < 4; i++)
- {
- if (input_shape[i] != op->inputShape[i] && i != axis)
- assert(false && "Not consistency input shapes\n");
- }
- op->outputShape[axis] += input_shape[axis];
-}
-
-} // namespace treebuilder
-} // namespace modelgen
diff --git a/compiler/nnc/utils/tflite_model_generator/Tree.h b/compiler/nnc/utils/tflite_model_generator/Tree.h
deleted file mode 100644
index 1d4f00db6..000000000
--- a/compiler/nnc/utils/tflite_model_generator/Tree.h
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef NNCC_TREE_H
-#define NNCC_TREE_H
-
-#include <iostream>
-#include <vector>
-#include <memory>
-#include <random>
-
-#include <stddef.h>
-
-namespace modelgen
-{
-
-enum class OpCodes
-{
- opConv2d,
- opConcatenation,
- opDepthwiseConv2d,
- opOpMaxPool2d,
- opAveragePool2d,
- opSoftmax,
- opFullyConnected,
- opCount,
- opFirst = 0,
- opLast = opCount - 1,
-};
-
-namespace treebuilder
-{
-
-/**
- * opcode is a code of operation.
- * inputs is a index of parent operations.
- * inputShape is a shape of left parent.
- * levelOwner is a index of level having the operation.
- *
- * @details In some cases the kernelShape will keep
- * not the kernel but specific data for operation.
- *
- */
-struct Operation
-{
- OpCodes opcode;
- std::vector<int32_t> inputs;
- std::vector<int32_t> kernelShape;
- std::vector<int32_t> inputShape;
- std::vector<int32_t> outputShape;
- size_t levelOwner;
-};
-
-/**
- * @param inputCnt is a number of tensors to tree's input.
- * @param hTree is a number of levels in tree.
- * @param inputShapeTree is a shape for input tree's tensor.
- * @param widthLevel is a number of operations on the level [i].
- * @param beginLevel keeps the vector of operations id which are beginners for level [i].
- * @param endLevel keeps the vector of operations id which are end for level [i].
- * @param opList keeps the vector of all used operations.
- *
- * @details beginLevel is array of indexes to opList.
- * for example: beginLevel[4] contains the id of first operation on the level 4.
- * id is index for opList array.
- */
-struct Tree
-{
- int inputCnt;
- int hTree;
- std::vector<int32_t> inputShapeTree;
- std::vector<int32_t> widthLevel;
- std::vector<int32_t> beginLevel;
- std::vector<int32_t> endLevel;
- std::vector<std::unique_ptr<Operation>> opList;
-};
-
-class TreeBuilder
-{
-public:
- TreeBuilder();
-
- std::unique_ptr<Tree> buildTree();
- /**
- * @details initTree creates first level and specific operations on its using inputShapeTree
- * inside every operation as size of input. This method are used when there aren't
- * operations on upper levels.
- */
- void initTree(Tree *t);
- void buildLevel(Tree *t);
-
- bool buildSketchOperation(Tree *t, Operation *op);
-
- /**
- * @details Currently Conv2D are build with stride = 1.
- * @param input_shape is the shape of input tensor.
- * @param op is the pointer to created operation.
- */
- void buildSketchConv2D(Tree *t, Operation *op);
- void buildSketchConcat(Tree *t, Operation *op);
- void buildSketchDepthwiseConv2D(Tree *t, Operation *op);
- void buildSketchPooling(Tree *t, Operation *op);
- void buildSketchSoftmax(Tree *t, Operation *op);
- void buildSketchFullyConnected(Tree *t, Operation *op);
-
-private:
- /**
- * @lookupConsistentOutput It Looks up operation with conststent output's shape.
- * @param t is a tree where doing search.
- * @param op is a operation for which looking a operation.
- * @param shape is a template of shape. -1 in this vector is ignorid axis.
- * For example in case of <1, 64, 64, -1> the lookupConsistentOutput looks up
- * operation with output <1, 64, 64, X>.
- * @param until_level is a level starting from which the searching doesn't perform.
- *
- * @details If the operation doesn't found then this method create such operation
- * and return its id.
- *
- */
- int32_t lookupConsistentOutput(Tree *t, Operation *op, std::vector<int32_t> &shape,
- int32_t until_level);
-
- void buildSketchConv2DForShape(std::vector<int32_t> &input_shape, Operation *op);
- void addConcatInput(std::vector<int32_t> &input_shape, int32_t axis, Operation *op);
-
- std::random_device _rd;
- std::mt19937 _gen;
-};
-
-} // namespace treebuilder
-} // namespace modelgen
-#endif // NNCC_TREE_H
diff --git a/compiler/nnkit-caffe/CMakeLists.txt b/compiler/nnkit-caffe/CMakeLists.txt
index 53954d00a..07e2ea4f1 100644
--- a/compiler/nnkit-caffe/CMakeLists.txt
+++ b/compiler/nnkit-caffe/CMakeLists.txt
@@ -1,4 +1,4 @@
-nncc_find_package(Caffe QUIET)
+nnas_find_package(Caffe QUIET)
if(NOT Caffe_FOUND)
return()
diff --git a/compiler/nnkit-caffe/README.md b/compiler/nnkit-caffe/README.md
new file mode 100644
index 000000000..c5d8bb291
--- /dev/null
+++ b/compiler/nnkit-caffe/README.md
@@ -0,0 +1 @@
+# nnkit-caffe
diff --git a/compiler/nnkit-mocotf/README.md b/compiler/nnkit-mocotf/README.md
new file mode 100644
index 000000000..e81692ca8
--- /dev/null
+++ b/compiler/nnkit-mocotf/README.md
@@ -0,0 +1 @@
+# nnkit-mocotf
diff --git a/compiler/nnkit-mocotf/support/include/nnkit/support/moco/tf/Backend.h b/compiler/nnkit-mocotf/support/include/nnkit/support/moco/tf/Backend.h
index d83ce1eb1..ad5e06d2a 100644
--- a/compiler/nnkit-mocotf/support/include/nnkit/support/moco/tf/Backend.h
+++ b/compiler/nnkit-mocotf/support/include/nnkit/support/moco/tf/Backend.h
@@ -43,6 +43,9 @@ class Backend final : public nnkit::Backend
public:
Backend(const char *pb_path, const char *info_path);
+ void setInputOutputFromGraph(const std::unique_ptr<loco::Graph> &loco_graph,
+ ParsedTensors &parsed_tensors);
+
void prepare(const std::function<void(nnkit::TensorContext &)> &f) override;
void run(void) override;
diff --git a/compiler/nnkit-mocotf/support/src/Backend.cpp b/compiler/nnkit-mocotf/support/src/Backend.cpp
index 51e1f7f28..2d9e21fd7 100644
--- a/compiler/nnkit-mocotf/support/src/Backend.cpp
+++ b/compiler/nnkit-mocotf/support/src/Backend.cpp
@@ -24,7 +24,7 @@
#include "nnkit/support/tftestinfo/TensorInfoParser.h"
#include <moco/tf/Frontend.h>
-#include <moco/tf/Names.h>
+#include <moco/Names.h>
#include <stdex/Memory.h>
#include <nncc/core/ADT/tensor/Buffer.h>
@@ -42,10 +42,52 @@ namespace moco
namespace tf
{
+void Backend::setInputOutputFromGraph(const std::unique_ptr<loco::Graph> &loco_graph,
+ ParsedTensors &parsed_tensors)
+{
+ auto inputs = loco_graph.get()->inputs();
+ auto outputs = loco_graph.get()->outputs();
+ uint32_t input_idx = 0;
+ uint32_t output_idx = 0;
+ for (auto &parsed_tensor : parsed_tensors)
+ {
+ if (parsed_tensor->kind() == ParsedTensor::Kind::Input)
+ {
+ if (!parsed_tensor->hasShape())
+ {
+ auto input_shape = inputs->at(input_idx++)->shape();
+
+ uint32_t size = input_shape->rank();
+ parsed_tensor->mutable_shape().resize(size);
+ for (uint32_t d = 0; d < size; d++)
+ {
+ parsed_tensor->mutable_shape().dim(d) = input_shape->dim(d).value();
+ }
+ }
+ _inputs.emplace_back(std::move(parsed_tensor));
+ }
+ else // Output
+ {
+ if (!parsed_tensor->hasShape())
+ {
+ auto output_shape = outputs->at(output_idx++)->shape();
+
+ uint32_t size = output_shape->rank();
+ parsed_tensor->mutable_shape().resize(size);
+ for (uint32_t d = 0; d < size; d++)
+ {
+ parsed_tensor->mutable_shape().dim(d) = output_shape->dim(d).value();
+ }
+ }
+ _outputs.emplace_back(std::move(parsed_tensor));
+ }
+ }
+}
+
Backend::Backend(const char *pb_path, const char *info_path)
{
// read test.info
- ::moco::tf::ModelSignature sig;
+ ::moco::ModelSignature sig;
auto parsed_tensors = nnkit::support::tftestinfo::parse(info_path);
@@ -53,21 +95,25 @@ Backend::Backend(const char *pb_path, const char *info_path)
{
if (parsed_tensor->kind() == ParsedTensor::Kind::Input)
{
- sig.add_input(::moco::tf::TensorName(parsed_tensor->name()));
- _inputs.emplace_back(std::move(parsed_tensor));
+ sig.add_input(::moco::TensorName(parsed_tensor->name()));
}
else
{
- sig.add_output(::moco::tf::TensorName(parsed_tensor->name()));
- _outputs.emplace_back(std::move(parsed_tensor));
+ sig.add_output(::moco::TensorName(parsed_tensor->name()));
}
+ if (parsed_tensor->hasShape())
+ sig.shape(parsed_tensor->name(), parsed_tensor->shape());
}
// get loco::Graph
::moco::tf::Frontend moco;
+ // After converting, all shapes will be determined.
auto loco_graph = moco.load(sig, pb_path, ::moco::tf::Frontend::FileType::Binary);
+ // Set input and output from loco graph.
+ setInputOutputFromGraph(loco_graph, parsed_tensors);
+
// set member vars
_loco_graph = std::move(loco_graph);
_sess = stdex::make_unique<locomotiv::Session>(_loco_graph.get());
diff --git a/compiler/nnkit-onnxrt/CMakeLists.txt b/compiler/nnkit-onnxrt/CMakeLists.txt
index 1950abb56..f72b03a02 100644
--- a/compiler/nnkit-onnxrt/CMakeLists.txt
+++ b/compiler/nnkit-onnxrt/CMakeLists.txt
@@ -1,4 +1,4 @@
-nncc_find_package(ONNXRuntime QUIET)
+nnas_find_package(ONNXRuntime QUIET)
if(NOT ONNXRuntime_FOUND)
return()
diff --git a/compiler/nnkit-onnxrt/README.md b/compiler/nnkit-onnxrt/README.md
new file mode 100644
index 000000000..ccc948ce3
--- /dev/null
+++ b/compiler/nnkit-onnxrt/README.md
@@ -0,0 +1 @@
+# nnkit-onnxrt
diff --git a/compiler/nnkit-tf/CMakeLists.txt b/compiler/nnkit-tf/CMakeLists.txt
index 401746f58..ef2d42183 100644
--- a/compiler/nnkit-tf/CMakeLists.txt
+++ b/compiler/nnkit-tf/CMakeLists.txt
@@ -1,4 +1,4 @@
-nncc_find_package(TensorFlow QUIET)
+nnas_find_package(TensorFlow EXACT 1.13 QUIET)
if(NOT TensorFlow_FOUND)
return()
diff --git a/compiler/nnkit-tf/README.md b/compiler/nnkit-tf/README.md
new file mode 100644
index 000000000..82d279bb8
--- /dev/null
+++ b/compiler/nnkit-tf/README.md
@@ -0,0 +1 @@
+# nnkit-tf
diff --git a/compiler/nnkit-tf/support/CMakeLists.txt b/compiler/nnkit-tf/support/CMakeLists.txt
index 471a1c70f..0f5c0a6dd 100644
--- a/compiler/nnkit-tf/support/CMakeLists.txt
+++ b/compiler/nnkit-tf/support/CMakeLists.txt
@@ -1,9 +1,9 @@
file(GLOB_RECURSE SOURCES "src/*.cpp")
-add_library(nnkit_support_tf-1.12 STATIC ${SOURCES})
-set_target_properties(nnkit_support_tf-1.12 PROPERTIES POSITION_INDEPENDENT_CODE ON)
-target_include_directories(nnkit_support_tf-1.12 PUBLIC include)
-target_link_libraries(nnkit_support_tf-1.12 nnkit_intf_backend stdex nnkit_support_tftestinfo)
-target_link_libraries(nnkit_support_tf-1.12 tensorflow)
+add_library(nnkit_support_tf-1.13 STATIC ${SOURCES})
+set_target_properties(nnkit_support_tf-1.13 PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(nnkit_support_tf-1.13 PUBLIC include)
+target_link_libraries(nnkit_support_tf-1.13 nnkit_intf_backend stdex nnkit_support_tftestinfo)
+target_link_libraries(nnkit_support_tf-1.13 tensorflow-1.13)
-add_library(nnkit_support_tf ALIAS nnkit_support_tf-1.12)
+add_library(nnkit_support_tf ALIAS nnkit_support_tf-1.13)
diff --git a/compiler/nnkit-tf/support/include/nnkit/support/tf/Runner.h b/compiler/nnkit-tf/support/include/nnkit/support/tf/Runner.h
index b115ed191..f782ea879 100644
--- a/compiler/nnkit-tf/support/include/nnkit/support/tf/Runner.h
+++ b/compiler/nnkit-tf/support/include/nnkit/support/tf/Runner.h
@@ -19,6 +19,7 @@
#include "nnkit/support/tftestinfo/ParsedTensor.h"
#include "nnkit/support/tf/TensorDataMap.h"
+#include <angkor/TensorShape.h>
#include <tensorflow/c/c_api.h>
@@ -36,10 +37,45 @@ using nnkit::support::tftestinfo::ParsedTensor;
class Runner final
{
public:
+ enum class DataType
+ {
+ Unknown, // Unknown type (serves as a default value)
+
+ U8, // 8-bit unsigned integer
+ U16, // 16-bit unsigned integer
+ U32, // 32-bit unsigned integer
+ U64, // 64-bit unsigned integer
+
+ S8, // 8-bit signed integer
+ S16, // 16-bit signed integer
+ S32, // 32-bit signed integer
+ S64, // 64-bit signed integer
+
+ FLOAT, // floating-point
+ };
+
+public:
Runner(const char *pb_path);
~Runner();
+ /**
+ * @brief Get tensor shape from GraphDef for input tensor only.
+ *
+ * @note If the node cannot be found or shape you provided is wrong or not enough though shape
+ * must be needed because of unknown shape in GraphDef, it returns false.
+ */
+ bool getTensorShapeFromGraphDef(const std::unique_ptr<ParsedTensor> &tensor,
+ angkor::TensorShape &shape);
+
+ /**
+ * @brief Get tensor data type from GraphDef.
+ *
+ * @note If the node cannot be found or dtype of the node is unknown, it returns false.
+ */
+ bool getTensorDtypeFromGraphDef(const std::unique_ptr<ParsedTensor> &tensor,
+ Runner::DataType &dtype);
+
void prepareInputs(const std::vector<std::unique_ptr<ParsedTensor>> &inputs,
TensorDataMap &data_map);
diff --git a/compiler/nnkit-tf/support/src/Backend.cpp b/compiler/nnkit-tf/support/src/Backend.cpp
index 377421591..f28e05f74 100644
--- a/compiler/nnkit-tf/support/src/Backend.cpp
+++ b/compiler/nnkit-tf/support/src/Backend.cpp
@@ -22,6 +22,8 @@
#include "nnkit/support/tf/TensorContext.h"
#include "nnkit/support/tf/Runner.h"
+#include <angkor/TensorShape.h>
+
#include <nnkit/Backend.h>
#include <cstring> // memcpy
@@ -38,11 +40,26 @@ using nnkit::support::tftestinfo::ParsedTensor;
Backend::Backend(const char *pb_path, const char *info_path) : _tf_runner(pb_path)
{
auto parsed_tensors = nnkit::support::tftestinfo::parse(info_path);
-
for (auto &parsed_tensor : parsed_tensors)
{
if (parsed_tensor->kind() == ParsedTensor::Kind::Input)
+ {
+ // user didn't specify input
+ if (!parsed_tensor->hasShape())
+ {
+ angkor::TensorShape shape;
+ if (!_tf_runner.getTensorShapeFromGraphDef(parsed_tensor, shape))
+ throw oops::UserExn(
+ "Info you provided may be wrong or not enough. Please check the info file.");
+
+ parsed_tensor->mutable_shape().resize(shape.rank());
+ for (int r = 0; r < shape.rank(); r++)
+ {
+ parsed_tensor->mutable_shape().dim(r) = shape.dim(r);
+ }
+ }
_inputs.emplace_back(std::move(parsed_tensor));
+ }
else
_outputs.emplace_back(std::move(parsed_tensor));
}
@@ -73,6 +90,12 @@ void Backend::run(void)
const size_t byte_size = TF_TensorByteSize(actual);
const uint8_t *tf_data = reinterpret_cast<const uint8_t *>(TF_TensorData(actual));
+ const uint32_t shape_rank = TF_NumDims(actual);
+ _outputs[n]->mutable_shape().resize(shape_rank);
+ for (uint32_t r = 0; r < shape_rank; r++)
+ {
+ _outputs[n]->mutable_shape().dim(r) = TF_Dim(actual, r);
+ }
uint8_t *dest = _data_map.allocate(_outputs[n].get());
std::memcpy(dest, tf_data, byte_size);
diff --git a/compiler/nnkit-tf/support/src/Runner.cpp b/compiler/nnkit-tf/support/src/Runner.cpp
index e3b0adae3..0d36ee2f4 100644
--- a/compiler/nnkit-tf/support/src/Runner.cpp
+++ b/compiler/nnkit-tf/support/src/Runner.cpp
@@ -165,6 +165,80 @@ Runner::~Runner()
TF_DeleteStatus(_status);
}
+bool Runner::getTensorShapeFromGraphDef(const std::unique_ptr<ParsedTensor> &tensor,
+ angkor::TensorShape &shape)
+{
+ assert(!tensor->hasShape());
+ TF_Output tensor_op = {TF_GraphOperationByName(_graph, tensor->nodeName().c_str()),
+ tensor->tensorIndex()};
+
+ if (tensor_op.oper == nullptr)
+ return false;
+
+ int dim_size = TF_GraphGetTensorNumDims(_graph, tensor_op, _status);
+ if (dim_size == -1)
+ return false;
+ int64_t dims[dim_size];
+
+ TF_GraphGetTensorShape(_graph, tensor_op, dims, dim_size, _status);
+
+ shape.resize(dim_size);
+ for (int d = 0; d < dim_size; d++)
+ {
+ if (dims[d] == -1)
+ return false;
+ shape.dim(d) = dims[d];
+ }
+ return true;
+}
+
+bool Runner::getTensorDtypeFromGraphDef(const std::unique_ptr<ParsedTensor> &tensor,
+ Runner::DataType &dtype)
+{
+ TF_Output tensor_op = {TF_GraphOperationByName(_graph, tensor->nodeName().c_str()),
+ tensor->tensorIndex()};
+
+ if (tensor_op.oper == nullptr)
+ return false;
+
+ TF_DataType tf_dtype = TF_OperationOutputType(tensor_op);
+
+ switch (tf_dtype)
+ {
+ case TF_DataType::TF_FLOAT:
+ dtype = DataType::FLOAT;
+ break;
+ case TF_DataType::TF_UINT8:
+ dtype = DataType::U8;
+ break;
+ case TF_DataType::TF_UINT16:
+ dtype = DataType::U16;
+ break;
+ case TF_DataType::TF_UINT32:
+ dtype = DataType::U32;
+ break;
+ case TF_DataType::TF_UINT64:
+ dtype = DataType::U64;
+ break;
+ case TF_DataType::TF_INT8:
+ dtype = DataType::S8;
+ break;
+ case TF_DataType::TF_INT16:
+ dtype = DataType::S16;
+ break;
+ case TF_DataType::TF_INT32:
+ dtype = DataType::S32;
+ break;
+ case TF_DataType::TF_INT64:
+ dtype = DataType::S64;
+ break;
+ default:
+ dtype = DataType::Unknown;
+ return false;
+ }
+ return true;
+}
+
void Runner::prepareInputs(const std::vector<std::unique_ptr<ParsedTensor>> &inputs,
TensorDataMap &data_map)
{
@@ -225,7 +299,7 @@ void Runner::run()
TF_DeleteSessionOptions(options);
if (TF_GetCode(_status) != TF_OK)
- throw std::runtime_error("Error while creating a new session");
+ throw std::runtime_error(TF_Message(_status));
TF_SessionRun(_sess,
nullptr, // Run options.
@@ -237,7 +311,7 @@ void Runner::run()
);
if (TF_GetCode(_status) != TF_OK)
- throw std::runtime_error("Error while running a session");
+ throw std::runtime_error(TF_Message(_status));
TF_CloseSession(_sess, _status);
TF_DeleteSession(_sess, _status);
diff --git a/compiler/nnkit-tflite/CMakeLists.txt b/compiler/nnkit-tflite/CMakeLists.txt
index fdab06a77..2ca9a13b8 100644
--- a/compiler/nnkit-tflite/CMakeLists.txt
+++ b/compiler/nnkit-tflite/CMakeLists.txt
@@ -1,4 +1,4 @@
-nncc_find_package(TensorFlowLite QUIET EXACT 1.12)
+nnas_find_package(TensorFlowLite QUIET EXACT 1.13.1)
if(NOT TensorFlowLite_FOUND)
return()
diff --git a/compiler/nnkit-tflite/README.md b/compiler/nnkit-tflite/README.md
new file mode 100644
index 000000000..6d294f6b0
--- /dev/null
+++ b/compiler/nnkit-tflite/README.md
@@ -0,0 +1 @@
+# nnkit-tflite
diff --git a/compiler/nnkit-tflite/backend/Backend.cpp b/compiler/nnkit-tflite/backend/Backend.cpp
index 7d766063e..08ba338e8 100644
--- a/compiler/nnkit-tflite/backend/Backend.cpp
+++ b/compiler/nnkit-tflite/backend/Backend.cpp
@@ -16,8 +16,8 @@
#include "nnkit/support/tflite/AbstractBackend.h"
-#include <tensorflow/contrib/lite/kernels/register.h>
-#include <tensorflow/contrib/lite/model.h>
+#include <tensorflow/lite/kernels/register.h>
+#include <tensorflow/lite/model.h>
#include <stdexcept>
diff --git a/compiler/nnkit-tflite/support/CMakeLists.txt b/compiler/nnkit-tflite/support/CMakeLists.txt
index 0a3e2fbe8..90d694868 100644
--- a/compiler/nnkit-tflite/support/CMakeLists.txt
+++ b/compiler/nnkit-tflite/support/CMakeLists.txt
@@ -1,10 +1,10 @@
file(GLOB_RECURSE SOURCES "src/*.cpp")
-# TODO Rename nnkit_support_tflite-1.12 as nnkit_tflite_support-1.12
-add_library(nnkit_support_tflite-1.12 STATIC ${SOURCES})
-set_target_properties(nnkit_support_tflite-1.12 PROPERTIES POSITION_INDEPENDENT_CODE ON)
-target_include_directories(nnkit_support_tflite-1.12 PUBLIC include)
-target_link_libraries(nnkit_support_tflite-1.12 nnkit_intf_backend)
-target_link_libraries(nnkit_support_tflite-1.12 tensorflowlite-1.12)
+# TODO Rename nnkit_support_tflite-1.13.1 as nnkit_tflite_support-1.13.1
+add_library(nnkit_support_tflite-1.13.1 STATIC ${SOURCES})
+set_target_properties(nnkit_support_tflite-1.13.1 PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(nnkit_support_tflite-1.13.1 PUBLIC include)
+target_link_libraries(nnkit_support_tflite-1.13.1 nnkit_intf_backend)
+target_link_libraries(nnkit_support_tflite-1.13.1 tensorflowlite-1.13.1)
-add_library(nnkit_support_tflite ALIAS nnkit_support_tflite-1.12)
+add_library(nnkit_support_tflite ALIAS nnkit_support_tflite-1.13.1)
diff --git a/compiler/nnkit-tflite/support/include/nnkit/support/tflite/AbstractBackend.h b/compiler/nnkit-tflite/support/include/nnkit/support/tflite/AbstractBackend.h
index d2f6aa9f3..7b89c7f42 100644
--- a/compiler/nnkit-tflite/support/include/nnkit/support/tflite/AbstractBackend.h
+++ b/compiler/nnkit-tflite/support/include/nnkit/support/tflite/AbstractBackend.h
@@ -18,7 +18,7 @@
#define __NNKIT_SUPPORT_TFLITE_ABSTRACT_BACKEND_H__
#include <nnkit/Backend.h>
-#include <tensorflow/contrib/lite/interpreter.h>
+#include <tensorflow/lite/interpreter.h>
namespace nnkit
{
diff --git a/compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorSet.h b/compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorSet.h
index d28ab6e77..4226a3ac4 100644
--- a/compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorSet.h
+++ b/compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorSet.h
@@ -17,7 +17,7 @@
#ifndef __NNKIT_SUPPORT_TFLITE_TENSOR_SET_H__
#define __NNKIT_SUPPORT_TFLITE_TENSOR_SET_H__
-#include <tensorflow/contrib/lite/context.h>
+#include <tensorflow/lite/context.h>
#include <cstdint>
diff --git a/compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorSets.h b/compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorSets.h
index 570803117..13dea981e 100644
--- a/compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorSets.h
+++ b/compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorSets.h
@@ -19,7 +19,7 @@
#include "nnkit/support/tflite/TensorSet.h"
-#include <tensorflow/contrib/lite/interpreter.h>
+#include <tensorflow/lite/interpreter.h>
namespace nnkit
{
diff --git a/compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorUtils.h b/compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorUtils.h
index 05fb7d58c..20b34a3c2 100644
--- a/compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorUtils.h
+++ b/compiler/nnkit-tflite/support/include/nnkit/support/tflite/TensorUtils.h
@@ -17,7 +17,7 @@
#ifndef __NNKIT_SUPPORT_TENSOR_UTILS_H__
#define __NNKIT_SUPPORT_TENSOR_UTILS_H__
-#include <tensorflow/contrib/lite/context.h>
+#include <tensorflow/lite/context.h>
#include <nncc/core/ADT/tensor/Shape.h>
namespace nnkit
diff --git a/compiler/nnkit/actions/HDF5/CMakeLists.txt b/compiler/nnkit/actions/HDF5/CMakeLists.txt
index b799f6df1..63d3320c5 100644
--- a/compiler/nnkit/actions/HDF5/CMakeLists.txt
+++ b/compiler/nnkit/actions/HDF5/CMakeLists.txt
@@ -1,4 +1,4 @@
-find_package(HDF5 COMPONENTS CXX QUIET)
+nnas_find_package(HDF5 QUIET)
if(NOT HDF5_FOUND)
return()
diff --git a/compiler/nnkit/tools/benchmark/CMakeLists.txt b/compiler/nnkit/tools/benchmark/CMakeLists.txt
index 4f2d6693c..c2cde00f4 100644
--- a/compiler/nnkit/tools/benchmark/CMakeLists.txt
+++ b/compiler/nnkit/tools/benchmark/CMakeLists.txt
@@ -1,3 +1,11 @@
+if(NOT TARGET nnkit_support_cmdline)
+ return()
+endif(NOT TARGET nnkit_support_cmdline)
+
+if(NOT TARGET nnkit_support_backend)
+ return()
+endif(NOT TARGET nnkit_support_backend)
+
file(GLOB_RECURSE SOURCES "src/*.cpp")
add_executable(nnkit-benchmark ${SOURCES})
diff --git a/compiler/nnkit/tools/run/CMakeLists.txt b/compiler/nnkit/tools/run/CMakeLists.txt
index 99c15390c..5f42ed941 100644
--- a/compiler/nnkit/tools/run/CMakeLists.txt
+++ b/compiler/nnkit/tools/run/CMakeLists.txt
@@ -1,3 +1,19 @@
+if(NOT TARGET nnkit_intf_action)
+ return()
+endif(NOT TARGET nnkit_intf_action)
+
+if(NOT TARGET nnkit_intf_backend)
+ return()
+endif(NOT TARGET nnkit_intf_backend)
+
+if(NOT TARGET nnkit_support_cmdline)
+ return()
+endif(NOT TARGET nnkit_support_cmdline)
+
+if(NOT TARGET nnkit_support_backend)
+ return()
+endif(NOT TARGET nnkit_support_backend)
+
add_executable(nnkit-run nnkit-run.cpp)
target_link_libraries(nnkit-run nnkit_intf_action)
target_link_libraries(nnkit-run nnkit_intf_backend)
diff --git a/compiler/nnop/CMakeLists.txt b/compiler/nnop/CMakeLists.txt
index 7af1e9e35..82c0e3a86 100644
--- a/compiler/nnop/CMakeLists.txt
+++ b/compiler/nnop/CMakeLists.txt
@@ -2,7 +2,7 @@ add_library(nnop INTERFACE)
target_include_directories(nnop INTERFACE include)
target_link_libraries(nnop INTERFACE angkor)
-nncc_find_package(GTest QUIET)
+nnas_find_package(GTest QUIET)
if(NOT GTest_FOUND)
return()
diff --git a/compiler/nnop/README.md b/compiler/nnop/README.md
new file mode 100644
index 000000000..89edf81b4
--- /dev/null
+++ b/compiler/nnop/README.md
@@ -0,0 +1 @@
+# nnop
diff --git a/compiler/nnsuite/README.md b/compiler/nnsuite/README.md
new file mode 100644
index 000000000..d3b2828ed
--- /dev/null
+++ b/compiler/nnsuite/README.md
@@ -0,0 +1 @@
+# nnsuite
diff --git a/compiler/nnsuite/conv/nnkit-caffe/CMakeLists.txt b/compiler/nnsuite/conv/nnkit-caffe/CMakeLists.txt
index 8aae31003..6445cc6fb 100644
--- a/compiler/nnsuite/conv/nnkit-caffe/CMakeLists.txt
+++ b/compiler/nnsuite/conv/nnkit-caffe/CMakeLists.txt
@@ -11,7 +11,7 @@ target_link_libraries(nnsuite_conv_caffe nnsuite_conv)
target_link_libraries(nnsuite_conv_caffe nnkit_support_caffe)
target_link_libraries(nnsuite_conv_caffe stdex)
-nncc_find_package(GTest QUIET)
+nnas_find_package(GTest QUIET)
if(NOT GTest_FOUND)
return()
diff --git a/compiler/nnsuite/conv/nnkit-tflite/CMakeLists.txt b/compiler/nnsuite/conv/nnkit-tflite/CMakeLists.txt
index 482c150ff..c1cf88812 100644
--- a/compiler/nnsuite/conv/nnkit-tflite/CMakeLists.txt
+++ b/compiler/nnsuite/conv/nnkit-tflite/CMakeLists.txt
@@ -11,7 +11,7 @@ target_link_libraries(nnsuite_conv_tflite nnsuite_conv)
target_link_libraries(nnsuite_conv_tflite nnkit_support_tflite-1.7)
target_link_libraries(nnsuite_conv_tflite stdex)
-nncc_find_package(GTest QUIET)
+nnas_find_package(GTest QUIET)
if(NOT GTest_FOUND)
return()
diff --git a/compiler/one-cmds/CMakeLists.txt b/compiler/one-cmds/CMakeLists.txt
new file mode 100644
index 000000000..1472295c3
--- /dev/null
+++ b/compiler/one-cmds/CMakeLists.txt
@@ -0,0 +1,81 @@
+set(ONE_COMMAND_FILES
+ one-build
+ one-import
+ one-import-bcq
+ one-import-tf
+ one-import-tflite
+ one-optimize
+ one-quantize
+ one-pack
+ one-codegen
+ one-prepare-venv
+)
+
+foreach(ONE_COMMAND IN ITEMS ${ONE_COMMAND_FILES})
+
+ set(ONE_COMMAND_FILE ${ONE_COMMAND})
+ set(ONE_COMMAND_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${ONE_COMMAND_FILE}")
+ set(ONE_COMMAND_BIN "${CMAKE_CURRENT_BINARY_DIR}/${ONE_COMMAND_FILE}")
+ set(ONE_COMMAND_TARGET "${ONE_COMMAND}_target")
+
+ add_custom_command(OUTPUT ${ONE_COMMAND_BIN}
+ COMMAND ${CMAKE_COMMAND} -E copy "${ONE_COMMAND_SRC}" "${ONE_COMMAND_BIN}"
+ DEPENDS ${ONE_COMMAND_SRC}
+ COMMENT "Generate ${ONE_COMMAND_BIN}"
+ )
+
+ add_custom_target(${ONE_COMMAND_TARGET} ALL DEPENDS ${ONE_COMMAND_BIN})
+
+ install(FILES ${ONE_COMMAND}
+ PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE
+ GROUP_READ GROUP_EXECUTE
+ WORLD_READ WORLD_EXECUTE
+ DESTINATION bin)
+
+endforeach(ONE_COMMAND)
+
+set(ONE_UTILITY_FILES
+ one-build.template.cfg
+ utils.py
+)
+
+foreach(ONE_UTILITY IN ITEMS ${ONE_UTILITY_FILES})
+
+ set(ONE_UTILITY_FILE ${ONE_UTILITY})
+ set(ONE_UTILITY_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${ONE_UTILITY_FILE}")
+ set(ONE_UTILITY_BIN "${CMAKE_CURRENT_BINARY_DIR}/${ONE_UTILITY_FILE}")
+ set(ONE_UTILITY_TARGET "${ONE_UTILITY}_target")
+
+ add_custom_command(OUTPUT ${ONE_UTILITY_BIN}
+ COMMAND ${CMAKE_COMMAND} -E copy "${ONE_UTILITY_SRC}" "${ONE_UTILITY_BIN}"
+ DEPENDS ${ONE_UTILITY_SRC}
+ COMMENT "Generate ${ONE_UTILITY_BIN}"
+ )
+
+ add_custom_target(${ONE_UTILITY_TARGET} ALL DEPENDS ${ONE_UTILITY_BIN})
+
+ install(FILES ${ONE_UTILITY}
+ PERMISSIONS OWNER_WRITE OWNER_READ
+ GROUP_READ
+ WORLD_READ
+ DESTINATION bin)
+
+endforeach(ONE_UTILITY)
+
+set(ONE_DOCUMENT_FILES
+ how-to-use-one-commands.txt
+ how-to-prepare-virtualenv.txt
+ how-to-create-hdf5-dataset.txt
+)
+
+foreach(ONE_DOCUMENT IN ITEMS ${ONE_DOCUMENT_FILES})
+
+ install(FILES ${ONE_DOCUMENT} DESTINATION doc)
+
+endforeach(ONE_DOCUMENT)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+add_subdirectory(tests)
diff --git a/compiler/one-cmds/README.md b/compiler/one-cmds/README.md
new file mode 100644
index 000000000..2ce6eb42b
--- /dev/null
+++ b/compiler/one-cmds/README.md
@@ -0,0 +1,3 @@
+# one-cmds
+
+_one-cmds_ provides user commands driver
diff --git a/compiler/one-cmds/how-to-create-hdf5-dataset.txt b/compiler/one-cmds/how-to-create-hdf5-dataset.txt
new file mode 100644
index 000000000..5dc26b2cb
--- /dev/null
+++ b/compiler/one-cmds/how-to-create-hdf5-dataset.txt
@@ -0,0 +1,97 @@
+About
+-----
+
+Last update: 2020-11-06
+
+This document briefly explains how to create an input dataset for one-quantize.
+
+The input dataset of one-quantize has the form of hdf5.
+For users who are not familiar with hdf5, we provide a tool to convert raw data to hdf5.
+
+Workflow to generate input dataset (hdf5 file)
+1. Pre-process input data for the target model and save them as raw data files
+2. Package the raw data files into the hdf5 file using rawdata2hdf5
+
+Note: Users should prepare raw data which can be fed to the target model.
+This is because we don't know which pre-processing logic was used for the target model.
+
+rawdata2hdf5
+---------------
+
+rawdata2hdf5 is a tool to package raw data files into an hdf5 file,
+which is the format of input dataset for one-quantize.
+
+Usage: rawdata2hdf5 --data_list <path/to/text/file> --output_path <path/to/output/file>
+
+Example
+---------------
+
+Let's make an input dataset for InceptionV3 model.
+
+1. Download sample images (You can use your own dataset)
+
+$ wget https://github.com/Samsung/ONE/files/5499172/img_files.zip
+$ unzip img_files.zip
+$ tree img_files
+img_files
+├── bald-eagle.jpg
+├── cow.jpg
+├── deer-in-wild.jpg
+├── fox.jpg
+├── ladybird.jpg
+├── orange-portocaliu.jpg
+├── pink-lotus.jpg
+├── red-church.jpg
+├── tomatoes.jpg
+└── young-dachshund.jpg
+
+2. Pre-process the images and save them as raw data files
+
+In this example, we use Pillow and numpy for simple pre-processing.
+
+$ pip install Pillow numpy
+
+Run the pre-processing logic for the target model.
+We provide a short python script that scales the image data from -1 to 1.
+(This is different from the original pre-processing of InceptionV3.
+Visit the below link to find the exact algorithm)
+https://github.com/tensorflow/models/blob/v2.3.0/research/slim/preprocessing/inception_preprocessing.py
+
+$ cat > preprocess.py << EOF
+import os, shutil, PIL.Image, numpy as np
+
+input_dir = 'img_files'
+output_dir = 'raw_files'
+list_file = 'datalist.txt'
+
+if os.path.exists(output_dir):
+ shutil.rmtree(output_dir, ignore_errors=True)
+os.makedirs(output_dir)
+
+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)
+ img = ((img / 255) - 0.5) * 2.0
+ output_file = output_dir + '/' + f.replace('jpg', 'data')
+ img.tofile(output_file)
+ datalist.writelines(os.path.abspath(output_file) + '\n')
+ datalist.close()
+EOF
+
+$ python preprocess.py
+
+After running preprocess.py, 'raw_files' and 'datalist.txt' will be created.
+raw_files: a directory where raw data files are saved
+datalist.txt: a text file that contains the list of raw data files.
+
+3. Run rawdata2hdf5 with datalist.txt
+
+$ rawdata2hdf5 --data_list datalist.txt --output_path dataset.h5
+
+The contents of the hdf5 file can be printed in the console using h5dump
+$ h5dump dataset.h5
+
+Now you can call one-quantize with dataset.h5.
diff --git a/compiler/one-cmds/how-to-prepare-virtualenv.txt b/compiler/one-cmds/how-to-prepare-virtualenv.txt
new file mode 100644
index 000000000..f3dcf704b
--- /dev/null
+++ b/compiler/one-cmds/how-to-prepare-virtualenv.txt
@@ -0,0 +1,54 @@
+About
+-----
+
+Last update: 2020-09-15
+
+This document explains about 'one-prepare-venv' command.
+
+'one-prepare-venv' will prepare python3 virtual environment with tensorflow-cpu
+version 2.3.0, recommanded 2.x version as of now, so that 'one-import-tf'
+command can execute properly.
+
+
+Prerequisite
+------------
+
+Please install these required packages before venv preparation.
+
+$ sudo apt-get update
+$ sudo apt-get upgrade
+$ sudo apt-get install python3-pip python3-venv
+
+
+How to run for Ubuntu
+---------------------
+
+Just run 'one-prepare-venv' command
+
+$ one-prepare-venv
+
+There will be venv folder as of result.
+
+
+How to run for Windows
+----------------------
+
+1. First, please prepare Python 3.5-3.7
+2. Open the Command Prompt as an administrator
+3. cd(change directory) to the directory where one-compiler is installed
+4. run below command
+```
+$ ONE\install\bin> python -m venv venv
+$ ONE\install\bin> cd venv/Scripts
+$ ONE\install\bin\venv/Scripts> pip.exe install -U pip
+$ ONE\install\bin\venv/Scripts> pip.exe install -U tensorflow-cpu==2.3.0
+```
+
+After running the above command, go back to MinGW and run one-compiler.
+
+
+Trouble shooting
+----------------
+
+If you have any problems, please click 'New issue' at
+https://github.com/Samsung/ONE/issues page.
diff --git a/compiler/one-cmds/how-to-use-one-commands.txt b/compiler/one-cmds/how-to-use-one-commands.txt
new file mode 100644
index 000000000..62a497828
--- /dev/null
+++ b/compiler/one-cmds/how-to-use-one-commands.txt
@@ -0,0 +1,210 @@
+About
+-----
+
+Last update: 2020-10-29
+
+This document briefly explains how to use one-* commands.
+Detailed options are not explained here. Run the command to see options.
+
+Compilation flow for running with onert;
+1) one-import will import model files generated from famous frameworks
+2) one-optimize will optimize models. This step is optional.
+3) one-quantize will quantize models. This step is also optional.
+4) one-pack will pack to nnpkg so that we can run the model with our onert
+ runtime
+
+Compilation flow for NPU
+1) one-import will import model files generated from famous frameworks
+2) one-optimize will optimize models. This step is optional.
+3) one-quantize will quantize models. Depending on the NPUs.
+4) one-codegen will compile to binary codes.
+
+
+common features
+---------------
+
+[configuration file]
+
+You can run one-commands with configuration file as well as command line parameters. The
+configuration file should be written with the options the one-commands need to run.
+
+```
+# configuration_file.cfg
+
+[The_driver_you_want_to_run]
+input_path=/input/path/to/convert
+output_path=...
+option_0=...
+option_1=...
+...
+
+```
+
+You can see a template file for how to write a configuration file in `one-build.template.cfg`.
+
+[options to write]
+
+Sometimes you want to change certain options without touching the configuration file. If you
+pass the option directly to the command line, the option is processed prior to the configuration
+file. A list of options can be found in each driver's help message with `-h` option.
+
+e.g.
+```
+$ ./one-import tf -C my-conf.cfg -i path/to/overwrite.pb
+```
+
+
+one-build
+---------
+
+one-build is an integrated driver that can execute one-commands at once. It's nice to run each
+driver individually, but sometimes you'll want to put together the most frequently used commands
+and run them all at once. You can do this with one-build and its configuration file.
+
+For one-build, the configuration file needs 'one-build' section that consists of list of driver.
+
+```
+# one-build.template.cfg
+[one-build]
+one-import-tf=True
+one-import-tflite=False
+one-import-bcq=False
+one-optimize=True
+one-quantize=False
+one-pack=True
+one-codegen=False
+
+[one-import-tf]
+...
+
+[one-optimize]
+...
+
+[one-pack]
+...
+
+```
+See 'one-build.template.cfg' for more details.
+
+
+one-import
+----------
+
+one-import will invokes one-import-* commands.
+
+Syntax: one-import [framework] [options]
+
+Currently supported frameworks are 'tf', 'tflite' for TensorFlow and TensorFlow
+lite.
+
+
+one-import-bcq
+--------------
+
+This will convert Tensorflow model file (.pb) to our circle model file with applying BCQ.
+To execute this command, original Tensorflow model file must include BCQ information.
+
+This command invokes following scripts internally.
+- generate_bcq_metadata : Generate BCQ metadata in the model
+- generate_bcq_info : Designate BCQ information nodes as model output automatically
+- tf2tfliteV2 : Convert Tensorflow model to tflite model
+- tflite2circle : Convert Tensorflow Lite model to circle model
+When this command is finished, BCQ information nodes will be removed if BCQ information
+was valid and applying BCQ is done correctly without any errors.
+
+As tf2tfliteV2.py runs TensorFlow lite converter, you need to have TensorFlow
+installed in your system. We recommand to use 2.3.0 for now.
+
+We provide python virtual environment and one-import-bcq will enter and leave
+this environment so that you don't need to explictly 'activate' virtual
+environment.
+
+
+one-import-tf
+-------------
+
+This will convert TensorFlow model (.pb) file to our circle model. You can also
+directly call this command. one-import-tf invokes tf2tfliteV2.py script that
+will internally use TensorFlow lite converter and then invoke tflite2circle
+converter to convert tflite model to circle model.
+
+As tf2tfliteV2.py runs TensorFlow lite converter, you need to have TensorFlow
+installed in your system. We recommand to use 2.3.0 for now.
+
+We provide python virtual environment and one-import-tf will enter and leave
+this environment so that you don't need to explictly 'activate' virtual
+environment.
+
+
+one-import-tflite
+-----------------
+
+You can use one-import-tflite to convert TensorFlow lite model (.tflite) file to
+our circle model. Internally this will invoke tflite2circle.
+
+
+one-optimize
+------------
+
+one-optimize provides network or operator transformation shown below.
+
+Current transformation options are
+- fold_dequantize : This removes Dequantize operation which can be folded
+- fuse_add_with_tconv: This fuses Add operator with the preceding TConv operator if possible
+- fuse_bcq: This enables Binary-Coded-bases Quantized DNNs
+ - read https://arxiv.org/abs/2005.09904 for detailed information
+- fuse_instnorm: This will convert instance normalization related operators to
+ one InstanceNormalization operator that our onert provides for faster
+ execution.
+- fuse_preactivation_batchnorm: This fuses batch normalization operators of pre-activations to Conv operators.
+- fuse_activation_function: This fuses Activation function to a preceding operator.
+- make_batchnorm_gamma_positive: This makes negative gamma of batch normalization into a small positive value (1e-10).
+ Note that this pass can change the execution result of the model.
+ So, use it only when the impact is known to be acceptable.
+- resolve_customop_add: This will convert Custom(Add) to normal Add operator
+- resolve_customop_batchmatmul: This will convert Custom(BatchMatMul) to
+ normal BatchMatMul operator
+- resolve_customop_matmul: This will convert Custom(MatMul) to normal MatMul
+ operator
+
+
+one-quantize
+------------
+
+one-quantize will quantize float32 model to uint8 so that the model can benefit
+for speed that our onert runtime and NPU provides. For convolution type
+operators we currently support layer-wise quantization. Later we will support
+int16 and channel-wise quantization.
+
+Internally this calls circle-quantizer and record-minmax tools.
+
+
+one-pack
+--------
+
+one-pack will generate a package from circle model to nnpackage for our onert
+runtime.
+
+Output is a folder with the model(s) and meta information.
+
+ex) if you have a model named '20200719.circle' and want to pack to 'testnnpack'
+
+$ one-pack -i 20200709.circle -o testnnpack
+
+$ tree testnnpack
+testnnpack
+└── 20200709
+ ├── 20200709.circle
+ └── metadata
+ └── MANIFEST
+
+
+one-codegen
+-----------
+
+one-codegen, like one-import will invoke backend code generation commands.
+As of now, our ONE repo does not provide any code generation commands yet.
+
+Syntax: one-codegen [target-backend] [options]
+
+This will invoke [target-backend]-compile command if available.
diff --git a/compiler/one-cmds/one-build b/compiler/one-cmds/one-build
new file mode 100644
index 000000000..82b193f9e
--- /dev/null
+++ b/compiler/one-cmds/one-build
@@ -0,0 +1,134 @@
+#!/usr/bin/env bash
+''''export SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # '''
+''''export PY_PATH=${SCRIPT_PATH}/venv/bin/python # '''
+''''test -f ${PY_PATH} && exec ${PY_PATH} "$0" "$@" # '''
+''''echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command." # '''
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import configparser
+import os
+import subprocess
+import sys
+
+import utils as _utils
+
+
+def _get_parser():
+ parser = argparse.ArgumentParser(
+ description='command line tool to run ONE drivers in customized order')
+
+ _utils._add_default_arg(parser)
+
+ return parser
+
+
+def _parse_arg(parser):
+ args = parser.parse_args()
+ # print version
+ if args.version:
+ _utils._print_version_and_exit(__file__)
+
+ return args
+
+
+def _verify_arg(parser, args):
+ """verify given arguments"""
+ # check if required arguments is given
+ if not _utils._is_valid_attr(args, 'config'):
+ parser.error('-C/--config argument is required')
+
+
+def _get_driver_name(driver_name):
+ return {
+ 'one-import-bcq': 'one-import-bcq',
+ 'one-import-tf': 'one-import-tf',
+ 'one-import-tflite': 'one-import-tflite',
+ 'one-optimize': 'one-optimize',
+ 'one-pack': 'one-pack',
+ 'one-codegen': 'one-codegen'
+ }[driver_name]
+
+
+def _parse_cfg(args):
+ config = configparser.ConfigParser()
+ parsed = config.read(os.path.expanduser(getattr(args, 'config')))
+ if not parsed:
+ raise FileNotFoundError('Not found given configuration file')
+ return config
+
+
+def _is_available_driver(config, driver_name):
+ return config.has_option('one-build', driver_name) and config.getboolean(
+ 'one-build', driver_name)
+
+
+def _verify_cfg(driver_list, config):
+ if not config.has_section('one-build'):
+ raise ImportError('\'one-build\' section is required in configuraion file')
+
+ import_driver_cnt = 0
+ if _is_available_driver(config, 'one-import-tf'):
+ import_driver_cnt += 1
+ if _is_available_driver(config, 'one-import-tflite'):
+ import_driver_cnt += 1
+ if _is_available_driver(config, 'one-import-bcq'):
+ import_driver_cnt += 1
+ if import_driver_cnt > 1:
+ raise AssertionError('Only one import-* driver can be executed')
+
+
+def main():
+ # parse arguments
+ # since the configuration file path is required first,
+ # parsing of the configuration file proceeds after this.
+ parser = _get_parser()
+ args = _parse_arg(parser)
+
+ # verify arguments
+ _verify_arg(parser, args)
+
+ # parse configuration file
+ config = _parse_cfg(args)
+
+ # verify configuration file
+ drivers = [
+ 'one-import-tf', 'one-import-tflite', 'one-import-bcq', 'one-optimize',
+ 'one-quantize', 'one-pack', 'one-codegen'
+ ]
+ _verify_cfg(drivers, config)
+
+ # get sections to run
+ section_to_run = []
+ for d in drivers:
+ if _is_available_driver(config, d):
+ section_to_run.append(d)
+
+ # run
+ dir_path = os.path.dirname(os.path.realpath(__file__))
+ for section in section_to_run:
+ driver_path = os.path.join(dir_path, _get_driver_name(section))
+ cmd = [driver_path, '--config', getattr(args, 'config'), '--section', section]
+ with subprocess.Popen(
+ cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1) as p:
+ for line in p.stdout:
+ sys.stdout.buffer.write(line)
+ if p.returncode != 0:
+ sys.exit(p.returncode)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/compiler/one-cmds/one-build.template.cfg b/compiler/one-cmds/one-build.template.cfg
new file mode 100644
index 000000000..ab6ac81d7
--- /dev/null
+++ b/compiler/one-cmds/one-build.template.cfg
@@ -0,0 +1,24 @@
+[one-build]
+one-import-tf=True
+one-import-tflite=False
+one-import-bcq=False
+one-optimize=True
+one-quantize=False
+one-pack=True
+one-codegen=False
+
+[one-import-tf]
+input_path=/path/to/inception_v3.pb
+output_path=inception_v3.circle
+input_arrays=input
+input_shapes=1,299,299,3
+output_arrays=InceptionV3/Predictions/Reshape_1
+converter_version=v1
+
+[one-optimize]
+input_path=inception_v3.circle
+output_path=inception_v3.opt.circle
+
+[one-pack]
+input_path=inception_v3.opt.circle
+output_path=inception_v3_pack
diff --git a/compiler/one-cmds/one-codegen b/compiler/one-cmds/one-codegen
new file mode 100644
index 000000000..f2d82307c
--- /dev/null
+++ b/compiler/one-cmds/one-codegen
@@ -0,0 +1,111 @@
+#!/usr/bin/env bash
+''''export SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # '''
+''''export PY_PATH=${SCRIPT_PATH}/venv/bin/python # '''
+''''test -f ${PY_PATH} && exec ${PY_PATH} "$0" "$@" # '''
+''''echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command." # '''
+''''exit 255 # '''
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import os
+import subprocess
+import sys
+import tempfile
+
+import utils as _utils
+
+
+def _get_backends_list():
+ dir_path = os.path.dirname(os.path.realpath(__file__))
+ files = [f for f in os.listdir(dir_path) if os.path.isfile(os.path.join(dir_path, f))]
+ backends_list = []
+ for cand in files:
+ if cand.endswith('-compile'):
+ # 8 : length of '-compile'
+ backends_list.append(cand[:-8])
+ return backends_list
+
+
+def _get_parser():
+ parser = argparse.ArgumentParser(description='command line tool for code generation')
+
+ _utils._add_default_arg(parser)
+
+ # get backend list in the directory
+ backends_list = _get_backends_list()
+ if not backends_list:
+ backends_list_message = '(There is no available backend drivers)'
+ else:
+ backends_list_message = '(available backend drivers: ' + '.'.join(
+ backends_list) + ')'
+ backend_help_message = 'backend name to use ' + backends_list_message
+ parser.add_argument('-b', '--backend', type=str, help=backend_help_message)
+
+ return parser
+
+
+def _verify_arg(parser, args):
+ """verify given arguments"""
+ # check if required arguments is given
+ missing = []
+ if not _utils._is_valid_attr(args, 'backend'):
+ missing.append('-b/--backend')
+ if len(missing):
+ parser.error('the following arguments are required: ' + ' '.join(missing))
+
+
+def _parse_arg(parser):
+ args, unknown_args = parser.parse_known_args()
+ # print version
+ if args.version:
+ _utils._print_version_and_exit(__file__)
+
+ return args, unknown_args
+
+
+def main():
+ # parse arguments
+ parser = _get_parser()
+ args, unknown_args = _parse_arg(parser)
+
+ # parse configuration file
+ _utils._parse_cfg(args, 'one-codegen')
+
+ # verify arguments
+ _verify_arg(parser, 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:
+ # make a command to run given backend driver
+ codegen_path = os.path.join(dir_path, getattr(args, 'backend') + '-compile')
+ codegen_cmd = [codegen_path] + unknown_args
+
+ f.write((' '.join(codegen_cmd) + '\n').encode())
+
+ # run backend driver
+ with subprocess.Popen(
+ codegen_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+ bufsize=1) as p:
+ for line in p.stdout:
+ sys.stdout.buffer.write(line)
+ f.write(line)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/compiler/one-cmds/one-import b/compiler/one-cmds/one-import
new file mode 100644
index 000000000..e8ce8817c
--- /dev/null
+++ b/compiler/one-cmds/one-import
@@ -0,0 +1,101 @@
+#!/usr/bin/env bash
+''''export SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # '''
+''''export PY_PATH=${SCRIPT_PATH}/venv/bin/python # '''
+''''test -f ${PY_PATH} && exec ${PY_PATH} "$0" "$@" # '''
+''''echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command." # '''
+''''exit 255 # '''
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import os
+import subprocess
+import sys
+
+import utils as _utils
+
+
+def _get_parser():
+ parser = argparse.ArgumentParser(
+ description='command line tool to convert various format to circle',
+ formatter_class=argparse.RawTextHelpFormatter)
+
+ # configuration file
+ parser.add_argument('-C', '--config', type=str, help='run with configuation file')
+
+ # driver
+ parser.add_argument(
+ 'driver', type=str, help='driver name to run (supported: tf, tflite, bcq)')
+
+ # version
+ dir_path = os.path.dirname(os.path.realpath(__file__))
+ one_version_path = os.path.join(dir_path, 'one-version')
+ version = subprocess.check_output([one_version_path]).decode('utf-8')
+ version_str = '\n'.join(['one-import version {}'.format(version), \
+ 'Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved', \
+ 'Licensed under the Apache License, Version 2.0', \
+ 'https://github.com/Samsung/ONE'])
+ parser.add_argument('-v', '--version', action='version', version=version_str)
+
+ return parser
+
+
+def _verify_arg(parser, args):
+ """verify given arguments"""
+ pass
+ # TODO verify arguments
+
+
+def _parse_arg(parser):
+ args, unknown_args = parser.parse_known_args()
+
+ return args, unknown_args
+
+
+def _get_driver_name(driver_name):
+ return {
+ 'bcq': 'one-import-bcq',
+ 'tf': 'one-import-tf',
+ 'tflite': 'one-import-tflite'
+ }[driver_name]
+
+
+def _convert(args, unknown_args):
+ # get directory path
+ dir_path = os.path.dirname(os.path.realpath(__file__))
+ # make cmd
+ cmd = [sys.executable, os.path.join(dir_path, _get_driver_name(args.driver))]
+ if _utils._is_valid_attr(args, 'config'):
+ cmd.append('--config')
+ cmd.append(os.path.expanduser(args.config))
+ return_code = subprocess.call(cmd + unknown_args)
+ if return_code != 0:
+ sys.exit(return_code)
+
+
+def main():
+ # parse arguments
+ parser = _get_parser()
+ args, unknown_args = _parse_arg(parser)
+
+ # verify arguments
+ _verify_arg(parser, args)
+
+ # convert
+ _convert(args, unknown_args)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/compiler/one-cmds/one-import-bcq b/compiler/one-cmds/one-import-bcq
new file mode 100644
index 000000000..5ea1f57fa
--- /dev/null
+++ b/compiler/one-cmds/one-import-bcq
@@ -0,0 +1,222 @@
+#!/usr/bin/env bash
+''''export SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # '''
+''''export PY_PATH=${SCRIPT_PATH}/venv/bin/python # '''
+''''test -f ${PY_PATH} && exec ${PY_PATH} "$0" "$@" # '''
+''''echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command." # '''
+''''exit 255 # '''
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import os
+import subprocess
+import sys
+import tempfile
+
+import utils as _utils
+import generate_bcq_output_arrays as _bcq_info_gen
+
+
+def _get_parser():
+ parser = argparse.ArgumentParser(
+ description='command line tool to convert TensorFlow with BCQ to circle')
+
+ _utils._add_default_arg(parser)
+
+ ## tf2tfliteV2 arguments
+ tf2tfliteV2_group = parser.add_argument_group('converter arguments')
+
+ # converter version
+ converter_version = tf2tfliteV2_group.add_mutually_exclusive_group()
+ converter_version.add_argument(
+ '--v1',
+ action='store_const',
+ dest='converter_version',
+ const='--v1',
+ help='use TensorFlow Lite Converter 1.x')
+ converter_version.add_argument(
+ '--v2',
+ action='store_const',
+ dest='converter_version',
+ const='--v2',
+ help='use TensorFlow Lite Converter 2.x')
+
+ parser.add_argument('--converter_version', type=str, help=argparse.SUPPRESS)
+
+ # input and output path.
+ tf2tfliteV2_group.add_argument(
+ '-i', '--input_path', type=str, help='full filepath of the input file')
+ tf2tfliteV2_group.add_argument(
+ '-o', '--output_path', type=str, help='full filepath of the output file')
+
+ # input and output arrays.
+ tf2tfliteV2_group.add_argument(
+ '-I',
+ '--input_arrays',
+ type=str,
+ help='names of the input arrays, comma-separated')
+ tf2tfliteV2_group.add_argument(
+ '-s',
+ '--input_shapes',
+ type=str,
+ help=
+ 'shapes corresponding to --input_arrays, colon-separated (ex:"1,4,4,3:1,20,20,3")'
+ )
+ tf2tfliteV2_group.add_argument(
+ '-O',
+ '--output_arrays',
+ type=str,
+ help='names of the output arrays, comma-separated')
+
+ return parser
+
+
+def _verify_arg(parser, args):
+ """verify given arguments"""
+ # check if required arguments is given
+ missing = []
+ if not _utils._is_valid_attr(args, 'input_path'):
+ missing.append('-i/--input_path')
+ if not _utils._is_valid_attr(args, 'output_path'):
+ missing.append('-o/--output_path')
+ if len(missing):
+ parser.error('the following arguments are required: ' + ' '.join(missing))
+
+
+def _parse_arg(parser):
+ args = parser.parse_args()
+ # print version
+ if args.version:
+ _utils._print_version_and_exit(__file__)
+
+ return args
+
+
+def _make_generate_bcq_metadata_cmd(args, driver_path, output_path):
+ """make a command for running generate_bcq_metadata"""
+ cmd = [sys.executable, driver_path]
+ # input_path
+ if _utils._is_valid_attr(args, 'input_path'):
+ cmd.append('--input_path')
+ cmd.append(os.path.expanduser(getattr(args, 'input_path')))
+ # output_path
+ if _utils._is_valid_attr(args, 'output_path'):
+ cmd.append('--output_path')
+ cmd.append(os.path.expanduser(output_path))
+ # output_arrays
+ if _utils._is_valid_attr(args, 'output_arrays'):
+ cmd.append('--output_arrays')
+ cmd.append(getattr(args, 'output_arrays'))
+
+ return cmd
+
+
+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'
+
+ with open(logfile_path, 'wb') as f, tempfile.TemporaryDirectory() as tmpdir:
+ # make a command to generate BCQ information metadata
+ generate_bcq_metadata_path = os.path.join(dir_path, 'generate_bcq_metadata.py')
+ generate_bcq_metadata_output_path = os.path.join(
+ tmpdir,
+ os.path.splitext(os.path.basename(args.input_path))[0] + '_withmeta.pb')
+ generate_bcq_metadata_cmd = _make_generate_bcq_metadata_cmd(
+ args, generate_bcq_metadata_path, generate_bcq_metadata_output_path)
+
+ f.write((' '.join(generate_bcq_metadata_cmd) + '\n').encode())
+
+ # generate BCQ information metadata
+ with subprocess.Popen(
+ generate_bcq_metadata_cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ bufsize=1) as p:
+ for line in p.stdout:
+ sys.stdout.buffer.write(line)
+ f.write(line)
+ if p.returncode != 0:
+ sys.exit(p.returncode)
+
+ # get output_arrays with BCQ
+ bcq_output_arrays = _bcq_info_gen.get_bcq_output_arrays(
+ generate_bcq_metadata_output_path, getattr(args, 'output_arrays'))
+
+ # make a command to convert from tf with BCQ to tflite
+ tf2tfliteV2_path = os.path.join(dir_path, 'tf2tfliteV2.py')
+ tf2tfliteV2_output_path = os.path.join(
+ tmpdir,
+ os.path.splitext(
+ os.path.basename(generate_bcq_metadata_output_path))[0]) + '.tflite'
+ tf2tfliteV2_cmd = _utils._make_tf2tfliteV2_cmd(args, tf2tfliteV2_path,
+ generate_bcq_metadata_output_path,
+ tf2tfliteV2_output_path)
+ try:
+ output_arrays_idx = tf2tfliteV2_cmd.index('--output_arrays')
+ tf2tfliteV2_cmd[output_arrays_idx + 1] = ','.join(bcq_output_arrays)
+ except ValueError:
+ pass
+
+ f.write((' '.join(tf2tfliteV2_cmd) + '\n').encode())
+
+ # convert tf to tflite
+ with subprocess.Popen(
+ tf2tfliteV2_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+ bufsize=1) as p:
+ for line in p.stdout:
+ sys.stdout.buffer.write(line)
+ f.write(line)
+ if p.returncode != 0:
+ sys.exit(p.returncode)
+
+ # make a command to convert from tflite to circle
+ tflite2circle_path = os.path.join(dir_path, 'tflite2circle')
+ tflite2circle_cmd = _utils._make_tflite2circle_cmd(tflite2circle_path,
+ tf2tfliteV2_output_path,
+ getattr(args, 'output_path'))
+
+ f.write((' '.join(tflite2circle_cmd) + '\n').encode())
+
+ # convert tflite to circle
+ with subprocess.Popen(
+ tflite2circle_cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ bufsize=1) as p:
+ for line in p.stdout:
+ sys.stdout.buffer.write(line)
+ f.write(line)
+ if p.returncode != 0:
+ sys.exit(p.returncode)
+
+
+def main():
+ # parse arguments
+ parser = _get_parser()
+ args = _parse_arg(parser)
+
+ # parse configuration file
+ _utils._parse_cfg(args, 'one-import-bcq')
+
+ # verify arguments
+ _verify_arg(parser, args)
+
+ # _convert
+ _convert(args)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/compiler/one-cmds/one-import-tf b/compiler/one-cmds/one-import-tf
new file mode 100644
index 000000000..49009d331
--- /dev/null
+++ b/compiler/one-cmds/one-import-tf
@@ -0,0 +1,195 @@
+#!/usr/bin/env bash
+''''export SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # '''
+''''export PY_PATH=${SCRIPT_PATH}/venv/bin/python # '''
+''''test -f ${PY_PATH} && exec ${PY_PATH} "$0" "$@" # '''
+''''echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command." # '''
+''''exit 255 # '''
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import os
+import subprocess
+import sys
+import tempfile
+
+import utils as _utils
+
+
+def _get_parser():
+ parser = argparse.ArgumentParser(
+ description='command line tool to convert TensorFlow to circle')
+
+ _utils._add_default_arg(parser)
+
+ ## tf2tfliteV2 arguments
+ tf2tfliteV2_group = parser.add_argument_group('converter arguments')
+
+ # converter version
+ converter_version = tf2tfliteV2_group.add_mutually_exclusive_group()
+ converter_version.add_argument(
+ '--v1',
+ action='store_const',
+ dest='converter_version_cmd',
+ const='--v1',
+ help='use TensorFlow Lite Converter 1.x')
+ converter_version.add_argument(
+ '--v2',
+ action='store_const',
+ dest='converter_version_cmd',
+ const='--v2',
+ help='use TensorFlow Lite Converter 2.x')
+
+ #converter_version.set_defaults(converter_version='--v1')
+
+ parser.add_argument('--converter_version', type=str, help=argparse.SUPPRESS)
+
+ # input model format
+ model_format_arg = tf2tfliteV2_group.add_mutually_exclusive_group()
+ model_format_arg.add_argument(
+ '--graph_def',
+ action='store_const',
+ dest='model_format_cmd',
+ const='--graph_def',
+ help='use graph def file(default)')
+ model_format_arg.add_argument(
+ '--saved_model',
+ action='store_const',
+ dest='model_format_cmd',
+ const='--saved_model',
+ help='use saved model')
+ model_format_arg.add_argument(
+ '--keras_model',
+ action='store_const',
+ dest='model_format_cmd',
+ const='--keras_model',
+ help='use keras model')
+
+ parser.add_argument('--model_format', type=str, help=argparse.SUPPRESS)
+
+ # input and output path.
+ tf2tfliteV2_group.add_argument(
+ '-i', '--input_path', type=str, help='full filepath of the input file')
+ tf2tfliteV2_group.add_argument(
+ '-o', '--output_path', type=str, help='full filepath of the output file')
+
+ # input and output arrays.
+ tf2tfliteV2_group.add_argument(
+ '-I',
+ '--input_arrays',
+ type=str,
+ help='names of the input arrays, comma-separated')
+ tf2tfliteV2_group.add_argument(
+ '-s',
+ '--input_shapes',
+ type=str,
+ help=
+ 'shapes corresponding to --input_arrays, colon-separated (ex:"1,4,4,3:1,20,20,3")'
+ )
+ tf2tfliteV2_group.add_argument(
+ '-O',
+ '--output_arrays',
+ type=str,
+ help='names of the output arrays, comma-separated')
+
+ return parser
+
+
+def _verify_arg(parser, args):
+ """verify given arguments"""
+ # check if required arguments is given
+ missing = []
+ if not _utils._is_valid_attr(args, 'input_path'):
+ missing.append('-i/--input_path')
+ if not _utils._is_valid_attr(args, 'output_path'):
+ missing.append('-o/--output_path')
+ if len(missing):
+ parser.error('the following arguments are required: ' + ' '.join(missing))
+
+
+def _parse_arg(parser):
+ args = parser.parse_args()
+ # print version
+ if args.version:
+ _utils._print_version_and_exit(__file__)
+
+ return args
+
+
+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'
+
+ with open(logfile_path, 'wb') as f, tempfile.TemporaryDirectory() as tmpdir:
+ # make a command to convert from tf to tflite
+ tf2tfliteV2_path = os.path.join(dir_path, 'tf2tfliteV2.py')
+ tf2tfliteV2_output_path = os.path.join(
+ tmpdir,
+ os.path.splitext(os.path.basename(args.output_path))[0]) + '.tflite'
+ tf2tfliteV2_cmd = _utils._make_tf2tfliteV2_cmd(args, tf2tfliteV2_path,
+ getattr(args, 'input_path'),
+ tf2tfliteV2_output_path)
+
+ f.write((' '.join(tf2tfliteV2_cmd) + '\n').encode())
+
+ # convert tf to tflite
+ with subprocess.Popen(
+ tf2tfliteV2_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+ bufsize=1) as p:
+ for line in p.stdout:
+ sys.stdout.buffer.write(line)
+ f.write(line)
+ if p.returncode != 0:
+ sys.exit(p.returncode)
+
+ # make a command to convert from tflite to circle
+ tflite2circle_path = os.path.join(dir_path, 'tflite2circle')
+ tflite2circle_cmd = _utils._make_tflite2circle_cmd(tflite2circle_path,
+ tf2tfliteV2_output_path,
+ getattr(args, 'output_path'))
+
+ f.write((' '.join(tflite2circle_cmd) + '\n').encode())
+
+ # convert tflite to circle
+ with subprocess.Popen(
+ tflite2circle_cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ bufsize=1) as p:
+ for line in p.stdout:
+ sys.stdout.buffer.write(line)
+ f.write(line)
+ if p.returncode != 0:
+ sys.exit(p.returncode)
+
+
+def main():
+ # parse arguments
+ parser = _get_parser()
+ args = _parse_arg(parser)
+
+ # parse configuration file
+ _utils._parse_cfg(args, 'one-import-tf')
+
+ # verify arguments
+ _verify_arg(parser, args)
+
+ # convert
+ _convert(args)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/compiler/one-cmds/one-import-tflite b/compiler/one-cmds/one-import-tflite
new file mode 100644
index 000000000..fba697f24
--- /dev/null
+++ b/compiler/one-cmds/one-import-tflite
@@ -0,0 +1,111 @@
+#!/usr/bin/env bash
+''''export SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # '''
+''''export PY_PATH=${SCRIPT_PATH}/venv/bin/python # '''
+''''test -f ${PY_PATH} && exec ${PY_PATH} "$0" "$@" # '''
+''''echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command." # '''
+''''exit 255 # '''
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import os
+import subprocess
+import sys
+import tempfile
+
+import utils as _utils
+
+
+def _get_parser():
+ parser = argparse.ArgumentParser(
+ description='command line tool to convert TensorFlow lite to circle')
+
+ _utils._add_default_arg(parser)
+
+ ## tflite2circle arguments
+ tflite2circle_group = parser.add_argument_group('converter arguments')
+
+ # input and output path.
+ tflite2circle_group.add_argument(
+ '-i', '--input_path', type=str, help='full filepath of the input file')
+ tflite2circle_group.add_argument(
+ '-o', '--output_path', type=str, help='full filepath of the output file')
+
+ return parser
+
+
+def _verify_arg(parser, args):
+ """verify given arguments"""
+ # check if required arguments is given
+ missing = []
+ if not _utils._is_valid_attr(args, 'input_path'):
+ missing.append('-i/--input_path')
+ if not _utils._is_valid_attr(args, 'output_path'):
+ missing.append('-o/--output_path')
+ if len(missing):
+ parser.error('the following arguments are required: ' + ' '.join(missing))
+
+
+def _parse_arg(parser):
+ args = parser.parse_args()
+ # print version
+ if args.version:
+ _utils._print_version_and_exit(__file__)
+
+ return args
+
+
+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'
+
+ with open(logfile_path, 'wb') as f:
+ # make a command to convert from tflite to circle
+ tflite2circle_path = os.path.join(dir_path, 'tflite2circle')
+ tflite2circle_cmd = _utils._make_tflite2circle_cmd(tflite2circle_path,
+ getattr(args, 'input_path'),
+ getattr(args, 'output_path'))
+
+ f.write((' '.join(tflite2circle_cmd) + '\n').encode())
+
+ # convert tflite to circle
+ with subprocess.Popen(
+ tflite2circle_cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ bufsize=1) as p:
+ for line in p.stdout:
+ sys.stdout.buffer.write(line)
+ f.write(line)
+
+
+def main():
+ # parse arguments
+ parser = _get_parser()
+ args = _parse_arg(parser)
+
+ # parse configuration file
+ _utils._parse_cfg(args, 'one-import-tflite')
+
+ # verify arguments
+ _verify_arg(parser, args)
+
+ # convert
+ _convert(args)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/compiler/one-cmds/one-optimize b/compiler/one-cmds/one-optimize
new file mode 100644
index 000000000..4c5f10903
--- /dev/null
+++ b/compiler/one-cmds/one-optimize
@@ -0,0 +1,155 @@
+#!/usr/bin/env bash
+''''export SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # '''
+''''export PY_PATH=${SCRIPT_PATH}/venv/bin/python # '''
+''''test -f ${PY_PATH} && exec ${PY_PATH} "$0" "$@" # '''
+''''echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command." # '''
+''''exit 255 # '''
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import os
+import subprocess
+import sys
+import tempfile
+
+import utils as _utils
+
+
+def _get_parser():
+ parser = argparse.ArgumentParser(
+ description='command line tool to optimize circle model')
+
+ _utils._add_default_arg(parser)
+
+ ## circle2circle arguments
+ circle2circle_group = parser.add_argument_group('arguments for optimization')
+
+ # input and output path.
+ circle2circle_group.add_argument(
+ '-i', '--input_path', type=str, help='full filepath of the input file')
+ circle2circle_group.add_argument(
+ '-o', '--output_path', type=str, help='full filepath of the output file')
+
+ # optimization pass
+ circle2circle_group.add_argument(
+ '--all', action='store_true', help='enable all optimization pass')
+ circle2circle_group.add_argument(
+ '--fold_dequantize', action='store_true', help='fold Dequantize op')
+ circle2circle_group.add_argument(
+ '--fuse_add_with_tconv', action='store_true', help='fuse Add op to Transposed')
+ circle2circle_group.add_argument(
+ '--fuse_batchnorm_with_tconv',
+ action='store_true',
+ help='fuse BatchNorm op to Transposed Convolution op')
+ circle2circle_group.add_argument(
+ '--fuse_bcq', action='store_true', help='apply Binary Coded Quantization')
+ circle2circle_group.add_argument(
+ '--fuse_preactivation_batchnorm',
+ action='store_true',
+ help='fuse BatchNorm operators of pre-activations to Convolution op')
+ circle2circle_group.add_argument(
+ '--make_batchnorm_gamma_positive',
+ action='store_true',
+ help="""make negative gamma of BatchNorm to a small positive value (1e-10).
+ Note that this pass can change the execution result of the model.
+ So, use it only when the impact is known to be acceptable.""")
+ circle2circle_group.add_argument(
+ '--fuse_activation_function',
+ action='store_true',
+ help='fuse Activation function to a preceding operator')
+ circle2circle_group.add_argument(
+ '--fuse_instnorm', action='store_true', help='fuse ops to InstanceNorm operator')
+ circle2circle_group.add_argument(
+ '--resolve_customop_add',
+ action='store_true',
+ help='convert Custom(Add) op to Add op')
+ circle2circle_group.add_argument(
+ '--resolve_customop_batchmatmul',
+ action='store_true',
+ help='convert Custom(BatchMatmul) op to BatchMatmul op')
+ circle2circle_group.add_argument(
+ '--resolve_customop_matmul',
+ action='store_true',
+ help='convert Custom(Matmul) op to Matmul op')
+
+ return parser
+
+
+def _verify_arg(parser, args):
+ """verify given arguments"""
+ # check if required arguments is given
+ missing = []
+ if not _utils._is_valid_attr(args, 'input_path'):
+ missing.append('-i/--input_path')
+ if not _utils._is_valid_attr(args, 'output_path'):
+ missing.append('-o/--output_path')
+ if len(missing):
+ parser.error('the following arguments are required: ' + ' '.join(missing))
+
+
+def _parse_arg(parser):
+ args = parser.parse_args()
+ # print version
+ if args.version:
+ _utils._print_version_and_exit(__file__)
+
+ return args
+
+
+def _optimize(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:
+ # make a command to optimize circle model
+ circle2circle_path = os.path.join(dir_path, 'circle2circle')
+ circle2circle_cmd = _utils._make_circle2circle_cmd(args, circle2circle_path,
+ getattr(args, 'input_path'),
+ getattr(args, 'output_path'))
+
+ f.write((' '.join(circle2circle_cmd) + '\n').encode())
+
+ # optimize
+ with subprocess.Popen(
+ circle2circle_cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ bufsize=1) as p:
+ for line in p.stdout:
+ sys.stdout.buffer.write(line)
+ f.write(line)
+ if p.returncode != 0:
+ sys.exit(p.returncode)
+
+
+def main():
+ # parse arguments
+ parser = _get_parser()
+ args = _parse_arg(parser)
+
+ # parse configuration file
+ _utils._parse_cfg(args, 'one-optimize')
+
+ # verify arguments
+ _verify_arg(parser, args)
+
+ # optimize
+ _optimize(args)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/compiler/one-cmds/one-pack b/compiler/one-cmds/one-pack
new file mode 100644
index 000000000..99df4a829
--- /dev/null
+++ b/compiler/one-cmds/one-pack
@@ -0,0 +1,120 @@
+#!/usr/bin/env bash
+''''export SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # '''
+''''export PY_PATH=${SCRIPT_PATH}/venv/bin/python # '''
+''''test -f ${PY_PATH} && exec ${PY_PATH} "$0" "$@" # '''
+''''echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command." # '''
+''''exit 255 # '''
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import os
+import subprocess
+import sys
+import tempfile
+
+import utils as _utils
+
+
+def _get_parser():
+ parser = argparse.ArgumentParser(
+ description='command line tool to package circle and metadata into nnpackage')
+
+ _utils._add_default_arg(parser)
+
+ ## model2nnpkg arguments
+ model2nnpkg_group = parser.add_argument_group('arguments for packaging')
+
+ # input and output path.
+ model2nnpkg_group.add_argument(
+ '-i', '--input_path', type=str, help='full filepath of the input file')
+ model2nnpkg_group.add_argument(
+ '-o', '--output_path', type=str, help='full filepath of the output file')
+
+ return parser
+
+
+def _verify_arg(parser, args):
+ """verify given arguments"""
+ # check if required arguments is given
+ missing = []
+ if not _utils._is_valid_attr(args, 'input_path'):
+ missing.append('-i/--input_path')
+ if not _utils._is_valid_attr(args, 'output_path'):
+ missing.append('-o/--output_path')
+ if len(missing):
+ parser.error('the following arguments are required: ' + ' '.join(missing))
+
+
+def _parse_arg(parser):
+ args = parser.parse_args()
+ # print version
+ if args.version:
+ _utils._print_version_and_exit(__file__)
+
+ return args
+
+
+def _make_model2nnpkg_cmd(driver_path, input_path, output_path):
+ """make a command for running model2nnpkg"""
+ cmd = [os.path.expanduser(driver_path)]
+ cmd.append('-o')
+ cmd.append(os.path.expanduser(output_path))
+ cmd.append(os.path.expanduser(input_path))
+ return cmd
+
+
+def _pack(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:
+ # make a command to package circle model and metadata into nnpackage
+ model2nnpkg_path = os.path.join(dir_path, 'model2nnpkg.sh')
+ model2nnpkg_cmd = _make_model2nnpkg_cmd(model2nnpkg_path,
+ getattr(args, 'input_path'),
+ getattr(args, 'output_path'))
+
+ f.write((' '.join(model2nnpkg_cmd) + '\n').encode())
+
+ # convert tflite to circle
+ with subprocess.Popen(
+ model2nnpkg_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+ bufsize=1) as p:
+ for line in p.stdout:
+ sys.stdout.buffer.write(line)
+ f.write(line)
+ if p.returncode != 0:
+ sys.exit(p.returncode)
+
+
+def main():
+ # parse arguments
+ parser = _get_parser()
+ args = _parse_arg(parser)
+
+ # parse configuration file
+ _utils._parse_cfg(args, 'one-pack')
+
+ # verify arguments
+ _verify_arg(parser, args)
+
+ # package
+ _pack(args)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/compiler/one-cmds/one-prepare-venv b/compiler/one-cmds/one-prepare-venv
new file mode 100644
index 000000000..e5c88411f
--- /dev/null
+++ b/compiler/one-cmds/one-prepare-venv
@@ -0,0 +1,56 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+
+DRIVER_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+VENV_ACTIVATE=${DRIVER_PATH}/venv/bin/activate
+
+function error_no_ensurepip ()
+{
+ echo "ERROR: python3 'ensurepip' module is not found."
+ echo " On ubuntu, try following command:"
+ echo
+ echo " apt install python$(python3 --version | awk '{print $2}' | awk -F. '{print $1"."$2}')-venv"
+ echo
+ echo " You may need root privilege for this."
+ exit 1
+}
+
+if [ ! -f ${VENV_ACTIVATE} ]; then
+ # Install prerequisites
+ python3 -m ensurepip --version > /dev/null 2>&1 || error_no_ensurepip
+ python3 -m pip install --user -U virtualenv
+ # Create python virtual enviornment
+ python3 -m venv "${DRIVER_PATH}/venv"
+fi
+
+# Install tensorflow
+source "${VENV_ACTIVATE}"
+
+# TODO remove version number of 'pip==20.2.1 setuptools==49.3.0'
+# NOTE adding version is for temporary hotfix of setuptools 50.x.y version
+python -m pip --default-timeout=1000 --trusted-host pypi.org --trusted-host files.pythonhost.org \
+ install -U pip==20.2.1 setuptools==49.3.0
+python -m pip --default-timeout=1000 --trusted-host pypi.org --trusted-host files.pythonhost.org \
+ install tensorflow-cpu==2.3.0
+python -m pip --default-timeout=1000 --trusted-host pypi.org --trusted-host files.pythonhost.org \
+ install Pillow==6.2.2
+
+# Create python symoblic link
+rm -f ${DRIVER_PATH}/python
+ln -s venv/bin/python ${DRIVER_PATH}/python
diff --git a/compiler/one-cmds/one-quantize b/compiler/one-cmds/one-quantize
new file mode 100644
index 000000000..9bdfea8b8
--- /dev/null
+++ b/compiler/one-cmds/one-quantize
@@ -0,0 +1,232 @@
+#!/usr/bin/env bash
+''''export SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # '''
+''''export PY_PATH=${SCRIPT_PATH}/venv/bin/python # '''
+''''test -f ${PY_PATH} && exec ${PY_PATH} "$0" "$@" # '''
+''''echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command." # '''
+''''exit 255 # '''
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import os
+import subprocess
+import sys
+import tempfile
+
+import utils as _utils
+
+
+def _get_parser():
+ parser = argparse.ArgumentParser(
+ description='command line tool to quantize circle model')
+
+ _utils._add_default_arg(parser)
+
+ # input and output path.
+ parser.add_argument(
+ '-i', '--input_path', type=str, help='full filepath of the input file')
+ parser.add_argument(
+ '-d', '--input_data', type=str, help='full filepath of the input data file')
+ parser.add_argument(
+ '-o', '--output_path', type=str, help='full filepath of the output file')
+
+ ## arguments for quantization
+ quantization_group = parser.add_argument_group('arguments for quantization')
+
+ quantization_group.add_argument(
+ '--input_dtype',
+ type=str,
+ help='input data type (supported: float32, default=float32)')
+ quantization_group.add_argument(
+ '--quantized_dtype',
+ type=str,
+ help='output quantized data type (supported: uint8, int16, default=uint8)')
+ quantization_group.add_argument(
+ '--granularity',
+ type=str,
+ help='quantize granularity (supported: layer, channel, default=layer)')
+ quantization_group.add_argument(
+ '--min_percentile', type=str, help='minimum percentile (0.0~100.0, default=1.0)')
+ quantization_group.add_argument(
+ '--max_percentile', type=str, help='maximum percentile (0.0~100.0, default=99.0)')
+ quantization_group.add_argument(
+ '--mode',
+ type=str,
+ help='record mode (supported: percentile/moving_average, default=percentile)')
+
+ # set default values
+ quantization_group.set_defaults(
+ input_dtype='float32',
+ quantized_dtype='uint8',
+ granularity='layer',
+ min_percentile='1.0',
+ max_percentile='99.0',
+ mode='percentile')
+
+ return parser
+
+
+def _verify_arg(parser, args):
+ """verify given arguments"""
+ # check if required arguments is given
+ missing = []
+ if not _utils._is_valid_attr(args, 'input_path'):
+ missing.append('-i/--input_path')
+ if not _utils._is_valid_attr(args, 'input_data'):
+ missing.append('-d/--input_data')
+ if not _utils._is_valid_attr(args, 'output_path'):
+ missing.append('-o/--output_path')
+ if len(missing):
+ parser.error('the following arguments are required: ' + ' '.join(missing))
+
+
+def _parse_arg(parser):
+ args = parser.parse_args()
+ # print version
+ if args.version:
+ _utils._print_version_and_exit(__file__)
+
+ return args
+
+
+def _quantize(args):
+ # get file path to log
+ dir_path = os.path.dirname(os.path.realpath(__file__))
+ logfile_path = os.path.realpath(args.output_path) + '.log'
+
+ with open(logfile_path, 'wb') as f, tempfile.TemporaryDirectory() as tmpdir:
+ # get driver path
+ circle_quantizer_path = os.path.join(dir_path, 'circle-quantizer')
+ record_minmax_path = os.path.join(dir_path, 'record-minmax')
+
+ ## make a command to quantize and dequantize the weights of the model
+ circle_quantizer_cmd = [circle_quantizer_path]
+ # quantize_dequantize_weights
+ circle_quantizer_cmd.append('--quantize_dequantize_weights')
+ if _utils._is_valid_attr(args, 'input_dtype'):
+ circle_quantizer_cmd.append(getattr(args, 'input_dtype'))
+ if _utils._is_valid_attr(args, 'quantized_dtype'):
+ circle_quantizer_cmd.append(getattr(args, 'quantized_dtype'))
+ if _utils._is_valid_attr(args, 'granularity'):
+ circle_quantizer_cmd.append(getattr(args, 'granularity'))
+ # input and output path
+ if _utils._is_valid_attr(args, 'input_path'):
+ circle_quantizer_cmd.append(getattr(args, 'input_path'))
+ tmp_output_path_1 = os.path.join(
+ tmpdir,
+ os.path.splitext(os.path.basename(args.input_path))[0]) + '1.circle'
+ circle_quantizer_cmd.append(tmp_output_path_1)
+
+ f.write((' '.join(circle_quantizer_cmd) + '\n').encode())
+
+ # run circle-quantizer
+ with subprocess.Popen(
+ circle_quantizer_cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ bufsize=1) as p:
+ for line in p.stdout:
+ sys.stdout.buffer.write(line)
+ f.write(line)
+ if p.returncode != 0:
+ sys.exit(p.returncode)
+
+ ## make a command to record min-max value of each tensor while running the representative dataset
+ circle_record_minmax_cmd = [record_minmax_path]
+ # input and output path
+ circle_record_minmax_cmd.append('--input_model')
+ circle_record_minmax_cmd.append(tmp_output_path_1)
+ tmp_output_path_2 = os.path.join(
+ tmpdir,
+ os.path.splitext(os.path.basename(args.input_path))[0]) + '2.circle'
+ circle_record_minmax_cmd.append('--output_model')
+ circle_record_minmax_cmd.append(tmp_output_path_2)
+ # input data
+ if _utils._is_valid_attr(args, 'input_data'):
+ circle_record_minmax_cmd.append('--input_data')
+ circle_record_minmax_cmd.append(getattr(args, 'input_data'))
+ # min and max percentile
+ if _utils._is_valid_attr(args, 'min_percentile'):
+ circle_record_minmax_cmd.append('--min_percentile')
+ circle_record_minmax_cmd.append(getattr(args, 'min_percentile'))
+ if _utils._is_valid_attr(args, 'max_percentile'):
+ circle_record_minmax_cmd.append('--max_percentile')
+ circle_record_minmax_cmd.append(getattr(args, 'max_percentile'))
+ # mode
+ if _utils._is_valid_attr(args, 'mode'):
+ circle_record_minmax_cmd.append('--mode')
+ circle_record_minmax_cmd.append(getattr(args, 'mode'))
+
+ f.write((' '.join(circle_record_minmax_cmd) + '\n').encode())
+
+ # run record-minmax
+ with subprocess.Popen(
+ circle_record_minmax_cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ bufsize=1) as p:
+ for line in p.stdout:
+ sys.stdout.buffer.write(line)
+ f.write(line)
+ if p.returncode != 0:
+ sys.exit(p.returncode)
+
+ ## make a second command to quantize the model using the embedded information
+ circle_quantizer_cmd = [circle_quantizer_path]
+ # quantize_dequantize_weights
+ circle_quantizer_cmd.append('--quantize_with_minmax')
+ if _utils._is_valid_attr(args, 'input_dtype'):
+ circle_quantizer_cmd.append(getattr(args, 'input_dtype'))
+ if _utils._is_valid_attr(args, 'quantized_dtype'):
+ circle_quantizer_cmd.append(getattr(args, 'quantized_dtype'))
+ if _utils._is_valid_attr(args, 'granularity'):
+ circle_quantizer_cmd.append(getattr(args, 'granularity'))
+ # input and output path
+ circle_quantizer_cmd.append(tmp_output_path_2)
+ if _utils._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
+ with subprocess.Popen(
+ circle_quantizer_cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ bufsize=1) as p:
+ for line in p.stdout:
+ sys.stdout.buffer.write(line)
+ f.write(line)
+ if p.returncode != 0:
+ sys.exit(p.returncode)
+
+
+def main():
+ # parse arguments
+ parser = _get_parser()
+ args = _parse_arg(parser)
+
+ # parse configuration file
+ _utils._parse_cfg(args, 'one-quantize')
+
+ # verify arguments
+ _verify_arg(parser, args)
+
+ # quantize
+ _quantize(args)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/compiler/one-cmds/requires.cmake b/compiler/one-cmds/requires.cmake
new file mode 100644
index 000000000..b1aabdb97
--- /dev/null
+++ b/compiler/one-cmds/requires.cmake
@@ -0,0 +1,8 @@
+require("tf2tfliteV2")
+require("tflite2circle")
+require("circle2circle")
+require("circle-quantizer")
+require("record-minmax")
+require("vconone")
+require("bcq-tools")
+require("rawdata2hdf5")
diff --git a/compiler/one-cmds/tests/CMakeLists.txt b/compiler/one-cmds/tests/CMakeLists.txt
new file mode 100644
index 000000000..412787a64
--- /dev/null
+++ b/compiler/one-cmds/tests/CMakeLists.txt
@@ -0,0 +1,62 @@
+# Install one-cmds test scripts
+
+# Gather test scripts
+file(GLOB TESTITEMS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "./*.test")
+file(GLOB CONFIGITEMS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "./*.cfg")
+
+# Create a script to run the tests at installation folder
+set(DRIVER_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/runtestall.sh")
+
+file(WRITE "${DRIVER_SCRIPT}" "#!/bin/bash\n\n")
+file(APPEND "${DRIVER_SCRIPT}" "SCRIPT_PATH=$(cd $(dirname $\{BASH_SOURCE\[0\]\}) && pwd)\n")
+file(APPEND "${DRIVER_SCRIPT}" "pushd $SCRIPT_PATH > /dev/null\n")
+file(APPEND "${DRIVER_SCRIPT}" "rm -rf runtestall.log\n")
+file(APPEND "${DRIVER_SCRIPT}" "export PATH=$SCRIPT_PATH/../bin:$PATH\n")
+file(APPEND "${DRIVER_SCRIPT}" "if [[ $# -ge 1 ]]; then\n")
+file(APPEND "${DRIVER_SCRIPT}" " USER_PATH=$1\n")
+file(APPEND "${DRIVER_SCRIPT}" " export PATH=$USER_PATH:$PATH\n")
+file(APPEND "${DRIVER_SCRIPT}" "fi\n")
+file(APPEND "${DRIVER_SCRIPT}" "\n")
+
+foreach(TESTITEM IN ITEMS ${TESTITEMS})
+ get_filename_component(ITEM_PREFIX ${TESTITEM} NAME_WE)
+
+ set(TESTITEM_SCRIPT_FILE "${ITEM_PREFIX}.test")
+ set(TESTITEM_SCRIPT_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${TESTITEM_SCRIPT_FILE}")
+
+ file(APPEND "${DRIVER_SCRIPT}" "/bin/bash ${TESTITEM_SCRIPT_FILE} | tee -a runtestall.log\n")
+
+ install(FILES ${TESTITEM} DESTINATION test)
+
+endforeach(TESTITEM)
+
+foreach(CONFIGITEM IN ITEMS ${CONFIGITEMS})
+ get_filename_component(ITEM_PREFIX ${CONFIGITEM} NAME_WE)
+ install(FILES ${CONFIGITEM} DESTINATION test)
+endforeach(CONFIGITEM)
+
+file(APPEND "${DRIVER_SCRIPT}" "popd> /dev/null")
+
+set(PREPARE_TEST_MATERIALS_SH "${CMAKE_CURRENT_SOURCE_DIR}/prepare_test_materials.sh")
+set(PREPROCESS_IMAGES_PY "${CMAKE_CURRENT_SOURCE_DIR}/preprocess_images.py")
+
+install(FILES ${DRIVER_SCRIPT}
+ PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE
+ GROUP_READ GROUP_EXECUTE
+ WORLD_READ WORLD_EXECUTE
+ DESTINATION test)
+
+install(FILES ${PREPARE_TEST_MATERIALS_SH}
+ PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE
+ GROUP_READ GROUP_EXECUTE
+ WORLD_READ WORLD_EXECUTE
+ DESTINATION test)
+
+install(FILES ${PREPROCESS_IMAGES_PY}
+ PERMISSIONS OWNER_WRITE OWNER_READ
+ GROUP_READ
+ WORLD_READ
+ DESTINATION test)
+
+install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/README.txt
+ DESTINATION test)
diff --git a/compiler/one-cmds/tests/README.txt b/compiler/one-cmds/tests/README.txt
new file mode 100644
index 000000000..0d4d0ecbe
--- /dev/null
+++ b/compiler/one-cmds/tests/README.txt
@@ -0,0 +1,27 @@
+one-cmds testing
+================
+
+Run 'runtestall.sh' program to test ONE command line programs, all at once.
+
+Steps:
+1) run 'one-prepare-venv' in bin folder to prepare python virtual-env with TensorFlow
+ - you need to run this only once
+ - read 'doc/how-to-prepare-virtualenv.txt' for more information
+----------------------------------------------
+bin/one-prepare-venv
+----------------------------------------------
+
+2) run 'test/prepare_test_materials.sh' to download test material models
+ - you need to run this only once
+ - you need internet connection to download files
+ - you may need to install 'wget' and 'unzip' packages
+----------------------------------------------
+test/prepare_test_materials.sh
+----------------------------------------------
+
+3) run 'test/runtestall.sh' to run the test
+----------------------------------------------
+test/runtestall.sh
+----------------------------------------------
+
+End.
diff --git a/compiler/one-cmds/tests/one-build_001.cfg b/compiler/one-cmds/tests/one-build_001.cfg
new file mode 100644
index 000000000..8524bbd1f
--- /dev/null
+++ b/compiler/one-cmds/tests/one-build_001.cfg
@@ -0,0 +1,20 @@
+[one-build]
+one-import-tf=True
+one-import-tflite=False
+one-import-bcq=False
+one-optimize=True
+one-quantize=False
+one-pack=False
+one-codegen=False
+
+[one-import-tf]
+input_path=inception_v3.pb
+output_path=inception_v3.circle
+input_arrays=input
+input_shapes=1,299,299,3
+output_arrays=InceptionV3/Predictions/Reshape_1
+v2=True
+
+[one-optimize]
+input_path=inception_v3.circle
+output_path=inception_v3.opt.circle
diff --git a/compiler/one-cmds/tests/one-build_001.test b/compiler/one-cmds/tests/one-build_001.test
new file mode 100644
index 000000000..fb4877344
--- /dev/null
+++ b/compiler/one-cmds/tests/one-build_001.test
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+configfile="one-build_001.cfg"
+outputfile="inception_v3.opt.circle"
+
+# run test
+one-build -C ${configfile} > /dev/null
+
+if [[ ! -s "${outputfile}" ]]; then
+ trap_err_onexit
+fi
+
+echo "${filename_ext} SUCCESS"
diff --git a/compiler/one-cmds/tests/one-build_002.cfg b/compiler/one-cmds/tests/one-build_002.cfg
new file mode 100644
index 000000000..183077680
--- /dev/null
+++ b/compiler/one-cmds/tests/one-build_002.cfg
@@ -0,0 +1,24 @@
+[one-build]
+one-import-tf=True
+one-import-tflite=False
+one-import-bcq=False
+one-optimize=True
+one-quantize=False
+one-pack=True
+one-codegen=False
+
+[one-import-tf]
+input_path=inception_v3.pb
+output_path=inception_v3.circle
+input_arrays=input
+input_shapes=1,299,299,3
+output_arrays=InceptionV3/Predictions/Reshape_1
+v2=True
+
+[one-optimize]
+input_path=inception_v3.circle
+output_path=inception_v3.opt.circle
+
+[one-pack]
+input_path=inception_v3.opt.circle
+output_path=inception_v3_pkg
diff --git a/compiler/one-cmds/tests/one-build_002.test b/compiler/one-cmds/tests/one-build_002.test
new file mode 100644
index 000000000..fdfd607e2
--- /dev/null
+++ b/compiler/one-cmds/tests/one-build_002.test
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+configfile="one-build_002.cfg"
+outputfile="inception_v3_pkg"
+
+# run test
+one-build -C ${configfile} > /dev/null
+
+if [[ ! -s "${outputfile}" ]]; then
+ trap_err_onexit
+fi
+
+echo "${filename_ext} SUCCESS"
diff --git a/compiler/one-cmds/tests/one-build_neg_001.test b/compiler/one-cmds/tests/one-build_neg_001.test
new file mode 100644
index 000000000..2ea26d23f
--- /dev/null
+++ b/compiler/one-cmds/tests/one-build_neg_001.test
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with missing configuration file
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Not found given configuration file" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+configfile="one-build_neg_001.cfg"
+
+# run test
+one-build -C ${configfile} > ${filename}.log 2>&1
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-build_neg_002.cfg b/compiler/one-cmds/tests/one-build_neg_002.cfg
new file mode 100644
index 000000000..360c601e0
--- /dev/null
+++ b/compiler/one-cmds/tests/one-build_neg_002.cfg
@@ -0,0 +1,20 @@
+[one-build]
+one-import-tf=True
+one-import-tflite=False
+one-import-bcq=False
+one-optimize=True
+one-quantize=False
+one-pack=True
+one-codegen=False
+
+[one-import-tf]
+input_path=inception_v3.pb
+output_path=inception_v3.circle
+input_arrays=input
+input_shapes=1,299,299,3
+output_arrays=InceptionV3/Predictions/Reshape_1
+v2=True
+
+[one-optimize]
+input_path=inception_v3.circle
+output_path=inception_v3.opt.circle
diff --git a/compiler/one-cmds/tests/one-build_neg_002.test b/compiler/one-cmds/tests/one-build_neg_002.test
new file mode 100644
index 000000000..15550d889
--- /dev/null
+++ b/compiler/one-cmds/tests/one-build_neg_002.test
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with missing one-pack section in configuration file
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "configuration file must have 'one-pack' section" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+configfile="one-build_neg_002.cfg"
+
+# run test
+one-build -C ${configfile} > ${filename}.log 2>&1
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-build_neg_003.cfg b/compiler/one-cmds/tests/one-build_neg_003.cfg
new file mode 100644
index 000000000..91e7875ac
--- /dev/null
+++ b/compiler/one-cmds/tests/one-build_neg_003.cfg
@@ -0,0 +1,15 @@
+[one-import-tf]
+input_path=inception_v3.pb
+output_path=inception_v3.circle
+input_arrays=input
+input_shapes=1,299,299,3
+output_arrays=InceptionV3/Predictions/Reshape_1
+v2=True
+
+[one-optimize]
+input_path=inception_v3.circle
+output_path=inception_v3.opt.circle
+
+[one-pack]
+input_path=inception_v3.opt.circle
+output_path=inception_v3_pkg
diff --git a/compiler/one-cmds/tests/one-build_neg_003.test b/compiler/one-cmds/tests/one-build_neg_003.test
new file mode 100644
index 000000000..a8ad24049
--- /dev/null
+++ b/compiler/one-cmds/tests/one-build_neg_003.test
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with missing one-build section in configuration file
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "'one-build' section is required in configuraion file" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+configfile="one-build_neg_003.cfg"
+
+# run test
+one-build -C ${configfile} > ${filename}.log 2>&1
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-build_neg_004.cfg b/compiler/one-cmds/tests/one-build_neg_004.cfg
new file mode 100644
index 000000000..4d312c47c
--- /dev/null
+++ b/compiler/one-cmds/tests/one-build_neg_004.cfg
@@ -0,0 +1,24 @@
+[one-build]
+one-import-tf=True
+one-import-tflite=False
+one-import-bcq=False
+one-optimize=True
+one-quantize=False
+one-pack=False
+one-codegen=False
+
+[one-import-tf]
+input_path=inception_v3.pb
+output_path=inception_v3.circle
+input_arrays=input
+input_shapes=1,299,299,3
+output_arrays=InceptionV3/Predictions/Reshape_1
+v2=True
+
+[one-optimize]
+input_path=inception_v3.circle
+output_path=inception_v3.opt.circle
+
+[one-optimize]
+input_path=inception_v4.circle
+output_path=inception_v4.opt.circle
diff --git a/compiler/one-cmds/tests/one-build_neg_004.test b/compiler/one-cmds/tests/one-build_neg_004.test
new file mode 100644
index 000000000..aebdfe5ad
--- /dev/null
+++ b/compiler/one-cmds/tests/one-build_neg_004.test
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with duplicate section
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "section 'one-optimize' already exists" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+configfile="one-build_neg_004.cfg"
+
+# run test
+one-build -C ${configfile} > ${filename}.log 2>&1
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import-bcq_001.test b/compiler/one-cmds/tests/one-import-bcq_001.test
new file mode 100644
index 000000000..74b8bab32
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_001.test
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./bcq.pb"
+outputfile="./bcq.circle"
+
+rm -rf $outputfile
+rm -rf $outputfile.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder \
+--output_arrays MatMul >> /dev/null
+
+if [[ ! -s "${outputfile}" ]]; then
+ trap_err_onexit
+fi
+
+echo "${filename_ext} SUCCESS"
diff --git a/compiler/one-cmds/tests/one-import-bcq_neg_001.test b/compiler/one-cmds/tests/one-import-bcq_neg_001.test
new file mode 100644
index 000000000..9a2b455ef
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_neg_001.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid input array
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: Invalid tensors" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./bcq.pb"
+outputfile="./bcq.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder_null \
+--output_arrays MatMul > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import-bcq_neg_002.test b/compiler/one-cmds/tests/one-import-bcq_neg_002.test
new file mode 100644
index 000000000..5779aa03e
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_neg_002.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid output array
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: Invalid tensors" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./bcq.pb"
+outputfile="./bcq.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder \
+--output_arrays MatMul_null > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import-bcq_neg_003.test b/compiler/one-cmds/tests/one-import-bcq_neg_003.test
new file mode 100644
index 000000000..9cc9bcb7f
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_neg_003.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid input path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "No such file or directory" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./bcq_null.pb"
+outputfile="./bcq.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder \
+--output_arrays MatMul > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import-bcq_neg_004.test b/compiler/one-cmds/tests/one-import-bcq_neg_004.test
new file mode 100644
index 000000000..dc83b98c8
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_neg_004.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid input path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Error parsing message" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./while_3.pbtxt"
+outputfile="./bcq.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder \
+--output_arrays MatMul > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import-bcq_neg_005.test b/compiler/one-cmds/tests/one-import-bcq_neg_005.test
new file mode 100644
index 000000000..ad1196a67
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_neg_005.test
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid output path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Failed to write circle" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./bcq.pb"
+outputfile="."
+
+rm -rf ${outputfile}.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder \
+--output_arrays MatMul > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import-bcq_neg_006.test b/compiler/one-cmds/tests/one-import-bcq_neg_006.test
new file mode 100644
index 000000000..f761aa7c0
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_neg_006.test
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid input shapes
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: The shape of tensor" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./bcq.pb"
+outputfile="./bcq.circle"
+
+rm -rf ${outputfile}.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder --input_shapes "1,32,32" \
+--output_arrays MatMul > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import-bcq_neg_007.test b/compiler/one-cmds/tests/one-import-bcq_neg_007.test
new file mode 100644
index 000000000..5013254f5
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_neg_007.test
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid input shapes
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: The shape of tensor" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./bcq.pb"
+outputfile="./bcq.circle"
+
+rm -rf ${outputfile}.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder --input_shapes "30,30" \
+--output_arrays MatMul > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import-bcq_neg_008.test b/compiler/one-cmds/tests/one-import-bcq_neg_008.test
new file mode 100644
index 000000000..e7d5d2e03
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_neg_008.test
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid input shapes
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: invalid literal for" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./bcq.pb"
+outputfile="./bcq.circle"
+
+rm -rf ${outputfile}.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder --input_shapes "32,O" \
+--output_arrays MatMul > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import-bcq_neg_009.test b/compiler/one-cmds/tests/one-import-bcq_neg_009.test
new file mode 100644
index 000000000..ef990438a
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import-bcq_neg_009.test
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid input shapes
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "must have the same number of items" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./bcq.pb"
+outputfile="./bcq.circle"
+
+rm -rf ${outputfile}.log
+
+# run test
+one-import-bcq \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Placeholder --input_shapes "32,32:1" \
+--output_arrays MatMul > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_001.test b/compiler/one-cmds/tests/one-import_001.test
new file mode 100644
index 000000000..165f49193
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_001.test
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3.circle"
+
+# Note: Do not remove output circle file as it's used for quantize tests
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input --input_shapes "1,299,299,3" \
+--output_arrays InceptionV3/Predictions/Reshape_1 >> /dev/null
+
+if [[ ! -s "${outputfile}" ]]; then
+ trap_err_onexit
+fi
+
+echo "${filename_ext} SUCCESS"
diff --git a/compiler/one-cmds/tests/one-import_002.cfg b/compiler/one-cmds/tests/one-import_002.cfg
new file mode 100644
index 000000000..9a90abecd
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_002.cfg
@@ -0,0 +1,16 @@
+[one-build]
+one-import-tf=True
+one-import-tflite=False
+one-import-bcq=False
+one-optimize=False
+one-quantize=False
+one-pack=False
+one-codegen=False
+
+[one-import-tf]
+input_path=inception_v3.pb
+output_path=inception_v3.circle
+input_arrays=input
+input_shapes=1,299,299,3
+output_arrays=InceptionV3/Predictions/Reshape_1
+v2=True
diff --git a/compiler/one-cmds/tests/one-import_002.test b/compiler/one-cmds/tests/one-import_002.test
new file mode 100644
index 000000000..0f8d69099
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_002.test
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# positive usage with overriding option
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+configfile="one-import_002.cfg"
+outputfile="inception_v3_cmd.circle"
+
+# run test
+one-import tf -C ${configfile} \
+--output_path=inception_v3_cmd.circle > /dev/null
+
+if [[ ! -s "${outputfile}" ]]; then
+ trap_err_onexit
+fi
+
+echo "${filename_ext} SUCCESS"
diff --git a/compiler/one-cmds/tests/one-import_neg_001.test b/compiler/one-cmds/tests/one-import_neg_001.test
new file mode 100644
index 000000000..5a233d2f3
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_001.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage improper input model
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: Invalid tensors 'input' were found" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.tflite"
+outputfile="./inception_v3.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input --input_shapes "1,299,299,3" \
+--output_arrays InceptionV3/Predictions/Reshape_1 > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_neg_002.test b/compiler/one-cmds/tests/one-import_neg_002.test
new file mode 100644
index 000000000..78b77511a
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_002.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with unsupported dynamic tensor
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "is incompatible with result type" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./while_3.pbtxt"
+outputfile="./while_3.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays Hole,Hole_2 --input_shapes "1,1:1,1" \
+--output_arrays Output > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_neg_003.test b/compiler/one-cmds/tests/one-import_neg_003.test
new file mode 100644
index 000000000..b0e95b6d1
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_003.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid output array
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: Invalid tensors" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input --input_shapes "1,299,299,3" \
+--output_arrays InceptionV3/Predictions/Reshape_2 > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_neg_004.test b/compiler/one-cmds/tests/one-import_neg_004.test
new file mode 100644
index 000000000..afb206ad9
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_004.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid input shape
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: The shape of tensor" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input --input_shapes "1,299,299,1" \
+--output_arrays InceptionV3/Predictions/Reshape_1 > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_neg_005.test b/compiler/one-cmds/tests/one-import_neg_005.test
new file mode 100644
index 000000000..045fb89b7
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_005.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid input shape
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: The shape of tensor" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input --input_shapes "1,299,299" \
+--output_arrays InceptionV3/Predictions/Reshape_1 > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_neg_006.test b/compiler/one-cmds/tests/one-import_neg_006.test
new file mode 100644
index 000000000..bb3e2344c
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_006.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid input shape
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ConverterError: <unknown>:0: error:" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input --input_shapes "0,299,299,3" \
+--output_arrays InceptionV3/Predictions/Reshape_1 > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_neg_007.test b/compiler/one-cmds/tests/one-import_neg_007.test
new file mode 100644
index 000000000..bcfc4bcc9
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_007.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid input shape
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: invalid literal" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input --input_shapes "None,299,299,3" \
+--output_arrays InceptionV3/Predictions/Reshape_1 > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_neg_008.test b/compiler/one-cmds/tests/one-import_neg_008.test
new file mode 100644
index 000000000..2f48fd708
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_008.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid input shape
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "must have the same number of items" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input,InceptionV3/Predictions/Shape --input_shapes "1,299,299,3" \
+--output_arrays InceptionV3/Predictions/Reshape_1 > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_neg_009.test b/compiler/one-cmds/tests/one-import_neg_009.test
new file mode 100644
index 000000000..79e463d64
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_009.test
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid output path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Failed to write circle" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="."
+
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input --input_shapes "1,299,299,3" \
+--output_arrays InceptionV3/Predictions/Reshape_1 > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-import_neg_010.test b/compiler/one-cmds/tests/one-import_neg_010.test
new file mode 100644
index 000000000..05677a6d4
--- /dev/null
+++ b/compiler/one-cmds/tests/one-import_neg_010.test
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid input_arrays
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "ValueError: Invalid tensors" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-import tf \
+--input_path ${inputfile} \
+--output_path ${outputfile} \
+--input_arrays input2 --input_shapes "1,299,299,3" \
+--output_arrays InceptionV3/Predictions/Reshape_1 > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-optimize_001.test b/compiler/one-cmds/tests/one-optimize_001.test
new file mode 100644
index 000000000..240a62506
--- /dev/null
+++ b/compiler/one-cmds/tests/one-optimize_001.test
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+outputfile="./inception_v3-opt.circle"
+
+rm -rf ${outputfile}
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-optimize --all \
+--input_path ${inputfile} \
+--output_path ${outputfile} >> /dev/null
+
+if [[ ! -s "${outputfile}" ]]; then
+ trap_err_onexit
+fi
+
+echo "${filename_ext} SUCCESS"
diff --git a/compiler/one-cmds/tests/one-optimize_neg_001.test b/compiler/one-cmds/tests/one-optimize_neg_001.test
new file mode 100644
index 000000000..4ee509697
--- /dev/null
+++ b/compiler/one-cmds/tests/one-optimize_neg_001.test
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# this test should fail
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Invalid input file" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3-opt.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-optimize --all \
+--input_path ${inputfile} \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-optimize_neg_002.test b/compiler/one-cmds/tests/one-optimize_neg_002.test
new file mode 100644
index 000000000..2c2a29a87
--- /dev/null
+++ b/compiler/one-cmds/tests/one-optimize_neg_002.test
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid input path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Failed to open file" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circletxt"
+outputfile="./inception_v3-opt.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# run test
+one-optimize --all \
+--input_path ${inputfile} \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-optimize_neg_003.test b/compiler/one-cmds/tests/one-optimize_neg_003.test
new file mode 100644
index 000000000..95f08fd95
--- /dev/null
+++ b/compiler/one-cmds/tests/one-optimize_neg_003.test
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage without output folder path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "the following arguments are required: -o/--output_path" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-optimize --all \
+--input_path "${inputfile}" > "${filename}.log" 2>&1
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-pack_001.test b/compiler/one-cmds/tests/one-pack_001.test
new file mode 100644
index 000000000..3d746dffd
--- /dev/null
+++ b/compiler/one-cmds/tests/one-pack_001.test
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+outputfolder="nnpack"
+
+rm -rf ${outputfolder}
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-pack \
+-i ${inputfile} \
+-o ${outputfolder} >> /dev/null
+
+if [[ ! -d "${outputfolder}" ]]; then
+ trap_err_onexit
+fi
+
+echo "${filename_ext} SUCCESS"
diff --git a/compiler/one-cmds/tests/one-pack_neg_001.test b/compiler/one-cmds/tests/one-pack_neg_001.test
new file mode 100644
index 000000000..6b7411db1
--- /dev/null
+++ b/compiler/one-cmds/tests/one-pack_neg_001.test
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid input path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "inception_v2.circle does not exist" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+rm -rf ${filename}.log
+
+# run test
+one-pack \
+-i ./inception_v2.circle \
+-o nnpack > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-pack_neg_002.test b/compiler/one-cmds/tests/one-pack_neg_002.test
new file mode 100644
index 000000000..6427c260e
--- /dev/null
+++ b/compiler/one-cmds/tests/one-pack_neg_002.test
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with filename without extension
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "modelfile does not have extension" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+rm -rf ${filename}.log
+rm -rf nnpack
+
+# prepare dummy file
+touch ./sample
+
+# run test
+one-pack \
+-i ./sample \
+-o nnpack > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-pack_neg_003.test b/compiler/one-cmds/tests/one-pack_neg_003.test
new file mode 100644
index 000000000..5db4a3805
--- /dev/null
+++ b/compiler/one-cmds/tests/one-pack_neg_003.test
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage without output folder path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "the following arguments are required: -o/--output_path" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+rm -rf ${filename}.log
+
+# prepare dummy file
+touch ./sample.circle
+
+# run test
+one-pack \
+-i ./sample.circle > "${filename}.log" 2>&1
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_001.test b/compiler/one-cmds/tests/one-quantize_001.test
new file mode 100644
index 000000000..ef25ac800
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_001.test
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ./inception_v3.circle \
+--input_data ./inception_v3_test_data.h5 \
+--output_path ./inception_v3.quantized.circle >> /dev/null
+
+if [[ ! -s "${outputfile}" ]]; then
+ trap_err_onexit
+fi
+
+echo "${filename_ext} SUCCESS"
diff --git a/compiler/one-cmds/tests/one-quantize_neg_001.test b/compiler/one-cmds/tests/one-quantize_neg_001.test
new file mode 100644
index 000000000..ccf16fbf8
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_001.test
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with unsupported input dtype
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Unsupported input type" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# test begin
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+one-quantize \
+--input_dtype float64 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_002.test b/compiler/one-cmds/tests/one-quantize_neg_002.test
new file mode 100644
index 000000000..387e53637
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_002.test
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with unsupported input dtype
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Unsupported output type" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint16 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_003.test b/compiler/one-cmds/tests/one-quantize_neg_003.test
new file mode 100644
index 000000000..47cf0691f
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_003.test
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with wrong representative dataset
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Input shape mismatch" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ./mobilenet_test_data.h5 \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_004.test b/compiler/one-cmds/tests/one-quantize_neg_004.test
new file mode 100644
index 000000000..8d53ca1d1
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_004.test
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid output path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Failed to export" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="."
+
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_005.test b/compiler/one-cmds/tests/one-quantize_neg_005.test
new file mode 100644
index 000000000..4504730d0
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_005.test
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid input path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Invalid input file" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./while_3.pbtxt"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}.log
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_006.test b/compiler/one-cmds/tests/one-quantize_neg_006.test
new file mode 100644
index 000000000..63e7aecef
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_006.test
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid input path
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Failed to open file: ./inception_v2.circle" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v2.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}.log
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_007.test b/compiler/one-cmds/tests/one-quantize_neg_007.test
new file mode 100644
index 000000000..4796f9e89
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_007.test
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid input_data
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Given data file is not HDF5" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3.circle"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_008.test b/compiler/one-cmds/tests/one-quantize_neg_008.test
new file mode 100644
index 000000000..1656c6ba2
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_008.test
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid mode
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Unsupported mode" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--mode average \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_009.test b/compiler/one-cmds/tests/one-quantize_neg_009.test
new file mode 100644
index 000000000..6ecc5c32f
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_009.test
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid max_percentile
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Percentile must be ranged from" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--max_percentile 101 \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_010.test b/compiler/one-cmds/tests/one-quantize_neg_010.test
new file mode 100644
index 000000000..209645990
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_010.test
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid max_percentile
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Percentile must be ranged from" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--max_percentile -1 \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_011.test b/compiler/one-cmds/tests/one-quantize_neg_011.test
new file mode 100644
index 000000000..ea44c2ffe
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_011.test
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid min_percentile
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Percentile must be ranged from" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--min_percentile 101 \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_012.test b/compiler/one-cmds/tests/one-quantize_neg_012.test
new file mode 100644
index 000000000..b744051ae
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_012.test
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid min_percentile
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Percentile must be ranged from" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--min_percentile -1 \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/one-quantize_neg_013.test b/compiler/one-cmds/tests/one-quantize_neg_013.test
new file mode 100644
index 000000000..dec1cbd06
--- /dev/null
+++ b/compiler/one-cmds/tests/one-quantize_neg_013.test
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# negative usage with invalid min_percentile
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Unsupported granularity" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./inception_v3.circle"
+inputdata="./inception_v3_test_data.h5"
+outputfile="./inception_v3.quantized.circle"
+
+rm -rf ${outputfile}.log
+
+# to create inception_v3.circle
+if [[ ! -s ${inputfile} ]]; then
+ /bin/bash one-import_001.test >> /dev/null
+ return_code=$?
+ if [[ ${return_code} != 0 ]]; then
+ trap_err_onexit
+ fi
+fi
+
+# run test
+one-quantize \
+--input_dtype float32 \
+--quantized_dtype uint8 \
+--input_path ${inputfile} \
+--input_data ${inputdata} \
+--granularity layered \
+--output_path ${outputfile} > ${filename}.log
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/prepare_test_materials.sh b/compiler/one-cmds/tests/prepare_test_materials.sh
new file mode 100644
index 000000000..cb1067e28
--- /dev/null
+++ b/compiler/one-cmds/tests/prepare_test_materials.sh
@@ -0,0 +1,78 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# See https://github.com/Samsung/ONE/issues/4155 for information
+
+SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+pushd $SCRIPT_PATH > /dev/null
+
+if [[ ! -s "inception_v3.pb" ]]; then
+ rm -rf inception_v3_2018_04_27.tgz
+ wget https://storage.googleapis.com/download.tensorflow.org/models/tflite/model_zoo/upload_20180427/inception_v3_2018_04_27.tgz
+ tar zxvf inception_v3_2018_04_27.tgz
+fi
+
+if [[ ! -s "while_3.pbtxt" ]]; then
+ rm -rf while_3.zip
+ wget https://github.com/Samsung/ONE/files/5095630/while_3.zip
+ unzip while_3.zip
+ # https://github.com/Samsung/ONE/issues/4155#issuecomment-689320297
+fi
+
+if [[ ! -s "mobilenet_test_data.h5" ]]; then
+ rm -rf mobilenet_test_data.zip
+ wget https://github.com/Samsung/ONE/files/5139460/mobilenet_test_data.zip
+ unzip mobilenet_test_data.zip
+ # https://github.com/Samsung/ONE/issues/4155#issuecomment-689321538
+fi
+
+if [[ ! -s "bcq.pb" ]]; then
+ rm -rf bcq.pb.zip
+ wget https://github.com/Samsung/ONE/files/5153842/bcq.pb.zip
+ unzip bcq.pb.zip
+ # https://github.com/Samsung/ONE/issues/4155#issuecomment-689324597
+fi
+
+if [[ ! -s "img_files" ]]; then
+ rm -rf img_files.zip
+ wget https://github.com/Samsung/ONE/files/5499172/img_files.zip
+ unzip img_files.zip
+ # https://github.com/Samsung/ONE/issues/3213#issuecomment-722757499
+fi
+
+if [ ! -d "raw_files" ] || [ ! -s "datalist.txt" ]; then
+ ../bin/venv/bin/python preprocess_images.py
+fi
+
+if [[ ! -s "inception_v3_test_data.h5" ]]; then
+ ../bin/venv/bin/python ../bin/rawdata2hdf5 \
+ --data_list datalist.txt \
+ --output_path inception_v3_test_data.h5
+fi
+
+# prepare 'inception_v3.circle' file used for quantization test
+inputfile="./inception_v3.pb"
+outputfile="./inception_v3.circle"
+
+if [[ ! -s ${outputfile} ]]; then
+ ../bin/one-import-tf \
+ --input_path ${inputfile} \
+ --output_path ${outputfile} \
+ --input_arrays input --input_shapes "1,299,299,3" \
+ --output_arrays InceptionV3/Predictions/Reshape_1
+fi
+
+popd > /dev/null
diff --git a/compiler/one-cmds/tests/preprocess_images.py b/compiler/one-cmds/tests/preprocess_images.py
new file mode 100644
index 000000000..99991585d
--- /dev/null
+++ b/compiler/one-cmds/tests/preprocess_images.py
@@ -0,0 +1,35 @@
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os, shutil, PIL.Image, numpy as np
+
+input_dir = 'img_files'
+output_dir = 'raw_files'
+list_file = 'datalist.txt'
+
+if os.path.exists(output_dir):
+ shutil.rmtree(output_dir, ignore_errors=True)
+os.makedirs(output_dir)
+
+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)
+ img = ((img / 255) - 0.5) * 2.0
+ output_file = output_dir + '/' + f.replace('jpg', 'data')
+ img.tofile(output_file)
+ datalist.writelines(os.path.abspath(output_file) + '\n')
+ datalist.close()
diff --git a/compiler/one-cmds/tests/rawdata2hdf5_001.test b/compiler/one-cmds/tests/rawdata2hdf5_001.test
new file mode 100644
index 000000000..887d81bc4
--- /dev/null
+++ b/compiler/one-cmds/tests/rawdata2hdf5_001.test
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+outputfile="./output_testdata.h5"
+
+rm -rf ${outputfile}
+
+# run test
+rawdata2hdf5 \
+--data_list datalist.txt \
+--output_path ./output_testdata.h5 >> /dev/null
+
+if [[ ! -s "${outputfile}" ]]; then
+ trap_err_onexit
+fi
+
+echo "${filename_ext} SUCCESS"
diff --git a/compiler/one-cmds/tests/rawdata2hdf5_neg_001.test b/compiler/one-cmds/tests/rawdata2hdf5_neg_001.test
new file mode 100644
index 000000000..45ad88f2d
--- /dev/null
+++ b/compiler/one-cmds/tests/rawdata2hdf5_neg_001.test
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "No such file" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+inputfile="./wronglist.txt"
+outputfile="./output_testdata.h5"
+
+rm -rf ${inputfile}
+
+touch ${inputfile}
+echo "non-existing-file.data" >> ${inputfile}
+
+# run test
+rawdata2hdf5 \
+--data_list ${inputfile} \
+--output_path ${outputfile} > ${filename}.log 2>&1
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/rawdata2hdf5_neg_002.test b/compiler/one-cmds/tests/rawdata2hdf5_neg_002.test
new file mode 100644
index 000000000..6bd078fdf
--- /dev/null
+++ b/compiler/one-cmds/tests/rawdata2hdf5_neg_002.test
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "UnicodeDecodeError" "${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="./output_testdata.h5"
+
+# run test
+rawdata2hdf5 \
+--data_list ${inputfile} \
+--output_path ${outputfile} > ${filename}.log 2>&1
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/rawdata2hdf5_neg_003.test b/compiler/one-cmds/tests/rawdata2hdf5_neg_003.test
new file mode 100644
index 000000000..64559c2e7
--- /dev/null
+++ b/compiler/one-cmds/tests/rawdata2hdf5_neg_003.test
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "the following arguments are required: -l/--data_list" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+outputfile="./output_testdata.h5"
+
+# run test
+rawdata2hdf5 \
+--output_path ${outputfile} > ${filename}.log 2>&1
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/tests/rawdata2hdf5_neg_004.test b/compiler/one-cmds/tests/rawdata2hdf5_neg_004.test
new file mode 100644
index 000000000..277bc0ec8
--- /dev/null
+++ b/compiler/one-cmds/tests/rawdata2hdf5_neg_004.test
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+filename_ext="$(basename -- $0)"
+filename="${filename_ext%.*}"
+
+trap_err_onexit()
+{
+ if grep -q "Unable to create file" "${filename}.log"; then
+ echo "${filename_ext} SUCCESS"
+ exit 0
+ fi
+
+ echo "${filename_ext} FAILED"
+ exit 255
+}
+
+trap trap_err_onexit ERR
+
+outputfile="./non_existing_dir/output_testdata.h5"
+
+# run test
+rawdata2hdf5 \
+--data_list datalist.txt \
+--output_path ${outputfile} > ${filename}.log 2>&1
+
+echo "${filename_ext} FAILED"
+exit 255
diff --git a/compiler/one-cmds/utils.py b/compiler/one-cmds/utils.py
new file mode 100644
index 000000000..6eff9d772
--- /dev/null
+++ b/compiler/one-cmds/utils.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import configparser
+import os
+import subprocess
+import sys
+
+
+def _add_default_arg(parser):
+ # version
+ parser.add_argument(
+ '-v',
+ '--version',
+ action='store_true',
+ help='show program\'s version number and exit')
+
+ # configuration file
+ parser.add_argument('-C', '--config', type=str, help='run with configuation file')
+ # section name that you want to run in configuration file
+ parser.add_argument('-S', '--section', type=str, help=argparse.SUPPRESS)
+
+
+def _is_valid_attr(args, attr):
+ return hasattr(args, attr) and getattr(args, attr)
+
+
+def _parse_cfg(args, driver_name):
+ """parse configuration file. If the option is directly given to the command line,
+ the option is processed prior to the configuration file."""
+ if _is_valid_attr(args, 'config'):
+ config = configparser.ConfigParser()
+ config.read(args.config)
+ # if section is given, verify given section
+ if _is_valid_attr(args, 'section'):
+ if not config.has_section(args.section):
+ raise AssertionError('configuration file must have \'' + driver_name +
+ '\' section')
+ for key in config[args.section]:
+ if not _is_valid_attr(args, key):
+ setattr(args, key, config[args.section][key])
+ # if section is not given, section name is same with its driver name
+ else:
+ if not config.has_section(driver_name):
+ raise AssertionError('configuration file must have \'' + driver_name +
+ '\' section')
+ secton_to_run = driver_name
+ for key in config[secton_to_run]:
+ if not _is_valid_attr(args, key):
+ setattr(args, key, config[secton_to_run][key])
+
+
+def _make_tf2tfliteV2_cmd(args, driver_path, input_path, output_path):
+ """make a command for running tf2tfliteV2.py"""
+ cmd = [sys.executable, os.path.expanduser(driver_path)]
+ # model_format
+ if _is_valid_attr(args, 'model_format_cmd'):
+ cmd.append(getattr(args, 'model_format_cmd'))
+ elif _is_valid_attr(args, 'model_format'):
+ cmd.append('--' + getattr(args, 'model_format'))
+ else:
+ cmd.append('--graph_def') # default value
+ # converter version
+ if _is_valid_attr(args, 'converter_version_cmd'):
+ cmd.append(getattr(args, 'converter_version_cmd'))
+ elif _is_valid_attr(args, 'converter_version'):
+ cmd.append('--' + getattr(args, 'converter_version'))
+ else:
+ cmd.append('--v1') # default value
+ # input_path
+ if _is_valid_attr(args, 'input_path'):
+ cmd.append('--input_path')
+ cmd.append(os.path.expanduser(input_path))
+ # output_path
+ if _is_valid_attr(args, 'output_path'):
+ cmd.append('--output_path')
+ cmd.append(os.path.expanduser(output_path))
+ # input_arrays
+ if _is_valid_attr(args, 'input_arrays'):
+ cmd.append('--input_arrays')
+ cmd.append(getattr(args, 'input_arrays'))
+ # input_shapes
+ if _is_valid_attr(args, 'input_shapes'):
+ cmd.append('--input_shapes')
+ cmd.append(getattr(args, 'input_shapes'))
+ # output_arrays
+ if _is_valid_attr(args, 'output_arrays'):
+ cmd.append('--output_arrays')
+ cmd.append(getattr(args, 'output_arrays'))
+
+ return cmd
+
+
+def _make_tflite2circle_cmd(driver_path, input_path, output_path):
+ """make a command for running tflite2circle"""
+ cmd = [driver_path, input_path, output_path]
+ return [os.path.expanduser(c) for c in cmd]
+
+
+def _make_circle2circle_cmd(args, driver_path, input_path, output_path):
+ """make a command for running circle2circle"""
+ cmd = [os.path.expanduser(c) for c in [driver_path, input_path, output_path]]
+ # optimization pass
+ if _is_valid_attr(args, 'all'):
+ cmd.append('--all')
+ if _is_valid_attr(args, 'fold_dequantize'):
+ cmd.append('--fold_dequantize')
+ if _is_valid_attr(args, 'fuse_add_with_tconv'):
+ cmd.append('--fuse_add_with_tconv')
+ if _is_valid_attr(args, 'fuse_batchnorm_with_tconv'):
+ cmd.append('--fuse_batchnorm_with_tconv')
+ if _is_valid_attr(args, 'fuse_bcq'):
+ cmd.append('--fuse_bcq')
+ if _is_valid_attr(args, 'fuse_instnorm'):
+ cmd.append('--fuse_instnorm')
+ if _is_valid_attr(args, 'resolve_customop_add'):
+ cmd.append('--resolve_customop_add')
+ if _is_valid_attr(args, 'resolve_customop_batchmatmul'):
+ cmd.append('--resolve_customop_batchmatmul')
+ if _is_valid_attr(args, 'resolve_customop_matmul'):
+ cmd.append('--resolve_customop_matmul')
+
+ return cmd
+
+
+def _print_version_and_exit(file_path):
+ """print version of the file located in the file_path"""
+ script_path = os.path.realpath(file_path)
+ dir_path = os.path.dirname(script_path)
+ script_name = os.path.splitext(os.path.basename(script_path))[0]
+ # run one-version
+ subprocess.call([os.path.join(dir_path, 'one-version'), script_name])
+ sys.exit()
diff --git a/compiler/oneco-value-pbtxt-test/CMakeLists.txt b/compiler/oneco-value-pbtxt-test/CMakeLists.txt
new file mode 100644
index 000000000..6d9563f40
--- /dev/null
+++ b/compiler/oneco-value-pbtxt-test/CMakeLists.txt
@@ -0,0 +1,53 @@
+option(ONECO_VALUE_PBTXT_TEST "Enable oneco value test for pbtxt input model" ON)
+
+if(NOT ONECO_VALUE_PBTXT_TEST)
+ return()
+endif(NOT ONECO_VALUE_PBTXT_TEST)
+
+if(NOT TARGET onnxkit)
+ message(STATUS "oneco: Skip test material preparation as onnxkit is not defined")
+ return()
+endif(NOT TARGET onnxkit)
+
+#
+# Copy [Testcase]/test.pbtxt to Testcase.pbtxt in binary folder
+# Encode Testcase.pbtxt to Testcase.pb
+#
+set(TEST_PBTXT_FILE "test.pbtxt")
+set(TEST_REPO "${CMAKE_CURRENT_SOURCE_DIR}") # Where to find tests
+set(TEST_SPACE "${CMAKE_CURRENT_BINARY_DIR}") # Where to run tests
+
+file(GLOB PBTXTFILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*/${TEST_PBTXT_FILE}")
+
+unset(TEST_DEPS)
+
+foreach(PBTXTFILE IN ITEMS ${PBTXTFILES})
+ get_filename_component(DIR_NAME ${PBTXTFILE} DIRECTORY)
+
+ set(PBTXT_SOURCE_FILE "${DIR_NAME}.pbtxt")
+ set(PBTXT_SOURCE_PATH "${TEST_SPACE}/${PBTXT_SOURCE_FILE}")
+
+ set(PB_OUTPUT_FILE "${DIR_NAME}.pb")
+ set(PB_OUTPUT_PATH "${TEST_SPACE}/${PB_OUTPUT_FILE}")
+
+ # Copy files
+ add_custom_command(
+ OUTPUT ${PBTXT_SOURCE_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TEST_REPO}/${PBTXTFILE}" "${PBTXT_SOURCE_PATH}"
+ COMMENT "Copy ${PBTXT_SOURCE_FILE}"
+ DEPENDS "${TEST_REPO}/${PBTXTFILE}"
+ )
+
+ # Use onnxkit to encode
+ add_custom_command(
+ OUTPUT ${PB_OUTPUT_PATH}
+ COMMAND $<TARGET_FILE:onnxkit> encode ${PBTXT_SOURCE_PATH} ${PB_OUTPUT_PATH}
+ DEPENDS ${PBTXT_SOURCE_PATH}
+ COMMENT "Generate ${PB_OUTPUT_FILE}"
+ )
+
+ list(APPEND TEST_DEPS "${PB_OUTPUT_PATH}")
+endforeach(PBTXTFILE)
+
+# This target enforces CMake to generate all the dependencies during "build" phase
+add_custom_target(oneco_value_pbtxt_test_deps ALL DEPENDS ${TEST_DEPS})
diff --git a/compiler/mocotest-onnx/Const_000/test.pbtxt b/compiler/oneco-value-pbtxt-test/Const_000/test.pbtxt
index c5ae298d4..c5ae298d4 100644
--- a/compiler/mocotest-onnx/Const_000/test.pbtxt
+++ b/compiler/oneco-value-pbtxt-test/Const_000/test.pbtxt
diff --git a/compiler/mocotest-onnx/Identity_000/test.pbtxt b/compiler/oneco-value-pbtxt-test/Identity_000/test.pbtxt
index e8aff1d9d..e8aff1d9d 100644
--- a/compiler/mocotest-onnx/Identity_000/test.pbtxt
+++ b/compiler/oneco-value-pbtxt-test/Identity_000/test.pbtxt
diff --git a/compiler/oneco-value-pbtxt-test/README.md b/compiler/oneco-value-pbtxt-test/README.md
new file mode 100644
index 000000000..6ae4519c1
--- /dev/null
+++ b/compiler/oneco-value-pbtxt-test/README.md
@@ -0,0 +1 @@
+# oneco-value-pbtxt-test
diff --git a/compiler/mocotest-onnx/requires.cmake b/compiler/oneco-value-pbtxt-test/requires.cmake
index 0f21fa998..0f21fa998 100644
--- a/compiler/mocotest-onnx/requires.cmake
+++ b/compiler/oneco-value-pbtxt-test/requires.cmake
diff --git a/compiler/oneco/CMakeLists.txt b/compiler/oneco/CMakeLists.txt
new file mode 100644
index 000000000..10f466948
--- /dev/null
+++ b/compiler/oneco/CMakeLists.txt
@@ -0,0 +1,36 @@
+nnas_find_package(Protobuf QUIET)
+nnas_find_package(ONNXSource EXACT 1.6.0 QUIET)
+
+if(NOT Protobuf_FOUND)
+ return()
+endif(NOT Protobuf_FOUND)
+
+if(NOT ONNXSource_FOUND)
+ return()
+endif(NOT ONNXSource_FOUND)
+
+add_subdirectory(proto)
+
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(moco_onnx_frontend SHARED ${SOURCES})
+target_include_directories(moco_onnx_frontend PRIVATE src)
+target_include_directories(moco_onnx_frontend PUBLIC include)
+target_link_libraries(moco_onnx_frontend PUBLIC moco_onnx_proto)
+target_link_libraries(moco_onnx_frontend PUBLIC loco)
+target_link_libraries(moco_onnx_frontend PRIVATE stdex)
+target_link_libraries(moco_onnx_frontend PRIVATE cwrap)
+
+nnas_find_package(GTest QUIET)
+
+if(NOT GTest_FOUND)
+ return()
+endif(NOT GTest_FOUND)
+
+add_executable(moco_onnx_frontend_test ${TESTS})
+target_include_directories(moco_onnx_frontend_test PRIVATE src)
+target_link_libraries(moco_onnx_frontend_test gtest_main)
+target_link_libraries(moco_onnx_frontend_test moco_onnx_frontend)
+add_test(moco_onnx_frontend_test moco_onnx_frontend_test)
diff --git a/compiler/oneco/README.md b/compiler/oneco/README.md
new file mode 100644
index 000000000..9687fafc4
--- /dev/null
+++ b/compiler/oneco/README.md
@@ -0,0 +1 @@
+# oneco
diff --git a/compiler/moco-onnx/include/moco/onnx/Frontend.h b/compiler/oneco/include/moco/onnx/Frontend.h
index 5d3527590..5d3527590 100644
--- a/compiler/moco-onnx/include/moco/onnx/Frontend.h
+++ b/compiler/oneco/include/moco/onnx/Frontend.h
diff --git a/compiler/moco-onnx/proto/CMakeLists.txt b/compiler/oneco/proto/CMakeLists.txt
index e72985671..e72985671 100644
--- a/compiler/moco-onnx/proto/CMakeLists.txt
+++ b/compiler/oneco/proto/CMakeLists.txt
diff --git a/compiler/moco-onnx/requires.cmake b/compiler/oneco/requires.cmake
index 4e99b0eac..4e99b0eac 100644
--- a/compiler/moco-onnx/requires.cmake
+++ b/compiler/oneco/requires.cmake
diff --git a/compiler/moco-onnx/src/Convert.cpp b/compiler/oneco/src/Convert.cpp
index b56b03ef7..b56b03ef7 100644
--- a/compiler/moco-onnx/src/Convert.cpp
+++ b/compiler/oneco/src/Convert.cpp
diff --git a/compiler/moco-onnx/src/Convert.h b/compiler/oneco/src/Convert.h
index 0935afeea..0935afeea 100644
--- a/compiler/moco-onnx/src/Convert.h
+++ b/compiler/oneco/src/Convert.h
diff --git a/compiler/moco-onnx/src/Frontend.cpp b/compiler/oneco/src/Frontend.cpp
index d633c1c2e..d633c1c2e 100644
--- a/compiler/moco-onnx/src/Frontend.cpp
+++ b/compiler/oneco/src/Frontend.cpp
diff --git a/compiler/moco-onnx/src/Frontend.test.cpp b/compiler/oneco/src/Frontend.test.cpp
index 58e1144da..58e1144da 100644
--- a/compiler/moco-onnx/src/Frontend.test.cpp
+++ b/compiler/oneco/src/Frontend.test.cpp
diff --git a/compiler/moco-onnx/src/GraphBuilder.h b/compiler/oneco/src/GraphBuilder.h
index 7271eb81a..7271eb81a 100644
--- a/compiler/moco-onnx/src/GraphBuilder.h
+++ b/compiler/oneco/src/GraphBuilder.h
diff --git a/compiler/moco-onnx/src/GraphBuilderContext.cpp b/compiler/oneco/src/GraphBuilderContext.cpp
index 00d3f4b06..00d3f4b06 100644
--- a/compiler/moco-onnx/src/GraphBuilderContext.cpp
+++ b/compiler/oneco/src/GraphBuilderContext.cpp
diff --git a/compiler/moco-onnx/src/GraphBuilderContext.h b/compiler/oneco/src/GraphBuilderContext.h
index f1f394b50..f1f394b50 100644
--- a/compiler/moco-onnx/src/GraphBuilderContext.h
+++ b/compiler/oneco/src/GraphBuilderContext.h
diff --git a/compiler/moco-onnx/src/GraphBuilderRegistry.h b/compiler/oneco/src/GraphBuilderRegistry.h
index 1bf4d9514..1bf4d9514 100644
--- a/compiler/moco-onnx/src/GraphBuilderRegistry.h
+++ b/compiler/oneco/src/GraphBuilderRegistry.h
diff --git a/compiler/moco-onnx/src/Onnxutil.cpp b/compiler/oneco/src/Onnxutil.cpp
index 93f06677f..93f06677f 100644
--- a/compiler/moco-onnx/src/Onnxutil.cpp
+++ b/compiler/oneco/src/Onnxutil.cpp
diff --git a/compiler/moco-onnx/src/Onnxutil.h b/compiler/oneco/src/Onnxutil.h
index 0c2fcac33..0c2fcac33 100644
--- a/compiler/moco-onnx/src/Onnxutil.h
+++ b/compiler/oneco/src/Onnxutil.h
diff --git a/compiler/moco-onnx/src/Op/Constant.cpp b/compiler/oneco/src/Op/Constant.cpp
index c14d2729b..c14d2729b 100644
--- a/compiler/moco-onnx/src/Op/Constant.cpp
+++ b/compiler/oneco/src/Op/Constant.cpp
diff --git a/compiler/moco-onnx/src/Op/Constant.h b/compiler/oneco/src/Op/Constant.h
index e25441d58..e25441d58 100644
--- a/compiler/moco-onnx/src/Op/Constant.h
+++ b/compiler/oneco/src/Op/Constant.h
diff --git a/compiler/moco-onnx/src/Op/Constant_V1.cpp b/compiler/oneco/src/Op/Constant_V1.cpp
index 916f5fa3a..916f5fa3a 100644
--- a/compiler/moco-onnx/src/Op/Constant_V1.cpp
+++ b/compiler/oneco/src/Op/Constant_V1.cpp
diff --git a/compiler/moco-onnx/src/Op/Constant_V9.cpp b/compiler/oneco/src/Op/Constant_V9.cpp
index 56dc6cca0..56dc6cca0 100644
--- a/compiler/moco-onnx/src/Op/Constant_V9.cpp
+++ b/compiler/oneco/src/Op/Constant_V9.cpp
diff --git a/compiler/moco-onnx/src/Op/Identity.cpp b/compiler/oneco/src/Op/Identity.cpp
index 6314b6f96..6314b6f96 100644
--- a/compiler/moco-onnx/src/Op/Identity.cpp
+++ b/compiler/oneco/src/Op/Identity.cpp
diff --git a/compiler/moco-onnx/src/Op/Identity.h b/compiler/oneco/src/Op/Identity.h
index 41367bea0..41367bea0 100644
--- a/compiler/moco-onnx/src/Op/Identity.h
+++ b/compiler/oneco/src/Op/Identity.h
diff --git a/compiler/moco-onnx/src/Op/Identity_V1.cpp b/compiler/oneco/src/Op/Identity_V1.cpp
index 6ae65589e..6ae65589e 100644
--- a/compiler/moco-onnx/src/Op/Identity_V1.cpp
+++ b/compiler/oneco/src/Op/Identity_V1.cpp
diff --git a/compiler/onnx-tools/CMakeLists.txt b/compiler/onnx-tools/CMakeLists.txt
new file mode 100644
index 000000000..ac4500e0e
--- /dev/null
+++ b/compiler/onnx-tools/CMakeLists.txt
@@ -0,0 +1,21 @@
+set(ONNX_TOOL_FILES
+ onnx-dump.py
+ onnx-ops.py
+)
+
+foreach(ONNX_TOOL IN ITEMS ${ONNX_TOOL_FILES})
+
+ set(ONNX_TOOL_FILE ${ONNX_TOOL})
+ set(ONNX_TOOL_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${ONNX_TOOL_FILE}")
+ set(ONNX_TOOL_BIN "${CMAKE_CURRENT_BINARY_DIR}/${ONNX_TOOL_FILE}")
+ set(ONNX_TOOL_TARGET "${ONNX_TOOL}_target")
+
+ add_custom_command(OUTPUT ${ONNX_TOOL_BIN}
+ COMMAND ${CMAKE_COMMAND} -E copy "${ONNX_TOOL_SRC}" "${ONNX_TOOL_BIN}"
+ DEPENDS ${ONNX_TOOL_SRC}
+ COMMENT "Generate ${ONNX_TOOL_BIN}"
+ )
+
+ add_custom_target(${ONNX_TOOL_TARGET} ALL DEPENDS ${ONNX_TOOL_BIN})
+
+endforeach(ONNX_TOOL)
diff --git a/compiler/onnx-tools/README.md b/compiler/onnx-tools/README.md
new file mode 100644
index 000000000..f1b886132
--- /dev/null
+++ b/compiler/onnx-tools/README.md
@@ -0,0 +1,65 @@
+# onnx-tools
+
+_onnx-tools_ provides developer tools to support ONNX format in compiler frontend.
+
+## onnx-dump.py
+
+Use `onnx-dump.py` to dump ONNX model graph in human readable text format.
+
+For example,
+
+```
+[General] -----------------------------
+IR version = 6
+Producer = pytorch 1.6
+
+[Operators] ---------------------------
+ 3 Conv
+ 3 Relu
+...
+
+[Initializers] ------------------------
+"0.bias" FLOAT [16]
+"0.weight" FLOAT [16, 1, 3, 3]
+...
+
+[Nodes] -------------------------------
+Conv("Conv_0")
+ A dilations: [1, 1], group: 1, kernel_shape: [3, 3], pads: [1, 1, 1, 1], strides: [2, 2]
+ I "input.1"
+ I "0.weight"
+ I "0.bias"
+ O "7"
+Relu("Relu_1")
+ I "7"
+ O "8"
+...
+
+[Graph Input/Output]-------------------
+ I: "input.1" FLOAT [1, 1, 28, 28]
+ O: "21" FLOAT [1, 10]
+```
+
+In `[Nodes]` section, `A` is for attributes for the node, `I` for input name and `O` for output name.
+
+`I` and `O` also applies to `[Graph Input/Output]` section.
+
+## onnx-ops.py
+
+Use `onnx-ops.py` to dump ONNX model operators.
+
+You can use with other command line tools to analyze operators in the model file.
+
+For example,
+```bash
+$ python onnx-ops.py mymodel.onnx | sort | uniq -c
+ 1 Concat
+ 1 Constant
+ 3 Conv
+ 1 Gather
+ 1 GlobalAveragePool
+ 3 Relu
+ 1 Reshape
+ 1 Shape
+ 1 Unsqueeze
+```
diff --git a/compiler/onnx-tools/onnx-dump.py b/compiler/onnx-tools/onnx-dump.py
new file mode 100644
index 000000000..4f169cbe9
--- /dev/null
+++ b/compiler/onnx-tools/onnx-dump.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import onnx
+import os
+import sys
+
+from onnx import AttributeProto, TensorProto
+from onnx import numpy_helper
+from onnx import helper
+
+
+def _data_type_str(data_type):
+ return TensorProto.DataType.Name(data_type)
+
+
+def _get_attribute_value(attr):
+ if attr.type == AttributeProto.TENSOR:
+ return "{}, {}".format(
+ _data_type_str(attr.t.data_type), numpy_helper.to_array(attr.t))
+ if attr.type == AttributeProto.GRAPH:
+ # TODO revise when graph node is available
+ return "<graph>"
+ if attr.type == AttributeProto.TENSORS:
+ # TODO revise to see contents
+ return "<tensors>..."
+ if attr.type == AttributeProto.GRAPHS:
+ # TODO revise when graph node is available
+ return "<graphs>..."
+ return helper.get_attribute_value(attr)
+
+
+def _dump_header(onnx_model):
+ print("[General] -----------------------------")
+ print("IR version =", onnx_model.ir_version)
+ print("Producer =", onnx_model.producer_name, onnx_model.producer_version)
+ print("")
+
+
+def _dump_operators(onnx_model):
+ opcodes_dict = dict()
+ for node in onnx_model.graph.node:
+ if node.op_type in opcodes_dict:
+ opcodes_dict[node.op_type] = opcodes_dict[node.op_type] + 1
+ else:
+ opcodes_dict[node.op_type] = 1
+
+ print("[Operators] ---------------------------")
+ for opcode_key in opcodes_dict:
+ print("{:>5} {}".format(opcodes_dict[opcode_key], opcode_key))
+
+ print("")
+
+
+def _dump_initializers(onnx_model):
+ print("[Initializers] ------------------------")
+ for initializer in onnx_model.graph.initializer:
+ init_name = '"{}"'.format(initializer.name)
+ dtstr = _data_type_str(initializer.data_type)
+ print('{:<15} {} {}'.format(init_name, dtstr, initializer.dims))
+
+ print("")
+
+
+def _dump_nodes(onnx_model):
+ print("[Nodes] -------------------------------")
+
+ for node in onnx_model.graph.node:
+ print('{0}("{1}")'.format(node.op_type, node.name))
+
+ attribute = ''
+ for attr in node.attribute:
+ if attribute != '':
+ attribute += ', '
+ attribute += "{}: {}".format(attr.name, _get_attribute_value(attr))
+
+ if attribute != '':
+ print(' A {0}'.format(attribute))
+
+ for inp in node.input:
+ print(' I "{0}"'.format(inp))
+ for out in node.output:
+ print(' O "{0}"'.format(out))
+
+ print("")
+
+
+def _dump_inputoutputs(onnx_model):
+ print("[Graph Input/Output]-------------------")
+ for mod_input in onnx_model.graph.input:
+ io_name = '"{}"'.format(mod_input.name)
+ dtstr = _data_type_str(mod_input.type.tensor_type.elem_type)
+ shape = mod_input.type.tensor_type.shape
+ input_shape = [dim.dim_value for dim in shape.dim]
+ print(' I: {:<15} {} {}'.format(io_name, dtstr, input_shape))
+
+ for mod_output in onnx_model.graph.output:
+ io_name = '"{}"'.format(mod_output.name)
+ dtstr = _data_type_str(mod_output.type.tensor_type.elem_type)
+ shape = mod_output.type.tensor_type.shape
+ output_shape = [dim.dim_value for dim in shape.dim]
+ print(' O: {:<15} {} {}'.format(io_name, dtstr, output_shape))
+
+ print("")
+
+
+def _dump_graph(onnx_model):
+ _dump_header(onnx_model)
+ _dump_operators(onnx_model)
+ _dump_initializers(onnx_model)
+ _dump_nodes(onnx_model)
+ _dump_inputoutputs(onnx_model)
+
+
+def _help_exit(cmd_name):
+ print('Dump ONNX model file Graph')
+ print('Usage: {0} [onnx_path]'.format(cmd_name))
+ print('')
+ exit()
+
+
+def main():
+ if len(sys.argv) < 2:
+ _help_exit(os.path.basename(sys.argv[0]))
+
+ onnx_model = onnx.load(sys.argv[1])
+ onnx.checker.check_model(onnx_model)
+
+ _dump_graph(onnx_model)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/compiler/onnx-tools/onnx-ops.py b/compiler/onnx-tools/onnx-ops.py
new file mode 100644
index 000000000..5292dc70e
--- /dev/null
+++ b/compiler/onnx-tools/onnx-ops.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import onnx
+import os
+import sys
+
+
+def _dump_operators(onnx_model):
+ for node in onnx_model.graph.node:
+ print(node.op_type)
+
+
+def _help_exit(cmd_name):
+ print('Dump ONNX model file Operators')
+ print('Usage: {0} [onnx_path]'.format(cmd_name))
+ print('')
+ exit()
+
+
+def main():
+ if len(sys.argv) < 2:
+ _help_exit(os.path.basename(sys.argv[0]))
+
+ onnx_model = onnx.load(sys.argv[1])
+ onnx.checker.check_model(onnx_model)
+
+ _dump_operators(onnx_model)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/compiler/onnx2circle/CMakeLists.txt b/compiler/onnx2circle/CMakeLists.txt
new file mode 100644
index 000000000..a0d393bd9
--- /dev/null
+++ b/compiler/onnx2circle/CMakeLists.txt
@@ -0,0 +1,27 @@
+# TODO Allow users to force onnx2circle build
+if(NOT TARGET mir2loco)
+ return()
+endif(NOT TARGET mir2loco)
+
+if(NOT TARGET mir_onnx_importer)
+ return()
+endif(NOT TARGET mir_onnx_importer)
+
+if(NOT TARGET exo)
+ return()
+endif(NOT TARGET exo)
+
+message(STATUS "Build onnx2circle: TRUE")
+
+set(SOURCES "src/onnx2circle.cpp")
+
+add_executable(onnx2circle ${SOURCES})
+target_link_libraries(onnx2circle PRIVATE moco_log)
+target_link_libraries(onnx2circle PRIVATE exo)
+target_link_libraries(onnx2circle PRIVATE locop)
+target_link_libraries(onnx2circle PRIVATE hermes_std)
+target_link_libraries(onnx2circle PRIVATE stdex)
+target_link_libraries(onnx2circle PRIVATE angkor cwrap)
+target_link_libraries(onnx2circle PRIVATE mir2loco)
+target_link_libraries(onnx2circle PRIVATE mir_onnx_importer)
+install(TARGETS onnx2circle DESTINATION bin)
diff --git a/compiler/onnx2circle/README.md b/compiler/onnx2circle/README.md
new file mode 100644
index 000000000..55b73870e
--- /dev/null
+++ b/compiler/onnx2circle/README.md
@@ -0,0 +1,3 @@
+# onnx2circle
+
+_onnx2circle_ is a ONNX-to-Circle model converter.
diff --git a/compiler/onnx2circle/requires.cmake b/compiler/onnx2circle/requires.cmake
new file mode 100644
index 000000000..f52e40416
--- /dev/null
+++ b/compiler/onnx2circle/requires.cmake
@@ -0,0 +1,9 @@
+require("stdex")
+require("hermes-std")
+require("mir2loco")
+require("mir")
+require("exo")
+require("locop")
+require("loco")
+require("cwrap")
+require("angkor")
diff --git a/compiler/onnx2circle/src/onnx2circle.cpp b/compiler/onnx2circle/src/onnx2circle.cpp
new file mode 100644
index 000000000..c329ed3d5
--- /dev/null
+++ b/compiler/onnx2circle/src/onnx2circle.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "exo/LoggingContext.h"
+#include "exo/CircleExporter.h"
+
+#include "mir2loco.h"
+#include "ONNXImporterImpl.h"
+
+#include "locop/FormattedGraph.h"
+
+#include "hermes/ConsoleReporter.h"
+#include "hermes/EnvConfig.h"
+
+#include "stdex/Memory.h"
+
+#include <cassert>
+
+#include <iostream>
+#include <stdexcept>
+#include <string>
+
+//
+// Logging Support
+//
+namespace
+{
+
+struct Logger final : public hermes::Source
+{
+ Logger(hermes::Context *ctx) { activate(ctx->sources(), ctx->bus()); }
+ ~Logger() { deactivate(); }
+};
+
+struct LoggingContext
+{
+ static hermes::Context *get(void)
+ {
+ using EnvConfig = hermes::EnvConfig<hermes::EnvFormat::BooleanNumber>;
+
+ static hermes::Context *ctx = nullptr;
+
+ if (ctx == nullptr)
+ {
+ ctx = new hermes::Context;
+ ctx->sinks()->append(stdex::make_unique<hermes::ConsoleReporter>());
+ ctx->config(stdex::make_unique<EnvConfig>("ONNX2CIRCLE_Log"));
+ }
+
+ return ctx;
+ }
+};
+
+void print_help()
+{
+ std::cerr << "Usage: onnx2circle <path/to/onnx> <path/to/circle/model> " << std::endl;
+}
+
+} // namespace
+
+#define LOGGER(name) \
+ ::Logger name { ::LoggingContext::get() }
+
+#define INFO(name) HERMES_INFO(name)
+
+int main(int argc, char **argv)
+{
+ using EnvConfig = hermes::EnvConfig<hermes::EnvFormat::BooleanNumber>;
+
+ // This line allows users to control all the exo-circle loggers via ONNX2CIRCLE_Log_Backend
+ exo::LoggingContext::get()->config(stdex::make_unique<EnvConfig>("ONNX2CIRCLE_Log_Backend"));
+
+ LOGGER(l);
+
+ // TODO We need better args parsing in future
+ if (!(argc == 3))
+ {
+ print_help();
+ return 255;
+ }
+
+ std::string onnx_path{argv[1]}; // .pb file
+ std::string circle_path{argv[2]};
+
+ std::cout << "Import from '" << onnx_path << "'" << std::endl;
+ auto mir_g = mir_onnx::loadModel(onnx_path);
+ auto loco_g = mir2loco::Transformer().transform(mir_g.get());
+ std::cout << "Import from '" << onnx_path << "' - Done" << std::endl;
+
+ INFO(l) << "Import Graph" << std::endl;
+ INFO(l) << locop::fmt<locop::Formatter::LinearV1>(loco_g) << std::endl;
+
+ std::cout << "Export into '" << circle_path << "'" << std::endl;
+ exo::CircleExporter(loco_g.get()).dumpToFile(circle_path.c_str());
+ std::cout << "Export into '" << circle_path << "' - Done" << std::endl;
+
+ return 0;
+}
diff --git a/compiler/onnx2tflite-integration-test/CMakeLists.txt b/compiler/onnx2tflite-integration-test/CMakeLists.txt
new file mode 100644
index 000000000..9904be470
--- /dev/null
+++ b/compiler/onnx2tflite-integration-test/CMakeLists.txt
@@ -0,0 +1,120 @@
+nnas_include(TargetRequire)
+nncc_find_resource(ONNXTests)
+
+unset(REQUIRED_TARGETS)
+list(APPEND REQUIRED_TARGETS onnx2tflite)
+list(APPEND REQUIRED_TARGETS onnxkit)
+list(APPEND REQUIRED_TARGETS nnkit-run)
+list(APPEND REQUIRED_TARGETS nnkit_onnx_backend)
+list(APPEND REQUIRED_TARGETS nnkit_tflite_backend)
+list(APPEND REQUIRED_TARGETS nnkit_randomize_action)
+list(APPEND REQUIRED_TARGETS nnkit_HDF5_export_action)
+list(APPEND REQUIRED_TARGETS nnkit_HDF5_import_action)
+list(APPEND REQUIRED_TARGETS i5diff)
+TargetRequire_Return(${REQUIRED_TARGETS})
+
+message(STATUS "Build onnx2tflite-integration-test: ON")
+
+set(TEST_REPO "${ONNXTests_DIR}")
+set(TEST_PBTXT_FILENAME "test.pbtxt")
+
+unset(TESTCASES)
+
+macro(add NAME)
+ list(APPEND TESTCASES ${NAME})
+endmacro(add)
+
+# Read "test.lst"
+include("test.lst")
+# Read "test.local.lst" if exists
+include("test.local.lst" OPTIONAL)
+
+unset(DEPS)
+unset(KEYS)
+
+foreach(PREFIX IN ITEMS ${TESTCASES})
+ if(NOT IS_DIRECTORY "${TEST_REPO}/${PREFIX}")
+ message(FATAL_ERROR "Missing '${PREFIX}' test")
+ endif()
+
+ set(PBTXT_SOURCE_PATH "${TEST_REPO}/${PREFIX}/${TEST_PBTXT_FILENAME}")
+
+ set(PBTXT_FILE "${PREFIX}.pbtxt")
+ set(PBTXT_PATH "${CMAKE_CURRENT_BINARY_DIR}/${PBTXT_FILE}")
+
+ set(ONNX_FILE "${PREFIX}.onnx")
+ set(ONNX_PATH "${CMAKE_CURRENT_BINARY_DIR}/${ONNX_FILE}")
+
+ # Copy .pbtxt
+ add_custom_command(OUTPUT ${PBTXT_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${PBTXT_SOURCE_PATH}" "${PBTXT_PATH}"
+ DEPENDS ${PBTXT_SOURCE_PATH}
+ COMMENT "Generate ${PBTXT_FILE}"
+ )
+
+ # Generate .onnx from .pbtxt
+ add_custom_command(OUTPUT ${ONNX_PATH}
+ COMMAND $<TARGET_FILE:onnxkit> encode ${PBTXT_PATH} ${ONNX_PATH}
+ DEPENDS ${PBTXT_PATH}
+ COMMENT "Generate ${ONNX_FILE}"
+ )
+
+ list(APPEND DEPS ${INFO_PATH} ${ONNX_PATH})
+ list(APPEND KEYS ${PREFIX})
+endforeach(PREFIX)
+
+##
+## Copy testall
+##
+set(TEST_RUNNER_TEMPLATE "${CMAKE_CURRENT_SOURCE_DIR}/testall.sh")
+set(TEST_RUNNER "${CMAKE_CURRENT_BINARY_DIR}/run")
+
+add_custom_command(
+ OUTPUT ${TEST_RUNNER}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TEST_RUNNER_TEMPLATE}" "${TEST_RUNNER}"
+ DEPENDS ${TEST_RUNNER_TEMPLATE}
+ COMMENT "Generate test runner"
+)
+
+list(APPEND DEPS "${TEST_RUNNER}")
+
+###
+### Generate test.config
+###
+set(TOOLCHAIN_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/toolchain.config")
+
+add_custom_command(
+ OUTPUT ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'NNKIT_RUN_PATH=\"$<TARGET_FILE:nnkit-run>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'ONNX_BACKEND_PATH=\"$<TARGET_FILE:nnkit_onnx_backend>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TFLITE_BACKEND_PATH=\"$<TARGET_FILE:nnkit_tflite_backend>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'RANDOMIZE_ACTION_PATH=\"$<TARGET_FILE:nnkit_randomize_action>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'HDF5_EXPORT_ACTION_PATH=\"$<TARGET_FILE:nnkit_HDF5_export_action>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'HDF5_IMPORT_ACTION_PATH=\"$<TARGET_FILE:nnkit_HDF5_import_action>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'ONNX2TFLITE_PATH=\"$<TARGET_FILE:onnx2tflite>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'I5DIFF_PATH=\"$<TARGET_FILE:i5diff>\"' >> ${TOOLCHAIN_CONFIG}
+ DEPENDS
+ nnkit-run
+ nnkit_onnx_backend
+ nnkit_tflite_backend
+ nnkit_randomize_action
+ nnkit_HDF5_export_action
+ nnkit_HDF5_import_action
+ onnx2tflite
+ i5diff
+ COMMENT "Generate toolchain configuration"
+)
+
+list(APPEND DEPS "${TOOLCHAIN_CONFIG}")
+
+# This "onnx2tflite_integration_test_deps" target enforces CMake to generate all the dependencies during "build" phase
+add_custom_target(onnx2tflite_integration_test_deps ALL DEPENDS ${DEPS})
+
+# Run tests
+add_test(
+ NAME onnx2tflite_integration_test
+ COMMAND "${TEST_RUNNER}"
+ "${TOOLCHAIN_CONFIG}"
+ ${KEYS}
+)
diff --git a/compiler/onnx2tflite-integration-test/README.md b/compiler/onnx2tflite-integration-test/README.md
new file mode 100644
index 000000000..cf160fe67
--- /dev/null
+++ b/compiler/onnx2tflite-integration-test/README.md
@@ -0,0 +1 @@
+# onnx2tflite-integration-test
diff --git a/compiler/onnx2tflite-integration-test/requires.cmake b/compiler/onnx2tflite-integration-test/requires.cmake
new file mode 100644
index 000000000..ac96b57f7
--- /dev/null
+++ b/compiler/onnx2tflite-integration-test/requires.cmake
@@ -0,0 +1,6 @@
+require("i5diff")
+require("nnkit-onnxrt")
+require("nnkit-tflite")
+require("nnkit")
+require("onnx2tflite")
+require("onnxkit")
diff --git a/compiler/onnx2tflite-integration-test/test.lst b/compiler/onnx2tflite-integration-test/test.lst
new file mode 100644
index 000000000..58fd26a73
--- /dev/null
+++ b/compiler/onnx2tflite-integration-test/test.lst
@@ -0,0 +1,5 @@
+#add(Const_000)
+add(UNIT_Identity_000)
+add(UNIT_Gemm_000)
+# runtime used in testing does not support 11 op version, skip this test until we update
+#add(UNIT_Gemm_001)
diff --git a/compiler/onnx2tflite-integration-test/testall.sh b/compiler/onnx2tflite-integration-test/testall.sh
new file mode 100755
index 000000000..a1ab44405
--- /dev/null
+++ b/compiler/onnx2tflite-integration-test/testall.sh
@@ -0,0 +1,112 @@
+#!/bin/bash
+
+WORKDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+
+if [[ $# -lt 1 ]]; then
+ echo "USAGE: $0 ..."
+ echo
+ echo "ARGUMENTS:"
+ echo " [toolchain.config path]"
+ echo " [Prefix1]"
+ echo " [Prefix2]"
+ echo " ..."
+ exit 3
+fi
+
+CONFIG_PATH="$1"; shift
+
+source "${CONFIG_PATH}"
+
+echo "-- Use onnx2tflite at '${ONNX2TFLITE_PATH}'"
+echo "-- Use nnkit-run at '${NNKIT_RUN_PATH}'"
+echo "-- Use ONNX backend: ${ONNX_BACKEND_PATH}"
+echo "-- Use TFLITE backend: ${TFLITE_BACKEND_PATH}"
+echo "-- Use randomize action: ${RANDOMIZE_ACTION_PATH}"
+echo "-- Use HDF5 export action: ${HDF5_EXPORT_ACTION_PATH}"
+echo "-- Use HDF5 import action: ${HDF5_IMPORT_ACTION_PATH}"
+echo "-- Use i5diff: ${I5DIFF_PATH}"
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+pushd "${WORKDIR}"
+
+while [[ $# -ne 0 ]]; do
+ PREFIX="$1"; shift
+
+ TESTED+=("${PREFIX}")
+
+ PASSED_TAG="${PREFIX}.passed"
+
+ rm -f "${PASSED_TAG}"
+
+ exec 2>&1
+
+ echo "-- Use '${PREFIX}.onnx'"
+
+ # Show commands
+ set -x
+
+ # Generate tflite
+ "${ONNX2TFLITE_PATH}" \
+ "-b" \
+ "${WORKDIR}/${PREFIX}.onnx" \
+ "${WORKDIR}/${PREFIX}.tflite"
+
+ if [[ $? -ne 0 ]]; then
+ continue
+ fi
+
+ # Run ONNX
+ "${NNKIT_RUN_PATH}" \
+ --backend "${ONNX_BACKEND_PATH}" \
+ --backend-arg "${WORKDIR}/${PREFIX}.onnx" \
+ --pre "${RANDOMIZE_ACTION_PATH}" \
+ --pre "${HDF5_EXPORT_ACTION_PATH}" \
+ --pre-arg "${WORKDIR}/${PREFIX}.input.h5" \
+ --post "${HDF5_EXPORT_ACTION_PATH}" \
+ --post-arg "${WORKDIR}/${PREFIX}.expected.h5"
+
+ if [[ $? -ne 0 ]]; then
+ continue
+ fi
+
+ # Run T/F Lite
+ "${NNKIT_RUN_PATH}" \
+ --backend "${TFLITE_BACKEND_PATH}" \
+ --backend-arg "${WORKDIR}/${PREFIX}.tflite" \
+ --pre "${HDF5_IMPORT_ACTION_PATH}" \
+ --pre-arg "${WORKDIR}/${PREFIX}.input.h5" \
+ --post "${HDF5_EXPORT_ACTION_PATH}" \
+ --post-arg "${WORKDIR}/${PREFIX}.obtained.h5"
+
+ if [[ $? -ne 0 ]]; then
+ continue
+ fi
+
+ "${I5DIFF_PATH}" -d 0.001 "${PREFIX}.expected.h5" "${PREFIX}.obtained.h5"
+
+ if [[ $? -eq 0 ]]; then
+ touch "${PASSED_TAG}"
+ fi
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ PASSED+=("$PREFIX")
+ else
+ FAILED+=("$PREFIX")
+ fi
+done
+popd
+
+if [[ ${#TESTED[@]} -ne ${#PASSED[@]} ]]; then
+ echo "FAILED"
+ for TEST in "${FAILED[@]}"
+ do
+ echo "- ${TEST}"
+ done
+ exit 3
+fi
+
+echo "PASSED"
+exit 0
diff --git a/compiler/onnx2tflite/CMakeLists.txt b/compiler/onnx2tflite/CMakeLists.txt
new file mode 100644
index 000000000..140fba94b
--- /dev/null
+++ b/compiler/onnx2tflite/CMakeLists.txt
@@ -0,0 +1,8 @@
+nnas_include(TargetRequire)
+
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+add_executable(onnx2tflite ${SOURCES})
+target_link_libraries(onnx2tflite mir_onnx_importer)
+target_link_libraries(onnx2tflite mir2loco)
+target_link_libraries(onnx2tflite exo)
diff --git a/compiler/onnx2tflite/requires.cmake b/compiler/onnx2tflite/requires.cmake
new file mode 100644
index 000000000..b16a51141
--- /dev/null
+++ b/compiler/onnx2tflite/requires.cmake
@@ -0,0 +1,3 @@
+require("mir")
+require("mir2loco")
+require("exo")
diff --git a/compiler/onnx2tflite/src/Driver.cpp b/compiler/onnx2tflite/src/Driver.cpp
new file mode 100644
index 000000000..2028b5cb0
--- /dev/null
+++ b/compiler/onnx2tflite/src/Driver.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ONNXImporterImpl.h>
+#include <mir2loco.h>
+#include <exo/TFLExporter.h>
+
+#include <iostream>
+
+namespace
+{
+
+// String decorators?
+std::string quote(const std::string &s) { return "'" + s + "'"; }
+
+std::unique_ptr<mir::Graph> import(const std::string &onnx_path)
+{
+ return mir_onnx::loadModel(onnx_path);
+}
+
+std::unique_ptr<loco::Graph> transform(const std::unique_ptr<mir::Graph> &mir_graph)
+{
+ mir2loco::Transformer transformer;
+ return transformer.transform(mir_graph.get());
+}
+
+void printHelp()
+{
+ std::cout << "Usage: onnx2tflite <mode> <path/to/onnx> <path/to/output>\n"
+ "Modes: -t (text file); -b (binary file)"
+ << std::endl;
+}
+
+} // namespace
+
+// ONNX-to-MIR (mir-onnx-importer)
+// MIR-to-LOCO (mir2loco)
+// LOCO-to-TFLITE (exo-tflite)
+int main(int argc, char **argv)
+{
+ // onnx2tflite <mode> <path/to/onnx> <path/to/output>
+ // modes: -t (text file); -b (binary file)
+ if (argc != 4)
+ {
+ printHelp();
+ exit(1);
+ }
+ std::string mode{argv[1]};
+ std::string onnx_path{argv[2]};
+ std::string tflite_path{argv[3]};
+
+ std::cout << "Import " << quote(onnx_path) << std::endl;
+ std::unique_ptr<mir::Graph> mir_graph;
+ if (mode == "-t")
+ mir_graph = mir_onnx::importModelFromTextFile(onnx_path);
+ else if (mode == "-b")
+ mir_graph = mir_onnx::importModelFromBinaryFile(onnx_path);
+ else
+ {
+ printHelp();
+ exit(1);
+ }
+ std::cout << "Import " << quote(onnx_path) << " - Done" << std::endl;
+
+ auto loco_graph = transform(mir_graph);
+
+ exo::TFLExporter(loco_graph.get()).dumpToFile(tflite_path.c_str());
+
+ return 0;
+}
diff --git a/compiler/onnxkit/CMakeLists.txt b/compiler/onnxkit/CMakeLists.txt
index 954bbfdc4..81c3622c9 100644
--- a/compiler/onnxkit/CMakeLists.txt
+++ b/compiler/onnxkit/CMakeLists.txt
@@ -1,5 +1,5 @@
-nncc_find_package(Protobuf QUIET)
-nncc_find_package(ONNXSource EXACT 1.4.1 QUIET)
+nnas_find_package(Protobuf QUIET)
+nnas_find_package(ONNXSource EXACT 1.6.0 QUIET)
if(NOT Protobuf_FOUND)
return()
diff --git a/compiler/oops/CMakeLists.txt b/compiler/oops/CMakeLists.txt
new file mode 100644
index 000000000..f12572d54
--- /dev/null
+++ b/compiler/oops/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_library(oops INTERFACE)
+target_include_directories(oops INTERFACE include)
+target_link_libraries(oops INTERFACE pepper_str)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(oops_test test.cpp)
+target_link_libraries(oops_test oops)
diff --git a/compiler/oops/README.md b/compiler/oops/README.md
new file mode 100644
index 000000000..9794cf8aa
--- /dev/null
+++ b/compiler/oops/README.md
@@ -0,0 +1 @@
+# oops
diff --git a/compiler/oops/include/oops/InternalExn.h b/compiler/oops/include/oops/InternalExn.h
new file mode 100644
index 000000000..0e11085c0
--- /dev/null
+++ b/compiler/oops/include/oops/InternalExn.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OOPS_INTERNAL_EXN_H__
+#define __OOPS_INTERNAL_EXN_H__
+
+#include <exception>
+#include <string>
+
+/// @ brief throw internal exception with message
+#define INTERNAL_EXN(msg) throw oops::InternalExn(__FILE__, __LINE__, msg)
+
+/// @ brief throw internal exception with message and value
+#define INTERNAL_EXN_V(msg, val) throw oops::InternalExn(__FILE__, __LINE__, msg, val)
+
+namespace oops
+{
+
+template <typename T> uint32_t to_uint32(T a) { return static_cast<uint32_t>(a); }
+
+/**
+ * @brief Exception caused by internal error
+ *
+ * Note: Please use the above MACROs
+ */
+class InternalExn : public std::exception
+{
+public:
+ InternalExn(const char *filename, const int line, const std::string &msg)
+ : _filename(filename), _line(line), _msg(msg)
+ {
+ construct_full_msg();
+ }
+
+ explicit InternalExn(const char *filename, const int line, const std::string &msg, uint32_t val)
+ : _filename(filename), _line(line), _msg(msg + ": " + std::to_string(val))
+ {
+ construct_full_msg();
+ }
+
+ explicit InternalExn(const char *filename, const int line, const std::string &msg,
+ const std::string &val)
+ : _filename(filename), _line(line), _msg(msg + ": " + val)
+ {
+ construct_full_msg();
+ }
+
+ const char *what() const noexcept override { return _full_msg.c_str(); }
+
+private:
+ const std::string _filename;
+ const uint32_t _line;
+ const std::string _msg;
+
+private:
+ void construct_full_msg()
+ {
+ _full_msg =
+ "Internal Exception. " + _msg + " [" + _filename + ":" + std::to_string(_line) + "]";
+ }
+
+ std::string _full_msg;
+};
+
+} // namespace oops
+
+#endif // __OOPS_INTERNAL_EXN_H__
diff --git a/compiler/oops/include/oops/UserExn.h b/compiler/oops/include/oops/UserExn.h
new file mode 100644
index 000000000..d0138322d
--- /dev/null
+++ b/compiler/oops/include/oops/UserExn.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OOPS_USER_EXN_H__
+#define __OOPS_USER_EXN_H__
+
+#include <pepper/str.h>
+
+#include <exception>
+#include <string>
+#include <map>
+
+namespace oops
+{
+
+/**
+ * @brief Exception to user
+ *
+ * Pass msg and one additional info, e.g.,
+ * ex) UserExn("Unsupported rank", 4);
+ * ex) UserExn("Unsupported layout", "NHWC");
+ *
+ * Or pass msg with attribute pairs of name & val ,
+ * ex) UserExn("Node has unsupported layout",
+ * "Node", node->name(),
+ * "layout", node->layout());
+ */
+class UserExn : public std::exception
+{
+public:
+ UserExn() = delete;
+
+ template <typename... Info> UserExn(const std::string &msg, Info &&... args)
+ {
+ std::stringstream out;
+
+ out << "Error: " << msg + ": ";
+
+ build_info(out, args...);
+
+ _msg = out.str();
+ }
+
+ const char *what() const noexcept override { return _msg.c_str(); };
+
+private:
+ template <typename Attr, typename Val, typename... AttsVals>
+ void build_info(std::stringstream &out, Attr &attr, Val &val, AttsVals &... args)
+ {
+ out << pepper::str(attr, " = ", val);
+ out << ", ";
+
+ build_info(out, args...);
+ }
+
+ template <typename Attr, typename Val>
+ void build_info(std::stringstream &out, Attr &attr, Val &val)
+ {
+ out << pepper::str(attr, " = ", val);
+ }
+
+ void build_info(std::stringstream &) { /* empty */}
+
+ // when only one info of string is provided
+ void build_info(std::stringstream &out, const std::string &val) { out << val; }
+
+ // when only one info of uint32_t is provided
+ void build_info(std::stringstream &out, const uint32_t &val) { out << val; }
+
+private:
+ std::string _msg;
+};
+
+} // namespace oops
+
+#endif // __OOPS_USER_EXN_H__
diff --git a/compiler/oops/test.cpp b/compiler/oops/test.cpp
new file mode 100644
index 000000000..666f62f54
--- /dev/null
+++ b/compiler/oops/test.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "oops/InternalExn.h"
+#include "oops/UserExn.h"
+
+#include <gtest/gtest.h>
+
+namespace
+{
+
+void batman() { INTERNAL_EXN("Here comes Joker"); }
+
+void star_wars() { INTERNAL_EXN_V("Something is approaching", "Darth Vader"); }
+
+enum class InfinityStones
+{
+ SpaceStone,
+ RealityStone,
+ OtherStones,
+};
+
+void avengers()
+{
+ std::string where;
+ std::string separator = ":";
+ try
+ {
+ // exception will be raised in next line
+ where = __FILE__ + separator + std::to_string(__LINE__ + 1);
+ INTERNAL_EXN_V("Last stone was gathered", oops::to_uint32(InfinityStones::SpaceStone));
+ }
+ catch (const oops::InternalExn &e)
+ {
+ auto msg = std::string(e.what());
+ ASSERT_TRUE(msg.find("Last stone was gathered: 0") != std::string::npos);
+ ASSERT_TRUE(msg.find(where) != std::string::npos);
+ }
+}
+
+} // namespace
+
+TEST(oopsTest, InternalExn)
+{
+ ASSERT_THROW(batman(), oops::InternalExn);
+ ASSERT_THROW(star_wars(), oops::InternalExn);
+
+ avengers();
+}
+
+TEST(oopsTest, UserExn_one_info_after_msg)
+{
+ try
+ {
+ throw oops::UserExn("Not a member of Avenger", "Kingsman");
+ }
+ catch (const oops::UserExn &e)
+ {
+ auto msg = std::string(e.what());
+ ASSERT_TRUE(msg.find("Not a member of Avenger: Kingsman") != std::string::npos);
+ }
+}
+
+TEST(oopsTest, UserExn_two_pairs_after_msg)
+{
+ try
+ {
+ std::string hero("Spiderman");
+
+ // clang-format off
+ throw oops::UserExn("Hero's age is wrong",
+ "Hero", hero,
+ "Age", 97);
+ // clang-format on
+ }
+ catch (const oops::UserExn &e)
+ {
+ auto msg = std::string(e.what());
+ ASSERT_TRUE(msg.find("Hero = Spiderman, Age = 97") != std::string::npos);
+ }
+}
diff --git a/compiler/pepper-assert/CMakeLists.txt b/compiler/pepper-assert/CMakeLists.txt
new file mode 100644
index 000000000..314ba51ad
--- /dev/null
+++ b/compiler/pepper-assert/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_library(pepper_assert INTERFACE)
+target_include_directories(pepper_assert INTERFACE include)
diff --git a/compiler/pepper-assert/README.md b/compiler/pepper-assert/README.md
new file mode 100644
index 000000000..df41371cc
--- /dev/null
+++ b/compiler/pepper-assert/README.md
@@ -0,0 +1 @@
+# pepper-assert
diff --git a/compiler/pepper-assert/include/pepper/assert.h b/compiler/pepper-assert/include/pepper/assert.h
new file mode 100644
index 000000000..fc3b4f40a
--- /dev/null
+++ b/compiler/pepper-assert/include/pepper/assert.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __PEPPER_ASSERT_H__
+#define __PEPPER_ASSERT_H__
+
+#include <cassert>
+
+//
+// This example shows how to use DBGARG macro.
+//
+// void f(DBGARG(uint32_t, n))
+// {
+// assert(n < 128);
+// }
+//
+// This will make it easy to remove unused variable warnings in Release build.
+//
+#ifdef NDEBUG
+#define DBGARG(TYP, VAR) TYP
+#else
+#define DBGARG(TYP, VAR) TYP VAR
+#endif // NDEBUG
+
+#endif // __PEPPER_ASSERT_H__
diff --git a/compiler/pepper-env/CMakeLists.txt b/compiler/pepper-env/CMakeLists.txt
new file mode 100644
index 000000000..7371d4caf
--- /dev/null
+++ b/compiler/pepper-env/CMakeLists.txt
@@ -0,0 +1,19 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(pepper_env STATIC ${SOURCES})
+set_target_properties(pepper_env PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(pepper_env PUBLIC include)
+target_link_libraries(pepper_env PRIVATE nncc_common)
+target_link_libraries(pepper_env PUBLIC nncc_coverage)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+# Google Test is mandatory for test
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(pepper_env_test ${TESTS})
+target_link_libraries(pepper_env_test pepper_env)
diff --git a/compiler/pepper-env/README.md b/compiler/pepper-env/README.md
new file mode 100644
index 000000000..91a1f8be8
--- /dev/null
+++ b/compiler/pepper-env/README.md
@@ -0,0 +1,3 @@
+# pepper-env
+
+_pepper-env_ makes it easy to access "process environment variables".
diff --git a/compiler/pepper-env/include/pepper/env.h b/compiler/pepper-env/include/pepper/env.h
new file mode 100644
index 000000000..233c8b421
--- /dev/null
+++ b/compiler/pepper-env/include/pepper/env.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __PEPPER_ENV_H__
+#define __PEPPER_ENV_H__
+
+#include <string>
+
+//
+// KVStore: Key-Value Store Interface
+//
+namespace pepper // TODO Extract this section if necessary
+{
+
+enum class KVStoreTrait
+{
+ Queryable,
+};
+
+template <KVStoreTrait Trait> class KVStoreInterface;
+
+template <> class KVStoreInterface<KVStoreTrait::Queryable>
+{
+public:
+ KVStoreInterface() = default;
+
+public:
+ virtual ~KVStoreInterface() = default;
+
+public: // Core interface (PLEASE PREFER TO THE BELOW HELPERS)
+ //
+ // "query(k)" SHOULD
+ // - return a valid C-string if the key "k" exists in the store, or
+ // - return nullptr otherwise.
+ //
+ // DESIGN NOTE - Why "query" instead of "get"?
+ //
+ // Let us consider the following class declarations as an example:
+ //
+ // struct Base {
+ // virtual const char *get(const char *) const = 0;
+ // const char *get(const std::string &s) const { return nullptr; }
+ // };
+ //
+ // struct Derived : public Base {
+ // const char *get(const char *) const final { return nullptr; }
+ // };
+ //
+ // It is impossible to write the code of the following form:
+ //
+ // Derived obj;
+ //
+ // std::string s = ...;
+ // obj.get(s);
+ // ^^^^^^^^^^^
+ // error: no viable conversion from 'std::string' (aka 'basic_string<char>') to 'const char *'
+ //
+ // Please refer to the concept of name hiding in C++ for more details.
+ virtual const char *query(const char *k) const = 0;
+
+public: // Derived helper methods
+ const char *get(const std::string &k) const { return query(k.c_str()); }
+
+ /**
+ * NOTE
+ *
+ * get(k, v) same as query(k) if the key "k" exists in the store.
+ * get(k, v) returns "v" otherwise
+ */
+ std::string get(const std::string &key, const std::string &default_value) const;
+};
+
+} // namespace pepper
+
+//
+// ProcessEnvironment
+//
+namespace pepper
+{
+
+struct ProcessEnvironment final : public KVStoreInterface<KVStoreTrait::Queryable>
+{
+ const char *query(const char *k) const final;
+};
+
+} // namespace pepper
+
+#endif // __PEPPER_ENV_H__
diff --git a/compiler/pepper-env/src/env.cpp b/compiler/pepper-env/src/env.cpp
new file mode 100644
index 000000000..273db7b4b
--- /dev/null
+++ b/compiler/pepper-env/src/env.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pepper/env.h"
+
+//
+// KVStoreInterface
+//
+namespace pepper
+{
+
+std::string KVStoreInterface<KVStoreTrait::Queryable>::get(const std::string &key,
+ const std::string &default_value) const
+{
+ if (auto p = query(key.c_str()))
+ {
+ return p;
+ }
+ return default_value;
+}
+
+} // namespace pepper
+
+//
+// ProcessEnvironment
+//
+#include <cstdlib>
+
+namespace pepper
+{
+
+const char *ProcessEnvironment::query(const char *k) const { return std::getenv(k); }
+
+} // namespace pepper
diff --git a/compiler/pepper-env/src/env.test.cpp b/compiler/pepper-env/src/env.test.cpp
new file mode 100644
index 000000000..73244da8b
--- /dev/null
+++ b/compiler/pepper-env/src/env.test.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pepper/env.h"
+
+#include <gtest/gtest.h>
+
+using namespace pepper;
+
+namespace
+{
+
+struct EmptyKVStore : public KVStoreInterface<KVStoreTrait::Queryable>
+{
+ const char *query(const char *k) const final { return nullptr; }
+};
+
+} // namespace
+
+TEST(KVStoreQueryableInterfaceTests, get_with_default_value)
+{
+ EmptyKVStore kvs;
+
+ auto obtained = kvs.get("K", "V");
+
+ ASSERT_EQ(obtained, "V");
+}
diff --git a/compiler/pepper-str/CMakeLists.txt b/compiler/pepper-str/CMakeLists.txt
index 1e7e8de4b..cbe01b86a 100644
--- a/compiler/pepper-str/CMakeLists.txt
+++ b/compiler/pepper-str/CMakeLists.txt
@@ -6,7 +6,7 @@ if(NOT ENABLE_TEST)
endif(NOT ENABLE_TEST)
# Google Test is mandatory for test
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
GTest_AddTest(pepper_str_test test.cpp)
target_link_libraries(pepper_str_test pepper_str)
diff --git a/compiler/pepper-strcast/CMakeLists.txt b/compiler/pepper-strcast/CMakeLists.txt
index 321edeb9f..5f87e9488 100644
--- a/compiler/pepper-strcast/CMakeLists.txt
+++ b/compiler/pepper-strcast/CMakeLists.txt
@@ -13,7 +13,7 @@ if(NOT ENABLE_TEST)
endif(NOT ENABLE_TEST)
# Google Test is mandatory for test
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
GTest_AddTest(pepper_strcast_test ${TESTS})
target_link_libraries(pepper_strcast_test pepper_strcast)
diff --git a/compiler/plier-tf/CMakeLists.txt b/compiler/plier-tf/CMakeLists.txt
index 7a71319ab..b817d138b 100644
--- a/compiler/plier-tf/CMakeLists.txt
+++ b/compiler/plier-tf/CMakeLists.txt
@@ -1,16 +1,6 @@
-nncc_find_package(Protobuf QUIET)
-# TensorFlowSource package is used to use ~.proto files
-nncc_find_package(TensorFlowSource EXACT 1.12 QUIET)
-
-if(NOT Protobuf_FOUND)
- return()
-endif(NOT Protobuf_FOUND)
-
-if(NOT TensorFlowSource_FOUND)
+if(NOT TARGET mio_tf)
return()
-endif(NOT TensorFlowSource_FOUND)
-
-add_subdirectory(proto)
+endif(NOT TARGET mio_tf)
file(GLOB_RECURSE SOURCES "src/*.cpp")
file(GLOB_RECURSE TESTS "src/*.test.cpp")
@@ -22,14 +12,17 @@ target_include_directories(plier_tf PUBLIC include)
target_link_libraries(plier_tf PUBLIC angkor)
target_link_libraries(plier_tf PUBLIC loco)
-target_link_libraries(plier_tf PUBLIC plier_tf_proto)
+target_link_libraries(plier_tf PUBLIC mio_tf)
+
+# Apply global configurations (e.g. warnings as error)
+target_link_libraries(plier_tf PRIVATE nncc_common)
if(NOT ENABLE_TEST)
return()
endif(NOT ENABLE_TEST)
# Google Test is mandatory for test
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
GTest_AddTest(plier_tf_test ${TESTS})
target_link_libraries(plier_tf_test plier_tf)
diff --git a/compiler/plier-tf/include/plier/tf/TestHelper.h b/compiler/plier-tf/include/plier/tf/TestHelper.h
index be1038ad1..2062f2a00 100644
--- a/compiler/plier-tf/include/plier/tf/TestHelper.h
+++ b/compiler/plier-tf/include/plier/tf/TestHelper.h
@@ -23,32 +23,15 @@
#include <tensorflow/core/framework/graph.pb.h>
-#include <istream>
-
namespace plier
{
namespace tf
{
-struct membuf : std::streambuf
-{
- membuf(char const *base, size_t size)
- {
- char *p(const_cast<char *>(base));
- this->setg(p, p, p + size);
- }
-};
-
-struct imemstream : virtual membuf, std::istream
-{
- imemstream(char const *base, size_t size)
- : membuf(base, size), std::istream(static_cast<std::streambuf *>(this))
- {
- }
-};
-
bool parse_graphdef(char const *pbtxt, tensorflow::GraphDef &graphdef);
+bool parse_nodedef(char const *pbtxt, tensorflow::NodeDef &nodedef);
+
} // namespace tf
} // namespace plier
diff --git a/compiler/plier-tf/proto/CMakeLists.txt b/compiler/plier-tf/proto/CMakeLists.txt
deleted file mode 100644
index 30d99fd4e..000000000
--- a/compiler/plier-tf/proto/CMakeLists.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-# Minimal Protocol Buffer specification for GraphDef file (.pb) encoding/decoding
-unset(PROTO_FILES)
-list(APPEND PROTO_FILES tensorflow/core/framework/versions.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/resource_handle.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/types.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/tensor.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/tensor_shape.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/attr_value.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/op_def.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/node_def.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/function.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/graph.proto)
-
-Protobuf_Generate(GRAPHDEF_PROTO
- "${CMAKE_CURRENT_BINARY_DIR}/generated"
- "${TensorFlowSource_DIR}"
- ${PROTO_FILES})
-
-add_library(plier_tf_proto STATIC ${GRAPHDEF_PROTO_SOURCES})
-set_target_properties(plier_tf_proto PROPERTIES POSITION_INDEPENDENT_CODE ON)
-target_include_directories(plier_tf_proto PUBLIC ${GRAPHDEF_PROTO_INCLUDE_DIRS})
-target_link_libraries(plier_tf_proto PUBLIC libprotobuf)
diff --git a/compiler/plier-tf/requires.cmake b/compiler/plier-tf/requires.cmake
index 5f3ce2fd7..ebf09bcad 100644
--- a/compiler/plier-tf/requires.cmake
+++ b/compiler/plier-tf/requires.cmake
@@ -1,2 +1,3 @@
require("angkor")
require("loco")
+require("mio-tf")
diff --git a/compiler/plier-tf/src/Convert.cpp b/compiler/plier-tf/src/Convert.cpp
index 15fc2da31..c36df53ed 100644
--- a/compiler/plier-tf/src/Convert.cpp
+++ b/compiler/plier-tf/src/Convert.cpp
@@ -124,6 +124,8 @@ loco::DataType as_loco_datatype(const tensorflow::DataType tf_dtype)
{
switch (tf_dtype)
{
+ case tensorflow::DT_INT8:
+ return loco::DataType::S8;
case tensorflow::DT_UINT8:
return loco::DataType::U8;
case tensorflow::DT_FLOAT:
@@ -179,7 +181,7 @@ void copy_shape(const tensorflow::TensorShapeProto &tf_shape,
int64_t dim_value = tf_shape.dim(d).size();
assert(dim_value < std::numeric_limits<uint32_t>::max());
- if (dim_value >= 0ULL)
+ if (dim_value >= 0LL)
{
uint32_t dim_value32 = static_cast<uint32_t>(dim_value);
to_shape.dim(d) = dim_value32;
diff --git a/compiler/plier-tf/src/Convert.test.cpp b/compiler/plier-tf/src/Convert.test.cpp
index a543db07d..d6dfed4a0 100644
--- a/compiler/plier-tf/src/Convert.test.cpp
+++ b/compiler/plier-tf/src/Convert.test.cpp
@@ -103,3 +103,13 @@ TEST(plier_Convert, to_data_layout)
ASSERT_EQ(plier::tf::as_data_layout("NHWC"), plier::tf::DataLayout::NHWC);
ASSERT_EQ(plier::tf::as_data_layout("NCHW"), plier::tf::DataLayout::NCHW);
}
+
+TEST(plier_Convert, copy_shape_thrown_on_unknown_dim)
+{
+ tensorflow::TensorShapeProto tf_shape;
+ nncc::core::ADT::tensor::Shape angkor_shape;
+
+ tf_shape.add_dim()->set_size(-1);
+
+ ASSERT_ANY_THROW(plier::tf::copy_shape(tf_shape, angkor_shape));
+}
diff --git a/compiler/plier-tf/src/TestHelper.cpp b/compiler/plier-tf/src/TestHelper.cpp
index a292a2627..a551e89f9 100644
--- a/compiler/plier-tf/src/TestHelper.cpp
+++ b/compiler/plier-tf/src/TestHelper.cpp
@@ -23,6 +23,29 @@
#include <google/protobuf/text_format.h>
#include <cstring>
+#include <istream>
+
+namespace
+{
+
+struct membuf : std::streambuf
+{
+ membuf(char const *base, size_t size)
+ {
+ char *p(const_cast<char *>(base));
+ this->setg(p, p, p + size);
+ }
+};
+
+struct imemstream : virtual membuf, std::istream
+{
+ imemstream(char const *base, size_t size)
+ : membuf(base, size), std::istream(static_cast<std::streambuf *>(this))
+ {
+ }
+};
+
+} // namespace
namespace plier
{
@@ -36,5 +59,12 @@ bool parse_graphdef(char const *pbtxt, tensorflow::GraphDef &graphdef)
return google::protobuf::TextFormat::Parse(&iis, &graphdef);
}
+bool parse_nodedef(char const *pbtxt, tensorflow::NodeDef &nodedef)
+{
+ imemstream mempb(pbtxt, std::strlen(pbtxt));
+ google::protobuf::io::IstreamInputStream iis(&mempb);
+ return google::protobuf::TextFormat::Parse(&iis, &nodedef);
+}
+
} // namespace tf
} // namespace plier
diff --git a/compiler/pota-quantization-value-test/CMakeLists.txt b/compiler/pota-quantization-value-test/CMakeLists.txt
new file mode 100644
index 000000000..73b9ead73
--- /dev/null
+++ b/compiler/pota-quantization-value-test/CMakeLists.txt
@@ -0,0 +1,69 @@
+unset(QUANTIZATION_VALUE_TEST)
+unset(QUANTIZATION_VALUE_TEST_WITH_PARAM)
+
+macro(addTest NAME GRANULARITY DTYPE)
+ list(APPEND QUANTIZATION_VALUE_TEST ${NAME})
+ list(APPEND QUANTIZATION_VALUE_TEST_WITH_PARAM ${NAME} ${GRANULARITY} ${DTYPE})
+endmacro(addTest)
+
+# Read "test.lst"
+include("test.lst")
+# Read "test.local.lst" if exists
+include("test.local.lst" OPTIONAL)
+
+unset(TEST_DEPS)
+
+get_target_property(ARTIFACTS_BIN_PATH testDataGenerator BINARY_DIR)
+
+set(VIRTUALENV "${NNCC_OVERLAY_DIR}/venv_1_13_2")
+
+###
+### Generate test.config
+###
+set(TEST_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/test.config")
+
+add_custom_command(
+ OUTPUT ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'RECORD_MINMAX_PATH=\"$<TARGET_FILE:record-minmax>\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'CIRCLE_QUANTIZER_PATH=\"$<TARGET_FILE:circle-quantizer>\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'CIRCLE_TENSORDUMP_PATH=\"$<TARGET_FILE:circle-tensordump>\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'VIRTUALENV=\"${VIRTUALENV}\"' >> ${TEST_CONFIG}
+ DEPENDS record-minmax
+ DEPENDS circle-quantizer
+ DEPENDS circle-tensordump
+ COMMENT "Generate test configuration"
+)
+
+list(APPEND TEST_DEPS "${TEST_CONFIG}")
+
+# This enforces CMake to generate all the dependencies during "build" phase
+add_custom_target(pota_quantization_value_test_deps ALL DEPENDS ${TEST_DEPS})
+
+# Run tests
+add_test(
+ NAME pota_fake_wquant_test
+ COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/test_fake_wquant.sh"
+ "${TEST_CONFIG}"
+ "${ARTIFACTS_BIN_PATH}"
+ ${QUANTIZATION_VALUE_TEST_WITH_PARAM}
+)
+
+add_test(
+ NAME pota_record_minmax_test
+ COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/test_record_minmax.sh"
+ "${TEST_CONFIG}"
+ "${ARTIFACTS_BIN_PATH}"
+ ${QUANTIZATION_VALUE_TEST_WITH_PARAM}
+)
+
+add_test(
+ NAME pota_quantization_test
+ COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/test_quantization.sh"
+ "${TEST_CONFIG}"
+ "${ARTIFACTS_BIN_PATH}"
+ ${QUANTIZATION_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)
diff --git a/compiler/pota-quantization-value-test/README.md b/compiler/pota-quantization-value-test/README.md
new file mode 100644
index 000000000..d6d003b4b
--- /dev/null
+++ b/compiler/pota-quantization-value-test/README.md
@@ -0,0 +1,55 @@
+# pota-quantization-value-test
+
+`pota-quantization-value-test` checks whether a Circle model listed in `test.lst` is correctly quantized (`pota` denotes post-training affine). The circle models are generated from the recipes saved in `res/TensorFlowLiteRecipes`.
+
+Write `test.local.lst` for local test.
+
+### Test process
+
+#### Step 1. Fake quantization
+
+Run `circle-quantizer` with `--quantize_dequantize_weights` option.
+
+Dump the fake-quantized model with `circle-tensordump`.
+
+Compare the dumped model with the expected output in "expected_outputs/<model_name>/\<granularity\>/<quantized_type>/fake_quantization/<tensor_name>.json"
+
+The expected output should include
+ (1) values of weights (only for conv, transposed_conv, depthwise_conv, and fc layers)
+
+#### Step 2. Record moving avg of min and moving avg of max for activations
+
+Run `record-minmax` with the fake-quantized model (input data is saved in "test_inputs/<model_name>/\<granularity\>/<quantized_type>/<record_number>.txt")
+
+Dump the minmax-recorded model with `circle-tensordump`.
+Compare the dumped model with the expected output in "expected_outputs/<model_name>/\<granularity\>/<quantized_type>/record_minmax/<tensor_name>.json"
+
+The expected output should include
+ (1) min/max of activations
+
+#### Step 3. Quantization
+
+Run `circle-quantizer` with `--quantize_with_minmax` option.
+
+Dump the quantized model with `circle-tensordump`.
+
+Compare the dumped model with the expected output in "expected_outputs/<model_name>/\<granularity\>/<quantized_type>/quantization/<tensor_name>.json"
+
+The expected output should include
+ (1) scale, zero point of activations
+ (2) scale, zero point, values of weights
+ (3) scale, values (weights) of bias
+
+### Golden data
+
+Golden data was generated as follows.
+
+(1) Generate random h5 input for a target model (using gen_h5_random_inputs.py in `record-minmax-conversion-test`)
+
+(2) Run `dalgona` with the target model, input data, and analysis code named GenGoldenWeights.py for uint8 (GenGoldenWeightsSym.py for int16) (https://github.com/Samsung/ONE/pull/3501)
+
+(3) Do fake quantization using circle-quantizer
+
+(4) Run `dalgona` with the fake-quantized model, input data, and analysis code named GenGoldenActBias.py for uint8 (GenGoldenActBiasSym.py for int16) (https://github.com/Samsung/ONE/pull/3501)
+
+(5) Edit generated data for some operators (concat: scale propagation, mean: axis data)
diff --git a/compiler/pota-quantization-value-test/compare_tensors.py b/compiler/pota-quantization-value-test/compare_tensors.py
new file mode 100755
index 000000000..9c9b639bd
--- /dev/null
+++ b/compiler/pota-quantization-value-test/compare_tensors.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python3
+import h5py as h5
+import numpy as np
+import argparse
+import os.path
+import json
+import sys
+
+#
+# This script checks if the min/max values recorded in the circle model are the same with the expected values
+#
+# Basic usage:
+# compare_tensors.py --input_h5 <path/to/iput/h5> --expect_dir <path/to/expect/dir> --mode <compare_mode>
+# ex: compare_minmax.py --input_h5 Add_000.h5 --expect_dir expected_outputs/Add_000 --mode fake_quantization
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--input_h5', type=str, required=True)
+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"]
+
+model = args.input_h5
+expect_dir = args.expect_dir
+mode = args.mode
+
+failed_cases = 0
+
+if mode not in supported_modes:
+ raise SystemExit("Unsupported mode. --mode should be one of " + str(supported_modes))
+
+
+def compare_fake_quantization(tensor, tensor_name, expect_dir):
+ global failed_cases
+ with open(expect_dir + "/" + tensor_name + ".json", "r") as expect_file:
+ json_load = json.load(expect_file)
+ expected_weights = np.array(json_load["weights"])
+ input_weights = tensor["weights"][:]
+ if np.allclose(input_weights, expected_weights, rtol=1.e-5, atol=1.e-5) == False:
+ print("Fake-quantized weights of " + tensor_name + " (" + str(input_weights) +
+ ") do not match with expected value (" + str(expected_weights) + ").")
+ failed_cases += 1
+
+
+def compare_record_minmax(tensor, tensor_name, expect_dir):
+ global failed_cases
+ with open(expect_dir + "/" + tensor_name + ".json", "r") as expect_file:
+ json_load = json.load(expect_file)
+ expected_min = np.array(json_load["min"])
+ expected_max = np.array(json_load["max"])
+ input_min = tensor["min"][:]
+ input_max = tensor["max"][:]
+ if np.allclose(input_min, expected_min, rtol=1.e-5, atol=1.e-5) == False:
+ print("Recorded min of " + tensor_name + " (" + str(input_min) +
+ ") does not match with expected value (" + str(expected_min) + ").")
+ failed_cases += 1
+ if np.allclose(input_max, expected_max, rtol=1.e-5, atol=1.e-5) == False:
+ print("Recorded max of " + tensor_name + " (" + str(input_max) +
+ ") does not match with expected value (" + str(expected_max) + ").")
+ failed_cases += 1
+
+
+def compare_quantization(tensor, tensor_name, expect_dir):
+ global failed_cases
+ with open(expect_dir + "/" + tensor_name + ".json", "r") as expect_file:
+ json_load = json.load(expect_file)
+ for key in json_load:
+ if key == "weights":
+ expected_weights = np.array(json_load["weights"])
+ input_weights = tensor["weights"][:]
+ abs_tolerance = 1
+ # We use higher tolerance for int64 data (bias of int16-quantized model)
+ if tensor["weights"].dtype == 'int64':
+ abs_tolerance = 5
+
+ if np.allclose(
+ input_weights, expected_weights, rtol=0, atol=abs_tolerance) == False:
+ print("Quantized weights of " + tensor_name + " (" + str(input_weights) +
+ ") do not match with expected value (" + str(expected_weights) +
+ ").")
+ failed_cases += 1
+
+ if key == "scale":
+ expected_scale = np.array(json_load["scale"])
+ input_scale = tensor["scale"][:]
+ if np.allclose(input_scale, expected_scale, rtol=1.e-5, atol=1.e-5) == False:
+ print("Quantized scale of " + tensor_name + " (" + str(input_scale) +
+ ") do not match with expected value (" + str(expected_scale) + ").")
+ failed_cases += 1
+
+ if key == "zero_point":
+ expected_zero_point = np.array(json_load["zero_point"])
+ input_zero_point = tensor["zero_point"][:]
+ if np.allclose(
+ input_zero_point, expected_zero_point, rtol=0, atol=1) == False:
+ print("Quantized zero_point of " + tensor_name + " (" +
+ str(input_zero_point) + ") do not match with expected value (" +
+ str(expected_zero_point) + ").")
+ failed_cases += 1
+
+
+with h5.File(model, "r") as input:
+ for tensor_name in input.keys():
+ # We only check the given golden data
+ if os.path.isfile(expect_dir + "/" + tensor_name + ".json"):
+ print("Compare " + tensor_name)
+ if mode == "fake_quantization":
+ compare_fake_quantization(input[tensor_name], tensor_name, expect_dir)
+ elif mode == "record_minmax":
+ compare_record_minmax(input[tensor_name], tensor_name, expect_dir)
+ elif mode == "quantization":
+ compare_quantization(input[tensor_name], tensor_name, expect_dir)
+ else:
+ raise SystemExit("Unsupproted mode.")
+
+sys.exit(failed_cases)
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ifm1.json
new file mode 100644
index 000000000..a7298cb58
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00014653272228315473,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ifm2.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ifm2.json
new file mode 100644
index 000000000..ab968c9fc
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ifm2.json
@@ -0,0 +1,32 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 4096,
+ 8192,
+ -12288
+ ],
+ [
+ -16384,
+ -20479,
+ 24575
+ ]
+ ],
+ [
+ [
+ -28671,
+ 32767,
+ 16384
+ ],
+ [
+ -8192,
+ 12288,
+ -4096
+ ]
+ ]
+ ]
+ ],
+ "scale": 0.0002441480755805969,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..3cb0552e9
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00037035736022517085,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/record_minmax/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/record_minmax/ifm1.json
new file mode 100644
index 000000000..097ef6a03
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/record_minmax/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.801437664031982,
+ "max": 4.600067481994629
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..5ebbba10e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -11.26651382446289,
+ "max": 12.135499725341797
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ifm1.json
new file mode 100644
index 000000000..a223fa4aa
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.038489170372486115,
+ "zero_point": 129.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ifm2.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ifm2.json
new file mode 100644
index 000000000..ec6082d55
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ifm2.json
@@ -0,0 +1,32 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 136,
+ 153,
+ 68
+ ],
+ [
+ 51,
+ 34,
+ 221
+ ]
+ ],
+ [
+ [
+ 0,
+ 255,
+ 187
+ ],
+ [
+ 85,
+ 170,
+ 102
+ ]
+ ]
+ ]
+ ],
+ "scale": 0.05882352963089943,
+ "zero_point": 119.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..afa9b1a8e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0892433300614357,
+ "zero_point": 134.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/record_minmax/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/record_minmax/ifm1.json
new file mode 100644
index 000000000..0138d54cf
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/record_minmax/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.9815891456604,
+ "max": 4.833149127960205
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..8edbed5b6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Add_002/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -11.962269973754882,
+ "max": 10.79477970123291
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/quantization/ifm.json
new file mode 100644
index 000000000..353f15a6b
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0001523942337371409,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..c4ace78d4
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00012122748012188822,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/record_minmax/ifm.json
new file mode 100644
index 000000000..2918a2323
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.9582903289794915,
+ "max": 4.9935017013549805
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..4d78b2007
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -3.9722607898712154,
+ "max": 3.720821704864502
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/quantization/ifm.json
new file mode 100644
index 000000000..0528cc9cc
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.03911808878183365,
+ "zero_point": 127.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..ac5da0bda
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.027372928336262703,
+ "zero_point": 141.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/record_minmax/ifm.json
new file mode 100644
index 000000000..8701c51ff
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.9830295753479,
+ "max": 4.992084045410156
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..b2bb2d227
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/AveragePool2D_000/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -3.863597021102905,
+ "max": 3.1164999485015867
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ifm1.json
new file mode 100644
index 000000000..71265a270
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0002441480755805969,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ifm2.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ifm2.json
new file mode 100644
index 000000000..53d7cdba3
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ifm2.json
@@ -0,0 +1,28 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 4096,
+ 8192
+ ],
+ [
+ -12288,
+ -16384
+ ]
+ ],
+ [
+ [
+ -20479,
+ 24575
+ ],
+ [
+ -28671,
+ 32767
+ ]
+ ]
+ ]
+ ],
+ "scale": 0.0002441480755805969,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..71265a270
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0002441480755805969,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/record_minmax/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/record_minmax/ifm1.json
new file mode 100644
index 000000000..5f5c917d3
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/record_minmax/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.591858749389648,
+ "max": 3.884731464385986
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..700674c7c
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -7.0,
+ "max": 8.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ifm1.json
new file mode 100644
index 000000000..522880618
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.05882352963089943,
+ "zero_point": 119.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ifm2.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ifm2.json
new file mode 100644
index 000000000..17ba25363
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ifm2.json
@@ -0,0 +1,28 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 136,
+ 153
+ ],
+ [
+ 68,
+ 51
+ ]
+ ],
+ [
+ [
+ 34,
+ 221
+ ],
+ [
+ 0,
+ 255
+ ]
+ ]
+ ]
+ ],
+ "scale": 0.05882352963089943,
+ "zero_point": 119.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..522880618
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.05882352963089943,
+ "zero_point": 119.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/record_minmax/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/record_minmax/ifm1.json
new file mode 100644
index 000000000..dc8d1db1e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/record_minmax/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "min": -2.8125765800476072,
+ "max": 4.720572299957276
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..700674c7c
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Concatenation_001/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -7.0,
+ "max": 8.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/fake_quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/fake_quantization/ker.json
new file mode 100644
index 000000000..8817cbef7
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/fake_quantization/ker.json
@@ -0,0 +1,48 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 1.000030517578125,
+ 2.00006103515625
+ ],
+ [
+ -3.000091552734375,
+ -4.0001220703125
+ ]
+ ],
+ [
+ [
+ -4.999908447265625,
+ 5.99993896484375
+ ],
+ [
+ -6.999969482421875,
+ 8.0
+ ]
+ ]
+ ],
+ [
+ [
+ [
+ 4.0001220703125,
+ -2.00006103515625
+ ],
+ [
+ 3.000091552734375,
+ -1.000030517578125
+ ]
+ ],
+ [
+ [
+ -8.0,
+ -5.99993896484375
+ ],
+ [
+ 6.999969482421875,
+ 4.999908447265625
+ ]
+ ]
+ ]
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/bias.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/bias.json
new file mode 100644
index 000000000..b00d8d211
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/bias.json
@@ -0,0 +1,10 @@
+{
+ "weights": [
+ 26925029,
+ 53850057
+ ],
+ "scale": [
+ 3.714016479907864e-08,
+ 3.714016479907864e-08
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ifm.json
new file mode 100644
index 000000000..df5d06c09
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00015212147263810039,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ker.json
new file mode 100644
index 000000000..94c794fbb
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ker.json
@@ -0,0 +1,61 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 4096,
+ 8192
+ ],
+ [
+ -12288,
+ -16384
+ ]
+ ],
+ [
+ [
+ -20479,
+ 24575
+ ],
+ [
+ -28671,
+ 32767
+ ]
+ ]
+ ],
+ [
+ [
+ [
+ 16384,
+ -8192
+ ],
+ [
+ 12288,
+ -4096
+ ]
+ ],
+ [
+ [
+ -32767,
+ -24575
+ ],
+ [
+ 28671,
+ 20479
+ ]
+ ]
+ ]
+ ],
+ "scale": [
+ 0.00024414807580797754,
+ 0.00024414807580797754
+ ],
+ "zero_point": 0.0,
+ "min": [
+ -8.0,
+ -8.0
+ ],
+ "max": [
+ 8.0,
+ 8.0
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..e02eeb9dc
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.002048635622486472,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/record_minmax/ifm.json
new file mode 100644
index 000000000..263de8644
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.964057750701905,
+ "max": 4.984564266204834
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..4d969f6ef
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": 0.0,
+ "max": 67.1276399230957
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/fake_quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/fake_quantization/ker.json
new file mode 100644
index 000000000..6460e54cf
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/fake_quantization/ker.json
@@ -0,0 +1,48 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 1.0,
+ 2.0
+ ],
+ [
+ -3.0,
+ -4.0
+ ]
+ ],
+ [
+ [
+ -5.0,
+ 6.0
+ ],
+ [
+ -7.0,
+ 8.0
+ ]
+ ]
+ ],
+ [
+ [
+ [
+ 4.0,
+ -2.0
+ ],
+ [
+ 3.0,
+ -1.0
+ ]
+ ],
+ [
+ [
+ -8.0,
+ -6.0
+ ],
+ [
+ 7.0,
+ 5.0
+ ]
+ ]
+ ]
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/quantization/bias.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/quantization/bias.json
new file mode 100644
index 000000000..a55af0be5
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/quantization/bias.json
@@ -0,0 +1,10 @@
+{
+ "weights": [
+ 4374,
+ 8747
+ ],
+ "scale": [
+ 0.0002286423499283808,
+ 0.0002286423499283808
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/quantization/ifm.json
new file mode 100644
index 000000000..0e481bbfd
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0038869199343025684,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/quantization/ker.json
new file mode 100644
index 000000000..4e12a5550
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/quantization/ker.json
@@ -0,0 +1,64 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 136,
+ 153
+ ],
+ [
+ 68,
+ 51
+ ]
+ ],
+ [
+ [
+ 34,
+ 221
+ ],
+ [
+ 0,
+ 255
+ ]
+ ]
+ ],
+ [
+ [
+ [
+ 204,
+ 102
+ ],
+ [
+ 187,
+ 119
+ ]
+ ],
+ [
+ [
+ 0,
+ 34
+ ],
+ [
+ 255,
+ 221
+ ]
+ ]
+ ]
+ ],
+ "scale": [
+ 0.058823529411764705,
+ 0.058823529411764705
+ ],
+ "zero_point": [
+ 119.0,
+ 136.0
+ ],
+ "min": [
+ -7.0,
+ -8.0
+ ],
+ "max": [
+ 8.0,
+ 7.0
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/quantization/ofm.json
new file mode 100644
index 000000000..7d23cbad2
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.05829785391688347,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/record_minmax/ifm.json
new file mode 100644
index 000000000..af8dc16de
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": 0.022708916887640953,
+ "max": 0.9911645770072937
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..5f7bd9942
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/channel/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": 0.0,
+ "max": 14.86595230102539
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/fake_quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/fake_quantization/ker.json
new file mode 100644
index 000000000..2558bb2be
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/fake_quantization/ker.json
@@ -0,0 +1,48 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 1.0039215087890625,
+ 2.007843017578125
+ ],
+ [
+ -3.0117650032043457,
+ -4.015686511993408
+ ]
+ ],
+ [
+ [
+ -5.019608497619629,
+ 6.023530006408691
+ ],
+ [
+ -7.027451515197754,
+ 7.9686279296875
+ ]
+ ]
+ ],
+ [
+ [
+ [
+ 4.01568603515625,
+ -2.007843494415283
+ ],
+ [
+ 3.0117645263671875,
+ -1.0039215087890625
+ ]
+ ],
+ [
+ [
+ -7.9686279296875,
+ -6.023530006408691
+ ],
+ [
+ 7.027451515197754,
+ 5.019608497619629
+ ]
+ ]
+ ]
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/quantization/bias.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/quantization/bias.json
new file mode 100644
index 000000000..50d44ece7
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/quantization/bias.json
@@ -0,0 +1,7 @@
+{
+ "weights": [
+ 4069,
+ 8138
+ ],
+ "scale": 0.0002457468386200985
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/quantization/ifm.json
new file mode 100644
index 000000000..24508860d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.003916590008884668,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/quantization/ker.json
new file mode 100644
index 000000000..b249a0ce5
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/quantization/ker.json
@@ -0,0 +1,52 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 143,
+ 159
+ ],
+ [
+ 79,
+ 63
+ ]
+ ],
+ [
+ [
+ 47,
+ 223
+ ],
+ [
+ 15,
+ 254
+ ]
+ ]
+ ],
+ [
+ [
+ [
+ 191,
+ 95
+ ],
+ [
+ 175,
+ 111
+ ]
+ ],
+ [
+ [
+ 0,
+ 31
+ ],
+ [
+ 239,
+ 207
+ ]
+ ]
+ ]
+ ],
+ "scale": 0.062745101749897,
+ "zero_point": 127.0,
+ "min": -7.9686279296875,
+ "max": 8.031373023986816
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..a2dd6681f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.037479765713214874,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/record_minmax/ifm.json
new file mode 100644
index 000000000..42f8b5617
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": 0.005472412034869194,
+ "max": 0.9987304735183716
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..1862e8cb2
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Conv2D_004/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": 0.0,
+ "max": 9.557340850830078
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/fake_quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/fake_quantization/ker.json
new file mode 100644
index 000000000..20c1f6759
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/fake_quantization/ker.json
@@ -0,0 +1,34 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 1.00018310546875,
+ 2.0,
+ 2.99981689453125,
+ 4.0001220703125
+ ],
+ [
+ -9.00006103515625,
+ 10.0,
+ -10.99993896484375,
+ 11.9998779296875
+ ]
+ ],
+ [
+ [
+ 5.0001220703125,
+ 6.0,
+ 6.9998779296875,
+ 8.000244140625
+ ],
+ [
+ 13.0,
+ -14.0,
+ 15.0,
+ -16.0
+ ]
+ ]
+ ]
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/bias.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/bias.json
new file mode 100644
index 000000000..632333144
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/bias.json
@@ -0,0 +1,14 @@
+{
+ "weights": [
+ 17503969,
+ 32507370,
+ 45510319,
+ 56887898
+ ],
+ "scale": [
+ 5.7129901172951205e-08,
+ 6.152450895548591e-08,
+ 6.591911673802062e-08,
+ 7.031372452055533e-08
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ifm.json
new file mode 100644
index 000000000..7105a686d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00014399811334442347,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ker.json
new file mode 100644
index 000000000..d465a7c17
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ker.json
@@ -0,0 +1,53 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 2521,
+ 4681,
+ 6553,
+ 8192
+ ],
+ [
+ -22685,
+ 23405,
+ -24029,
+ 24575
+ ]
+ ],
+ [
+ [
+ 12603,
+ 14043,
+ 15291,
+ 16384
+ ],
+ [
+ 32767,
+ -32767,
+ 32767,
+ -32767
+ ]
+ ]
+ ]
+ ],
+ "scale": [
+ 0.0003967406231879635,
+ 0.0004272591326639607,
+ 0.0004577776421399579,
+ 0.0004882961516159551
+ ],
+ "zero_point": 0.0,
+ "min": [
+ -13.0,
+ -14.0,
+ -15.0,
+ -16.0
+ ],
+ "max": [
+ 13.0,
+ 14.0,
+ 15.0,
+ 16.0
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..2d84cd7d8
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0031168656423687935,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/record_minmax/ifm.json
new file mode 100644
index 000000000..2ef9a69b9
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.7183862495422355,
+ "max": 4.684358768463135
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..ff55057b2
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": 0.0,
+ "max": 102.13033935546875
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/fake_quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/fake_quantization/ker.json
new file mode 100644
index 000000000..675eadcb6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/fake_quantization/ker.json
@@ -0,0 +1,34 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 1.0352935791015625,
+ 1.976470947265625,
+ 2.9568634033203125,
+ 3.95294189453125
+ ],
+ [
+ -8.972549438476562,
+ 9.976470947265625,
+ -11.011764526367188,
+ 11.9686279296875
+ ]
+ ],
+ [
+ [
+ 5.0039215087890625,
+ 6.023530960083008,
+ 7.035295486450195,
+ 8.01568603515625
+ ],
+ [
+ 13.027450561523438,
+ -14.023529052734375,
+ 14.988235473632812,
+ -16.0313720703125
+ ]
+ ]
+ ]
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/quantization/bias.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/quantization/bias.json
new file mode 100644
index 000000000..3cda45238
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/quantization/bias.json
@@ -0,0 +1,14 @@
+{
+ "weights": [
+ 2985,
+ 5473,
+ 7578,
+ 9382
+ ],
+ "scale": [
+ 0.0003349798455903035,
+ 0.0003654325561959198,
+ 0.00039588526680153606,
+ 0.00042633797740715233
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/quantization/ifm.json
new file mode 100644
index 000000000..97931cc58
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.003882720833644271,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/quantization/ker.json
new file mode 100644
index 000000000..add4d0f35
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/quantization/ker.json
@@ -0,0 +1,58 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 116,
+ 170,
+ 137,
+ 182
+ ],
+ [
+ 0,
+ 255,
+ 0,
+ 255
+ ]
+ ],
+ [
+ [
+ 162,
+ 213,
+ 177,
+ 219
+ ],
+ [
+ 255,
+ 0,
+ 255,
+ 0
+ ]
+ ]
+ ]
+ ],
+ "scale": [
+ 0.08627450980392157,
+ 0.09411764705882353,
+ 0.10196078431372549,
+ 0.10980392156862745
+ ],
+ "zero_point": [
+ 104.0,
+ 149.0,
+ 108.0,
+ 146.0
+ ],
+ "min": [
+ -8.972549019607843,
+ -14.023529411764706,
+ -11.011764705882353,
+ -16.031372549019608
+ ],
+ "max": [
+ 13.027450980392157,
+ 9.976470588235294,
+ 14.988235294117647,
+ 11.968627450980392
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/quantization/ofm.json
new file mode 100644
index 000000000..f587aac24
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.07756166160106659,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/record_minmax/ifm.json
new file mode 100644
index 000000000..fa8fffc3e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": 0.003264044094830751,
+ "max": 0.9900938200950622
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..612c0b4ea
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/channel/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": 0.0,
+ "max": 19.778222274780273
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/fake_quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/fake_quantization/ker.json
new file mode 100644
index 000000000..cd3479781
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/fake_quantization/ker.json
@@ -0,0 +1,34 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 0.9725494384765625,
+ 1.945098876953125,
+ 3.039216995239258,
+ 4.0117645263671875
+ ],
+ [
+ -8.996077537536621,
+ 9.9686279296875,
+ -10.94117546081543,
+ 12.035295486450195
+ ]
+ ],
+ [
+ [
+ 4.98431396484375,
+ 5.9568634033203125,
+ 7.050981521606445,
+ 8.023530960083008
+ ],
+ [
+ 13.007843017578125,
+ -13.980391502380371,
+ 14.95294189453125,
+ -16.04705810546875
+ ]
+ ]
+ ]
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/quantization/bias.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/quantization/bias.json
new file mode 100644
index 000000000..e60ff312e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/quantization/bias.json
@@ -0,0 +1,9 @@
+{
+ "weights": [
+ 2156,
+ 4312,
+ 6468,
+ 8624
+ ],
+ "scale": 0.0004638272181067826
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/quantization/ifm.json
new file mode 100644
index 000000000..4ec4ef2d7
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0038153529167175293,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/quantization/ker.json
new file mode 100644
index 000000000..01835fbde
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/quantization/ker.json
@@ -0,0 +1,38 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 140,
+ 148,
+ 157,
+ 165
+ ],
+ [
+ 58,
+ 214,
+ 42,
+ 231
+ ]
+ ],
+ [
+ [
+ 173,
+ 181,
+ 190,
+ 198
+ ],
+ [
+ 239,
+ 17,
+ 255,
+ 0
+ ]
+ ]
+ ]
+ ],
+ "scale": 0.12156862765550613,
+ "zero_point": 132.0,
+ "min": -16.04705810546875,
+ "max": 14.952940940856934
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..39c64f3ef
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.07362665981054306,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/record_minmax/ifm.json
new file mode 100644
index 000000000..bb4292efe
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": 0.02638142943382263,
+ "max": 0.9729149651527405
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..1c118e1db
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/DepthwiseConv2D_002/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": 0.0,
+ "max": 18.77479721069336
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/fake_quantization/weight.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/fake_quantization/weight.json
new file mode 100644
index 000000000..559e537fc
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/fake_quantization/weight.json
@@ -0,0 +1,76 @@
+{
+ "weights": [
+ [
+ 1.000030517578125,
+ 2.00006103515625,
+ -3.000091552734375,
+ -4.0001220703125,
+ -4.999908447265625,
+ 5.99993896484375,
+ -6.999969482421875,
+ 8.0,
+ 4.0001220703125,
+ -2.00006103515625,
+ 3.000091552734375,
+ -1.000030517578125,
+ -8.0,
+ -5.99993896484375,
+ 6.999969482421875,
+ 4.999908447265625
+ ],
+ [
+ 1.000030517578125,
+ 2.00006103515625,
+ -3.000091552734375,
+ -4.0001220703125,
+ -4.999908447265625,
+ 5.99993896484375,
+ -6.999969482421875,
+ 8.0,
+ 4.0001220703125,
+ -2.00006103515625,
+ 3.000091552734375,
+ -1.000030517578125,
+ -8.0,
+ -5.99993896484375,
+ 6.999969482421875,
+ 4.999908447265625
+ ],
+ [
+ 1.000030517578125,
+ 2.00006103515625,
+ -3.000091552734375,
+ -4.0001220703125,
+ -4.999908447265625,
+ 5.99993896484375,
+ -6.999969482421875,
+ 8.0,
+ 4.0001220703125,
+ -2.00006103515625,
+ 3.000091552734375,
+ -1.000030517578125,
+ -8.0,
+ -5.99993896484375,
+ 6.999969482421875,
+ 4.999908447265625
+ ],
+ [
+ 1.000030517578125,
+ 2.00006103515625,
+ -3.000091552734375,
+ -4.0001220703125,
+ -4.999908447265625,
+ 5.99993896484375,
+ -6.999969482421875,
+ 8.0,
+ 4.0001220703125,
+ -2.00006103515625,
+ 3.000091552734375,
+ -1.000030517578125,
+ -8.0,
+ -5.99993896484375,
+ 6.999969482421875,
+ 4.999908447265625
+ ]
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/bias.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/bias.json
new file mode 100644
index 000000000..0186c03f4
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/bias.json
@@ -0,0 +1,14 @@
+{
+ "weights": [
+ 27619368,
+ -55238737,
+ -82858105,
+ 110477474
+ ],
+ "scale": [
+ 3.620647604581258e-08,
+ 3.620647604581258e-08,
+ 3.620647604581258e-08,
+ 3.620647604581258e-08
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/in.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/in.json
new file mode 100644
index 000000000..1fd68cabe
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/in.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00014829720021225512,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/out.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/out.json
new file mode 100644
index 000000000..b2950218c
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/out.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.003870659740641713,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/weight.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/weight.json
new file mode 100644
index 000000000..69254d12b
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/quantization/weight.json
@@ -0,0 +1,95 @@
+{
+ "weights": [
+ [
+ 4096,
+ 8192,
+ -12288,
+ -16384,
+ -20479,
+ 24575,
+ -28671,
+ 32767,
+ 16384,
+ -8192,
+ 12288,
+ -4096,
+ -32767,
+ -24575,
+ 28671,
+ 20479
+ ],
+ [
+ 4096,
+ 8192,
+ -12288,
+ -16384,
+ -20479,
+ 24575,
+ -28671,
+ 32767,
+ 16384,
+ -8192,
+ 12288,
+ -4096,
+ -32767,
+ -24575,
+ 28671,
+ 20479
+ ],
+ [
+ 4096,
+ 8192,
+ -12288,
+ -16384,
+ -20479,
+ 24575,
+ -28671,
+ 32767,
+ 16384,
+ -8192,
+ 12288,
+ -4096,
+ -32767,
+ -24575,
+ 28671,
+ 20479
+ ],
+ [
+ 4096,
+ 8192,
+ -12288,
+ -16384,
+ -20479,
+ 24575,
+ -28671,
+ 32767,
+ 16384,
+ -8192,
+ 12288,
+ -4096,
+ -32767,
+ -24575,
+ 28671,
+ 20479
+ ]
+ ],
+ "scale": [
+ 0.00024414807580797754,
+ 0.00024414807580797754,
+ 0.00024414807580797754,
+ 0.00024414807580797754
+ ],
+ "zero_point": 0.0,
+ "min": [
+ -8.0,
+ -8.0,
+ -8.0,
+ -8.0
+ ],
+ "max": [
+ 8.0,
+ 8.0,
+ 8.0,
+ 8.0
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/record_minmax/in.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/record_minmax/in.json
new file mode 100644
index 000000000..68ff7d8df
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/record_minmax/in.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.8592542457580565,
+ "max": 4.7664618492126465
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/record_minmax/out.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/record_minmax/out.json
new file mode 100644
index 000000000..c453af298
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/int16/record_minmax/out.json
@@ -0,0 +1,4 @@
+{
+ "min": -15.112948303222655,
+ "max": 126.82991027832031
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/fake_quantization/weight.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/fake_quantization/weight.json
new file mode 100644
index 000000000..4661cb3ca
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/fake_quantization/weight.json
@@ -0,0 +1,76 @@
+{
+ "weights": [
+ [
+ 1.0039215087890625,
+ 2.007843017578125,
+ -3.0117650032043457,
+ -4.015686511993408,
+ -5.019608020782471,
+ 6.023530006408691,
+ -7.027451515197754,
+ 7.968626976013184,
+ 4.015686988830566,
+ -2.007843017578125,
+ 3.0117645263671875,
+ -1.0039215087890625,
+ -7.9686279296875,
+ -6.023530006408691,
+ 7.027451515197754,
+ 5.019608497619629
+ ],
+ [
+ 1.0039215087890625,
+ 2.007843017578125,
+ -3.0117650032043457,
+ -4.015686511993408,
+ -5.019608020782471,
+ 6.023530006408691,
+ -7.027451515197754,
+ 7.968626976013184,
+ 4.015686988830566,
+ -2.007843017578125,
+ 3.0117645263671875,
+ -1.0039215087890625,
+ -7.9686279296875,
+ -6.023530006408691,
+ 7.027451515197754,
+ 5.019608497619629
+ ],
+ [
+ 1.0039215087890625,
+ 2.007843017578125,
+ -3.0117650032043457,
+ -4.015686511993408,
+ -5.019608020782471,
+ 6.023530006408691,
+ -7.027451515197754,
+ 7.968626976013184,
+ 4.015686988830566,
+ -2.007843017578125,
+ 3.0117645263671875,
+ -1.0039215087890625,
+ -7.9686279296875,
+ -6.023530006408691,
+ 7.027451515197754,
+ 5.019608497619629
+ ],
+ [
+ 1.0039215087890625,
+ 2.007843017578125,
+ -3.0117650032043457,
+ -4.015686511993408,
+ -5.019608020782471,
+ 6.023530006408691,
+ -7.027451515197754,
+ 7.968626976013184,
+ 4.015686988830566,
+ -2.007843017578125,
+ 3.0117645263671875,
+ -1.0039215087890625,
+ -7.9686279296875,
+ -6.023530006408691,
+ 7.027451515197754,
+ 5.019608497619629
+ ]
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/quantization/bias.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/quantization/bias.json
new file mode 100644
index 000000000..4333c0fed
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/quantization/bias.json
@@ -0,0 +1,14 @@
+{
+ "weights": [
+ 4099,
+ -8199,
+ -12298,
+ 16398
+ ],
+ "scale": [
+ 0.00024393631821001058,
+ 0.00024393631821001058,
+ 0.00024393631821001058,
+ 0.00024393631821001058
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/quantization/in.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/quantization/in.json
new file mode 100644
index 000000000..8edac1bd9
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/quantization/in.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.003887734841555357,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/quantization/out.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/quantization/out.json
new file mode 100644
index 000000000..1b94f1652
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/quantization/out.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.061938945204019547,
+ "zero_point": 171.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/quantization/weight.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/quantization/weight.json
new file mode 100644
index 000000000..5ee46c87f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/quantization/weight.json
@@ -0,0 +1,100 @@
+{
+ "weights": [
+ [
+ 144,
+ 160,
+ 80,
+ 64,
+ 48,
+ 224,
+ 16,
+ 255,
+ 192,
+ 96,
+ 176,
+ 112,
+ 1,
+ 32,
+ 240,
+ 208
+ ],
+ [
+ 144,
+ 160,
+ 80,
+ 64,
+ 48,
+ 224,
+ 16,
+ 255,
+ 192,
+ 96,
+ 176,
+ 112,
+ 1,
+ 32,
+ 240,
+ 208
+ ],
+ [
+ 144,
+ 160,
+ 80,
+ 64,
+ 48,
+ 224,
+ 16,
+ 255,
+ 192,
+ 96,
+ 176,
+ 112,
+ 1,
+ 32,
+ 240,
+ 208
+ ],
+ [
+ 144,
+ 160,
+ 80,
+ 64,
+ 48,
+ 224,
+ 16,
+ 255,
+ 192,
+ 96,
+ 176,
+ 112,
+ 1,
+ 32,
+ 240,
+ 208
+ ]
+ ],
+ "scale": [
+ 0.06274509803921569,
+ 0.06274509803921569,
+ 0.06274509803921569,
+ 0.06274509803921569
+ ],
+ "zero_point": [
+ 128.0,
+ 128.0,
+ 128.0,
+ 128.0
+ ],
+ "min": [
+ -8.031372549019608,
+ -8.031372549019608,
+ -8.031372549019608,
+ -8.031372549019608
+ ],
+ "max": [
+ 7.968627450980392,
+ 7.968627450980392,
+ 7.968627450980392,
+ 7.968627450980392
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/record_minmax/in.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/record_minmax/in.json
new file mode 100644
index 000000000..48e4645c9
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/record_minmax/in.json
@@ -0,0 +1,4 @@
+{
+ "min": 0.010438590832054616,
+ "max": 0.9913724160194397
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/record_minmax/out.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/record_minmax/out.json
new file mode 100644
index 000000000..ec83b94d1
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/channel/uint8/record_minmax/out.json
@@ -0,0 +1,4 @@
+{
+ "min": -10.584291763305664,
+ "max": 5.210139312744141
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/fake_quantization/weight.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/fake_quantization/weight.json
new file mode 100644
index 000000000..e1da53ab0
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/fake_quantization/weight.json
@@ -0,0 +1,76 @@
+{
+ "weights": [
+ [
+ 1.0039215087890625,
+ 2.007843017578125,
+ -3.0117650032043457,
+ -4.015686511993408,
+ -5.019608497619629,
+ 6.023530006408691,
+ -7.027451515197754,
+ 7.9686279296875,
+ 4.01568603515625,
+ -2.007843494415283,
+ 3.0117645263671875,
+ -1.0039215087890625,
+ -7.9686279296875,
+ -6.023530006408691,
+ 7.027451515197754,
+ 5.019608497619629
+ ],
+ [
+ 1.0039215087890625,
+ 2.007843017578125,
+ -3.0117650032043457,
+ -4.015686511993408,
+ -5.019608497619629,
+ 6.023530006408691,
+ -7.027451515197754,
+ 7.9686279296875,
+ 4.01568603515625,
+ -2.007843494415283,
+ 3.0117645263671875,
+ -1.0039215087890625,
+ -7.9686279296875,
+ -6.023530006408691,
+ 7.027451515197754,
+ 5.019608497619629
+ ],
+ [
+ 1.0039215087890625,
+ 2.007843017578125,
+ -3.0117650032043457,
+ -4.015686511993408,
+ -5.019608497619629,
+ 6.023530006408691,
+ -7.027451515197754,
+ 7.9686279296875,
+ 4.01568603515625,
+ -2.007843494415283,
+ 3.0117645263671875,
+ -1.0039215087890625,
+ -7.9686279296875,
+ -6.023530006408691,
+ 7.027451515197754,
+ 5.019608497619629
+ ],
+ [
+ 1.0039215087890625,
+ 2.007843017578125,
+ -3.0117650032043457,
+ -4.015686511993408,
+ -5.019608497619629,
+ 6.023530006408691,
+ -7.027451515197754,
+ 7.9686279296875,
+ 4.01568603515625,
+ -2.007843494415283,
+ 3.0117645263671875,
+ -1.0039215087890625,
+ -7.9686279296875,
+ -6.023530006408691,
+ 7.027451515197754,
+ 5.019608497619629
+ ]
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/quantization/bias.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/quantization/bias.json
new file mode 100644
index 000000000..ecb49bb64
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/quantization/bias.json
@@ -0,0 +1,9 @@
+{
+ "weights": [
+ 415,
+ -829,
+ -1244,
+ 1658
+ ],
+ "scale": 0.00241205753304663
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/quantization/in.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/quantization/in.json
new file mode 100644
index 000000000..654824b5d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/quantization/in.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.03844216465950012,
+ "zero_point": 126.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/quantization/out.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/quantization/out.json
new file mode 100644
index 000000000..3baa42155
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/quantization/out.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.741962730884552,
+ "zero_point": 156.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/quantization/weight.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/quantization/weight.json
new file mode 100644
index 000000000..940224049
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/quantization/weight.json
@@ -0,0 +1,80 @@
+{
+ "weights": [
+ [
+ 143,
+ 159,
+ 79,
+ 63,
+ 47,
+ 223,
+ 15,
+ 254,
+ 191,
+ 95,
+ 175,
+ 111,
+ 0,
+ 31,
+ 239,
+ 207
+ ],
+ [
+ 143,
+ 159,
+ 79,
+ 63,
+ 47,
+ 223,
+ 15,
+ 254,
+ 191,
+ 95,
+ 175,
+ 111,
+ 0,
+ 31,
+ 239,
+ 207
+ ],
+ [
+ 143,
+ 159,
+ 79,
+ 63,
+ 47,
+ 223,
+ 15,
+ 254,
+ 191,
+ 95,
+ 175,
+ 111,
+ 0,
+ 31,
+ 239,
+ 207
+ ],
+ [
+ 143,
+ 159,
+ 79,
+ 63,
+ 47,
+ 223,
+ 15,
+ 254,
+ 191,
+ 95,
+ 175,
+ 111,
+ 0,
+ 31,
+ 239,
+ 207
+ ]
+ ],
+ "scale": 0.062745101749897,
+ "zero_point": 127.0,
+ "min": -7.9686279296875,
+ "max": 8.031373023986816
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/record_minmax/in.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/record_minmax/in.json
new file mode 100644
index 000000000..a8ec5b2b6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/record_minmax/in.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.832756385803223,
+ "max": 4.969995346069336
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/record_minmax/out.json b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/record_minmax/out.json
new file mode 100644
index 000000000..de3b41564
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/FullyConnected_003/layer/uint8/record_minmax/out.json
@@ -0,0 +1,4 @@
+{
+ "min": -115.99438369750976,
+ "max": 73.20612327575684
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/quantization/ifm.json
new file mode 100644
index 000000000..5d9052815
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00015059474390000105,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..25491f05d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00014986195310484618,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/record_minmax/ifm.json
new file mode 100644
index 000000000..54dd14d76
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.9345380973815915,
+ "max": 4.910526599884033
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..635018467
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -2.4704078197479244,
+ "max": 4.910526599884033
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/quantization/ifm.json
new file mode 100644
index 000000000..9bf6c9bff
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.03876218944787979,
+ "zero_point": 126.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..87de1116e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.029836513102054596,
+ "zero_point": 88.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/record_minmax/ifm.json
new file mode 100644
index 000000000..bb42bdf8e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.901860733032226,
+ "max": 4.982497882843018
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..bb3a52516
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/MaxPool2D_000/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -2.6258130359649656,
+ "max": 4.982497882843018
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/ifm.json
new file mode 100644
index 000000000..18c3b0421
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00015251495642587543,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..145ee8fda
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00013844699424225837,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/reduction_indices.json b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/reduction_indices.json
new file mode 100644
index 000000000..394cfb322
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/quantization/reduction_indices.json
@@ -0,0 +1,5 @@
+{
+ "weights": [
+ -1
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/record_minmax/ifm.json
new file mode 100644
index 000000000..8e49c0eb2
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.9974578094482425,
+ "max": 4.991122436523438
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..740c3076a
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -3.351332187652588,
+ "max": 4.536492500305176
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/quantization/ifm.json
new file mode 100644
index 000000000..ede36c6ad
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.039086975157260895,
+ "zero_point": 128.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..bd2fc7f62
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.028692100197076797,
+ "zero_point": 131.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/record_minmax/ifm.json
new file mode 100644
index 000000000..ae1dc5e90
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.993542575836181,
+ "max": 4.97363561630249
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..527ed8d46
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mean_000/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -3.766610870361328,
+ "max": 3.5498746299743655
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ifm1.json
new file mode 100644
index 000000000..f329b43be
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0001513722527306527,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ifm2.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ifm2.json
new file mode 100644
index 000000000..ab968c9fc
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ifm2.json
@@ -0,0 +1,32 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 4096,
+ 8192,
+ -12288
+ ],
+ [
+ -16384,
+ -20479,
+ 24575
+ ]
+ ],
+ [
+ [
+ -28671,
+ 32767,
+ 16384
+ ],
+ [
+ -8192,
+ 12288,
+ -4096
+ ]
+ ]
+ ]
+ ],
+ "scale": 0.0002441480755805969,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..4b5118c3e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.000991688808426261,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/record_minmax/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/record_minmax/ifm1.json
new file mode 100644
index 000000000..d333b93a8
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/record_minmax/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.790120906829833,
+ "max": 4.960014820098877
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..d0cb3786d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -30.124285202026368,
+ "max": 32.49466659545899
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ifm1.json
new file mode 100644
index 000000000..bbff8952d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.03780897706747055,
+ "zero_point": 131.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ifm2.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ifm2.json
new file mode 100644
index 000000000..ec6082d55
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ifm2.json
@@ -0,0 +1,32 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 136,
+ 153,
+ 68
+ ],
+ [
+ 51,
+ 34,
+ 221
+ ]
+ ],
+ [
+ [
+ 0,
+ 255,
+ 187
+ ],
+ [
+ 85,
+ 170,
+ 102
+ ]
+ ]
+ ]
+ ],
+ "scale": 0.05882352963089943,
+ "zero_point": 119.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..cec0bdf9a
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.232084259390831,
+ "zero_point": 111.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/record_minmax/ifm1.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/record_minmax/ifm1.json
new file mode 100644
index 000000000..7cdb53424
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/record_minmax/ifm1.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.954726142883301,
+ "max": 4.686561832427978
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..5f63577ea
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/Mul_001/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -25.874579315185546,
+ "max": 33.30691329956055
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/alpha.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/alpha.json
new file mode 100644
index 000000000..5f6db8d72
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/alpha.json
@@ -0,0 +1,13 @@
+{
+ "weights": [
+ [
+ [
+ 6553,
+ 19660,
+ 32767
+ ]
+ ]
+ ],
+ "scale": 1.5259254723787308e-05,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/ifm.json
new file mode 100644
index 000000000..e75377c9e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0001509107678430155,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..e4a89e2c0
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00015084103506524116,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/record_minmax/ifm.json
new file mode 100644
index 000000000..a34d48c2a
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.944893226623535,
+ "max": 4.942608108520508
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..640397c4d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -2.451441249847412,
+ "max": 4.942608108520508
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/alpha.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/alpha.json
new file mode 100644
index 000000000..7c001602f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/alpha.json
@@ -0,0 +1,13 @@
+{
+ "weights": [
+ [
+ [
+ 51,
+ 153,
+ 255
+ ]
+ ]
+ ],
+ "scale": 0.0019607844296842813,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/ifm.json
new file mode 100644
index 000000000..05ce9dd2c
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.03849203139543533,
+ "zero_point": 127.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..8f883094a
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.02848827838897705,
+ "zero_point": 82.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/record_minmax/ifm.json
new file mode 100644
index 000000000..76e719001
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.899785652160644,
+ "max": 4.915681838989258
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..2aa27ca64
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/PRelu_001/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -2.348829574584961,
+ "max": 4.915681838989258
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/quantization/ifm.json
new file mode 100644
index 000000000..5a52a1b7b
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0001474507007515058,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..ff9e41ec8
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0001422425702912733,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/record_minmax/ifm.json
new file mode 100644
index 000000000..c26d04075
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.831517105102539,
+ "max": 4.660862083435059
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..7108b4601
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": 0.0,
+ "max": 4.660862083435059
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/quantization/ifm.json
new file mode 100644
index 000000000..3b97773ce
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.03907399624586105,
+ "zero_point": 127.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..698a8a7ee
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.01955186203122139,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/record_minmax/ifm.json
new file mode 100644
index 000000000..fee2d92c0
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.978144645690918,
+ "max": 4.985724964141846
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..bd6199fc0
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/ReLU_000/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": 0.0,
+ "max": 4.985724964141846
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/fake_quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/fake_quantization/ker.json
new file mode 100644
index 000000000..6df24eb42
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/fake_quantization/ker.json
@@ -0,0 +1,48 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 0.999786376953125,
+ 2.0001220703125
+ ],
+ [
+ -2.999908447265625,
+ -4.000244140625
+ ],
+ [
+ 5.000030517578125,
+ -5.99981689453125
+ ]
+ ],
+ [
+ [
+ 7.000152587890625,
+ 7.99993896484375
+ ],
+ [
+ -9.000274658203125,
+ -10.00006103515625
+ ],
+ [
+ 10.999847412109375,
+ -12.00018310546875
+ ]
+ ],
+ [
+ [
+ 12.999969482421875,
+ 13.999755859375
+ ],
+ [
+ -15.000091552734375,
+ -15.9998779296875
+ ],
+ [
+ 17.000213623046875,
+ -18.0
+ ]
+ ]
+ ]
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/.json
new file mode 100644
index 000000000..a9a5c4735
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/.json
@@ -0,0 +1,5 @@
+{
+ "weights": [
+ 0
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ifm.json
new file mode 100644
index 000000000..82f7fa2b6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.00015178922330960631,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ker.json
new file mode 100644
index 000000000..8d0ceb1c6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ker.json
@@ -0,0 +1,58 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 1820,
+ 3641
+ ],
+ [
+ -5461,
+ -7282
+ ],
+ [
+ 9102,
+ -10922
+ ]
+ ],
+ [
+ [
+ 12743,
+ 14563
+ ],
+ [
+ -16384,
+ -18204
+ ],
+ [
+ 20024,
+ -21845
+ ]
+ ],
+ [
+ [
+ 23665,
+ 25485
+ ],
+ [
+ -27306,
+ -29126
+ ],
+ [
+ 30947,
+ -32767
+ ]
+ ]
+ ]
+ ],
+ "scale": [
+ 0.0005493331705679495
+ ],
+ "zero_point": 0.0,
+ "min": [
+ -18.0
+ ],
+ "max": [
+ 18.0
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ofm.json
new file mode 100644
index 000000000..f370bf44d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0122029148042202,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/record_minmax/ifm.json
new file mode 100644
index 000000000..8cd48cedd
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.942056541442871,
+ "max": 4.973677654266357
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/record_minmax/ofm.json
new file mode 100644
index 000000000..0ad23742d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/int16/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -269.66596435546876,
+ "max": 399.85290710449215
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/fake_quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/fake_quantization/ker.json
new file mode 100644
index 000000000..76a0440a0
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/fake_quantization/ker.json
@@ -0,0 +1,48 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 0.960784912109375,
+ 2.0588245391845703
+ ],
+ [
+ -3.0196075439453125,
+ -3.980391502380371
+ ],
+ [
+ 4.9411773681640625,
+ -6.039215087890625
+ ]
+ ],
+ [
+ [
+ 7.0,
+ 7.960784912109375
+ ],
+ [
+ -9.058823585510254,
+ -10.019607543945312
+ ],
+ [
+ 10.980392456054688,
+ -11.941176414489746
+ ]
+ ],
+ [
+ [
+ 13.039216995239258,
+ 14.000001907348633
+ ],
+ [
+ -14.960784912109375,
+ -16.05882453918457
+ ],
+ [
+ 17.019607543945312,
+ -17.980392456054688
+ ]
+ ]
+ ]
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/quantization/ifm.json
new file mode 100644
index 000000000..4c3669f6b
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.0038701011799275875,
+ "zero_point": 0.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/quantization/ker.json
new file mode 100644
index 000000000..04e0648de
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/quantization/ker.json
@@ -0,0 +1,60 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 138,
+ 146
+ ],
+ [
+ 109,
+ 102
+ ],
+ [
+ 167,
+ 87
+ ]
+ ],
+ [
+ [
+ 182,
+ 189
+ ],
+ [
+ 65,
+ 58
+ ],
+ [
+ 211,
+ 44
+ ]
+ ],
+ [
+ [
+ 226,
+ 233
+ ],
+ [
+ 22,
+ 14
+ ],
+ [
+ 255,
+ 0
+ ]
+ ]
+ ]
+ ],
+ "scale": [
+ 0.13725490196078433
+ ],
+ "zero_point": [
+ 131.0
+ ],
+ "min": [
+ -17.980392156862745
+ ],
+ "max": [
+ 17.019607843137255
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/quantization/ofm.json
new file mode 100644
index 000000000..2e1790508
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.25486624240875244,
+ "zero_point": 178.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/record_minmax/ifm.json
new file mode 100644
index 000000000..d46844baf
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": 0.006121497452259064,
+ "max": 0.9868757891654968
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..4441f1876
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/channel/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -45.46586318969727,
+ "max": 19.525028419494628
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/fake_quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/fake_quantization/ker.json
new file mode 100644
index 000000000..76a0440a0
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/fake_quantization/ker.json
@@ -0,0 +1,48 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 0.960784912109375,
+ 2.0588245391845703
+ ],
+ [
+ -3.0196075439453125,
+ -3.980391502380371
+ ],
+ [
+ 4.9411773681640625,
+ -6.039215087890625
+ ]
+ ],
+ [
+ [
+ 7.0,
+ 7.960784912109375
+ ],
+ [
+ -9.058823585510254,
+ -10.019607543945312
+ ],
+ [
+ 10.980392456054688,
+ -11.941176414489746
+ ]
+ ],
+ [
+ [
+ 13.039216995239258,
+ 14.000001907348633
+ ],
+ [
+ -14.960784912109375,
+ -16.05882453918457
+ ],
+ [
+ 17.019607543945312,
+ -17.980392456054688
+ ]
+ ]
+ ]
+ ]
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/quantization/ifm.json
new file mode 100644
index 000000000..dc5ca8dd5
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/quantization/ifm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 0.03869570419192314,
+ "zero_point": 126.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/quantization/ker.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/quantization/ker.json
new file mode 100644
index 000000000..bc150bbb0
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/quantization/ker.json
@@ -0,0 +1,52 @@
+{
+ "weights": [
+ [
+ [
+ [
+ 138,
+ 146
+ ],
+ [
+ 109,
+ 102
+ ],
+ [
+ 167,
+ 87
+ ]
+ ],
+ [
+ [
+ 182,
+ 189
+ ],
+ [
+ 65,
+ 58
+ ],
+ [
+ 211,
+ 44
+ ]
+ ],
+ [
+ [
+ 226,
+ 233
+ ],
+ [
+ 22,
+ 14
+ ],
+ [
+ 255,
+ 0
+ ]
+ ]
+ ]
+ ],
+ "scale": 0.13725490868091583,
+ "zero_point": 131.0,
+ "min": -17.980392456054688,
+ "max": 17.019609451293945
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/quantization/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/quantization/ofm.json
new file mode 100644
index 000000000..bfd862189
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/quantization/ofm.json
@@ -0,0 +1,4 @@
+{
+ "scale": 1.6333034038543701,
+ "zero_point": 127.0
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/record_minmax/ifm.json
new file mode 100644
index 000000000..2d2af08a6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/record_minmax/ifm.json
@@ -0,0 +1,4 @@
+{
+ "min": -4.890846576690674,
+ "max": 4.976558513641357
+}
diff --git a/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/record_minmax/ofm.json b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/record_minmax/ofm.json
new file mode 100644
index 000000000..24598f06e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/expected_outputs/TransposeConv_001/layer/uint8/record_minmax/ofm.json
@@ -0,0 +1,4 @@
+{
+ "min": -207.54233032226563,
+ "max": 208.95002136230468
+}
diff --git a/compiler/pota-quantization-value-test/gen_h5_explicit_inputs.py b/compiler/pota-quantization-value-test/gen_h5_explicit_inputs.py
new file mode 100755
index 000000000..9863c807a
--- /dev/null
+++ b/compiler/pota-quantization-value-test/gen_h5_explicit_inputs.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+import h5py as h5
+import numpy as np
+import tensorflow as tf
+import argparse
+import glob
+
+#
+# This script generates a pack of random input data (.h5) expected by the input tflite model
+#
+# Basic usage:
+# gen_h5_explicit_inputs.py --model <path/to/model/file> --input <path/to/input/directory> --output <path/to/output/file>
+# ex: gen_h5_explicit_inputs.py --model Add_000.tflite --input Add_000 --output Add_000.input.h5
+# (This will create Add_000.input.h5)
+#
+# The input directory should be organized as follows
+# <input_directory>/
+# -> <record_index>.txt
+# ...
+# Each txt file has the explicit values of inputs
+# Example. if the model has two inputs whose shapes are both (1, 3),
+# the first record file name is 0.txt, and its contents is something like below
+# 1, 2, 3
+# 4, 5, 6
+#
+parser = argparse.ArgumentParser()
+parser.add_argument('--model', type=str, required=True)
+parser.add_argument('--input', type=str, required=True)
+parser.add_argument('--output', type=str, required=True)
+args = parser.parse_args()
+
+model = args.model
+input = args.input
+output = args.output
+
+# Build TFLite interpreter. (to get the information of model input)
+interpreter = tf.lite.Interpreter(model)
+input_details = interpreter.get_input_details()
+
+# Create h5 file
+h5_file = h5.File(output, 'w')
+group = h5_file.create_group("value")
+group.attrs['desc'] = "Input data for " + model
+
+# Input files
+records = sorted(glob.glob(input + "/*.txt"))
+for i, record in enumerate(records):
+ sample = group.create_group(str(i))
+ sample.attrs['desc'] = "Input data " + str(i)
+ with open(record, 'r') as f:
+ lines = f.readlines()
+ for j, line in enumerate(lines):
+ data = np.array(line.split(','))
+ input_detail = input_details[j]
+ input_data = np.array(
+ data.reshape(input_detail["shape"]), input_detail["dtype"])
+ sample.create_dataset(str(j), data=input_data)
+
+h5_file.close()
diff --git a/compiler/pota-quantization-value-test/requires.cmake b/compiler/pota-quantization-value-test/requires.cmake
new file mode 100644
index 000000000..883a925df
--- /dev/null
+++ b/compiler/pota-quantization-value-test/requires.cmake
@@ -0,0 +1,4 @@
+require("record-minmax")
+require("circle-quantizer")
+require("circle-tensordump")
+require("common-artifacts")
diff --git a/compiler/pota-quantization-value-test/test.lst b/compiler/pota-quantization-value-test/test.lst
new file mode 100644
index 000000000..15606b8e4
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test.lst
@@ -0,0 +1,28 @@
+addTest(Add_002 layer uint8)
+addTest(Add_002 channel int16)
+addTest(AveragePool2D_000 layer uint8)
+addTest(AveragePool2D_000 channel int16)
+addTest(Concatenation_001 layer uint8)
+addTest(Concatenation_001 channel int16)
+addTest(Conv2D_004 channel uint8)
+addTest(Conv2D_004 channel int16)
+addTest(Conv2D_004 layer uint8)
+addTest(DepthwiseConv2D_002 channel uint8)
+addTest(DepthwiseConv2D_002 channel int16)
+addTest(DepthwiseConv2D_002 layer uint8)
+addTest(FullyConnected_003 channel uint8)
+addTest(FullyConnected_003 channel int16)
+addTest(FullyConnected_003 layer uint8)
+addTest(Mean_000 layer uint8)
+addTest(Mean_000 channel int16)
+addTest(MaxPool2D_000 layer uint8)
+addTest(MaxPool2D_000 channel int16)
+addTest(Mul_001 layer uint8)
+addTest(Mul_001 channel int16)
+addTest(PRelu_001 layer uint8)
+addTest(PRelu_001 channel int16)
+addTest(ReLU_000 layer uint8)
+addTest(ReLU_000 channel int16)
+addTest(TransposeConv_001 channel uint8)
+addTest(TransposeConv_001 channel int16)
+addTest(TransposeConv_001 layer uint8)
diff --git a/compiler/pota-quantization-value-test/test_fake_wquant.sh b/compiler/pota-quantization-value-test/test_fake_wquant.sh
new file mode 100755
index 000000000..1331703ee
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_fake_wquant.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+
+# This script tests the basic behavior of record-minmax
+#
+# HOW TO USE
+#
+# ./test_fake_quantization.sh <path/to/test.config> <path/to/work_dir> <TEST 1> <TEST 2> ...
+# test.config : set ${RECORD_MINMAX_PATH} and ${CIRCLE_QUANTIZER_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}.fake_quantized.passed"
+ rm -f "${PASSED_TAG}"
+
+ cat > "${TEST_RESULT_FILE}_fake_quantization.log" <(
+ exec 2>&1
+ set -ex
+
+ # Run circle-quantizer with --quantize_dequantize_weights
+ "${CIRCLE_QUANTIZER_PATH}" \
+ --quantize_dequantize_weights float32 "${DTYPE}" "${GRANULARITY}" \
+ "${WORKDIR}/${MODELNAME}.circle" \
+ "${TEST_RESULT_FILE}.fake_quantized.circle"
+
+ # Dump weights values (circle-tensordump)
+ "${CIRCLE_TENSORDUMP_PATH}" \
+ "${TEST_RESULT_FILE}.fake_quantized.circle" \
+ --tensors_to_hdf5 "${TEST_RESULT_FILE}.fake_quantized.circle.h5"
+
+ # Compare result
+ "${VIRTUALENV}/bin/python" "${COMPARE_SCRIPT_PATH}" \
+ --input_h5 "${TEST_RESULT_FILE}.fake_quantized.circle.h5" \
+ --expect_dir "${SOURCE_PATH}/expected_outputs/${MODELNAME}/${GRANULARITY}/${DTYPE}/fake_quantization" \
+ --mode fake_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/pota-quantization-value-test/test_inputs/Add_002/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/0.txt
new file mode 100644
index 000000000..a219546a1
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/0.txt
@@ -0,0 +1 @@
+-0.48516417,-4.5555663 ,-2.9907737 , 2.422857 , 1.010034 , 3.6436582 , 0.29334423,-4.0628953 , 1.0116768 , 3.0871766 , 3.3341465 , 4.3921704
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/1.txt
new file mode 100644
index 000000000..70d3139a0
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/1.txt
@@ -0,0 +1 @@
+-0.7787985 , 4.101575 ,-0.4839729 , 0.35971674,-4.3452406 ,-4.811665 ,-3.8693128 , 4.239326 , 0.44103175, 3.5549765 , 2.5334291 , 1.4546562
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/2.txt
new file mode 100644
index 000000000..3c38f8d5d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/2.txt
@@ -0,0 +1 @@
+ 3.5943313,-1.4843192, 1.956341 ,-1.3242344, 1.5901331,-3.641623 , 4.6022506,-0.307265 ,-0.6359913,-4.0109854,-1.2064985, 1.1137954
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/3.txt
new file mode 100644
index 000000000..e89a022f5
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/3.txt
@@ -0,0 +1 @@
+ 3.1036437 ,-0.39538398,-0.07278133, 4.547673 , 3.9132211 , 2.6468625 ,-4.2830634 ,-2.0573084 , 2.1074655 ,-4.0634165 ,-4.55598 ,-0.7942089
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/4.txt
new file mode 100644
index 000000000..2b00832cd
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/channel/int16/4.txt
@@ -0,0 +1 @@
+-2.7745228, 1.4813256, 4.4699864, 3.7466738,-2.9847758,-4.453416 , 3.2515864,-1.2459193,-4.44965 ,-1.8452735, 4.423347 , 4.2998137
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/0.txt
new file mode 100644
index 000000000..b6e2efa3d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/0.txt
@@ -0,0 +1 @@
+-0.8596993, 4.8127713,-3.4127183, 4.2323627,-2.2201376,-1.5362649,-4.9921966, 0.9565166, 3.2879171,-1.3590081,-3.771852 ,-4.1042285
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/1.txt
new file mode 100644
index 000000000..bcf2807ba
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/1.txt
@@ -0,0 +1 @@
+ 0.14624089, 4.7304125 , 4.833998 , 4.2321773 ,-2.0582533 ,-2.3694758 , 1.4213978 , 2.2444596 , 3.3630798 ,-0.70257574, 3.586656 ,-2.513805
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/2.txt
new file mode 100644
index 000000000..c3e32d2c5
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/2.txt
@@ -0,0 +1 @@
+ 2.175218 , 0.02776978,-2.6291077 , 3.5350094 ,-1.2364857 ,-3.3151364 ,-0.92507887, 2.8038094 ,-1.8781518 , 3.6221995 , 2.4015775 ,-2.9217577
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/3.txt
new file mode 100644
index 000000000..a92abd4f6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/3.txt
@@ -0,0 +1 @@
+-1.0345451,-1.5055941,-4.144375 ,-4.727011 , 1.5841546, 4.5780725,-4.24402 ,-2.3966947,-3.0370803,-1.0234503,-0.2750057, 3.2965126
diff --git a/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/4.txt
new file mode 100644
index 000000000..2f2937fcb
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Add_002/layer/uint8/4.txt
@@ -0,0 +1 @@
+-2.4460397 , 2.6090143 , 4.1773095 , 0.11204174,-3.3053472 , 2.5160108 ,-3.0612547 , 1.0667087 , 2.8952355 , 3.842513 , 0.6790793 ,-0.33375
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/0.txt
new file mode 100644
index 000000000..2a6b09b27
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/0.txt
@@ -0,0 +1 @@
+-4.629505 , 1.0121975 ,-0.13417433,-2.329806 ,-3.4927373 ,-0.7574039 ,-2.2674313 , 3.1983519 , 2.4298382 ,-0.23268977, 2.0218065 ,-1.5087285 ,-1.3953347 ,-3.8100643 ,-1.7438283 , 3.9852605 , 2.9817178 ,-4.0460877 , 0.09402129, 4.3802586 ,-1.0991771 , 0.4134776 , 2.8136911 ,-3.6254618 ,-3.925183 , 4.691824 , 4.381538 ,-3.235543 ,-2.6764185 , 2.659456 ,-3.2127233 , 0.0206281 , 3.4056723 ,-1.693684 , 1.1005328 ,-3.1486542 , 0.77198106, 1.4526777 ,-2.3614178 , 4.8214664 ,-3.1486242 , 0.58941853,-4.1100698 , 4.1982718 , 1.7219902 ,-2.4375956 ,-1.7505955 , 1.7465224 ,-2.7494361 , 4.0679016 , 1.8936038 ,-4.523818 ,-3.4124248 ,-4.809946 ,-1.939553 , 4.9411273 , 1.6261404 ,-2.6846552 , 2.1339247 , 0.61396503,-1.6662381 , 2.4282491 , 2.662007 ,-0.40868336
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/1.txt
new file mode 100644
index 000000000..470da6c74
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/1.txt
@@ -0,0 +1 @@
+ 0.70593804, 3.253847 , 1.1094694 , 0.5295975 , 0.5944647 ,-2.4391694 , 4.7912955 , 4.4374456 ,-2.942428 ,-3.5038033 ,-3.180417 , 2.1914082 ,-4.5295396 ,-3.0037553 ,-2.265191 , 0.20113531, 2.3805366 ,-0.9111223 ,-4.3170924 , 4.08436 , 1.1006241 ,-1.286977 , 4.811279 , 0.9131829 , 3.2051497 ,-2.8660698 ,-3.188871 , 1.4163305 , 4.061829 , 2.7783196 ,-3.4975152 , 3.4888391 , 2.5789826 ,-1.5264264 ,-0.13952135,-1.280177 , 2.4716458 , 2.6200528 ,-2.515086 , 3.441416 , 2.4515297 ,-0.9845471 , 0.9481396 , 1.1518412 , 1.6088997 , 1.445077 , 2.2620194 ,-2.0843177 ,-0.7263964 , 1.8159748 ,-3.3673623 , 0.2554476 ,-4.3550563 ,-1.4280493 ,-2.2702312 ,-4.7424164 ,-0.57241255,-2.813357 , 2.9161859 ,-0.9036504 , 0.00511268, 0.60724795, 4.8010454 , 1.6000834
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/2.txt
new file mode 100644
index 000000000..d9e048b61
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/2.txt
@@ -0,0 +1 @@
+ 7.07888961e-01, 4.75798702e+00,-1.47843570e-01,-1.95845592e+00, 4.26537895e+00,-3.03711486e+00,-1.35137546e+00,-1.10638596e-01,-1.02415502e+00,-2.65345359e+00, 5.48920631e-01,-4.38003826e+00, 3.61377740e+00,-2.91408587e+00,-3.22874010e-01,-4.74363208e-01, 3.45294738e+00, 1.02204478e+00,-1.44102740e+00, 6.80687547e-01,-2.44050741e+00, 3.71395111e+00,-2.14443612e+00, 3.70928717e+00, 1.35871637e+00, 9.73374963e-01, 1.57826161e+00,-2.91381836e-01, 1.46376801e+00, 2.96391749e+00, 1.08418810e+00,-3.50718546e+00, 4.68637037e+00, 1.04839933e+00, 2.24482760e-01, 2.38816309e+00, 3.18772525e-01,-3.90284014e+00,-3.32757282e+00,-1.61143410e+00,-1.26013708e+00, 2.24948835e+00, 7.63151050e-01, 4.18296242e+00,-8.69123042e-01, 3.19850564e-01, 3.52391124e-01, 3.30018830e+00,-4.64861393e+00,-4.64479780e+00,-2.68103647e+00,-1.13277221e+00, 2.02201343e+00,-4.05572534e-01, 3.06759548e+00,-3.55881310e+00,-1.14900565e+00,-3.00835490e+00, 1.31509733e+00, 2.50206441e-01, 2.47731134e-01, 4.98673916e+00,-1.74064383e-01,-4.43180744e-03
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/3.txt
new file mode 100644
index 000000000..cdbf98e8a
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/3.txt
@@ -0,0 +1 @@
+ 3.5591762 , 4.8821726 , 0.44271094, 4.786732 ,-2.4497197 , 2.4973536 , 2.034311 , 4.8329844 ,-3.9451184 , 4.9937835 , 2.0246332 ,-2.8319602 , 3.9617133 , 4.10946 ,-4.3191586 ,-2.8492777 ,-2.648121 ,-4.199404 ,-0.05163948,-4.7944984 , 2.8989205 , 1.4747709 ,-3.1194637 ,-2.877846 ,-0.39301065, 2.616311 , 2.6305614 , 1.7303206 , 3.6059175 ,-2.745988 , 2.5924454 , 3.0149276 , 4.0359216 ,-0.6135884 ,-2.5023808 ,-2.3395267 ,-3.0633461 ,-2.3836162 ,-4.4779797 ,-1.30866 , 1.9110863 , 0.654628 ,-4.559368 , 0.34231895,-0.8196542 , 4.7275734 , 3.2823656 ,-4.9644713 , 2.9191613 ,-3.4621727 ,-4.276584 ,-1.7153062 , 1.8820064 , 1.2659297 , 3.4141889 ,-4.905296 , 4.619848 ,-3.9501083 ,-1.5550466 , 3.6841137 , 1.7121594 , 1.9466268 , 1.5684807 , 4.5554323
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/4.txt
new file mode 100644
index 000000000..065d77df6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/channel/int16/4.txt
@@ -0,0 +1 @@
+-2.2269225 ,-1.2782103 ,-3.381931 ,-1.5229299 , 2.0681949 , 1.7630705 ,-0.81455594,-2.6558595 ,-3.4870632 ,-4.647749 , 2.4453654 ,-2.242679 ,-1.0272806 , 0.5656208 , 0.69442594,-4.4343104 ,-3.9649677 ,-3.8908577 ,-1.642287 , 3.0714357 , 1.0880747 ,-2.1665683 ,-4.0994506 , 2.004911 , 3.5922902 , 3.775 , 1.1580672 ,-1.4154137 ,-4.4964633 ,-1.696588 , 4.0220857 ,-1.2785947 ,-4.2075186 ,-4.515838 , 0.99715126, 3.0928102 ,-2.295537 ,-4.772882 ,-1.2936146 ,-2.6903791 , 0.10453273,-1.8041211 , 3.787591 , 0.9493053 ,-4.41586 , 3.4252715 ,-0.25001565, 4.655357 ,-1.8767506 , 0.00600041, 4.660605 , 2.550518 ,-3.830558 , 1.7777463 ,-0.7170577 ,-0.26554853,-3.5770113 ,-1.1354474 , 4.663121 , 3.100427 , 0.03313563,-1.7419808 ,-1.4426676 ,-3.912533
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/0.txt
new file mode 100644
index 000000000..e42cbf88b
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/0.txt
@@ -0,0 +1 @@
+-4.1358833e+00, 1.7854472e+00, 4.1751757e+00, 5.5915713e-01,-2.6459083e-01,-1.7176826e+00,-1.8155930e+00, 2.8710868e+00,-2.7043006e+00, 1.0959731e+00,-2.0176995e+00,-6.5950048e-01,-3.6413522e+00,-4.1966043e+00,-2.6820884e+00,-3.6055098e+00, 3.6852844e+00, 8.9128174e-02, 1.3107824e+00,-3.6425626e+00,-3.2318896e-01, 3.6238370e+00,-4.9837337e+00,-4.0550299e+00,-1.4882606e+00, 1.5547658e+00,-1.1696080e+00, 2.1651111e+00, 4.9318314e+00,-3.5928023e+00,-1.2348548e+00,-1.7002642e+00, 1.7365140e+00,-8.8151926e-01,-4.1655774e+00,-1.0166957e+00,-3.7440193e+00, 2.8588972e+00, 4.1286149e+00,-4.9504828e+00, 4.8477168e+00,-2.2587967e+00, 2.8542519e+00,-7.9565448e-01, 6.8252671e-01, 2.5875571e-01,-6.3935977e-01,-4.8547015e+00, 4.1373856e-03,-1.3893708e+00, 8.8775367e-01, 2.1222150e-01, 3.1871333e+00, 1.3869151e+00,-3.8274391e+00, 3.2623324e+00, 7.2669631e-01, 1.0303619e+00, 8.1438148e-01, 8.1272924e-01,-2.7527118e+00, 1.8215455e+00,-1.6416427e-01, 4.9103169e+00
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/1.txt
new file mode 100644
index 000000000..7caf8ce9e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/1.txt
@@ -0,0 +1 @@
+-4.250757 , 1.4186406 , 0.63726735,-0.35924944, 1.9436699 , 3.2695885 , 3.6638293 , 4.5166173 , 1.3807241 ,-1.9112543 ,-1.9026492 ,-0.4800549 , 2.818216 ,-4.6390033 ,-3.8570547 , 3.6634028 ,-1.2112037 ,-1.3335027 , 1.3524677 , 2.7240725 ,-3.8335826 , 1.1397903 ,-3.1570992 ,-4.802078 , 3.8334577 , 0.23457901, 0.7132307 , 2.9887354 , 2.9702394 ,-1.4113717 ,-0.66712093, 0.77366674, 1.9308351 ,-0.45465755, 4.925366 , 2.4214447 , 2.8401468 , 0.49789894, 0.53141665,-2.7466767 , 0.2059374 ,-4.9661317 ,-4.1334467 , 1.6928389 ,-0.42529574, 1.1033608 , 4.275776 , 1.5063075 , 2.3528252 , 0.79505247, 3.9829993 ,-4.8472476 ,-1.2752185 , 3.7365675 , 1.976164 ,-4.742636 ,-2.7199092 ,-2.9191706 ,-3.181069 ,-4.489485 , 4.0847454 , 2.2164 , 0.9725334 ,-0.72566307
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/2.txt
new file mode 100644
index 000000000..7facffa57
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/2.txt
@@ -0,0 +1 @@
+-3.8293874 ,-0.13678598,-2.5444264 , 1.654611 ,-4.3037786 ,-3.4240584 ,-4.5642533 , 4.1250315 , 1.0469195 , 4.2802887 , 3.1617825 ,-3.1706758 ,-0.99622065, 2.7707603 , 3.7494645 ,-1.4548893 , 2.328633 , 1.7976477 ,-1.2107176 ,-2.0178459 ,-0.6488357 ,-2.9393644 , 2.8918762 , 3.6192262 ,-4.1777225 , 1.3264071 , 0.32620123, 0.7890992 ,-3.304334 , 3.4893208 , 2.5354576 ,-4.7718143 , 3.8602633 , 0.4927564 , 2.2971296 ,-0.3296792 , 2.8115997 ,-0.75152504, 0.558675 ,-2.343631 , 4.650826 ,-3.0893488 , 0.8726873 , 0.24922371, 2.7634025 , 1.0358421 ,-3.862506 ,-3.169402 ,-2.5373347 , 0.9484093 , 4.1409917 ,-4.0408096 ,-2.7231216 ,-2.548547 ,-2.6315095 , 0.8164778 ,-3.017436 , 1.1860138 ,-1.8634807 , 1.8684052 , 1.8657844 , 1.7747321 ,-3.1472425 ,-1.3989028
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/3.txt
new file mode 100644
index 000000000..0be8fdd19
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/3.txt
@@ -0,0 +1 @@
+-2.0492268 ,-2.2555764 ,-1.3543441 ,-3.7278662 ,-4.8601675 , 3.1095552 , 4.6319957 , 3.0211062 , 1.7870535 , 4.8839574 ,-1.3494394 , 2.635408 ,-0.24201432, 1.312397 , 0.16790341, 2.42507 ,-3.101355 , 3.1760497 ,-4.500736 ,-2.53691 , 1.064206 , 0.62096214, 2.803344 ,-4.6166744 ,-4.624786 , 3.667064 ,-1.484021 , 4.9401817 ,-3.763283 , 3.4351027 ,-2.906393 , 4.9945946 ,-3.2997096 , 3.6325612 ,-0.47211674, 0.28783202, 1.8703817 ,-4.042374 ,-3.3353784 , 4.9085765 ,-1.6753131 ,-3.4926984 ,-4.8663344 ,-4.495712 , 2.3402312 ,-1.0722051 , 0.28559962, 2.1208072 , 1.3024254 , 3.4810693 , 0.09860361, 1.695624 , 1.3901931 , 1.6858819 , 3.8231227 , 4.5972557 ,-4.6835494 , 0.5753765 ,-2.2377403 , 0.13013013,-2.1165738 ,-0.26044115,-0.653468 , 1.1010929
diff --git a/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/4.txt
new file mode 100644
index 000000000..7e2d618f9
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/AveragePool2D_000/layer/uint8/4.txt
@@ -0,0 +1 @@
+ 4.397323 ,-0.51448834, 2.5729322 ,-4.3229046 , 1.149113 ,-3.8652143 ,-1.7352968 ,-0.7575065 ,-0.41720778, 4.327346 ,-4.2363043 , 0.8653738 ,-1.7511971 ,-0.7874244 ,-4.0734816 , 2.5622475 ,-3.1229742 ,-1.1783633 , 0.4017013 ,-0.76175183,-1.058416 , 1.128772 ,-3.0143378 ,-2.6688366 ,-2.575279 ,-4.326955 , 4.175434 , 4.791393 ,-1.10654 ,-4.4417224 , 3.5057635 , 1.5339037 ,-4.0297494 ,-3.7187057 ,-0.6645762 , 4.215642 , 1.6742749 , 2.5468905 , 1.73195 ,-3.3100636 ,-4.4818826 ,-2.5627983 ,-1.4624406 , 1.2433167 ,-4.005364 ,-4.3450556 ,-1.0652863 ,-1.0240986 , 3.989825 ,-4.1690702 ,-4.595108 ,-1.1154945 , 0.65749156, 2.5127344 , 2.509761 ,-4.3936505 , 3.6513395 ,-2.3340352 ,-4.3615093 , 3.5973237 , 0.9316653 , 1.9391845 , 3.6356397 , 0.8133118
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/0.txt
new file mode 100644
index 000000000..af1c2dff8
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/0.txt
@@ -0,0 +1 @@
+-4.0575085 , 2.5941508 ,-2.550309 ,-0.03760919
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/1.txt
new file mode 100644
index 000000000..0ede613ac
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/1.txt
@@ -0,0 +1 @@
+ 0.4857123,-4.032874 ,-3.687589 ,-1.235227
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/2.txt
new file mode 100644
index 000000000..b0b0392ba
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/2.txt
@@ -0,0 +1 @@
+ 0.21878362, 3.9175916 ,-4.6141233 , 3.709655
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/3.txt
new file mode 100644
index 000000000..d8a8cad12
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/3.txt
@@ -0,0 +1 @@
+-1.9645791,-1.4466153, 1.2543651,-1.0288917
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/4.txt
new file mode 100644
index 000000000..ca2a1c3b4
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/channel/int16/4.txt
@@ -0,0 +1 @@
+-2.1611342, 2.4875243, 3.096089 ,-1.1327268
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/0.txt
new file mode 100644
index 000000000..9def1c2eb
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/0.txt
@@ -0,0 +1 @@
+0.24671102,3.271825 ,3.979895 ,1.3334678
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/1.txt
new file mode 100644
index 000000000..eaec2409f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/1.txt
@@ -0,0 +1 @@
+ 1.9181111, 2.2396102,-2.8641696,-1.9045062
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/2.txt
new file mode 100644
index 000000000..3e05181cc
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/2.txt
@@ -0,0 +1 @@
+4.751434 ,2.8798263 ,0.15149078,2.9485583
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/3.txt
new file mode 100644
index 000000000..19d95b267
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/3.txt
@@ -0,0 +1 @@
+-1.5743442 , 0.6716824 , 0.75737774,-0.27396253
diff --git a/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/4.txt
new file mode 100644
index 000000000..d302e07a9
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Concatenation_001/layer/uint8/4.txt
@@ -0,0 +1 @@
+-1.0539489 , 1.9595883 , 0.19975437, 2.526178
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/0.txt
new file mode 100644
index 000000000..f82ad6704
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/0.txt
@@ -0,0 +1 @@
+ 1.4040831 , 4.8621206 , 0.22880335,-0.3116556 , 0.260938 ,-0.61554366, 3.779648 ,-4.650609 , 3.886638 ,-0.25574106,-0.45002133, 4.9870906 ,-2.3277295 ,-4.9648423 ,-3.7695415 , 3.2857463 ,-4.5514555 ,-3.7705963 , 3.8458307 ,-4.797776 ,-3.4295716 ,-4.6026535 ,-1.4011091 , 2.8851774
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/1.txt
new file mode 100644
index 000000000..722337286
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/1.txt
@@ -0,0 +1 @@
+-4.171929 ,-2.2911541 , 2.8965824 , 0.27504483,-1.6088463 ,-0.6509234 ,-3.262618 , 0.9633116 , 2.4504175 , 0.97706884, 0.4212074 , 1.4083375 ,-2.9757218 ,-3.1010823 ,-1.7146534 , 4.105306 , 0.07195274, 3.0232217 ,-2.7568955 ,-4.8887763 ,-3.4171093 ,-0.91494775, 2.5260248 , 4.74184
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/2.txt
new file mode 100644
index 000000000..1283a8ad1
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/2.txt
@@ -0,0 +1 @@
+ 0.14139967, 1.9541235 ,-4.945228 ,-0.48999134, 3.7479703 , 0.29318067, 0.21036309, 4.357736 ,-4.3354783 ,-1.9236348 , 0.49615476,-1.8418436 ,-2.425741 , 4.817022 , 1.5093465 , 2.417444 ,-4.69463 , 0.3433745 ,-4.5979595 ,-3.9027495 ,-0.29977685, 4.9239326 ,-0.39175773, 1.277211
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/3.txt
new file mode 100644
index 000000000..c931e1752
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/3.txt
@@ -0,0 +1 @@
+-3.692852 ,-1.0075341 ,-2.4409268 , 0.92995465,-3.1325107 , 4.028981 , 0.8446181 ,-2.2990613 , 4.0820794 , 3.1633005 , 4.1527267 ,-3.9514909 , 2.6104712 , 4.660645 ,-1.7398617 , 0.15663597,-3.6861904 ,-2.9019265 , 3.8828175 ,-2.712909 , 4.3699546 ,-3.5953352 ,-3.0655813 , 0.59767616
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/4.txt
new file mode 100644
index 000000000..d33c2dbec
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/int16/4.txt
@@ -0,0 +1 @@
+-2.8695228 , 2.865197 , 0.6635586 , 0.22709726, 2.85572 ,-4.2051144 , 1.5833759 ,-4.4277377 , 4.0004573 , 2.4766827 , 3.0412688 ,-4.8891425 ,-4.489896 , 3.0812325 , 2.1947708 , 1.6387184 , 0.31932488,-0.41092923,-0.0730476 , 0.7265327 , 4.1333 , 3.157228 , 4.7395325 , 3.4576747
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/0.txt
new file mode 100644
index 000000000..98e895c04
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/0.txt
@@ -0,0 +1 @@
+0.19242816,0.44059092,0.06788187,0.04543579,0.14106855,0.6858487 ,0.6214997 ,0.31582046,0.859484 ,0.3664256 ,0.86936104,0.871024 ,0.68752515,0.5296719 ,0.99137205,0.02956272,0.14838405,0.69830126,0.22359788,0.9060323 ,0.7141239 ,0.5573066 ,0.96645916,0.11426282
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/1.txt
new file mode 100644
index 000000000..f480f8086
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/1.txt
@@ -0,0 +1 @@
+0.57016104,0.2788207 ,0.8045938 ,0.7589986 ,0.81506515,0.8411593 ,0.4162234 ,0.1664247 ,0.5584996 ,0.7799966 ,0.4213713 ,0.97587234,0.79440975,0.5089373 ,0.90030503,0.78015554,0.10080549,0.5115089 ,0.77238286,0.9580212 ,0.8758745 ,0.14367636,0.4304664 ,0.55175275
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/2.txt
new file mode 100644
index 000000000..683ea39b0
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/2.txt
@@ -0,0 +1 @@
+0.6224246 ,0.30448085,0.29629433,0.44483584,0.30473125,0.6186932 ,0.45563242,0.5394331 ,0.22901213,0.4313142 ,0.4019574 ,0.02263176,0.3806077 ,0.27828163,0.23962335,0.26323524,0.6125012 ,0.5459546 ,0.6340052 ,0.19074932,0.2216875 ,0.77709603,0.03312786,0.02945002
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/3.txt
new file mode 100644
index 000000000..56c8c259e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/3.txt
@@ -0,0 +1 @@
+0.7524557 ,0.5408983 ,0.07039106,0.5143847 ,0.04857475,0.7305833 ,0.36986747,0.42291477,0.90452653,0.43744263,0.24857366,0.7537328 ,0.04559262,0.65276045,0.3851062 ,0.49503985,0.37213495,0.10627239,0.7085863 ,0.1913133 ,0.08057284,0.31767172,0.9685745 ,0.5942544
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/4.txt
new file mode 100644
index 000000000..ecb221e8b
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/channel/uint8/4.txt
@@ -0,0 +1 @@
+0.16251074,0.5574537 ,0.5857036 ,0.877607 ,0.29711136,0.02456062,0.8250261 ,0.21300122,0.5064036 ,0.5882086 ,0.7736793 ,0.09394809,0.98618525,0.6611699 ,0.5001983 ,0.06507304,0.88984424,0.57143325,0.07953393,0.02649987,0.9283147 ,0.65522593,0.18371649,0.12332761
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/0.txt
new file mode 100644
index 000000000..0614b5e83
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/0.txt
@@ -0,0 +1 @@
+0.01090685,0.0581577 ,0.637094 ,0.64067715,0.26264507,0.13692169,0.9649414 ,0.5117181 ,0.18012471,0.07855253,0.6358017 ,0.62257963,0.41469443,0.93169045,0.20763828,0.7634293 ,0.75929826,0.72708374,0.23463063,0.58222896,0.6351517 ,0.68781173,0.5558012 ,0.7652179
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/1.txt
new file mode 100644
index 000000000..b1c39382f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/1.txt
@@ -0,0 +1 @@
+0.57017624,0.08235867,0.03672464,0.40372616,0.7353964 ,0.59611887,0.7675548 ,0.21004233,0.09803218,0.20009473,0.8821493 ,0.17015271,0.14840214,0.99910176,0.37003204,0.22893582,0.43173164,0.3105084 ,0.41997132,0.43714985,0.08115962,0.71896386,0.7810953 ,0.00524598
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/2.txt
new file mode 100644
index 000000000..7e562de75
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/2.txt
@@ -0,0 +1 @@
+0.65292275,0.79842275,0.97853714,0.6711518 ,0.607567 ,0.40971732,0.74838483,0.95853555,0.32158023,0.911524 ,0.66938365,0.8573132 ,0.3047727 ,0.5561248 ,0.914098 ,0.07650814,0.37868017,0.29269257,0.19652605,0.63025194,0.61496884,0.32011527,0.8204132 ,0.21866946
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/3.txt
new file mode 100644
index 000000000..2958a7f54
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/3.txt
@@ -0,0 +1 @@
+0.4548901 ,0.56957537,0.0252368 ,0.4884317 ,0.7516498 ,0.02631272,0.22107519,0.95249426,0.34902394,0.11520014,0.808911 ,0.4148615 ,0.63615656,0.84020686,0.3633697 ,0.23993976,0.54176176,0.86938345,0.81628686,0.6380988 ,0.91891205,0.0406627 ,0.90289026,0.9429013
diff --git a/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/4.txt
new file mode 100644
index 000000000..fc969308e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Conv2D_004/layer/uint8/4.txt
@@ -0,0 +1 @@
+0.9309136 ,0.02123719,0.64467335,0.6910113 ,0.47402772,0.54622203,0.31527275,0.81530565,0.98981965,0.36102158,0.03114039,0.1902339 ,0.45183742,0.60178596,0.4683102 ,0.59810966,0.40558222,0.5420302 ,0.72699505,0.9575108 ,0.46746576,0.08518691,0.40302262,0.69213694
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/0.txt
new file mode 100644
index 000000000..cc434b0a8
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/0.txt
@@ -0,0 +1 @@
+-4.0618963 ,-0.56899416,-2.6450877 , 2.4534085 , 1.98115 , 1.906561 ,-3.9617727 ,-0.6071247 , 3.1096997 , 4.4270124 ,-2.8755112 ,-1.8822336 ,-2.3567479 , 1.9797888 ,-3.5018713 , 3.429169
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/1.txt
new file mode 100644
index 000000000..2c637a1d2
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/1.txt
@@ -0,0 +1 @@
+-1.6089132 , 1.4328785 ,-3.2579598 ,-2.1328773 ,-2.6566415 , 2.541386 ,-4.3314023 , 0.48684084, 3.3134763 ,-2.69083 ,-0.45710313,-3.6763198 , 0.22075526,-3.159208 ,-2.1573126 , 4.1621423
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/2.txt
new file mode 100644
index 000000000..4b57fe8e0
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/2.txt
@@ -0,0 +1 @@
+-4.061572 , 3.0518744 , 2.694435 ,-4.720131 , 1.3782452 , 4.083631 , 4.1221976 ,-1.2299284 , 3.096133 , 3.8382158 ,-1.9518853 , 4.350529 , 0.09219506, 2.6483617 , 0.74373996, 2.7447948
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/3.txt
new file mode 100644
index 000000000..49c3022c2
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/3.txt
@@ -0,0 +1 @@
+ 4.68769 ,-3.2768764 , 3.1849844 , 4.497627 ,-1.2611016 ,-3.1152303 ,-0.8408633 , 0.4938034 , 4.0921655 ,-2.3150117 , 0.10100875,-3.8374226 , 4.08059 ,-0.74594986,-3.1000822 , 4.3654246
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/4.txt
new file mode 100644
index 000000000..e02c8ca16
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/int16/4.txt
@@ -0,0 +1 @@
+-3.6168842 , 4.1935644 , 0.73750836, 4.6044145 , 2.8967912 ,-1.8085694 , 4.539956 ,-0.37032878, 1.9738418 , 1.5388782 ,-2.945171 ,-3.3875864 ,-4.516983 ,-3.4998245 ,-4.676514 ,-2.2738194
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/0.txt
new file mode 100644
index 000000000..f4fb503ea
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/0.txt
@@ -0,0 +1 @@
+0.4383064 ,0.8700848 ,0.86010957,0.08396256,0.7963264 ,0.4156023 ,0.28146362,0.82196397,0.9921972 ,0.09969576,0.23987265,0.6734369 ,0.5469574 ,0.20805728,0.32639247,0.76773816
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/1.txt
new file mode 100644
index 000000000..af4b01576
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/1.txt
@@ -0,0 +1 @@
+0.4565062 ,0.92036587,0.47286046,0.18118097,0.5347498 ,0.91550153,0.300375 ,0.00581101,0.38686675,0.91085213,0.07278002,0.35556316,0.13014294,0.7274307 ,0.13867259,0.27517235
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/2.txt
new file mode 100644
index 000000000..57716034e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/2.txt
@@ -0,0 +1 @@
+0.6900174 ,0.28745306,0.30255774,0.5095008 ,0.6689176 ,0.4914624 ,0.92629427,0.504829 ,0.33514255,0.49005315,0.08569656,0.60965323,0.82193315,0.12380831,0.06971261,0.8822662
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/3.txt
new file mode 100644
index 000000000..1e03d83b0
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/3.txt
@@ -0,0 +1 @@
+0.4240734 ,0.5430392 ,0.7536325 ,0.46065134,0.00315792,0.02719985,0.7080977 ,0.24389206,0.8114604 ,0.13292362,0.346597 ,0.70247084,0.55753845,0.01969242,0.82950485,0.66249627
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/4.txt
new file mode 100644
index 000000000..89ee30a6b
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/channel/uint8/4.txt
@@ -0,0 +1 @@
+0.31586212,0.19079527,0.9161567 ,0.8614566 ,0.9018915 ,0.34651542,0.62554437,0.05542602,0.8268219 ,0.38112178,0.9396123 ,0.49426383,0.8034765 ,0.72456217,0.5404088 ,0.8512237
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/0.txt
new file mode 100644
index 000000000..44f0ff107
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/0.txt
@@ -0,0 +1 @@
+0.31365377,0.6127105 ,0.7047126 ,0.2511918 ,0.16652136,0.36075932,0.44332707,0.77615815,0.60456425,0.26207635,0.28714025,0.11579613,0.89698446,0.67223394,0.3757766 ,0.11787009
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/1.txt
new file mode 100644
index 000000000..98e81041f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/1.txt
@@ -0,0 +1 @@
+0.9409595 ,0.3991174 ,0.43546647,0.221152 ,0.7794665 ,0.8619514 ,0.5903087 ,0.24476172,0.5932698 ,0.2727837 ,0.3980262 ,0.13329633,0.4319272 ,0.37872055,0.1721639 ,0.92437047
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/2.txt
new file mode 100644
index 000000000..e9867529b
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/2.txt
@@ -0,0 +1 @@
+0.6484028 ,0.09222967,0.76285905,0.02265582,0.2564394 ,0.11219095,0.22529566,0.09101159,0.15937322,0.3540595 ,0.25971088,0.4681136 ,0.4279646 ,0.5386553 ,0.11397707,0.7413688
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/3.txt
new file mode 100644
index 000000000..9b36fb520
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/3.txt
@@ -0,0 +1 @@
+0.9182678 ,0.8253187 ,0.6572848 ,0.46436486,0.45208713,0.42112917,0.24383743,0.16039051,0.24649048,0.63431305,0.31141657,0.25664324,0.721266 ,0.18996912,0.35422477,0.8826148
diff --git a/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/4.txt
new file mode 100644
index 000000000..6b8957dcc
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/DepthwiseConv2D_002/layer/uint8/4.txt
@@ -0,0 +1 @@
+0.97424644,0.9360494 ,0.6849295 ,0.21313633,0.23943195,0.32497332,0.5091704 ,0.67543274,0.49667478,0.73460567,0.5866559 ,0.5312464 ,0.8252662 ,0.36093768,0.7143621 ,0.7234413
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/0.txt
new file mode 100644
index 000000000..18b34c8b1
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/0.txt
@@ -0,0 +1 @@
+ 1.5887886e+00,-4.7446389e+00,-8.6568648e-01,-2.9789083e+00, 4.4470620e+00,-4.6563668e+00,-3.8466794e+00, 1.8815753e-03,-2.7699089e+00, 5.2776605e-01, 3.6518128e+00,-3.0939088e+00,-3.6008542e+00, 7.2454107e-01, 2.2568390e+00,-4.4835806e+00
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/1.txt
new file mode 100644
index 000000000..d652da699
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/1.txt
@@ -0,0 +1 @@
+ 4.770412 ,-1.7520845 , 2.4057522 ,-0.74166125,-0.10780027, 4.5796657 ,-3.513094 ,-3.0285823 , 1.2001143 , 2.806742 ,-2.0503895 , 2.8160958 ,-1.5392824 ,-3.7772799 , 2.9158401 ,-1.0586692
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/2.txt
new file mode 100644
index 000000000..e6d6e004f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/2.txt
@@ -0,0 +1 @@
+ 3.937408 ,-0.11191579, 2.2054992 , 2.847275 , 3.4895647 , 4.2361116 ,-3.2401278 ,-1.5813186 ,-4.558396 ,-0.89455926, 4.204445 , 3.5968838 , 2.773891 ,-2.9562843 ,-0.62606305,-0.03814701
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/3.txt
new file mode 100644
index 000000000..8b472058e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/3.txt
@@ -0,0 +1 @@
+ 3.5032003 , 4.6036057 , 0.28915945, 4.671659 ,-1.978598 , 2.1773603 ,-0.54175234,-3.0131943 ,-2.7422159 ,-3.4361897 , 0.2850049 , 4.1412387 ,-4.86403 ,-0.67577606,-1.4206086 ,-2.357092
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/4.txt
new file mode 100644
index 000000000..bba80be5f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/int16/4.txt
@@ -0,0 +1 @@
+ 2.5063417 , 0.22874236, 2.2677753 ,-4.4159026 , 1.7464 , 4.6051064 ,-4.2867146 , 2.730521 , 1.6372519 , 0.70292765, 3.459053 ,-4.162376 , 0.36788836, 2.213299 , 4.110952 , 1.6797827
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/0.txt
new file mode 100644
index 000000000..9b19de586
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/0.txt
@@ -0,0 +1 @@
+0.12934422,0.01033248,0.85648465,0.77248603,0.5128501 ,0.2453174 ,0.05065866,0.6601359 ,0.984665 ,0.57697976,0.58360994,0.79360527,0.90097004,0.26150337,0.1575109 ,0.9711614
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/1.txt
new file mode 100644
index 000000000..45247791a
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/1.txt
@@ -0,0 +1 @@
+0.23895125,0.30275205,0.9916519 ,0.52355504,0.2577219 ,0.03600567,0.75446343,0.8064663 ,0.07550113,0.919774 ,0.84333146,0.48820078,0.31365713,0.97172034,0.7472666 ,0.66353893
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/2.txt
new file mode 100644
index 000000000..851e72c7d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/2.txt
@@ -0,0 +1 @@
+0.6186688 ,0.4357826 ,0.63239735,0.64489084,0.17722449,0.7146202 ,0.5182415 ,0.45549247,0.21316396,0.9769707 ,0.18412311,0.05855984,0.6755795 ,0.8516815 ,0.20649713,0.32990783
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/3.txt
new file mode 100644
index 000000000..7ff3c7576
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/3.txt
@@ -0,0 +1 @@
+0.15501449,0.67026544,0.2957976 ,0.95577955,0.6215903 ,0.2029572 ,0.6069057 ,0.60434276,0.01298514,0.66787016,0.02053251,0.34120578,0.63562113,0.9166186 ,0.7134427 ,0.95491254
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/4.txt
new file mode 100644
index 000000000..fe60dbd26
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/channel/uint8/4.txt
@@ -0,0 +1 @@
+0.46877268,0.36748132,0.09441566,0.4476946 ,0.08834982,0.5387882 ,0.8359256 ,0.4374628 ,0.3835091 ,0.3577151 ,0.49470654,0.6017202 ,0.3546875 ,0.64218026,0.69008195,0.37631917
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/0.txt
new file mode 100644
index 000000000..233e5eae3
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/0.txt
@@ -0,0 +1 @@
+ 2.7731526 , 2.451602 , 3.7535272 ,-1.2774152 , 1.5482912 , 1.3402948 , 4.4792123 ,-4.4954367 , 3.354679 ,-3.3615496 ,-4.619757 ,-3.3659618 , 4.7626247 ,-1.3596478 ,-4.835548 , 0.78964525
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/1.txt
new file mode 100644
index 000000000..6a126081d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/1.txt
@@ -0,0 +1 @@
+ 0.5400839 ,-3.2621996 ,-3.4817135 , 3.8183312 , 0.48498327, 2.9812584 , 4.111276 , 0.11223658, 4.7201405 , 2.4256718 , 1.4895477 , 4.7596602 ,-0.32709372, 1.3507305 ,-0.30043927,-1.8077502
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/2.txt
new file mode 100644
index 000000000..eccd2c625
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/2.txt
@@ -0,0 +1 @@
+ 3.8758078 , 4.978636 ,-0.22925885,-2.6760504 ,-1.9160627 ,-4.609644 ,-0.9515802 , 3.558274 , 2.9096057 , 0.3340422 , 0.38608226,-0.32168412, 4.688853 ,-4.583811 ,-2.5113506 ,-4.6688786
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/3.txt
new file mode 100644
index 000000000..0da05277c
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/3.txt
@@ -0,0 +1 @@
+-2.9868221 , 2.4237797 , 1.0833962 ,-0.9231426 ,-2.1091506 ,-2.6163697 ,-0.23101932,-1.9252896 , 4.7034135 , 3.1088963 ,-2.345823 ,-2.7866168 ,-3.186763 ,-4.431844 , 3.3113294 , 0.9501982
diff --git a/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/4.txt
new file mode 100644
index 000000000..ace24f7c1
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/FullyConnected_003/layer/uint8/4.txt
@@ -0,0 +1 @@
+ 3.9716747 ,-2.254871 , 1.1943274 ,-2.212602 , 3.4311683 , 1.114989 , 4.0739036 , 0.47244295,-3.5793104 ,-3.359908 ,-4.7657595 , 2.0369127 ,-2.5619278 ,-3.4452975 ,-4.5852203 ,-1.137643
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/0.txt
new file mode 100644
index 000000000..1a4fc3ed0
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/0.txt
@@ -0,0 +1 @@
+ 2.2145607 , 0.88045335, 0.45151594, 2.852104 , 3.191637 ,-0.4578638 , 1.4858874 ,-2.1207588 ,-0.77495986,-4.1637363 , 0.83028954,-3.9974387 ,-3.3348315 , 3.7137656 ,-2.9883633 , 3.4332464 , 3.7178712 , 3.5850213 , 0.9240786 ,-0.07091421,-4.516931 , 3.965739 ,-4.828566 , 3.860382 , 0.3243482 , 1.6835089 ,-1.4710085 ,-2.6625636 , 1.942659 , 0.12808529, 1.3640044 ,-3.0124736 ,-3.646485 , 1.6046281 , 1.1087954 ,-2.4648561 ,-2.3274968 , 1.2196178 , 3.0752547 , 1.8316921 ,-2.926682 ,-2.247648 , 4.1264873 , 4.700915 ,-0.6861696 , 3.5246365 ,-2.5577545 , 1.832533 ,-4.3125343 ,-2.8579648 , 3.5299218 ,-0.67911506, 0.86782926,-2.918562 ,-3.3644724 ,-2.0097935 , 0.3721956 ,-1.3528451 , 3.8267515 , 4.916677 , 3.2055025 ,-0.64435905, 3.877367 ,-1.830818
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/1.txt
new file mode 100644
index 000000000..09c06c74c
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/1.txt
@@ -0,0 +1 @@
+ 4.5410523 , 4.4007382 , 3.3252192 , 0.40420002,-4.7642856 , 2.0282986 , 2.32176 , 3.160375 ,-4.3348713 ,-2.324847 , 4.327631 , 3.253995 , 0.53624976,-4.4896946 , 4.0600896 , 2.697662 ,-3.0693228 ,-4.7954664 , 2.010163 , 4.5790668 , 0.00921074,-4.638007 ,-2.612561 , 4.338762 ,-1.3632652 ,-0.55081725, 4.273717 , 3.1074166 , 3.1386747 ,-4.033469 ,-0.7298752 ,-3.4973295 , 4.454913 ,-0.5148646 ,-2.4100194 , 2.7154703 , 4.1507893 , 2.3424785 ,-1.7028755 ,-2.6013496 ,-1.831555 ,-4.07971 ,-1.039077 ,-1.8733021 ,-3.885844 , 3.5691998 ,-3.8779395 ,-4.7566814 ,-3.570575 ,-3.0510366 ,-4.6841617 ,-4.751285 ,-2.9700782 , 3.4774506 ,-1.3150035 ,-3.6287053 , 2.2280993 , 4.502896 , 3.9448938 , 3.3926914 , 1.560589 , 3.3307595 , 2.6545596 , 2.0503757
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/2.txt
new file mode 100644
index 000000000..24b7a248f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/2.txt
@@ -0,0 +1 @@
+ 4.5630627e+00,-4.5077333e+00, 6.8117022e-03,-1.1568142e-02, 2.3568916e+00,-2.9918964e+00,-4.8542055e-01, 4.7381549e+00, 3.1183126e+00,-2.6462586e+00, 3.0083582e+00, 1.4518642e-01,-2.4764729e+00,-4.8520207e+00,-4.8022575e+00,-1.8167463e-01,-3.1106927e+00,-2.4183941e+00,-4.1466684e+00,-3.6997426e+00,-3.9788694e+00,-3.0889416e+00,-2.2332447e+00, 1.8608164e+00, 2.8619974e+00,-3.6986623e+00,-1.3749057e+00,-9.2409855e-01, 2.7646086e+00,-3.3385031e+00, 7.6255083e-01, 1.0236104e+00,-1.7077237e+00,-4.4339476e+00,-1.1930060e+00,-1.7226344e+00,-3.1680160e+00,-1.8338548e+00,-2.6412952e+00,-8.2973856e-01, 4.2303777e+00, 3.4531716e-03,-3.3162324e+00, 8.4682000e-01, 2.5807633e+00, 2.7543969e+00, 6.8153429e-01, 4.7182851e+00, 4.2617507e+00,-1.4446728e+00,-4.3752551e+00, 3.5699592e+00, 9.6946698e-01,-2.0700858e+00, 2.0899124e+00, 1.6371955e+00,-9.5873147e-01, 3.1151581e+00, 2.9369416e+00, 4.4568644e+00,-9.4711387e-01,-4.1349549e+00, 3.3031983e+00, 4.1091359e-01
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/3.txt
new file mode 100644
index 000000000..088eb62cd
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/3.txt
@@ -0,0 +1 @@
+ 2.5168443 , 3.7492614 ,-3.7076504 , 0.49709523,-4.642194 , 1.8201847 ,-1.396746 ,-1.0660223 , 3.3333528 ,-1.7719259 ,-2.3515563 ,-2.0570705 ,-4.7125244 ,-1.593302 ,-2.1072757 ,-4.4396334 , 4.3185077 ,-2.7568438 ,-0.59535027,-3.9871383 ,-2.6216223 , 0.39957425,-1.3687986 ,-3.1157744 , 1.2557942 , 2.3428473 ,-4.906711 , 3.5663006 ,-0.46128616,-4.7818427 ,-0.8876555 , 2.5066485 ,-1.3254607 ,-3.6097736 , 1.2686944 ,-1.37061 , 4.762917 ,-3.489012 ,-2.7905307 ,-0.2612837 ,-3.3236315 , 0.8347171 , 2.5582032 , 0.42744452, 1.7428764 , 2.4122005 ,-3.6781132 , 2.8811646 ,-2.7060914 ,-0.4752588 , 0.44432116, 0.5011615 , 3.2550313 , 0.02670379, 2.6197197 ,-4.319786 ,-1.4056181 ,-3.3794782 , 0.66822946,-1.4262298 ,-0.2465175 ,-4.6432767 ,-3.580772 , 2.960096
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/4.txt
new file mode 100644
index 000000000..bb8129473
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/channel/int16/4.txt
@@ -0,0 +1 @@
+-4.9356976 , 3.9426446 ,-4.746647 , 2.3674695 , 0.54803735, 3.1911538 , 0.28858757, 0.4800329 , 2.0652595 ,-4.5046906 , 0.21695825,-0.17217463, 2.4329293 ,-1.2274694 ,-0.11534467,-2.096684 , 2.6882868 ,-2.5291932 , 0.56199783,-2.0743406 , 0.95846254, 4.004705 , 0.89853394, 2.9610496 , 2.9799032 , 1.5339601 ,-1.7136513 , 2.1797504 ,-4.2055335 , 1.5059681 , 3.0828342 ,-1.7946475 ,-2.7096524 , 3.1037905 , 0.75922704,-1.1446673 ,-2.084073 ,-1.2888353 ,-1.6958839 ,-0.8388285 ,-1.0279479 , 1.1291095 , 4.080411 , 3.6791847 , 0.9237894 ,-4.70821 , 0.5730598 ,-1.3565379 ,-2.7533107 ,-0.4583869 ,-1.4416862 ,-3.6039822 ,-1.1611387 ,-2.6919081 ,-0.6557734 ,-2.9248757 , 1.4998456 , 3.2239568 , 0.23668556,-3.4410136 ,-2.3170567 , 3.66808 , 1.9004405 , 4.3537745
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/0.txt
new file mode 100644
index 000000000..31a2db03e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/0.txt
@@ -0,0 +1 @@
+-4.1984134 , 3.7565446 , 1.3521377 ,-4.0263743 ,-1.929471 ,-3.7523155 , 1.3858393 , 4.1565247 ,-2.4681342 , 0.3598748 ,-2.0044599 , 3.7168603 , 3.6330557 , 3.0176272 ,-4.4643235 ,-0.1893698 , 3.8839848 ,-4.5703125 , 3.365731 , 4.5556674 , 4.954971 , 1.7591819 ,-0.9497736 ,-0.8527185 ,-1.1863561 ,-4.522639 ,-4.3187394 ,-3.702939 , 0.15341021, 0.8564923 , 1.9076811 , 4.2765 ,-3.7695112 ,-1.6033245 , 2.3159432 ,-1.6656336 , 1.4186145 , 4.334284 , 4.0654674 ,-4.518256 , 0.72815216, 2.5133176 ,-4.238172 , 1.0198449 ,-0.9638457 , 2.5847483 , 4.0381308 , 4.472872 , 0.11794223, 1.3358012 , 1.7975981 , 2.168553 ,-3.5131238 , 3.8412008 , 3.851232 ,-2.130775 , 3.556102 , 0.69062364,-4.668594 ,-4.619906 ,-2.87768 ,-1.0679495 ,-4.523185 , 4.184176
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/1.txt
new file mode 100644
index 000000000..2bdd62b24
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/1.txt
@@ -0,0 +1 @@
+ 2.9193265 , 4.315574 ,-3.7834768 , 3.4352486 , 4.1452866 ,-4.0322523 , 1.8039155 ,-4.080042 ,-1.1999705 , 4.9018297 ,-0.27180746, 1.709373 , 4.3322196 , 4.9179945 ,-3.977508 , 2.3486571 ,-0.11026379,-0.24730131, 2.3269305 , 2.1862001 , 0.92486495, 3.5822759 , 2.8370361 , 3.915398 ,-0.6385275 ,-0.02720119,-1.408676 ,-4.4472733 , 1.2901759 ,-4.60209 ,-2.9502335 ,-2.650517 ,-1.4038593 ,-2.967456 ,-2.0060933 ,-1.9603083 ,-0.4727794 ,-1.7877682 ,-3.9565926 , 1.4452418 , 2.5925353 ,-4.5134907 ,-4.195412 , 2.4681656 , 0.7140492 , 3.0753498 , 0.269442 ,-4.768041 ,-3.5370746 , 1.0272335 ,-0.7654047 ,-1.977087 , 3.1920779 , 0.37378865, 4.016262 ,-3.3201067 ,-4.7767315 ,-3.5074112 ,-4.094166 , 1.6035818 , 1.6506963 ,-3.2142932 , 4.7714067 ,-1.7164946
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/2.txt
new file mode 100644
index 000000000..8c770f61d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/2.txt
@@ -0,0 +1 @@
+-1.8028042 , 1.7280815 ,-3.0464594 ,-2.810487 , 0.582805 ,-1.786865 ,-1.7263526 ,-0.36871073, 3.3955328 ,-3.9523299 ,-1.880003 , 4.9068613 , 4.6292953 , 3.9778202 ,-1.859954 , 2.8149757 , 4.5020967 ,-4.160163 , 1.9295161 ,-1.2508658 , 0.5669804 , 0.99246883,-2.4829247 , 0.88920474,-3.7942843 , 2.4626305 , 4.3087935 , 3.0680852 , 3.0893688 , 3.1640174 ,-0.41890725, 0.5377459 ,-4.0344224 ,-4.5812287 , 0.5720303 , 1.802316 ,-0.31413126, 2.9586952 , 1.1723012 ,-4.696369 ,-3.7047153 ,-1.8109767 ,-3.6122723 , 1.2727392 , 4.4057164 , 3.8347735 ,-4.739083 , 2.4655118 , 0.45258832, 4.0693913 ,-3.3486447 ,-0.64714307, 1.4990507 , 2.771129 ,-0.6109979 ,-1.0617865 , 2.0837703 ,-1.633663 , 1.8431798 ,-4.3942385 , 4.8523426 , 1.1941985 , 3.0366988 , 4.7991366
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/3.txt
new file mode 100644
index 000000000..8a4c9ebb5
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/3.txt
@@ -0,0 +1 @@
+-2.2375767 ,-1.1274278 , 0.18025301,-4.598087 , 1.1042122 , 3.1241179 , 1.9084688 ,-1.214722 , 4.596646 , 4.1969523 , 4.658112 , 3.143779 ,-2.6940444 ,-1.5482163 , 1.542811 ,-1.1338089 , 3.721594 , 0.24673286, 4.71102 , 2.7811737 , 1.171089 , 4.145586 ,-2.6335135 , 1.1190183 ,-3.7932637 ,-4.6548123 ,-3.10302 ,-3.392706 ,-3.856141 , 0.6618614 , 0.9668614 , 4.4293485 , 1.3193 , 4.983464 , 1.659716 ,-3.185926 , 4.8983006 , 1.6323217 , 0.18800464,-1.9328839 , 4.6031475 , 3.459718 , 4.128766 ,-3.4701612 ,-2.3796144 , 1.6752707 ,-3.6569223 , 2.922704 , 3.642789 ,-1.6817225 , 3.151759 ,-1.5401909 ,-3.8259532 , 2.4556105 ,-4.4989905 , 1.2779988 ,-0.62634754, 3.5827441 ,-0.82541114, 2.1539748 , 4.583461 , 1.2231985 ,-1.4457659 ,-2.9194565
diff --git a/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/4.txt
new file mode 100644
index 000000000..5110f86aa
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/MaxPool2D_000/layer/uint8/4.txt
@@ -0,0 +1 @@
+-4.011289 , 0.9077414 ,-2.8109396 ,-4.33598 ,-2.6516347 ,-3.917852 , 3.2461808 , 1.7588768 ,-1.9439132 , 2.190185 , 1.5180751 , 0.3587409 ,-4.3434815 ,-4.1376143 , 3.750847 , 1.5820616 , 0.03843357, 4.71235 , 1.0592757 ,-1.7640393 , 0.44547582, 2.8698466 , 4.5816092 , 4.6638517 , 1.4207541 , 1.863644 , 3.6007912 , 0.6800818 ,-2.4884489 , 3.0707197 , 3.3961668 ,-4.331953 , 2.7828538 ,-0.16146964,-4.9070745 ,-2.9787786 , 0.3337284 ,-3.935533 ,-3.303555 , 2.376896 ,-4.7058997 ,-2.2409894 , 0.07352693,-2.6024988 , 4.9593167 ,-4.7717366 , 1.6590588 , 4.063875 ,-3.8855767 , 2.6274624 , 4.901856 , 4.157007 ,-3.292969 , 3.579326 , 3.9860668 ,-3.0936542 ,-4.7793274 , 0.71697485,-2.0354068 ,-2.1414943 , 3.6339438 , 0.10732502,-0.86129206, 4.4152017
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/0.txt
new file mode 100644
index 000000000..e0e52c398
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/0.txt
@@ -0,0 +1 @@
+ 4.5734663 , 3.96675 ,-2.7826853 , 4.377681 , 1.8424977 ,-2.8312624 , 0.65628445,-3.7023883 ,-1.8941027 , 0.53154576,-3.9718776 ,-3.3961854 ,-2.7500536 , 2.6793208 , 3.3515985 , 2.0939343 ,-4.3965416 ,-1.7462187 , 0.5660886 , 4.497879 ,-2.2529721 ,-4.8996797 ,-0.00740948,-2.941367 , 1.9482567 ,-2.462802 ,-0.7897884 , 3.1501546 , 3.1216884 ,-3.506249 , 2.871302 ,-3.964653 ,-0.40679944, 2.8930066 ,-4.783338 ,-1.8733944 , 2.2654383 ,-0.41361305,-3.7790897 ,-1.9458629 ,-2.274427 ,-2.9192872 ,-0.73215395, 2.8135974 , 2.1402152 , 4.516366 , 1.58816 ,-4.607831 ,-3.5409598 , 1.9784997 , 3.11111 , 1.0872442 ,-3.6907403 ,-4.774325 ,-4.9267297 , 1.2962086 , 2.4646177 , 2.2726526 , 4.8766675 ,-2.9272413 ,-0.06221364,-0.80498594,-2.319938 ,-3.8261194 ,-2.3452706 , 2.5408983 ,-0.80628425,-1.4547366 ,-4.4171157 , 3.1584027 , 4.2213454 , 3.0342784 , 2.0285478 , 3.4517126 , 1.870827 , 2.812075 , 1.0776864 ,-4.524331 , 3.1467574 ,-2.366355 ,-4.7368546 , 1.940347 , 4.282059 , 1.2666475 ,-4.9559174 , 2.8177614 , 1.1941892 ,-0.25412267,-2.833778 , 1.1770393 , 4.9503546 , 4.582686 ,-1.0778978 ,-2.9030416 , 3.2517505 , 1.556093 ,-3.7605543 , 0.5915735 ,-2.6323159 , 4.596147 ,-0.90292877, 2.8230112 , 4.9295835 , 3.523853 , 1.7742149 ,-2.6014073 , 2.162894 , 1.9364033 , 4.0920115 , 0.81613404, 2.4198878 ,-0.907447 ,-4.79113 ,-3.4193892 ,-0.3334577 ,-1.0439668 , 4.2233415 , 1.4482704 , 1.3646252 ,-0.9206041 , 4.4994802 ,-4.2411633 , 0.6763335 ,-1.3827848 , 1.8579848 , 1.6426222 , 0.904467 , 3.876264 ,-4.6476808 , 4.576801 ,-1.4680524 , 2.441134 , 3.2343059 , 0.23119794, 2.5640545 ,-0.7293438 , 3.7184558 ,-1.6056752 , 3.1490617 , 4.6837263 , 4.7100887 ,-2.785927 ,-0.1520597 ,-1.9914767 ,-4.00598 ,-2.7502792 , 3.7857378 , 2.8444788 , 4.9911737 , 0.29277426,-4.779576 , 3.223367 , 1.3517398 , 4.8757277 , 3.8083189 , 1.7660266 ,-2.1543872 , 4.822371 , 2.089687 ,-4.7373757 ,-2.4061642 , 2.0387447 ,-4.067881 ,-3.1757388 , 0.24974413,-0.24441184,-0.1168329 ,-0.35149318, 2.0035832 ,-4.248678 ,-1.4723817 , 3.8218668 ,-2.8085105 , 4.6995482 ,-3.0093114 ,-3.648268 ,-1.0374364 , 0.04459473, 2.3945484 ,-0.63439727, 3.3920286 , 2.403765 , 1.303556 , 3.232244 ,-0.44932058, 0.9601637 ,-3.3821623 ,-4.257736 ,-4.095783 , 0.42818338,-4.925627 ,-1.8419602 , 4.9393196 , 0.8049334 , 4.431875 , 2.8487725 , 2.1205912 , 1.7367444 ,-4.337498 ,-3.574642 ,-3.8927085 ,-0.35219863, 2.8415039 ,-0.2887568 ,-0.89806557, 2.669602 , 4.8017626 , 4.278042 ,-1.2604581 , 3.152027 , 2.1625066 , 1.5039738 ,-3.7209976 ,-0.72354925, 4.006067 ,-3.7651584 , 0.7198826 , 3.9594896 , 0.6228397 , 2.8464649 ,-0.18740664,-2.0530953 , 3.5185826 , 2.5037062 , 0.3990585 ,-4.423475 , 4.6931167 ,-1.0078553 , 0.74727917,-4.289701 , 1.697721 , 3.4963684 , 1.5796075 , 2.296678 ,-2.9379995 , 4.4748416 , 0.25155628, 4.1183267 , 0.9506131 , 1.2903908 ,-4.6828184 ,-2.309908 ,-4.2793307 ,-2.2069294 ,-4.038367 , 4.641971 ,-2.3178709 ,-2.2683682 ,-0.96986157, 2.6649144 , 2.3106637 ,-1.8052462 ,-4.9433284 , 1.7941002 , 4.80127 ,-0.06690114
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/1.txt
new file mode 100644
index 000000000..9a8f222e7
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/1.txt
@@ -0,0 +1 @@
+ 2.2282960e+00, 1.0135865e+00,-4.1930809e+00, 5.3674412e-01,-3.2516165e+00, 1.2745492e+00, 4.2867136e+00, 1.9524460e+00,-3.6757104e+00,-3.6086998e+00,-9.4525421e-01,-3.4005399e+00, 3.3607626e+00, 4.2363039e-01,-2.5177178e+00,-3.0130227e+00,-4.1442380e+00, 4.4951862e-01,-6.4387190e-01, 4.3701029e+00,-3.6790867e+00, 3.2749624e+00,-2.2554400e+00, 1.8269253e+00, 1.8358005e+00,-6.0994375e-01, 3.5964453e+00, 4.8953295e+00,-2.6134133e+00,-3.9301482e-01, 4.0286818e+00,-8.9392501e-01, 2.6430035e+00,-1.0339550e+00,-4.2311502e+00, 5.1657695e-01,-3.0095081e+00,-3.2156844e+00, 3.0075660e+00,-2.4905038e+00, 2.2380588e+00, 4.6933036e+00,-2.7880669e+00,-3.3672907e+00, 2.5187421e+00, 2.1843061e+00,-3.9957666e+00,-4.5409918e+00,-1.7282218e+00,-4.6849327e+00, 3.1863580e+00, 2.4342964e+00,-4.5180349e+00,-2.4310455e+00,-2.6789901e+00,-1.6438740e+00, 4.9613748e+00,-3.7800386e+00,-4.4277740e+00, 1.0571244e+00,-3.3765689e-02,-6.2219787e-01, 2.1075857e+00,-2.0555353e+00, 2.6996508e+00,-3.0303302e+00,-3.8262250e+00,-4.5048919e-01, 2.6760142e+00, 3.2696848e+00, 2.8136756e+00,-2.7064829e+00, 8.5861349e-01,-1.8871003e+00,-9.5355767e-01, 2.3704410e+00, 4.8897211e-02,-4.6371531e+00, 1.5693765e+00, 3.7866819e+00,-2.9738419e+00, 1.2106347e+00,-5.8760280e-03,-6.4124316e-01, 4.2396611e-01, 4.8550687e+00,-3.0650468e+00,-1.2087260e+00,-2.4833875e+00, 2.1272743e+00,-1.8991195e-01,-3.5372739e+00,-2.3402226e+00,-1.0234243e+00, 2.8981063e+00, 8.7964945e-02, 3.2136328e+00,-3.4051507e+00,-4.5538807e+00,-4.0228786e+00,-1.8993270e-01,-4.5704255e+00, 1.8850164e+00, 9.9910229e-01,-4.8424377e+00,-3.1492932e+00, 2.3922281e+00, 4.8503261e+00,-2.1037047e+00, 3.3602579e+00, 1.3546667e+00, 1.3481154e+00,-2.3604252e+00,-1.3253393e+00,-3.5330158e-01,-2.1313765e+00, 3.1442962e+00,-1.1570807e+00,-4.5890884e+00,-4.1608801e+00, 1.8554245e+00, 2.4646142e+00,-1.8453486e+00, 3.3489871e+00,-1.1248070e+00, 3.1451607e+00,-1.4458319e+00,-2.2727523e+00,-2.0378258e+00, 2.4566815e+00, 3.8839689e-01, 4.2570353e+00, 2.3613093e+00, 1.2956337e+00,-7.5734973e-01,-1.4549307e+00, 9.3240172e-01, 4.3444591e+00,-6.4935732e-01, 2.5328317e+00,-2.3545196e+00,-4.7553263e+00, 2.6134777e+00,-2.5526178e+00,-1.7996631e+00,-2.0215256e+00,-4.6141486e+00,-1.7283168e+00, 2.5297335e-01, 3.7009020e+00,-1.9858284e+00,-3.4631619e+00,-1.5858738e+00,-2.5620985e+00, 3.2822473e+00,-3.2632313e+00,-9.0714562e-01,-2.3562717e+00, 4.4088845e+00,-3.6630182e+00, 5.5761892e-01, 1.6045070e+00,-3.6806375e-01, 4.3184443e+00,-1.3219705e+00, 1.5496376e+00,-1.5801797e+00, 2.1545045e+00,-4.0106788e+00, 3.4172714e+00,-4.2495294e+00,-6.1115064e-03,-7.2607052e-01,-7.3130745e-01,-4.4462271e+00, 4.8119636e+00,-4.7460346e+00,-3.0464313e+00,-2.8801811e+00,-1.4347218e-03, 4.4133449e+00,-3.3173063e-01, 4.3802023e+00, 2.6040417e-01,-2.5531218e+00, 3.7436140e+00,-4.1636271e+00,-3.3907690e+00,-1.4418361e+00,-3.6933661e+00,-2.6342602e+00,-3.1492887e+00,-5.5590755e-01,-1.6814464e-01,-1.0868104e+00, 4.9451909e+00, 3.4104226e+00, 1.0342516e+00, 4.7993002e+00, 1.2480364e-01, 1.6109833e-01, 2.6366503e+00, 1.6535910e+00, 4.3810592e+00, 4.4755011e+00, 4.3265424e+00,-3.1934264e-01, 9.8549920e-01, 1.9962710e-01, 2.8525822e+00,-3.7352023e+00,-1.3402178e+00, 2.5931063e+00,-2.6708813e+00,-7.6831090e-01, 3.0769660e+00, 1.4107993e+00,-1.8936746e+00,-4.7568636e+00,-1.9222193e+00, 4.7693071e+00, 2.8644614e+00, 4.1877995e+00,-3.6974251e+00, 4.5314616e-01,-7.1986055e-01, 4.8653622e+00, 1.4722897e+00,-8.6220115e-01,-4.1846976e+00, 3.7767217e+00, 3.7630556e+00,-4.5851058e-01,-4.9183292e+00,-1.8750135e+00, 1.0773923e+00,-5.2709883e-01,-9.2767686e-01,-1.3984675e+00,-2.0892789e+00,-4.3801632e+00, 4.0080590e+00, 4.2269025e+00,-1.2195336e+00,-2.2649438e+00, 4.6874623e+00,-3.8354571e+00, 5.9588730e-01,-2.8315885e+00, 3.0605823e-01, 2.1416895e+00, 1.6045133e+00,-3.3075256e+00, 4.9898911e+00, 1.7708080e-02, 3.5305614e+00
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/2.txt
new file mode 100644
index 000000000..1b2e33401
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/2.txt
@@ -0,0 +1 @@
+ 1.9229428 , 2.1045275 , 2.0514195 , 1.7149676 ,-4.1647053 , 4.3958654 , 2.1192055 ,-2.4357705 , 2.249189 , 4.7986865 ,-1.0146881 , 2.5108647 , 0.7262246 ,-2.3110187 ,-0.434008 , 2.6220334 , 1.3261455 ,-2.0402927 , 0.6362597 , 0.12827367, 0.94167644, 1.6396433 , 2.802215 , 0.92637545,-2.8669958 , 2.1684341 , 4.7197456 ,-3.0393784 ,-1.5588902 ,-1.5589788 ,-1.2792847 ,-4.301159 , 3.6853306 , 3.5522077 ,-3.5120559 , 3.6523628 , 0.52381915,-4.3210206 , 3.1021209 ,-4.4059095 , 4.574733 ,-3.708168 ,-3.4609973 , 0.04494883, 4.6041393 , 4.6209555 ,-2.184693 , 3.3114836 , 4.0440845 ,-4.362543 ,-3.0185041 ,-3.4911432 ,-1.0443465 ,-3.1546419 ,-3.0831194 ,-1.8959469 ,-3.7653599 ,-1.8753844 , 3.969308 , 4.0960746 , 0.256032 ,-0.11065102, 4.753394 , 4.8433857 , 0.17249103, 0.44612473, 3.5996687 ,-3.7071083 , 4.15448 , 2.7609568 , 0.7979912 , 2.6985793 , 0.24981445,-0.7343978 ,-3.8946455 ,-3.4738345 ,-2.0124238 , 4.6603985 , 0.9002829 ,-2.2128618 ,-0.8752893 ,-3.0990481 , 2.770291 ,-1.4642559 , 0.4561498 , 0.5808671 , 2.4227936 ,-2.400878 , 0.6494001 , 1.0195295 ,-3.2693145 , 1.9889433 , 3.5208216 , 3.6280289 , 4.322899 ,-2.805155 , 3.7704606 , 0.6797415 , 4.442675 ,-0.5069875 , 1.3373847 , 4.6953626 ,-0.7946793 ,-2.7352958 ,-1.9969261 , 0.43059692, 2.50853 , 1.9314603 , 1.3780333 , 2.0536468 ,-1.572231 ,-4.5323825 ,-1.3175989 ,-1.5515776 ,-0.05870355, 0.32408538,-4.2935586 ,-1.561555 ,-1.7551405 ,-0.93950266, 3.2540953 ,-4.623753 ,-3.4944966 ,-0.7603045 , 0.76591074,-4.9114766 ,-2.679303 , 0.12950227, 4.094419 , 4.781908 ,-3.6946337 , 2.766349 ,-0.45678583,-2.275264 , 2.0858452 , 3.1182098 ,-1.2942638 , 4.4418044 , 2.2264028 ,-3.3838644 , 1.4427853 , 3.7365992 ,-1.1815038 , 1.4555137 , 0.22728541,-0.18817298, 3.454521 , 3.1835914 , 4.0786743 ,-1.5111316 , 1.1560454 ,-0.04693017, 0.44183066,-0.7420173 ,-1.2243766 , 3.4453049 ,-2.969513 ,-0.82397145, 4.870895 , 3.0178127 , 1.7217305 , 4.482936 , 1.9468685 , 3.9970267 , 4.7294793 , 2.9921744 , 4.470473 , 4.7626653 , 0.13104612,-4.651569 , 2.7991815 ,-4.734433 ,-2.4499187 , 1.0739365 ,-1.5583646 , 3.6531756 , 2.7731194 ,-4.72427 ,-4.5801177 ,-4.035709 , 2.5767221 ,-2.8133557 ,-1.8342617 , 3.5808434 ,-2.1022995 ,-3.5421894 ,-3.0776916 , 3.168665 ,-0.07246887,-1.2413273 , 4.7964606 ,-1.0624843 , 0.75939703, 2.5336463 ,-4.8622346 ,-4.9744167 , 2.1007512 , 1.5271608 , 0.37077245, 1.7765028 , 2.2724373 , 2.1864665 ,-0.37378153, 1.3559381 ,-1.4220421 ,-1.4756224 , 3.6143627 , 2.7846546 ,-2.5194893 , 3.005039 ,-3.6451447 ,-1.9118739 , 0.04718782,-3.0775185 ,-1.4801219 ,-2.35909 ,-0.4728799 , 4.610093 ,-4.472677 ,-4.530808 , 0.12514372, 0.05973044, 4.457302 , 3.1129916 , 3.6036162 , 4.5086145 ,-3.548999 , 0.4976606 ,-3.6525648 ,-2.1937015 ,-1.3205789 ,-2.6594079 , 4.415343 , 3.219482 ,-3.7286756 , 3.4116418 , 0.82889384,-3.0168123 , 4.382766 , 2.7633846 , 3.6949344 , 3.9806223 ,-0.6415279 ,-0.3193684 ,-1.3176754 ,-1.4990829 , 4.694691 ,-1.0581211 , 1.2103747 ,-0.26690048,-1.157015 ,-1.8951306 ,-0.8580171 ,-4.3080263 , 4.0737123 ,-1.2607352
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/3.txt
new file mode 100644
index 000000000..50ed09011
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/3.txt
@@ -0,0 +1 @@
+ 4.9386005 , 3.7248888 , 3.3261378 , 4.8302746 ,-3.9337704 ,-4.2943096 , 0.16059242, 0.17785172,-2.4971933 ,-2.933359 ,-4.598231 , 4.7816315 ,-0.6563864 , 4.452592 , 1.8066075 , 3.1572745 , 4.500678 ,-1.1609873 ,-1.6962403 , 1.567031 ,-3.3120036 , 1.8150452 ,-2.7486987 ,-1.6800771 , 1.4895486 , 1.120401 , 1.4983965 , 4.7132416 , 0.39645562,-3.12486 ,-0.5966056 , 4.618641 , 1.225812 , 0.99017185, 3.9918585 , 1.299415 ,-1.2995726 , 4.202907 , 3.8657827 ,-4.0268126 ,-0.90370494, 0.5030568 ,-2.9651542 ,-4.1249614 ,-2.8990393 ,-4.1228724 ,-1.2640246 ,-0.72640723,-1.7128279 , 2.7710931 , 2.8189523 ,-0.8384207 , 0.71266395, 3.8393862 ,-1.7801509 ,-3.1485069 , 3.2076547 , 2.267659 ,-3.745656 ,-4.373508 , 0.86005193,-4.9145784 , 0.9253047 , 1.1243923 , 0.46507052, 1.9978004 ,-4.642887 ,-2.1898057 , 0.88199854,-2.1837327 , 1.1112527 ,-1.4548608 ,-3.5766103 ,-1.5607064 ,-3.630397 ,-1.9193211 ,-0.8931484 ,-0.2812017 ,-1.2881653 ,-2.5051243 ,-3.5648384 ,-0.5431733 ,-0.47036746,-2.8132265 ,-0.4302025 ,-4.003176 , 0.31743896,-3.074693 ,-3.3994603 , 0.62276137, 0.12920536,-2.5154057 ,-0.22098878,-2.711012 ,-0.303956 , 4.6025276 , 3.1887815 ,-0.50345755,-2.6543994 ,-0.8452558 ,-1.4075644 , 3.6716504 , 2.7388885 ,-4.9426928 , 3.5494354 , 4.777085 ,-3.3904083 ,-2.4746811 ,-2.943489 , 1.3607427 , 1.313449 ,-2.7959676 , 4.5932074 , 0.2460288 ,-1.1802251 , 0.6807028 ,-3.7335384 ,-0.30950046, 0.0558207 ,-4.7604976 ,-4.5745177 ,-3.3872643 ,-1.102581 ,-1.5612804 ,-1.2933319 , 4.5290637 ,-2.5096595 , 0.8673844 , 0.6069363 , 0.8294639 ,-0.05487671,-2.5923786 , 3.2974155 , 2.252853 ,-2.4157743 , 1.6614583 , 1.975577 ,-2.7390766 ,-0.26459846, 0.8946814 ,-3.257953 , 4.0526175 ,-1.5219783 , 4.6063023 ,-0.09599628, 3.2825923 , 2.0063279 ,-3.597641 ,-0.41604096,-2.5593333 , 1.8169669 ,-3.6998532 ,-2.3723404 , 0.4008657 , 2.1002467 , 4.9284163 , 4.6011457 ,-4.8977246 , 4.7852945 , 1.2170111 ,-1.055987 , 2.27575 , 1.0601226 ,-4.176826 , 0.08197393, 4.0421042 , 3.6263971 , 2.6941037 ,-2.644993 , 0.10439859,-4.512112 , 3.7939842 ,-4.8532767 , 0.391317 , 3.6432517 ,-3.9992728 , 0.29700363, 1.2722415 ,-2.3793647 ,-3.377246 , 2.0930648 , 2.574604 ,-1.2509564 , 0.4457573 ,-0.46469867, 2.6793416 , 0.02566718,-0.11948132,-3.1046712 ,-0.6204446 ,-4.615342 , 4.057695 , 1.1312845 ,-3.0446556 ,-1.9381613 ,-0.92255247,-3.5459394 ,-1.1972907 , 0.5879403 ,-1.2265042 ,-2.6279037 , 3.7533212 ,-0.2950134 ,-1.6104454 , 4.7811155 , 3.9216835 ,-2.2905827 ,-3.9489107 ,-4.078132 , 4.878544 ,-2.1483154 ,-3.1480436 ,-1.8742744 , 0.38310575,-4.0457416 ,-1.5423136 , 4.9426446 , 2.80434 ,-2.758338 , 1.6596367 ,-4.559686 ,-1.2686385 ,-1.2173673 , 0.49475643,-2.4956207 ,-1.5008336 ,-1.7967415 ,-1.1574938 , 2.2852411 , 1.7171949 ,-3.328038 ,-3.1454384 ,-0.41883984, 3.822312 , 1.1161699 ,-1.5137968 , 3.1651397 , 3.2411747 , 1.2685378 , 2.7408757 ,-3.078621 , 3.3460293 ,-0.34918678,-1.0433053 , 0.9397743 ,-3.9071774 , 0.68924445, 4.896269 ,-4.234603 ,-4.8659916 , 1.472339 , 4.5464644 , 0.35857418, 3.4065645 ,-1.514736 , 4.2301235
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/4.txt
new file mode 100644
index 000000000..163c037cf
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/channel/int16/4.txt
@@ -0,0 +1 @@
+-0.91463715,-2.9258113 , 4.4465976 ,-0.84762925,-3.3510911 ,-0.15094744, 2.2284694 , 3.9705405 ,-1.6315348 , 4.698665 , 2.8595035 ,-2.4719086 , 4.2091336 ,-3.7003224 , 0.06198901, 4.24617 ,-3.7041452 , 1.4280707 , 0.61925036, 3.873551 , 0.3554166 , 3.0535998 ,-1.403015 , 2.5769274 , 4.0060935 ,-2.134697 , 0.61366636,-2.2069314 , 3.5629356 ,-4.94381 , 3.3054771 ,-0.42945656, 4.4868546 , 4.124087 ,-4.039486 , 0.75716823,-4.530404 ,-0.8464823 , 2.7817092 ,-4.954212 , 4.790015 , 2.5307322 , 0.635834 ,-3.393037 ,-3.7000508 ,-1.1439751 ,-2.4422479 , 3.9414582 ,-4.0586324 ,-3.5872777 , 2.2529798 , 0.50453144,-2.9947112 ,-0.76174486, 0.8427806 ,-0.90798455,-0.5518859 ,-1.1810572 , 1.2787138 ,-1.7791113 ,-4.661412 ,-3.7413049 , 0.03910514, 3.970302 ,-3.0697417 ,-4.107844 ,-1.985001 ,-2.434408 ,-3.0120797 , 0.34467867, 0.09826441, 3.1933572 , 0.09855966, 1.7976784 ,-3.3814316 ,-2.8423817 ,-4.787137 , 0.21746217,-1.8560363 ,-0.7145455 , 3.911294 , 4.6970305 ,-4.0105987 , 3.3843613 , 2.3087065 , 1.8619018 , 1.6607213 ,-4.1276345 ,-0.15251912, 3.1198032 , 1.8143575 , 2.178214 ,-4.6250186 , 4.4006424 ,-3.378407 , 3.6481302 , 4.4439235 , 4.5322957 , 2.7754776 , 1.9026359 ,-2.9371052 , 0.32501587, 4.980984 ,-3.2300677 , 4.190388 , 4.441369 , 0.8116277 ,-4.7056756 , 1.1501676 ,-0.9759702 ,-0.1920487 ,-3.2009268 , 4.654679 , 4.043145 , 4.579935 , 4.917842 ,-3.2166183 , 2.381046 , 2.3470554 , 0.04456256,-2.6785278 ,-2.1683002 ,-0.2686819 , 0.6097173 , 1.5071467 , 3.9692068 ,-3.4313831 ,-0.87708473, 3.9917011 , 0.7843428 ,-4.6622047 , 0.774621 ,-4.6538844 , 3.6392822 , 4.962988 , 1.4132729 ,-0.40482154,-1.8656421 ,-1.6113061 ,-1.3454957 , 0.40846685,-4.5410986 , 2.7158992 ,-1.8403106 ,-3.803351 , 4.406537 ,-1.5868717 , 2.7034876 ,-3.3383765 , 4.6084027 ,-1.691095 ,-0.52188784, 2.9010768 , 0.08786624, 2.7466853 ,-1.7457972 , 0.59371734,-0.1716976 ,-2.6220891 , 4.9432936 , 2.3500183 , 1.6905144 ,-2.7329378 , 4.003541 ,-1.1137847 , 3.9017355 , 0.9116626 , 4.233729 ,-2.6706429 , 3.4342804 ,-0.42729262, 1.174779 ,-4.944099 , 1.2316282 , 4.9237943 ,-2.2999635 ,-4.9210916 ,-1.9033331 , 0.43241265, 3.2149148 , 4.1269703 , 0.8590868 , 2.734273 , 1.658618 ,-2.1702065 ,-2.0058317 , 4.0706363 , 4.003833 ,-0.35835287, 2.5514262 , 1.2571276 ,-4.655018 , 3.6468434 , 0.06320113,-4.662375 , 1.0745742 ,-1.117399 , 4.167245 , 4.59434 ,-1.686359 ,-0.17328739, 0.3083307 , 3.3926466 , 2.2254786 ,-0.45468137, 2.4956248 ,-3.492782 ,-2.9805465 ,-1.0610795 ,-0.2784433 , 0.7163735 ,-3.0048254 ,-1.8024784 ,-3.3139167 ,-1.8410577 , 4.5702477 ,-3.4454951 ,-1.4504164 ,-1.7432297 ,-4.998418 ,-2.5524495 , 3.028534 , 4.075326 ,-2.2187853 ,-0.6484594 , 3.00815 ,-2.8010397 ,-4.5529976 , 1.7830837 , 0.3373458 , 0.19151935,-1.0437245 ,-3.6349878 , 1.1947471 ,-1.9664146 , 0.27316815,-0.20781417, 2.419226 , 0.02246885, 4.5222287 , 3.1069999 , 3.940458 , 4.2710595 , 3.4216619 , 2.8447206 , 2.7136886 ,-0.60954016, 2.9277234 , 3.995615 ,-0.30593097, 1.7800944 , 1.0608315 , 3.8786283 ,-2.7564247 , 1.8526665 ,-3.8638606
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/0.txt
new file mode 100644
index 000000000..182eb5290
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/0.txt
@@ -0,0 +1 @@
+ 3.4251418 , 1.8884782 ,-4.061519 ,-2.1329548 , 3.851976 , 3.668601 ,-0.7418167 , 2.379966 , 0.87259316,-3.96981 ,-4.627804 ,-3.3958297 , 3.025158 ,-1.299777 ,-4.322816 , 3.9173064 ,-0.55214256, 1.9224825 ,-4.8571157 ,-4.778045 , 3.3015614 , 0.56785774, 4.7985554 ,-0.4355816 , 4.9478025 , 1.7909397 ,-0.7620663 ,-0.09947702,-3.0230513 , 1.3817457 ,-4.5706887 ,-3.4097836 ,-4.7086477 ,-3.4651487 , 1.4401027 , 4.7513933 ,-1.0788624 ,-3.4946275 , 4.607974 ,-3.1215246 ,-1.4637078 ,-3.5266285 , 2.1268125 , 0.19458893, 4.058288 , 2.2452407 , 0.7575343 , 0.12213306, 4.885321 ,-1.2482406 ,-1.1034219 ,-4.054173 ,-3.6471267 , 4.774012 , 0.9450243 ,-2.5827825 ,-2.3991685 ,-2.8482654 , 0.9294943 ,-3.1165063 ,-1.6113516 , 0.04260086, 2.0987031 , 2.1601508 , 4.9740996 , 3.7719023 , 2.6817482 , 0.42131838,-1.4525859 ,-0.5124655 , 2.6313434 , 4.5606523 ,-4.6180778 , 4.788594 ,-0.8446551 ,-1.5460813 , 1.4288356 ,-1.9648911 ,-4.9766145 ,-2.405665 ,-0.30327383, 3.5204673 ,-3.848158 ,-2.6913974 ,-2.76141 , 4.336643 , 1.4205143 , 4.5898 ,-0.93183124, 4.2199287 ,-4.216924 ,-1.0979122 ,-2.3032405 ,-3.4457245 , 2.944412 , 2.137278 , 1.0326933 , 2.3116126 , 4.2138443 , 1.8283377 , 0.28901085,-1.8877143 , 0.50673705, 1.4360197 ,-2.924691 , 0.9819095 , 3.4656513 ,-2.541582 ,-1.9102442 , 3.3629627 ,-0.9675056 , 0.5937253 ,-2.4236617 ,-1.4193813 ,-0.7552614 ,-1.7121441 , 4.39647 ,-2.2712908 ,-4.3387337 , 1.5912663 , 0.8397044 , 0.17277755, 1.5272428 , 3.571715 ,-1.4471695 , 1.8623346 ,-4.3603377 , 1.2116091 , 4.960487 , 2.3681397 , 1.2925869 ,-4.3249073 , 2.4402251 ,-1.4506928 , 3.023616 ,-3.232099 ,-4.0106025 , 3.5774167 ,-0.6024932 , 1.0183483 ,-2.8215308 , 3.7395437 , 1.9100485 , 3.892712 , 4.6569633 ,-3.251774 ,-3.6923678 ,-4.8891983 ,-3.8605282 ,-4.0293036 ,-2.8199108 , 4.1668954 , 2.1569817 ,-2.9700332 ,-0.7035824 ,-0.5176811 ,-3.1826456 ,-3.334556 , 4.9103675 , 3.8513231 , 2.8609774 , 1.1845547 ,-1.4094447 ,-2.0445833 , 0.9833705 , 4.481276 , 3.83006 , 4.6240997 ,-4.268881 ,-0.85518706,-2.2650888 , 4.032545 , 0.9495817 , 1.1353155 ,-4.6551876 ,-2.2839146 , 2.6291692 ,-3.0398533 , 0.52652216,-1.8323399 ,-0.12300313, 0.46178594, 1.120684 , 1.4657134 ,-1.9794375 , 0.08941289,-4.4573083 , 2.7112565 , 4.9227715 , 2.4938288 ,-0.37153494,-4.1604757 , 4.7694197 ,-1.3021677 , 2.454714 ,-2.4902875 ,-2.760436 , 0.05183195,-2.6723208 ,-1.1471758 ,-2.2565122 , 0.20876396,-0.7288584 , 0.4386669 , 0.7846054 , 2.7294593 ,-3.836883 , 2.7501638 ,-4.775067 ,-3.2403855 ,-2.0307286 ,-1.6403166 , 4.9471517 , 1.0428456 , 2.5126355 , 3.0090203 ,-2.3476288 ,-2.9215205 , 3.8079188 , 0.83959275, 4.2670302 , 1.2338712 , 2.7329903 , 2.2549257 , 4.882931 , 0.12783106,-2.4392028 ,-2.4590807 , 4.2874207 ,-0.08333418,-3.4244132 ,-0.2235516 ,-4.23632 ,-1.3970895 , 2.1245553 ,-2.513883 ,-2.8092728 ,-1.9194845 ,-4.1932216 ,-3.7431748 ,-1.1063433 ,-3.714845 , 1.7230242 ,-0.19162221, 1.1123114 , 3.937181 , 2.6165597 ,-0.61531806, 0.44309503,-2.9260228 ,-3.1617007 , 0.0663496 , 2.4541974 ,-2.714474 , 4.2564497 , 1.2300675
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/1.txt
new file mode 100644
index 000000000..dd8037244
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/1.txt
@@ -0,0 +1 @@
+-4.8834 ,-4.6238756 , 2.020674 ,-2.3068821 , 3.7487323 ,-0.36079448, 0.08661745, 3.423143 , 3.3073757 ,-2.709357 , 4.4810205 , 3.4159606 , 4.1597505 ,-4.249789 , 2.3782206 ,-2.02848 , 0.90137833,-0.6249625 ,-3.5300052 ,-4.1113796 ,-3.768913 ,-3.59854 , 2.0896666 , 1.7677166 ,-2.3101497 ,-1.0116942 ,-3.7846713 , 2.4777756 , 3.413987 ,-2.1964507 , 0.08637846, 0.02552292,-1.9918599 , 0.7785565 ,-4.065995 , 0.8808776 ,-2.0446506 ,-1.8421272 , 0.42566776, 3.8834689 , 4.900111 ,-3.0617309 , 4.0613194 ,-3.3601153 , 3.678536 ,-4.1136184 ,-4.2903633 ,-2.6918027 , 3.4335177 ,-3.9272869 ,-1.6882807 ,-1.9629028 , 4.2125826 , 1.6536059 ,-1.1801353 , 4.8443203 , 2.9393198 , 0.4306524 , 4.390743 ,-4.6322317 , 2.932263 , 4.140538 , 2.7385068 , 2.620753 , 2.0725663 ,-1.3642436 ,-0.48539641,-4.2409816 ,-1.5950899 ,-1.688442 , 4.4769464 ,-1.25038 , 3.462903 , 0.5011836 , 0.981037 , 0.63532305,-3.4727957 , 4.6721544 ,-3.481392 , 2.8904114 ,-1.7057139 , 1.0501702 , 3.0799537 , 1.6698593 ,-1.3895478 , 4.487443 , 2.5352533 ,-0.19357985, 0.78166926, 3.5892236 ,-4.3259463 , 2.8381345 , 1.3652785 ,-0.40142608,-0.62102544,-3.088937 ,-4.0266094 , 4.7095647 , 2.0513067 ,-1.8115149 , 0.11062156,-4.5980725 , 2.809295 , 4.2042894 ,-3.4689455 ,-1.3418434 , 2.9026117 ,-1.6125411 , 2.153075 ,-3.4445221 , 3.4869678 , 1.8746428 , 0.8482056 , 3.0525062 , 1.715966 , 1.7684505 ,-2.0022326 ,-4.3427444 ,-3.1659825 , 1.6855526 , 3.1612136 , 2.0646648 ,-3.972224 ,-2.91726 ,-3.5450957 ,-2.7226381 ,-0.3273488 ,-2.5905557 , 3.6621993 ,-4.3285728 ,-0.6200474 , 0.08522832,-2.1981175 ,-3.4179437 , 2.5989106 ,-0.8503352 ,-3.3723786 , 3.9595454 ,-0.5431398 ,-2.6962373 , 1.9689399 ,-2.8925 ,-1.2064192 , 1.606632 , 2.2728612 ,-0.1403075 ,-4.8031726 , 0.1549256 ,-1.3698703 , 0.78889227,-2.286554 , 0.96417916,-0.10438658,-3.8131578 , 2.9322996 , 2.4103441 , 4.4864798 , 0.02176606,-1.1966147 ,-3.6921146 , 4.943659 ,-1.0050472 ,-1.2238564 ,-4.5758605 ,-2.6865735 , 1.7294792 , 4.180183 , 3.157911 ,-3.581904 ,-2.9112866 , 4.1674094 , 3.2326035 ,-2.7883985 ,-0.09154221, 0.8667318 ,-4.532571 , 0.816668 , 3.1307516 ,-4.1993947 ,-1.0503744 , 0.123965 , 0.17691068,-3.1465137 ,-1.4964765 , 3.4077635 ,-0.35415363, 1.9092371 ,-4.709203 , 1.148622 , 4.4766874 ,-2.193539 ,-3.7959206 , 1.4420112 ,-2.5300896 , 4.107192 , 3.4666913 ,-2.1158516 ,-3.182484 ,-2.8406513 ,-1.9396024 ,-2.3695247 , 3.8301885 ,-1.5032169 ,-0.48879272, 0.41695955,-1.1829228 , 4.822825 ,-2.9244933 ,-3.8178608 , 2.7742817 , 2.6998327 ,-3.1187122 , 2.508593 , 1.2989064 , 2.3436947 ,-0.39074868,-3.034766 ,-1.8690065 , 4.850296 ,-2.4549792 , 4.839528 , 2.2758777 , 2.6689568 , 3.2014422 , 3.6975234 ,-3.2566156 , 3.546554 , 1.9570364 ,-2.753807 , 2.3366053 ,-4.357898 , 4.9184504 ,-1.0057111 ,-3.8582199 , 1.2416974 , 4.355522 ,-2.7863925 , 0.4679685 , 2.6850772 , 2.9984746 , 2.434312 , 2.9931593 , 2.2637212 ,-0.18371914,-4.07688 ,-2.0402577 , 0.5173147 , 0.19596666, 4.71653 , 4.291663 ,-3.3575501 ,-1.0857964 ,-0.16504912, 3.6683955 , 2.9581416 ,-1.354989
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/2.txt
new file mode 100644
index 000000000..1295bfdba
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/2.txt
@@ -0,0 +1 @@
+ 1.2340723 ,-1.7371651 , 4.271641 ,-2.3332376 , 0.82301813,-3.4199295 ,-0.75806665,-2.2647665 , 2.613749 , 2.2658496 ,-2.1277714 ,-0.465433 ,-0.1323059 ,-1.9658507 ,-4.7780223 ,-4.392719 ,-0.81063855,-3.639001 ,-3.6398284 , 4.6309023 ,-0.17483327, 1.7921627 ,-1.1493484 ,-3.8145075 , 2.2367268 ,-0.40209827,-1.4159911 , 2.3032134 ,-4.154446 , 1.6760192 , 2.3430173 ,-1.386683 , 3.3363335 ,-2.976934 , 3.3983 ,-0.0069695 , 3.7025425 ,-1.8683758 , 0.72029626, 2.7558882 ,-4.4060984 , 2.553126 ,-3.5888321 , 1.8549582 ,-0.52258795, 4.6549897 , 0.8886988 ,-3.0400214 ,-3.6890693 , 3.6663766 ,-4.8026586 , 1.0636287 ,-2.9774907 , 0.39021772,-4.2414255 , 2.914968 ,-0.24334456,-4.0344954 ,-1.1011956 ,-3.8205252 , 0.05693521,-4.1379023 , 1.0584197 ,-4.0404034 , 4.841462 ,-1.2727845 , 2.6974225 ,-4.2507453 ,-2.7101111 ,-2.9800036 , 0.3082796 , 3.6763537 , 2.3277721 ,-4.9667864 ,-2.4498677 , 0.2704629 , 3.006634 ,-1.1129389 , 4.373073 ,-1.2066779 ,-3.1575904 ,-2.721046 ,-0.861226 , 1.7315729 , 2.255666 , 2.5448847 , 3.1268334 , 1.5189171 ,-3.1992466 , 0.607633 , 4.0749955 , 1.2546133 ,-1.5335796 ,-1.6200712 ,-3.9392874 , 1.053699 ,-0.87970537,-3.9218261 ,-2.2724128 , 0.82235074,-2.3400521 , 3.6467028 , 1.6891364 ,-1.6333519 , 2.2639709 ,-0.08272895,-3.076964 , 3.731091 , 3.7932968 , 2.496441 ,-4.12142 ,-2.0908666 ,-4.994248 ,-0.0429902 ,-4.6083336 ,-4.522535 , 4.717733 , 1.6715643 ,-4.779822 , 1.2919815 ,-4.6121325 ,-0.6206874 ,-2.6633883 ,-1.9632595 ,-3.2203329 ,-0.6556523 , 1.3083993 , 0.13287744, 4.599294 ,-1.1777852 ,-2.9159715 ,-0.25669238, 0.48217958,-3.9736347 ,-0.774503 ,-0.7264863 ,-3.0058725 ,-2.1682055 , 2.6579158 ,-4.4020653 , 3.0450368 , 1.3798735 ,-4.9858127 ,-4.5812607 ,-3.7349749 ,-4.4158583 , 1.631093 ,-3.0769646 ,-3.8406906 , 1.6544044 , 0.36895755,-1.8196682 ,-2.0880237 ,-3.708266 ,-2.0277069 , 1.0536597 ,-3.6726243 , 1.1704421 , 2.3201573 , 1.4994124 , 4.0197086 , 2.1001272 ,-0.39845964, 4.879206 ,-4.6042013 , 4.367211 , 2.2712052 , 2.7754369 ,-3.156667 , 4.349216 ,-4.111492 , 1.0267047 ,-2.3381946 , 4.8876834 , 4.876814 ,-0.28538027, 4.8861 ,-0.95963717, 0.46279734,-4.5789995 , 0.26168647,-0.8879058 , 2.4468584 , 1.3030591 , 3.7261188 , 3.9933589 , 2.4964094 ,-1.3851117 , 0.7147012 ,-3.8367457 , 0.79737735,-0.5907085 , 4.317288 , 0.7659837 ,-4.821792 ,-1.466433 ,-1.147227 ,-1.8638811 , 2.5115767 , 1.9449657 ,-2.4122007 ,-2.4968379 , 0.7738737 ,-1.4761454 , 4.131583 , 0.4211128 ,-2.4312468 ,-1.9722428 , 2.2810268 , 4.950381 ,-0.0406047 , 4.67312 , 0.66613483,-0.28880936, 3.2917845 , 1.6225572 , 4.809879 , 0.48241946,-3.654634 , 0.68542016, 1.3973923 , 3.479005 ,-1.4296091 , 0.64391786,-4.0887494 ,-2.186845 ,-4.5834355 ,-0.67726034, 2.4158256 ,-2.4787726 , 0.4353257 , 2.9205139 , 0.10488439, 2.0790074 ,-4.5518365 ,-3.3856661 , 3.940736 ,-1.7141095 ,-4.8946457 , 1.1085542 , 3.785141 ,-2.4175835 , 3.7720537 , 4.623048 , 2.2239215 , 0.11616404, 0.09229392,-3.637964 ,-2.334849 ,-0.95000714,-2.1338253 , 3.2281857 ,-4.0220475 , 4.7304025 ,-1.8075961 , 0.2428817
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/3.txt
new file mode 100644
index 000000000..378b5fea5
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/3.txt
@@ -0,0 +1 @@
+ 2.4605505 ,-2.7001262 ,-4.3874917 ,-2.9867616 ,-3.4332 , 0.76675916, 3.4377892 ,-0.6712793 , 1.8018581 , 1.8148962 , 2.0353577 ,-4.766427 , 3.2487285 , 3.886249 ,-2.8867183 ,-0.7906634 ,-4.376028 ,-4.2085958 ,-0.36025277, 0.6360799 ,-4.687723 , 4.8313313 , 3.3582768 , 2.1117954 , 0.9821817 , 3.3697798 ,-1.1784939 ,-3.1590316 ,-0.24019621, 0.20640443, 1.2808957 , 2.3346424 , 2.13951 , 0.61864626, 2.4020443 ,-1.9671458 ,-1.6852348 , 0.32225233,-2.3928862 ,-4.173372 ,-2.282281 ,-1.271318 , 3.0839682 ,-4.4726086 ,-0.635177 , 3.2710915 , 3.08071 ,-0.7311931 , 2.1444874 , 0.4102332 ,-3.332888 ,-4.8965516 , 3.903695 , 1.4920163 ,-4.041926 ,-0.3941788 , 3.6352818 ,-2.098405 ,-0.9248165 , 2.6277795 , 3.225142 ,-1.4461963 ,-4.2050753 ,-0.2213572 , 1.9704323 , 3.298732 ,-4.710403 , 3.6876736 , 2.0771818 , 1.3559113 , 1.328373 ,-4.4079022 ,-3.28067 , 3.8852313 , 2.322237 , 2.3243637 ,-1.9126451 , 4.6277676 , 1.7031307 , 0.74861574,-4.688967 , 3.9351206 ,-1.8054084 , 1.5824287 , 3.5381088 , 2.4798677 ,-3.3099444 ,-3.8518245 , 1.5562242 ,-1.9466928 , 0.08375791,-0.16754703, 2.9265418 ,-1.6599798 , 2.766202 ,-2.8269696 ,-0.19389874, 2.0869334 ,-1.5073173 ,-3.2024453 ,-3.6522708 ,-4.588111 ,-2.3425827 , 4.8709297 ,-1.4231887 , 1.0590451 ,-1.6406479 , 0.37192422, 0.7313186 , 0.3865313 ,-4.2832613 , 3.9712496 , 0.07653506, 0.2593589 ,-2.6036396 ,-0.45185068, 3.6537335 ,-0.6341783 ,-0.6381408 ,-1.0992868 , 2.766365 , 4.666631 , 4.416099 ,-3.6654727 ,-4.0626607 ,-3.4928396 ,-0.6944366 , 4.869798 , 4.2240977 , 0.9655519 ,-2.5654511 , 1.3396966 ,-3.7639391 ,-1.2369057 ,-3.7242758 ,-0.5189227 , 1.6548159 ,-2.6197302 , 4.2732763 , 2.239486 ,-4.316255 , 3.2419755 ,-1.9283817 , 0.22489135, 2.6034477 , 0.15818155, 2.0811818 , 0.836994 , 2.7832468 ,-0.68581384, 0.89475006,-3.1455147 ,-4.818614 ,-4.1738377 , 0.4281551 ,-2.935886 ,-3.7582467 , 0.58168256, 0.2854076 , 1.0492616 , 2.2415884 ,-4.4923434 ,-3.2479804 , 3.8439462 , 3.9802108 ,-0.9027783 , 1.7783072 ,-2.2782066 , 4.4638705 , 4.28735 , 4.291463 , 1.1685107 , 1.2765578 ,-3.7954235 ,-3.494621 , 4.4340134 ,-3.5995178 ,-4.3025713 , 3.3037348 ,-3.6675146 ,-1.7871013 ,-1.2922373 , 0.72924066,-4.7065907 , 2.1388702 , 2.3570008 , 3.9203117 , 0.07483537,-2.8389792 ,-1.795164 ,-4.380931 , 1.3189598 , 2.4404252 , 4.4774084 ,-1.2798066 ,-4.95842 , 1.8095461 , 4.2692375 ,-2.0918155 , 0.33083543,-3.794544 , 1.4940621 ,-3.9446015 ,-0.38208306, 0.30863285,-0.6832849 ,-2.5675633 ,-4.948772 , 1.5904989 , 3.0415509 ,-4.899339 , 0.9415345 ,-0.91124976, 4.4849253 ,-3.4605968 , 1.6737833 , 1.9091597 , 1.3111106 , 2.0829957 ,-2.1308084 ,-2.912219 , 1.1306196 , 2.231948 , 4.7522073 ,-2.1438766 ,-2.1000512 ,-0.2984778 ,-1.2093959 , 2.6259391 , 1.8113437 ,-4.137133 , 2.716111 , 3.4318748 ,-0.89123845,-3.70718 , 2.453927 ,-0.22418758,-3.098459 ,-4.4986243 , 0.85048616, 2.8023102 , 3.743153 , 0.9931644 , 3.8588202 , 1.7585737 ,-4.2855363 ,-2.5475764 ,-0.83141845,-1.9358089 , 3.1711586 , 2.4221613 ,-1.881327 ,-3.7230873 ,-4.55259 ,-0.42294836, 4.64625
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/4.txt
new file mode 100644
index 000000000..339435425
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mean_000/layer/uint8/4.txt
@@ -0,0 +1 @@
+-3.37344313e+00, 2.78325319e+00,-7.30300546e-01, 1.33456266e+00, 3.96648932e+00, 4.33421373e+00,-3.11558557e+00,-3.64659280e-02,-1.73589993e+00, 4.81018400e+00,-8.32905114e-01, 2.33330703e+00, 1.85830116e+00,-4.60395622e+00, 5.26070774e-01,-4.71355534e+00,-2.97202754e+00, 3.57638383e+00, 4.50985909e+00, 2.08423686e+00,-1.85349309e+00,-2.18306184e+00,-4.65403509e+00, 4.31280661e+00, 1.16069472e+00,-4.85344124e+00, 8.40563923e-02,-1.98723459e+00,-4.29561710e+00,-2.57372570e+00,-4.22641230e+00,-4.00811911e+00,-9.61861551e-01,-2.14665198e+00, 4.18120289e+00,-3.87826174e-01,-2.86187083e-01,-4.84979200e+00,-1.34733701e+00, 1.27489030e+00, 1.98844969e+00,-4.11230135e+00,-1.61191213e+00, 2.63515592e+00, 4.35539484e+00,-1.56582773e+00,-2.45283508e+00, 1.44556177e+00,-8.56053472e-01, 3.25111747e+00, 3.58699083e+00,-2.47732449e+00, 3.64130282e+00,-4.91288567e+00, 8.97059917e-01,-2.26010180e+00, 4.91831064e+00, 4.45047706e-01, 1.88655663e+00, 3.20642543e+00, 1.38243341e+00, 9.06112790e-01, 1.15262544e+00,-2.39862514e+00,-2.87477684e+00, 7.36831248e-01, 3.18799114e+00, 1.22698748e+00, 5.63625395e-01, 1.29130912e+00,-4.89572334e+00, 2.11258578e+00,-4.55420208e+00, 4.94569272e-01,-7.08617330e-01,-1.84863120e-01,-4.81965256e+00,-1.06512284e+00, 4.79633398e-02, 2.70429182e+00, 4.78289175e+00,-2.11806059e+00, 4.23046875e+00, 3.18022132e+00,-8.39496255e-01, 3.13150501e+00,-3.24103773e-01,-7.48505890e-01,-2.45754886e+00, 4.16639376e+00, 3.25864077e+00, 3.40006447e+00,-3.77217412e+00, 2.93266010e+00, 3.33685803e+00, 1.02347994e+00,-2.22839618e+00,-1.90375733e+00, 3.24283957e+00,-4.01684284e-01,-4.45417643e+00, 3.74440104e-01, 3.33520865e+00, 6.64106190e-01, 3.84395885e+00, 2.38586918e-01,-1.51634857e-01,-2.64977455e+00,-3.45786500e+00, 4.89002228e+00,-1.07323432e+00,-2.92749858e+00,-1.76510501e+00,-3.44604325e+00,-1.89681911e+00, 4.20239258e+00,-1.75864971e+00, 2.13181686e+00, 3.90355319e-01,-4.11911535e+00, 6.61891177e-02,-4.32988214e+00,-1.42876351e+00, 3.12163901e+00,-4.56227779e+00, 4.17938662e+00, 9.63881195e-01, 4.35952139e+00, 1.61931109e+00, 4.11196423e+00, 2.25612569e+00,-4.77538586e+00,-1.72600198e+00,-4.39411783e+00,-8.98730099e-01,-1.04562032e+00,-2.81517529e+00, 3.57167959e+00, 1.90318239e+00, 2.17302442e+00,-3.79942179e+00, 2.19838643e+00,-4.16209459e+00, 4.45025682e+00, 1.68786839e-01,-2.56879544e+00, 3.60925221e+00, 1.06542781e-01,-3.48755455e+00,-6.77028894e-01,-3.51582170e+00, 3.90697241e+00, 4.49116230e+00,-1.56180394e+00, 4.96249914e+00, 9.63374436e-01, 2.72304177e+00, 8.38046610e-01,-2.91993833e+00,-9.41783428e-01, 8.00800502e-01, 3.89176035e+00, 6.70560122e-01, 2.76782703e+00,-1.37075472e+00,-3.25303817e+00,-4.41226482e+00,-8.38777184e-01, 1.73568249e+00,-1.09438455e+00,-1.08815920e+00, 1.06787062e+00, 2.04415274e+00,-2.93027782e+00,-6.86941504e-01, 3.83109421e-01,-3.49270535e+00,-2.13225913e+00,-3.61786675e+00, 1.32213378e+00,-2.89654016e+00, 4.23944092e+00, 4.53665400e+00, 4.26081800e+00,-1.95718706e+00, 4.72295076e-01,-3.08592963e+00, 2.53354859e+00, 3.80069661e+00,-1.14408419e-01, 2.39438844e+00,-4.73618507e+00, 2.35079074e+00,-1.43686843e+00, 1.32946157e+00, 1.10381134e-01,-3.49878430e+00, 2.83181930e+00, 4.57872486e+00, 2.29953095e-01, 7.19881415e-01,-2.97208834e+00, 4.11286211e+00,-3.89149117e+00, 3.83631349e+00, 4.14627981e+00,-1.14082299e-01,-6.89825296e-01,-2.55468488e+00,-4.04466152e+00, 9.95541453e-01,-2.59181118e+00,-4.60567427e+00,-4.77339029e+00,-7.36041367e-02, 1.85957468e+00,-3.42530179e+00, 4.55782986e+00,-3.29603004e+00, 3.55632234e+00, 2.40858841e+00,-2.07399082e+00,-3.96705031e+00, 4.41718817e+00, 3.19581985e+00,-3.72379017e+00,-3.76826024e+00, 6.79764748e-01,-4.43838930e+00, 2.29627752e+00, 2.34923697e+00,-4.23308420e+00, 3.80186272e+00, 8.65862250e-01, 8.44927967e-01,-1.05974531e+00, 4.70531940e+00, 1.25060010e+00, 4.82314730e+00,-4.53093815e+00, 4.51410580e+00, 4.95166332e-01,-3.45584202e+00, 1.82002666e-03,-3.27616286e+00,-2.68104935e+00, 2.39554620e+00, 2.99364328e+00,-2.57998848e+00,-4.35891914e+00, 4.64737415e+00,-5.74958742e-01, 6.47293210e-01, 1.85961032e+00, 4.49567413e+00,-4.36166048e+00
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/0.txt
new file mode 100644
index 000000000..3b2a3c258
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/0.txt
@@ -0,0 +1 @@
+ 4.9167333 , 0.9170983 ,-2.4031715 , 0.4819133 , 0.21536288,-2.0262568 , 4.364642 , 1.7851653 , 2.0982797 , 0.5736603 , 2.5769486 , 3.68285
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/1.txt
new file mode 100644
index 000000000..dff8a3b09
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/1.txt
@@ -0,0 +1 @@
+ 3.8708763 , 3.263454 ,-4.796817 , 0.6411522 ,-3.0385532 , 0.49334133,-0.20283684,-0.88814104, 4.826072 ,-4.8037696 , 4.757636 ,-3.036691
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/2.txt
new file mode 100644
index 000000000..93e747284
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/2.txt
@@ -0,0 +1 @@
+-3.8694625 ,-3.5254061 ,-0.23680535, 4.1042504 , 3.2534697 ,-1.8511593 ,-1.9182487 , 2.6457057 , 0.12923336, 2.618141 , 1.2465005 ,-4.4625525
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/3.txt
new file mode 100644
index 000000000..c924e03d9
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/3.txt
@@ -0,0 +1 @@
+-2.5559328 , 1.768443 ,-1.4850446 ,-1.2771453 ,-2.7216687 , 2.80077 , 0.21637216,-0.6145739 ,-0.37175298, 3.8750615 ,-1.9910356 ,-1.657059
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/4.txt
new file mode 100644
index 000000000..1153c85ed
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/channel/int16/4.txt
@@ -0,0 +1 @@
+-1.6168976 ,-3.816399 ,-0.55625045, 4.961818 , 0.19316113,-2.6601286 ,-1.6928803 , 4.1208386 ,-1.4012221 , 2.7742999 , 0.75798005,-2.5877
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/0.txt
new file mode 100644
index 000000000..e580d6f85
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/0.txt
@@ -0,0 +1 @@
+-4.024665 , 3.0544488,-4.5645285,-3.2134292,-2.1543078, 4.039755 ,-4.613908 , 4.2014904, 3.8222141,-4.4992657,-4.02681 ,-3.2933445
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/1.txt
new file mode 100644
index 000000000..c593dfbb6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/1.txt
@@ -0,0 +1 @@
+-2.669042 , 2.479217 , 4.691815 , 1.8187722 ,-3.7656548 ,-2.0555806 ,-2.4494352 ,-3.2394514 ,-0.38215363,-1.543695 ,-0.6927158 , 2.3534324
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/2.txt
new file mode 100644
index 000000000..14520a177
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/2.txt
@@ -0,0 +1 @@
+ 4.036224 ,-1.2903051 , 1.2116423 , 3.92255 ,-0.48049024,-1.0290806 ,-0.9644837 , 1.3379688 ,-1.0027533 ,-1.9611529 , 3.7190473 , 0.45794436
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/3.txt
new file mode 100644
index 000000000..2238d5e9e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/3.txt
@@ -0,0 +1 @@
+ 4.560488 ,-1.2475324, 1.8892838,-2.0155866,-4.968927 , 0.3717404,-0.6095849, 3.2483344,-1.2499679, 1.4237018,-3.1225715, 3.0611598
diff --git a/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/4.txt
new file mode 100644
index 000000000..14a91ccc9
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/Mul_001/layer/uint8/4.txt
@@ -0,0 +1 @@
+-1.7167594, 2.116633 ,-1.3816848,-1.7106141,-3.273076 ,-4.148302 ,-2.1654181, 0.4368236, 3.4279666, 1.2954224, 1.3004405,-4.3022
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/0.txt
new file mode 100644
index 000000000..107491f8e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/0.txt
@@ -0,0 +1 @@
+ 0.5590226 ,-0.2806683 ,-1.6237477 ,-0.9041292 ,-2.2877202 , 3.4275887 , 0.7413508 ,-2.4284103 ,-0.39940628, 2.431437 ,-3.681079 ,-0.24288087, 3.3011584 ,-4.9507365 , 0.63297826, 3.0742207 ,-4.407745 ,-3.1469536 , 0.28014645, 1.7506292 ,-2.2447422 ,-0.5647249 , 4.763762 ,-1.9554822 ,-1.0236452 , 1.4784483 ,-0.15040281, 3.009691 , 4.0685706 ,-4.3577633 , 3.9074588 , 3.3200462 , 0.7937705 ,-4.491444 ,-1.5227276 ,-4.907054 , 3.0078046 ,-3.3134713 ,-4.180262 , 0.42208448,-4.764361 , 1.7373432 ,-2.4944234 , 1.3338212 , 0.5318029 , 2.0201192 , 1.274291 ,-3.891372
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/1.txt
new file mode 100644
index 000000000..f95a6c3ba
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/1.txt
@@ -0,0 +1 @@
+-2.5172353 , 1.8682998 , 2.6845884 , 1.8813597 ,-4.6693754 ,-3.2414548 ,-3.1801097 ,-1.5670214 , 1.9862102 , 3.857179 ,-3.0402668 ,-1.4183347 ,-2.7983398 ,-4.087585 ,-1.1274861 , 1.8738103 ,-2.563316 ,-2.973781 ,-0.872552 ,-4.4504313 ,-0.9188538 , 4.5734954 , 1.3559026 , 4.943204 ,-3.6803703 , 4.577067 ,-0.6116983 , 4.5055084 , 2.5480487 , 3.7308915 ,-0.3163238 ,-0.00772368, 3.0286303 ,-0.43645218, 0.87748104,-2.6953583 , 0.21743219, 2.431181 ,-1.2284794 , 0.35975334, 0.87034357,-2.5191767 , 4.030477 ,-1.2849646 ,-4.537441 ,-0.8822066 , 4.5059347 ,-0.9273924
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/2.txt
new file mode 100644
index 000000000..106889e6b
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/2.txt
@@ -0,0 +1 @@
+ 4.523605 ,-2.1303053 , 2.7449381 ,-4.449816 ,-1.4482541 , 4.643309 ,-2.5644886 , 4.3115034 ,-4.7736797 ,-1.9451635 ,-2.1877592 , 2.3639698 ,-1.8480709 ,-4.560132 ,-0.40588248, 4.368528 ,-0.25666243, 1.1258887 , 2.33142 ,-3.8270295 ,-4.337086 ,-0.6709232 , 4.9283085 ,-3.5181348 , 2.225021 ,-0.0831629 , 2.0482597 , 3.161154 ,-0.49435407, 2.9382129 ,-1.248886 ,-3.7053974 , 1.6736145 ,-1.3524985 ,-1.4007242 ,-4.291275 ,-3.391911 , 4.803692 , 1.631321 , 0.13381048,-2.9587808 , 3.9878602 ,-3.3585925 , 4.6802793 ,-1.7605352 , 3.4168313 , 1.2318416 ,-4.40287
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/3.txt
new file mode 100644
index 000000000..488c3483a
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/3.txt
@@ -0,0 +1 @@
+ 1.249105 ,-3.2594535 ,-1.7899538 ,-4.804654 ,-2.0324056 ,-1.9959925 , 3.5215054 , 0.5371311 , 1.9365969 ,-3.130136 ,-2.3590457 ,-4.653209 ,-2.0184708 , 3.5759254 ,-1.3521014 , 1.910826 , 3.8221822 ,-2.8988552 , 0.6571995 , 1.0839036 , 3.5422468 , 2.4680734 , 0.6148754 ,-3.4008195 , 4.558109 , 2.0105803 , 0.58087206, 1.3398736 , 2.770545 , 0.29666626, 4.1851935 , 0.04321287, 2.7680604 , 4.5661645 , 4.0127945 ,-4.8027678 , 4.1711125 ,-0.24452859, 0.4101852 , 1.5963763 ,-2.8356924 , 1.2876563 , 0.90424466, 2.965566 ,-1.9058269 , 4.759825 ,-2.2063546 ,-1.1309439
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/4.txt
new file mode 100644
index 000000000..a59688e23
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/channel/int16/4.txt
@@ -0,0 +1 @@
+-3.0078897 , 1.6800234 , 4.350201 , 0.22538732, 2.9894316 ,-4.234071 , 2.733158 ,-3.8551323 , 3.9647048 , 1.4266169 , 0.78519976,-0.5334222 , 0.6681823 , 2.8409274 , 2.335872 ,-3.757666 ,-3.321705 , 2.9423573 , 1.3080943 , 1.0453726 , 3.222387 , 3.1813147 ,-1.8588669 ,-3.2523947 ,-4.4175825 , 3.7631783 ,-3.4176416 , 1.2141145 , 1.3725096 ,-1.2283872 ,-2.9829195 ,-3.6383085 ,-2.0126016 ,-3.7627625 , 4.916868 , 0.73052526,-0.02047114,-3.9506733 , 2.3569562 ,-4.247723 ,-1.8913685 , 1.7365774 , 4.59158 , 3.654596 ,-4.2133813 ,-4.6193404 ,-1.3968121 ,-3.580963
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/0.txt
new file mode 100644
index 000000000..1f2993269
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/0.txt
@@ -0,0 +1 @@
+-3.3436873 ,-0.79453826, 2.2211137 , 2.6420908 ,-1.3191302 , 1.2973647 ,-4.506594 , 4.867371 ,-4.318404 , 1.6957753 ,-4.3091793 ,-3.2230556 , 4.9175825 ,-3.1527104 ,-2.6669753 ,-2.1135337 ,-3.7701926 ,-3.358504 ,-4.419803 , 3.2045574 ,-0.5828494 ,-3.5796826 ,-4.0088696 ,-4.7178082 , 2.2726505 , 2.1860175 , 3.7198956 ,-0.5788681 ,-3.7766652 ,-0.65016747, 3.707159 ,-2.240267 , 4.5772953 ,-0.54754776, 4.7143884 ,-3.196982 ,-3.6356654 , 3.7157805 , 3.1312432 , 0.58816016, 2.1710336 ,-1.600533 ,-3.689763 , 4.322089 , 0.4816874 , 2.2769346 ,-3.9072733 ,-0.58615017
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/1.txt
new file mode 100644
index 000000000..a19ea6696
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/1.txt
@@ -0,0 +1 @@
+-1.275483 ,-3.6622071 ,-0.87433696, 0.60946655, 1.4415421 , 3.3705983 , 2.2635043 , 3.3926573 ,-0.2936643 ,-0.5169573 , 3.2535644 , 2.1269164 ,-3.4180303 , 1.0427854 ,-1.3514856 , 3.6084783 , 4.569944 ,-0.79272085, 2.9771423 ,-1.6668562 , 4.8700657 , 0.3355385 , 0.76509756, 3.5142152 ,-1.6743544 , 4.794434 ,-2.958765 ,-0.23857778, 2.4555902 , 2.459867 , 3.3922994 ,-4.350212 , 0.6286153 , 0.8139546 , 4.1676807 ,-3.3461437 , 0.69633776,-4.6548877 , 0.98267466,-4.508397 ,-1.4581255 ,-1.2289628 , 3.8701873 , 3.334336 ,-3.5611253 , 2.6133575 ,-1.0554558 ,-3.3291767
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/2.txt
new file mode 100644
index 000000000..7113eb52e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/2.txt
@@ -0,0 +1 @@
+-0.6250365 ,-4.798417 ,-4.214081 ,-3.625409 , 2.4391694 , 4.1856265 , 3.2472587 ,-3.20996 ,-2.3537548 , 1.3749354 , 2.5947835 ,-1.8891864 ,-3.612735 , 2.246563 , 1.2701501 ,-2.8927476 ,-0.71078295,-3.6037376 ,-4.5916877 , 2.0044398 , 3.4437728 ,-1.0695096 , 4.3483944 ,-3.3387017 ,-0.9384242 , 1.4229002 ,-0.6568144 , 1.1164346 , 1.7145283 ,-2.596518 , 4.6728883 , 3.4737296 , 1.7935314 , 3.1263895 , 1.3614839 ,-3.824968 ,-3.0405738 , 3.1729462 ,-4.1985774 ,-2.9489865 ,-4.2080064 , 2.0368521 ,-2.858539 ,-0.03206728,-1.1123812 , 0.2994737 , 1.6906137 ,-0.8665008
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/3.txt
new file mode 100644
index 000000000..afeb2c0e6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/3.txt
@@ -0,0 +1 @@
+-4.5279946 ,-3.4497826 ,-2.058617 ,-0.39549035,-0.26672208, 3.0173857 , 3.2430282 , 1.9996022 , 1.3895315 , 1.7620904 ,-4.9040093 ,-3.2858686 ,-2.2823575 ,-1.4176623 ,-0.537347 , 0.68219584,-3.193989 ,-3.1675165 , 0.47214374,-4.390378 ,-1.8730192 , 1.4416525 ,-3.0460286 ,-0.73547626, 1.8686327 ,-0.8146671 ,-2.0906649 , 0.01226121,-0.06992937, 0.9302521 ,-2.1858516 , 4.8370657 ,-4.1847024 , 4.4963436 ,-1.3834711 ,-1.1244944 , 0.4290957 ,-4.2681174 , 1.2978764 , 3.4149706 ,-2.7011304 ,-3.1285405 ,-3.8857136 ,-0.18625297,-0.13618916, 2.427405 ,-1.7979074 ,-1.4174187
diff --git a/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/4.txt
new file mode 100644
index 000000000..99c6284d6
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/PRelu_001/layer/uint8/4.txt
@@ -0,0 +1 @@
+-0.40635094,-2.485209 ,-2.9641154 , 4.09174 ,-1.9137962 ,-2.0860991 , 1.6594787 , 0.53744185, 1.7737653 ,-1.7054961 , 2.5611186 ,-1.1456238 , 2.741241 ,-2.283051 ,-4.2111306 ,-0.8722772 , 1.6465468 ,-0.61518955, 0.08495517, 3.6847656 , 3.7826371 , 2.0023444 ,-3.5326133 , 2.3723035 , 3.7383325 ,-3.3514297 , 2.031452 ,-0.7364658 ,-4.3347225 ,-2.8146286 ,-1.37377 ,-3.518721 ,-0.19657679,-1.6831368 , 1.2457223 , 0.25099897,-4.4722757 ,-4.135197 ,-0.6378818 , 3.8833187 , 1.9291897 , 2.5969315 , 2.146067 ,-2.846719 ,-2.2562532 ,-2.6856182 , 2.824374 , 2.3662992
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/0.txt
new file mode 100644
index 000000000..42ce6be36
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/0.txt
@@ -0,0 +1 @@
+ 1.1826919 , 0.07451724, 3.48515 , 3.4905832 , 1.8009655 , 4.155749 , 3.3155255 , 2.6834202 ,-1.7111781 ,-2.2254407 ,-4.578932 ,-2.1239302 ,-0.1269101 ,-2.6022012 ,-4.8320093 , 0.2983099 ,-0.43314072,-0.66332716
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/1.txt
new file mode 100644
index 000000000..f677cc836
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/1.txt
@@ -0,0 +1 @@
+-1.2971772 ,-3.6082 ,-2.2253058 ,-4.4367466 ,-1.7221912 , 0.02547262,-3.641017 , 0.2953748 , 0.7217547 , 4.663728 , 4.262444 ,-3.196005 ,-1.6792587 ,-1.7463406 , 2.030074 , 0.67998594,-0.92862725,-1.7960806
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/2.txt
new file mode 100644
index 000000000..841ea9f8f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/2.txt
@@ -0,0 +1 @@
+ 2.2390285 ,-1.9557759 ,-1.2331479 ,-2.4810686 ,-0.5112022 , 1.741153 , 0.13645513,-2.3543327 ,-3.2610211 , 2.5739572 ,-0.50510126, 2.3544457 , 1.884411 ,-3.7153857 ,-1.7037194 ,-0.36849263,-4.819704 , 3.047652
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/3.txt
new file mode 100644
index 000000000..08ec9fe8f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/3.txt
@@ -0,0 +1 @@
+-0.9080747 ,-1.5609599 ,-0.40923035,-2.0569193 , 4.5904484 ,-0.02348744, 0.35939455, 2.2017193 , 2.2766497 ,-2.2080436 ,-2.6453862 ,-3.6456985 , 4.160244 , 1.7283534 , 4.5547447 ,-1.8674839 , 3.019465 , 1.1584582
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/4.txt
new file mode 100644
index 000000000..a4f2d97d1
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/channel/int16/4.txt
@@ -0,0 +1 @@
+ 4.5920744 , 3.827386 ,-2.1228654 , 3.7227573 ,-3.4464717 , 0.31313375, 0.5531476 ,-0.30391756,-0.21601346, 3.8968146 , 0.23224053,-0.6208954 ,-0.76323295,-1.1700501 ,-1.6203161 , 2.1780837 , 2.3581395 , 2.6519518
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/0.txt
new file mode 100644
index 000000000..eb058a1c3
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/0.txt
@@ -0,0 +1 @@
+-0.55411166,-4.1992335 , 1.4317423 ,-3.7261302 , 1.151971 ,-2.117022 ,-0.7386241 , 4.654951 , 1.4869142 ,-4.6252975 ,-3.305923 , 3.632628 ,-2.6403873 ,-4.862389 , 3.477561 ,-4.9842925 ,-3.6267536 , 4.9950438
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/1.txt
new file mode 100644
index 000000000..ff15f032d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/1.txt
@@ -0,0 +1 @@
+ 0.18094282,-0.58095986, 1.2765085 ,-0.534363 , 4.5564513 ,-0.28305855, 0.80606604,-3.3217795 ,-0.08041744,-3.7558215 ,-0.5370528 , 1.8984528 ,-0.09462419,-0.28595117, 4.6817894 ,-4.6653147 ,-4.127137 ,-2.3407753
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/2.txt
new file mode 100644
index 000000000..e564168bf
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/2.txt
@@ -0,0 +1 @@
+-0.62747055, 1.4133646 ,-0.9954612 ,-4.687624 ,-2.5390003 ,-4.534569 ,-1.1943612 ,-4.830596 , 4.3214984 ,-2.4795794 , 4.166298 ,-1.4772589 ,-4.074577 , 3.2332711 ,-1.5221404 ,-1.7308865 , 0.06814837, 2.944668
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/3.txt
new file mode 100644
index 000000000..c763b6311
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/3.txt
@@ -0,0 +1 @@
+-3.2136867 , 0.6229863 , 0.02772082,-0.00820862,-2.4893622 ,-0.6757174 ,-2.2024722 ,-2.0893583 , 0.33953062,-3.5438979 , 0.7000838 , 1.3219849 ,-0.02302017, 2.3125873 ,-1.5376673 ,-4.0330076 , 4.755884 , 2.729685
diff --git a/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/4.txt
new file mode 100644
index 000000000..12e13272d
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/ReLU_000/layer/uint8/4.txt
@@ -0,0 +1 @@
+ 0.82922786, 4.762074 ,-3.5043278 , 2.4521468 , 2.6450796 ,-2.8606322 , 0.8321993 ,-1.4020495 ,-0.25749585, 1.0287803 ,-3.911455 ,-1.8311876 , 2.763438 , 3.8604703 ,-3.5478592 ,-4.2335987 ,-3.6402035 ,-1.8485361
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/0.txt
new file mode 100644
index 000000000..a8874bc5f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/0.txt
@@ -0,0 +1 @@
+ 3.9384239 ,-3.7377489 , 0.97284186, 3.8309984 , 2.4125865 , 1.7141674 , 3.9459977 ,-0.304659 ,-3.4623327 , 4.4569106 , 4.209985 ,-0.6677348 , 3.4578135 , 1.6779743 , 2.502791 ,-1.324285 , 1.3139176 , 3.4334664 ,-2.2695086 ,-4.001059 ,-0.91164917, 4.4447775 ,-3.0275404 ,-2.0852396 , 3.6677403 ,-2.9595146 , 2.0921555 , 1.7570637 , 3.717391 ,-0.3216191 ,-0.8410847 , 2.662336
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/1.txt
new file mode 100644
index 000000000..715e680be
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/1.txt
@@ -0,0 +1 @@
+ 0.6663157 ,-0.04146723,-0.8193995 , 4.804576 ,-2.1357434 , 4.0829 ,-1.6380692 , 1.8043218 , 2.3431025 , 0.30111 , 1.2928191 ,-1.8559257 ,-0.68305963,-1.1502715 , 1.9492546 ,-2.7240746 , 2.9279857 ,-3.3329778 ,-4.8343406 ,-0.02708206, 1.1840513 , 3.6476028 , 4.75276 ,-4.9085226 ,-1.1922491 , 0.54225117, 3.17247 ,-2.7856457 ,-3.0866194 ,-2.2077718 , 1.6263398 , 3.7066603
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/2.txt
new file mode 100644
index 000000000..3ca893e61
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/2.txt
@@ -0,0 +1 @@
+-4.8507566 ,-1.267258 , 0.5099198 , 1.650726 , 3.4329638 ,-2.2652836 , 1.2157568 , 0.18305123, 3.6754217 ,-4.6185255 ,-1.0646905 ,-0.46092424, 2.046326 ,-2.8830478 , 4.156068 ,-2.0503244 , 0.0755459 ,-4.6472006 ,-0.50128895, 3.1129324 ,-4.4048553 , 0.47983927, 1.4510479 , 3.9226127 ,-4.767221 ,-2.795826 ,-4.816457 ,-3.6127663 ,-2.2712553 , 4.586938 , 1.1028811 , 1.5028698
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/3.txt
new file mode 100644
index 000000000..3fba8ecec
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/3.txt
@@ -0,0 +1 @@
+ 4.9431224 ,-3.4878132 ,-2.4831018 , 2.2395666 ,-2.3317611 ,-1.6786547 ,-2.4702384 , 3.2167027 , 1.7300137 , 2.8848834 ,-4.6395254 , 0.5527259 ,-2.915835 ,-1.0066313 ,-0.278253 , 4.6136203 ,-3.4183645 ,-1.5189631 ,-4.599058 , 3.3198457 ,-3.9464161 ,-0.6357558 , 0.32550323, 3.2147424 , 4.921844 ,-0.30067012, 3.9456701 , 0.5943688 ,-4.7229166 ,-3.6803844 ,-3.3813965 , 3.283583
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/4.txt
new file mode 100644
index 000000000..16cc23b79
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/int16/4.txt
@@ -0,0 +1 @@
+ 2.232644 , 4.465217 , 1.926956 ,-4.007337 ,-2.7392106 ,-2.4579394 , 2.913538 ,-1.7261469 , 3.8706868 , 0.06259949,-2.018361 , 1.2728635 ,-3.133289 ,-4.943454 ,-1.5415367 ,-4.8183494 , 4.348317 ,-2.4929109 ,-0.9018388 ,-4.776565 , 4.634248 , 3.0753953 , 2.3412373 ,-2.7086196 , 3.4485948 , 0.3561932 , 0.03650501,-2.8704169 , 1.0514414 , 3.3964615 , 1.2783849 , 4.974951
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/0.txt
new file mode 100644
index 000000000..fb728bb70
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/0.txt
@@ -0,0 +1 @@
+0.5177879 ,0.10991199,0.19134527,0.25834408,0.16297385,0.5499753 ,0.8782323 ,0.74750453,0.16825114,0.72425395,0.68458 ,0.9399099 ,0.81214494,0.73325175,0.6407931 ,0.02865177,0.04341139,0.44781777,0.59848577,0.72099334,0.654926 ,0.93810713,0.5193446 ,0.8657371 ,0.50826824,0.10122011,0.6946167 ,0.5009533 ,0.27305812,0.7708204 ,0.14410722,0.7092205
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/1.txt
new file mode 100644
index 000000000..8c72dc764
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/1.txt
@@ -0,0 +1 @@
+0.57410187,0.5534829 ,0.434663 ,0.55580896,0.9040647 ,0.16827786,0.82538676,0.25387943,0.7611494 ,0.49195638,0.00602222,0.20389748,0.541152 ,0.962896 ,0.37785006,0.9330408 ,0.9868882 ,0.57428783,0.830525 ,0.67987496,0.5576374 ,0.4303 ,0.8442439 ,0.21868347,0.45653513,0.7913927 ,0.31475154,0.6723579 ,0.5749264 ,0.07061622,0.6450232 ,0.52825755
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/2.txt
new file mode 100644
index 000000000..04ff6ae29
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/2.txt
@@ -0,0 +1 @@
+0.49751657,0.3004485 ,0.11624487,0.17704253,0.9022095 ,0.24667789,0.9204152 ,0.09801941,0.9194739 ,0.35418576,0.36659864,0.4962548 ,0.83799136,0.58057517,0.2948883 ,0.28411615,0.14429809,0.8460358 ,0.7026028 ,0.25956342,0.5251088 ,0.06569998,0.01754393,0.45209908,0.95638806,0.6044543 ,0.17229715,0.6828144 ,0.8684328 ,0.5829665 ,0.1456113 ,0.3334334
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/3.txt
new file mode 100644
index 000000000..1342dac2f
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/3.txt
@@ -0,0 +1 @@
+0.00850414,0.5746211 ,0.7659193 ,0.8643168 ,0.36803156,0.08386383,0.76002747,0.19255683,0.05220222,0.18169314,0.88597506,0.6793377 ,0.45955214,0.16984127,0.5275391 ,0.910098 ,0.64607793,0.3997594 ,0.38601097,0.40899974,0.10289235,0.896202 ,0.22364503,0.30232555,0.11873382,0.07853477,0.20674925,0.35148785,0.02880615,0.09937044,0.4382221 ,0.53562754
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/4.txt
new file mode 100644
index 000000000..e3e85392e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/channel/uint8/4.txt
@@ -0,0 +1 @@
+0.8097857 ,0.4602844 ,0.01609277,0.7885611 ,0.9090256 ,0.75475484,0.98657864,0.5927874 ,0.73494065,0.374227 ,0.23557834,0.6020654 ,0.0122237 ,0.37126908,0.38277507,0.67635936,0.4139088 ,0.8625733 ,0.37775922,0.15304309,0.6196326 ,0.4827059 ,0.76868814,0.5530773 ,0.3336473 ,0.11217184,0.5877591 ,0.5325879 ,0.48493427,0.6317438 ,0.9385114 ,0.02825027
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/0.txt
new file mode 100644
index 000000000..e9db48f9e
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/0.txt
@@ -0,0 +1 @@
+-1.4124781 , 0.42694193, 1.1734594 ,-3.5111153 ,-2.9756174 , 1.3682148 ,-2.318465 , 2.198896 ,-4.5043235 , 3.1775594 ,-0.42802384,-1.4872279 , 1.3821319 ,-4.771963 ,-0.12837897, 4.132799 , 3.697655 , 2.0807178 ,-3.621293 , 2.121878 ,-0.25654107, 0.42100102,-1.4009671 ,-2.9733627 ,-0.7058871 ,-2.831215 , 3.5669627 , 2.1420689 ,-1.8789555 , 0.8104939 ,-2.0503597 , 1.7788508
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/1.txt
new file mode 100644
index 000000000..479d062f1
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/1.txt
@@ -0,0 +1 @@
+ 3.4726453 , 3.0497985 ,-4.234619 ,-1.0526706 , 1.7278554 ,-3.341614 , 4.54768 , 3.0954597 ,-3.735109 , 2.8810751 ,-2.5381427 ,-3.2360535 ,-1.5378917 , 2.3052745 ,-3.170938 ,-3.327242 , 2.0654576 ,-2.2294598 ,-1.881382 , 0.13216451,-4.2825613 , 0.26616526, 4.6196365 ,-0.88623226, 1.7103885 ,-1.5865034 ,-3.9114466 ,-3.2227128 , 4.909618 , 2.3318915 , 0.84300846, 0.760918
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/2.txt
new file mode 100644
index 000000000..ae28234bd
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/2.txt
@@ -0,0 +1 @@
+-4.6097918,-4.21991 ,-3.9955974, 3.6492047, 2.9191775, 2.8082933, 1.6189331, 0.2730309,-1.5029653,-1.9471445, 4.8758197, 3.3177438, 3.1338058,-2.1281245,-1.7526287,-2.5518703,-1.7746793, 4.0455256,-0.5839861,-4.408046 ,-4.0034447, 1.5858272,-4.5896654, 4.7211285,-4.677515 ,-2.6027086,-4.7896166,-3.5512326,-1.9068764,-2.9705904,-4.854087 ,-4.892111
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/3.txt
new file mode 100644
index 000000000..fd40f84f4
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/3.txt
@@ -0,0 +1 @@
+ 2.1514777e-02, 2.6526773e+00,-3.0477784e+00, 1.3287724e+00,-4.1414630e-01,-1.7295350e-01, 7.6649576e-01,-1.8028022e+00,-7.0781744e-01,-2.5262204e-01,-3.0970418e+00,-1.3165286e+00,-4.6649928e+00, 2.0809033e+00,-1.5739973e+00,-4.0531826e-01,-2.1718202e+00, 2.0146034e+00, 2.5044403e+00,-1.1256610e+00, 1.3536702e+00, 1.0283234e-03,-1.8823910e+00, 4.7122188e+00, 9.4781297e-01, 3.2012525e+00,-5.5164534e-01,-2.6158772e+00,-1.8771547e+00,-3.1689723e+00, 4.9054880e+00,-3.4560370e+00
diff --git a/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/4.txt
new file mode 100644
index 000000000..e81c3b8e5
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_inputs/TransposeConv_001/layer/uint8/4.txt
@@ -0,0 +1 @@
+-2.0927553 ,-2.107511 ,-1.6963564 , 1.7006218 , 1.4575784 , 0.06095728, 1.2659966 , 4.1905265 , 1.3035946 , 4.9793477 ,-4.3388166 ,-0.23496658, 1.9831208 , 2.6154642 ,-0.2790228 ,-3.1774354 ,-3.178935 ,-1.1564373 ,-0.8199472 ,-2.245698 ,-4.8605046 ,-3.569018 ,-1.4226891 ,-4.1067843 , 2.6078918 ,-3.5830674 , 1.9065963 , 2.435578 ,-3.3216476 , 4.5930347 , 2.9191844 , 1.7885648
diff --git a/compiler/pota-quantization-value-test/test_quantization.sh b/compiler/pota-quantization-value-test/test_quantization.sh
new file mode 100755
index 000000000..5ebd72601
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_quantization.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+
+# This script tests the basic behavior of record-minmax
+#
+# HOW TO USE
+#
+# ./test_quantization.sh <path/to/test.config> <path/to/work_dir> <TEST 1> <TEST 2> ...
+# test.config : set ${RECORD_MINMAX_PATH} and ${CIRCLE_QUANTIZER_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}.quantization.passed"
+ rm -f "${PASSED_TAG}"
+
+ cat > "${TEST_RESULT_FILE}_quantization.log" <(
+ exec 2>&1
+ set -ex
+
+ # Run circle-quantizer with --quantize_with_minmax
+ "${CIRCLE_QUANTIZER_PATH}" \
+ --quantize_with_minmax float32 "${DTYPE}" "${GRANULARITY}" \
+ "${TEST_RESULT_FILE}.minmax_recorded.circle" \
+ "${TEST_RESULT_FILE}.quantized.circle"
+
+ # Dump scale, zp, weights values (circle-tensordump)
+ "${CIRCLE_TENSORDUMP_PATH}" \
+ "${TEST_RESULT_FILE}.quantized.circle" \
+ --tensors_to_hdf5 "${TEST_RESULT_FILE}.quantized.circle.h5"
+
+ # Compare result
+ "${VIRTUALENV}/bin/python" "${COMPARE_SCRIPT_PATH}" \
+ --input_h5 "${TEST_RESULT_FILE}.quantized.circle.h5" \
+ --expect_dir "${SOURCE_PATH}/expected_outputs/${MODELNAME}/${GRANULARITY}/${DTYPE}/quantization" \
+ --mode 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/pota-quantization-value-test/test_record_minmax.sh b/compiler/pota-quantization-value-test/test_record_minmax.sh
new file mode 100755
index 000000000..acb7574c0
--- /dev/null
+++ b/compiler/pota-quantization-value-test/test_record_minmax.sh
@@ -0,0 +1,100 @@
+#!/bin/bash
+
+# This script tests the basic behavior of record-minmax
+#
+# HOW TO USE
+#
+# ./test_record_minmax.sh <path/to/test.config> <path/to/work_dir> <TEST 1> <TEST 2> ...
+# test.config : set ${RECORD_MINMAX_PATH} and ${CIRCLE2CIRCLE_PATH}
+# work_dir : build directory of quantization-value-test (ex: build/compiler/quantization-value-test)
+
+SOURCE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+GEN_SCRIPT_PATH="${SOURCE_PATH}/gen_h5_explicit_inputs.py"
+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 RECORD-MINMAX: ${RECORD_MINMAX_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}.record_minmax.passed"
+ rm -f "${PASSED_TAG}"
+
+ cat > "${TEST_RESULT_FILE}_record_minmax.log" <(
+ exec 2>&1
+ set -ex
+
+ # Generate h5 input data
+ source "${VIRTUALENV}/bin/activate"
+ "${VIRTUALENV}/bin/python" "${GEN_SCRIPT_PATH}" \
+ --model "${WORKDIR}/${MODELNAME}.tflite" \
+ --input "${TEST_INPUT_PATH}/${MODELNAME}/${GRANULARITY}/${DTYPE}" \
+ --output "${TESTCASE_FILE}.input.h5"
+
+ if [[ $? -ne 0 ]]; then
+ echo "FAILED TO GENERATE INPUT"
+ continue
+ fi
+
+ # Run record-minmax
+ "${RECORD_MINMAX_PATH}" \
+ --input_model "${TEST_RESULT_FILE}.fake_quantized.circle" \
+ --input_data "${TESTCASE_FILE}.input.h5" \
+ --output_model "${TEST_RESULT_FILE}.minmax_recorded.circle"
+
+ # Dump min/max values (circle-tensordump)
+ "${CIRCLE_TENSORDUMP_PATH}" \
+ "${TEST_RESULT_FILE}.minmax_recorded.circle" \
+ --tensors_to_hdf5 "${TEST_RESULT_FILE}.minmax_recorded.circle.h5"
+
+ # Compare result
+ "${VIRTUALENV}/bin/python" "${COMPARE_SCRIPT_PATH}" \
+ --input_h5 "${TEST_RESULT_FILE}.minmax_recorded.circle.h5" \
+ --expect_dir "${SOURCE_PATH}/expected_outputs/${MODELNAME}/${GRANULARITY}/${DTYPE}/record_minmax" \
+ --mode record_minmax
+
+ 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/pp/CMakeLists.txt b/compiler/pp/CMakeLists.txt
index 183ada11d..2c25c6406 100644
--- a/compiler/pp/CMakeLists.txt
+++ b/compiler/pp/CMakeLists.txt
@@ -7,11 +7,12 @@ set_target_properties(pp PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_include_directories(pp PUBLIC include)
target_link_libraries(pp PRIVATE nncc_common)
-nncc_find_package(GTest QUIET)
-
-if(NOT GTest_FOUND)
+if(NOT ENABLE_TEST)
return()
-endif(NOT GTest_FOUND)
+endif(NOT ENABLE_TEST)
+
+# Google Test is mandatory for internal testing
+nnas_find_package(GTest REQUIRED)
add_executable(pp_test ${TESTS})
target_link_libraries(pp_test pp)
diff --git a/compiler/pp/src/LinearDocument.cpp b/compiler/pp/src/LinearDocument.cpp
index 2bc5f260c..2cd35ef98 100644
--- a/compiler/pp/src/LinearDocument.cpp
+++ b/compiler/pp/src/LinearDocument.cpp
@@ -63,9 +63,9 @@ const std::string &LinearDocument::line(uint32_t n) const
{
return _lines.at(lines() - n - 1);
}
+ default:
+ throw std::runtime_error{"Not supported Direction"};
}
-
- throw std::runtime_error{"unreachable"};
}
} // namespace pp
diff --git a/compiler/rawdata2hdf5/CMakeLists.txt b/compiler/rawdata2hdf5/CMakeLists.txt
new file mode 100644
index 000000000..9118772b6
--- /dev/null
+++ b/compiler/rawdata2hdf5/CMakeLists.txt
@@ -0,0 +1,17 @@
+set(rawdata2hdf5_FILE "rawdata2hdf5")
+set(rawdata2hdf5_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${rawdata2hdf5_FILE}")
+set(rawdata2hdf5_BIN "${CMAKE_CURRENT_BINARY_DIR}/${rawdata2hdf5_FILE}")
+
+add_custom_command(OUTPUT ${rawdata2hdf5_BIN}
+ COMMAND ${CMAKE_COMMAND} -E copy "${rawdata2hdf5_SRC}" "${rawdata2hdf5_BIN}"
+ DEPENDS ${rawdata2hdf5_SRC}
+ COMMENT "Generate ${rawdata2hdf5_BIN}"
+ )
+
+add_custom_target(rawdata2hdf5 ALL DEPENDS ${rawdata2hdf5_BIN})
+
+install(FILES ${rawdata2hdf5_BIN}
+ PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE
+ GROUP_READ GROUP_EXECUTE
+ WORLD_READ WORLD_EXECUTE
+ DESTINATION bin)
diff --git a/compiler/rawdata2hdf5/README.md b/compiler/rawdata2hdf5/README.md
new file mode 100644
index 000000000..cad92f34c
--- /dev/null
+++ b/compiler/rawdata2hdf5/README.md
@@ -0,0 +1,24 @@
+# rawdata2hdf5
+
+_rawdata2hdf5_ is a tool to convert raw data (assumed to be pre-processed) to an hdf5 file.
+
+## Prerequisite
+- Raw data pre-processed for the corresponding DNN model
+- List of data to convert (saved in the text file)
+- Python installed with _numpy_ and _h5py_ (See docs/how-to-prepare-virtualenv.txt)
+
+## Example
+```
+./rawdata2hdf5 \
+> --data_list=tmp/data/datalist.txt
+> --output_path=tmp/data/data.h5
+```
+
+## Arguments
+```
+ -h, --help Show this help message and exit
+ -l DATA_LIST, --data_list DATA_LIST
+ Path to the text file which lists the absolute paths of the raw data files to be converted.
+ -o OUTPUT_PATH, --output_path OUTPUT_PATH
+ Path to the output hdf5 file.
+```
diff --git a/compiler/rawdata2hdf5/rawdata2hdf5 b/compiler/rawdata2hdf5/rawdata2hdf5
new file mode 100644
index 000000000..db4296d73
--- /dev/null
+++ b/compiler/rawdata2hdf5/rawdata2hdf5
@@ -0,0 +1,116 @@
+#!/usr/bin/env bash
+''''export SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # '''
+''''export PY_PATH=${SCRIPT_PATH}/venv/bin/python # '''
+''''test -f ${PY_PATH} && exec ${PY_PATH} "$0" "$@" # '''
+''''echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command." # '''
+''''exit 255 # '''
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import h5py as h5
+import numpy as np
+import argparse
+import glob
+import os
+
+
+def get_parser():
+ """Create and return given the argument parser"""
+ parser = argparse.ArgumentParser(
+ description='command line tool to convert raw data files to hdf5 file')
+ parser.add_argument(
+ "-l",
+ "--data_list",
+ type=str,
+ help=
+ "Path to the text file which lists the absolute paths of the raw data files to be converted.",
+ required=True)
+ parser.add_argument(
+ "-o",
+ "--output_path",
+ type=str,
+ help="Path to the output hdf5 file.",
+ required=True)
+ return parser
+
+
+def verify_args(parser, args):
+ """Verify the given arguments"""
+
+ def is_valid_attr(args, attr):
+ return hasattr(args, attr) and getattr(args, attr)
+
+ # check if required arguments is given
+ missing = []
+ if not is_valid_attr(args, 'data_list'):
+ missing.append('-l/--data_list')
+ if not is_valid_attr(args, 'output_path'):
+ missing.append('-o/--output_path')
+ if len(missing):
+ parser.error('the following arguments are required: ' + ' '.join(missing))
+
+
+def create_hdf5(data_list, output_path):
+ """Create the hdf5 file using raw data files listed in data_list"""
+ h5_file = h5.File(output_path, 'w')
+ group = h5_file.create_group("value")
+ # We assume the raw input data have the correct type/shape for the corresponding model
+ # If this flag is set in the hdf5 file, record-minmax will skip type/shape check
+ group.attrs['rawData'] = '1'
+
+ if os.path.isfile(data_list) == False:
+ raise FileNotFoundError("No such file. " + data_list)
+
+ # Data list
+ datalist = []
+ with open(data_list, 'r') as f:
+ lines = f.readlines()
+ for line in lines:
+ if line.strip():
+ filename = line.rstrip()
+ if os.path.isfile(filename):
+ datalist.append(filename)
+ else:
+ raise FileNotFoundError("No such file. " + filename)
+
+ # Input files
+ num_converted = 0
+ for rawdata in datalist:
+ with open(rawdata, 'rb') as f:
+ sample = group.create_group(str(num_converted))
+ num_converted += 1
+ filename = os.path.basename(rawdata)
+ sample.attrs['desc'] = filename
+ raw_data = bytearray(f.read())
+ # The target model is DNN with one input data
+ sample.create_dataset('0', data=raw_data)
+
+ h5_file.close()
+
+ print("Raw data have been packaged to " + output_path)
+ print("Number of packaged data: " + str(num_converted))
+
+
+def main():
+ parser = get_parser()
+
+ args = parser.parse_args()
+
+ verify_args(parser, args)
+
+ create_hdf5(args.data_list, args.output_path)
+
+if __name__ == '__main__':
+ main()
diff --git a/compiler/record-minmax-conversion-test/CMakeLists.txt b/compiler/record-minmax-conversion-test/CMakeLists.txt
new file mode 100644
index 000000000..2221e1702
--- /dev/null
+++ b/compiler/record-minmax-conversion-test/CMakeLists.txt
@@ -0,0 +1,42 @@
+unset(RECORD_MINMAX_CONVERSION_TEST)
+
+macro(addTest NAME)
+ list(APPEND RECORD_MINMAX_CONVERSION_TEST ${NAME})
+endmacro(addTest)
+
+# Read "test.lst"
+include("test.lst")
+# Read "test.local.lst" if exists
+include("test.local.lst" OPTIONAL)
+
+unset(TEST_DEPS)
+
+get_target_property(ARTIFACTS_BIN_PATH testDataGenerator BINARY_DIR)
+
+###
+### Generate test.config
+###
+set(TEST_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/test.config")
+
+add_custom_command(
+ OUTPUT ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'RECORD_MINMAX_PATH=\"$<TARGET_FILE:record-minmax>\"' >> ${TEST_CONFIG}
+ DEPENDS record-minmax
+ COMMENT "Generate test configuration"
+)
+
+list(APPEND TEST_DEPS "${TEST_CONFIG}")
+
+# This enforces CMake to generate all the dependencies during "build" phase
+add_custom_target(record_minmax_conversion_test_deps ALL DEPENDS ${TEST_DEPS})
+
+# Run tests
+add_test(
+ NAME record_minmax_conversion_test
+ COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/testall.sh"
+ "${TEST_CONFIG}"
+ "${ARTIFACTS_BIN_PATH}"
+ "${NNCC_OVERLAY_DIR}/venv_1_13_2"
+ ${RECORD_MINMAX_CONVERSION_TEST}
+)
diff --git a/compiler/record-minmax-conversion-test/README.md b/compiler/record-minmax-conversion-test/README.md
new file mode 100644
index 000000000..cc06c1542
--- /dev/null
+++ b/compiler/record-minmax-conversion-test/README.md
@@ -0,0 +1,5 @@
+# record-minmax-conversion-test
+
+Run `record-minmax` with random input data and Circle models listed in `test.lst`. This test checks whether a given Circle model can be converted to a Circle model embedded with min/max values without failure.
+
+Write `test.local.lst` for local test.
diff --git a/compiler/record-minmax-conversion-test/gen_h5_random_inputs.py b/compiler/record-minmax-conversion-test/gen_h5_random_inputs.py
new file mode 100755
index 000000000..bdf86fe29
--- /dev/null
+++ b/compiler/record-minmax-conversion-test/gen_h5_random_inputs.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python3
+import h5py as h5
+import numpy as np
+import tensorflow as tf
+import argparse
+
+#
+# This script generates a pack of random input data (.h5) expected by the input tflite model
+#
+# Basic usage:
+# gen_h5_inputs.py --model <path/to/tflite/model> --num_data <number/of/data> --output <path/to/output/data>
+# ex: gen_h5_inputs.py --model add.tflite --num_data 3 --output add.tflite.input.h5
+# (This will create add.tflite.input.h5 composed of three random inputs in the same directory as the model)
+parser = argparse.ArgumentParser()
+parser.add_argument('--model', type=str, required=True)
+parser.add_argument('--num_data', type=int, required=True)
+parser.add_argument('--output', type=str, required=True)
+args = parser.parse_args()
+
+model = args.model
+
+num_data = args.num_data
+
+output_path = args.output
+
+# Build TFLite interpreter. (to get the information of model input)
+interpreter = tf.lite.Interpreter(model)
+input_details = interpreter.get_input_details()
+
+# Create h5 file
+h5_file = h5.File(output_path, 'w')
+group = h5_file.create_group("value")
+group.attrs['desc'] = "Input data for " + model
+
+# Generate random data
+for i in range(num_data):
+ sample = group.create_group(str(i))
+ sample.attrs['desc'] = "Input data " + str(i)
+
+ for j in range(len(input_details)):
+ input_detail = input_details[j]
+ # Generate random input [-5, 5)
+ input_data = np.array(10 * np.random.random_sample(input_detail["shape"]) - 5,
+ input_detail["dtype"])
+ sample.create_dataset(str(j), data=input_data)
+
+h5_file.close()
diff --git a/compiler/record-minmax-conversion-test/requires.cmake b/compiler/record-minmax-conversion-test/requires.cmake
new file mode 100644
index 000000000..9105c3e2e
--- /dev/null
+++ b/compiler/record-minmax-conversion-test/requires.cmake
@@ -0,0 +1,2 @@
+require("common-artifacts")
+require("record-minmax")
diff --git a/compiler/record-minmax-conversion-test/test.lst b/compiler/record-minmax-conversion-test/test.lst
new file mode 100644
index 000000000..771c3bd66
--- /dev/null
+++ b/compiler/record-minmax-conversion-test/test.lst
@@ -0,0 +1,16 @@
+addTest(Add_000)
+addTest(AveragePool2D_000)
+addTest(Concatenation_000)
+addTest(Conv2D_000)
+addTest(Conv2D_001)
+addTest(Conv2D_002)
+addTest(DepthwiseConv2D_000)
+addTest(FullyConnected_000)
+addTest(FullyConnected_001)
+addTest(MaxPool2D_000)
+addTest(Mul_000)
+addTest(Pad_000)
+addTest(Reshape_000)
+addTest(Reshape_001)
+addTest(Reshape_002)
+addTest(Softmax_000)
diff --git a/compiler/record-minmax-conversion-test/testall.sh b/compiler/record-minmax-conversion-test/testall.sh
new file mode 100755
index 000000000..29c9ed3d1
--- /dev/null
+++ b/compiler/record-minmax-conversion-test/testall.sh
@@ -0,0 +1,81 @@
+#!/bin/bash
+
+# This script tests the basic behavior of record-minmax
+#
+# HOW TO USE
+#
+# ./testall.sh <path/to/test.config> <path/to/work_dir> <TEST 1> <TEST 2> ...
+# test.config : set ${RECORD_MINMAX_PATH}
+# work_dir : build directory of record-minmax-conversion-test (ex: build/compiler/record-minmax-conversion-test)
+
+GEN_SOURCE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+GEN_SCRIPT_PATH="${GEN_SOURCE_PATH}/gen_h5_random_inputs.py"
+CONFIG_PATH="$1"; shift
+BIN_PATH=$(dirname "$CONFIG_PATH")
+WORKDIR="$1"; shift
+VIRTUALENV="$1"; shift
+
+source "${CONFIG_PATH}"
+
+echo "-- Found RECORD-MINMAX: ${RECORD_MINMAX_PATH}"
+echo "-- Found workdir: ${WORKDIR}"
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+pushd "${WORKDIR}"
+for TESTCASE in "$@"; do
+ TESTED+=("${TESTCASE}")
+
+ TESTCASE_FILE="${WORKDIR}/${TESTCASE}"
+
+ PASSED_TAG="${BIN_PATH}/${TESTCASE}.passed"
+ rm -f "${PASSED_TAG}"
+
+ cat > "${BIN_PATH}/${TESTCASE}.log" <(
+ exec 2>&1
+ set -ex
+
+ # Generate h5 input data
+ source "${VIRTUALENV}/bin/activate"
+ "${VIRTUALENV}/bin/python" "${GEN_SCRIPT_PATH}" \
+ --model "${TESTCASE_FILE}.tflite" \
+ --num_data 3 \
+ --output "${BIN_PATH}/${TESTCASE}.tflite.input.h5"
+
+ if [[ $? -ne 0 ]]; then
+ echo "FAILED TO GENERATE INPUT"
+ continue
+ fi
+
+ # Run record-minmax
+ "${RECORD_MINMAX_PATH}" \
+ --input_model "${TESTCASE_FILE}.circle" \
+ --input_data "${BIN_PATH}/${TESTCASE}.tflite.input.h5" \
+ --output_model "${BIN_PATH}/${TESTCASE}.out.circle"
+
+ 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/CMakeLists.txt b/compiler/record-minmax/CMakeLists.txt
new file mode 100644
index 000000000..f8a165bd3
--- /dev/null
+++ b/compiler/record-minmax/CMakeLists.txt
@@ -0,0 +1,32 @@
+nnas_find_package(HDF5 COMPONENTS STATIC QUIET)
+
+if(NOT HDF5_FOUND)
+ message(STATUS "Build record-minmax: FAILED (missing HDF5)")
+ return()
+endif(NOT HDF5_FOUND)
+
+set(DRIVER "driver/Driver.cpp")
+
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+add_executable(record-minmax ${DRIVER} ${SOURCES})
+target_include_directories(record-minmax PRIVATE include)
+target_include_directories(record-minmax PRIVATE ${HDF5_INCLUDE_DIRS})
+
+target_link_libraries(record-minmax ${HDF5_CXX_LIBRARIES})
+target_link_libraries(record-minmax arser)
+target_link_libraries(record-minmax safemain)
+target_link_libraries(record-minmax luci_import)
+target_link_libraries(record-minmax luci_export)
+target_link_libraries(record-minmax luci_interpreter)
+target_link_libraries(record-minmax vconone)
+
+install(TARGETS record-minmax DESTINATION bin)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+GTest_AddTest(record_minmax_function_test "${CMAKE_CURRENT_SOURCE_DIR}/tests/RecordFunction.test.cpp")
+target_include_directories(record_minmax_function_test PRIVATE include)
diff --git a/compiler/record-minmax/README.md b/compiler/record-minmax/README.md
new file mode 100644
index 000000000..6a491f279
--- /dev/null
+++ b/compiler/record-minmax/README.md
@@ -0,0 +1,18 @@
+# record-minmax
+
+_record-minmax_ is a tool to embed min/max values of activations to the circle model for post-training quantization.
+
+## Usage
+
+This will run with the path to the input model (.circle), a pack of input data (.h5), and the output model (.circle).
+
+```
+$ ./record-minmax <path_to_input_model> <path_to_input_data> <path_to_output_model>
+```
+
+For example,
+```
+$ ./record-minmax input.circle input.h5 out.circle
+```
+
+Output is a circle model where min/max values of activation tensors are saved in QuantizationParameters.
diff --git a/compiler/record-minmax/driver/Driver.cpp b/compiler/record-minmax/driver/Driver.cpp
new file mode 100644
index 000000000..8b09498c3
--- /dev/null
+++ b/compiler/record-minmax/driver/Driver.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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 "RecordMinMax.h"
+
+#include <arser/arser.h>
+#include <vconone/vconone.h>
+
+void print_version(void)
+{
+ std::cout << "record-minmax version " << vconone::get_string() << std::endl;
+ std::cout << vconone::get_copyright() << std::endl;
+}
+
+int entry(const int argc, char **argv)
+{
+ using namespace record_minmax;
+
+ arser::Arser arser(
+ "Embedding min/max values of activations to the circle model for post-training quantization");
+
+ arser.add_argument("--version")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("Show version information and exit")
+ .exit_with(print_version);
+
+ arser.add_argument("--input_model")
+ .nargs(1)
+ .type(arser::DataType::STR)
+ .required(true)
+ .help("Input model filepath");
+
+ arser.add_argument("--input_data")
+ .nargs(1)
+ .type(arser::DataType::STR)
+ .required(true)
+ .help("Input data filepath");
+
+ arser.add_argument("--output_model")
+ .nargs(1)
+ .type(arser::DataType::STR)
+ .required(true)
+ .help("Output model filepath");
+
+ arser.add_argument("--min_percentile")
+ .nargs(1)
+ .type(arser::DataType::FLOAT)
+ .help("Record n'th percentile of min");
+
+ arser.add_argument("--max_percentile")
+ .nargs(1)
+ .type(arser::DataType::FLOAT)
+ .help("Record n'th percentile of max");
+
+ arser.add_argument("--mode")
+ .nargs(1)
+ .type(arser::DataType::STR)
+ .help("Record mode. percentile (default) or moving_average");
+
+ try
+ {
+ arser.parse(argc, argv);
+ }
+ catch (const std::runtime_error &err)
+ {
+ std::cout << err.what() << std::endl;
+ std::cout << arser;
+ return 255;
+ }
+
+ auto input_model_path = arser.get<std::string>("--input_model");
+ auto input_data_path = arser.get<std::string>("--input_data");
+ auto output_model_path = arser.get<std::string>("--output_model");
+
+ // Default values
+ std::string mode("percentile");
+ float min_percentile = 1.0;
+ float max_percentile = 99.0;
+
+ if (arser["--min_percentile"])
+ min_percentile = arser.get<float>("--min_percentile");
+
+ if (arser["--max_percentile"])
+ max_percentile = arser.get<float>("--max_percentile");
+
+ if (arser["--mode"])
+ mode = arser.get<std::string>("--mode");
+
+ if (mode != "percentile" && mode != "moving_average")
+ throw std::runtime_error("Unsupported mode");
+
+ RecordMinMax rmm;
+
+ // Initialize interpreter and observer
+ rmm.initialize(input_model_path);
+
+ // Profile min/max while executing the given input data
+ rmm.profileData(mode, input_data_path, min_percentile, max_percentile);
+
+ // Save profiled values to the model
+ rmm.saveModel(output_model_path);
+
+ return EXIT_SUCCESS;
+}
diff --git a/compiler/record-minmax/include/MinMaxObserver.h b/compiler/record-minmax/include/MinMaxObserver.h
new file mode 100644
index 000000000..ce63438ac
--- /dev/null
+++ b/compiler/record-minmax/include/MinMaxObserver.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __RECORD_MINMAX_MINMAXOBSERVER_H__
+#define __RECORD_MINMAX_MINMAXOBSERVER_H__
+
+#include <luci_interpreter/Interpreter.h>
+#include <luci_interpreter/core/Tensor.h>
+
+#include <vector>
+#include <unordered_map>
+
+namespace record_minmax
+{
+
+struct MinMaxVectors
+{
+ std::vector<float> min_vector;
+ std::vector<float> max_vector;
+};
+
+class MinMaxMap
+{
+public:
+ // Record min/max of node
+ void recordMinMax(const luci::CircleNode *node, float min, float max)
+ {
+ MinMaxVectors &vectors = _minmax_map[node];
+ vectors.min_vector.push_back(min);
+ vectors.max_vector.push_back(max);
+ }
+
+ const std::unordered_map<const luci::CircleNode *, MinMaxVectors> *getMap() const
+ {
+ return &_minmax_map;
+ }
+
+private:
+ std::unordered_map<const luci::CircleNode *, MinMaxVectors> _minmax_map;
+};
+
+class MinMaxObserver : public luci_interpreter::ExecutionObserver
+{
+public:
+ MinMaxObserver()
+ {
+ // Do nothing
+ }
+
+ void postTensorWrite(const luci::CircleNode *node,
+ const luci_interpreter::Tensor *tensor) override;
+
+ const MinMaxMap *minMaxData() { return &_minmax_data; }
+
+private:
+ MinMaxMap _minmax_data;
+};
+
+} // namespace record_minmax
+
+#endif // __RECORD_MINMAX_MINMAXOBSERVER_H__
diff --git a/compiler/record-minmax/include/RecordFunction.h b/compiler/record-minmax/include/RecordFunction.h
new file mode 100644
index 000000000..b570c6a0a
--- /dev/null
+++ b/compiler/record-minmax/include/RecordFunction.h
@@ -0,0 +1,102 @@
+/*
+ * 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 <vector>
+#include <cassert>
+#include <algorithm>
+#include <cmath>
+#include <numeric>
+#include <stdexcept>
+
+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<float> &vector, float percentile)
+{
+ if (percentile < 0 || percentile > 100)
+ throw std::runtime_error("Percentile must be ranged from 0 to 100");
+
+ if (percentile == 0.0)
+ return vector.front();
+
+ if (percentile == 100.0)
+ return vector.back();
+
+ 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<float> copy;
+ copy.assign(vector.begin(), vector.end());
+ std::sort(copy.begin(), copy.end());
+
+ int index = static_cast<int>(std::floor((copy.size() - 1) * percentile / 100.0));
+
+ float percent_i = static_cast<float>(index) / static_cast<float>(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;
+}
+
+/**
+ * @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<float> &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 = [&](int start_index) {
+ assert(start_index >= 0 && start_index < vector.size());
+
+ float res = is_min ? std::numeric_limits<float>::max() : std::numeric_limits<float>::lowest();
+ for (int offset = 0; offset < batch_size; offset++)
+ {
+ int 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 (size_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/include/RecordMinMax.h b/compiler/record-minmax/include/RecordMinMax.h
new file mode 100644
index 000000000..ffdb17aec
--- /dev/null
+++ b/compiler/record-minmax/include/RecordMinMax.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __RECORD_MINMAX_H__
+#define __RECORD_MINMAX_H__
+
+#include <luci/IR/Module.h>
+#include <luci_interpreter/Interpreter.h>
+
+#include "MinMaxObserver.h"
+
+#include <memory>
+
+namespace record_minmax
+{
+
+class RecordMinMax
+{
+public:
+ explicit RecordMinMax() = default;
+
+ ~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);
+
+ void saveModel(const std::string &output_model_path);
+
+private:
+ std::unique_ptr<luci::Module> _module;
+ std::unique_ptr<luci_interpreter::Interpreter> _interpreter;
+ std::unique_ptr<MinMaxObserver> _observer;
+};
+
+} // namespace record_minmax
+
+#endif // __RECORD_MINMAX_H__
diff --git a/compiler/record-minmax/requires.cmake b/compiler/record-minmax/requires.cmake
new file mode 100644
index 000000000..f6804cef1
--- /dev/null
+++ b/compiler/record-minmax/requires.cmake
@@ -0,0 +1,4 @@
+require("luci")
+require("safemain")
+require("arser")
+require("vconone")
diff --git a/compiler/record-minmax/src/HDF5Importer.cpp b/compiler/record-minmax/src/HDF5Importer.cpp
new file mode 100644
index 000000000..a0e65eeb7
--- /dev/null
+++ b/compiler/record-minmax/src/HDF5Importer.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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 "HDF5Importer.h"
+
+#include <H5Cpp.h>
+
+#include <string>
+#include <cassert>
+#include <stdexcept>
+
+using Shape = luci_interpreter::Shape;
+using DataType = luci_interpreter::DataType;
+
+namespace
+{
+
+Shape toInternalShape(const H5::DataSpace &dataspace)
+{
+ int rank = dataspace.getSimpleExtentNdims();
+
+ std::vector<hsize_t> dims;
+ dims.resize(rank, 0);
+ dataspace.getSimpleExtentDims(dims.data());
+
+ Shape res(rank);
+ for (int axis = 0; axis < rank; ++axis)
+ {
+ res.dim(axis) = dims[axis];
+ }
+
+ return res;
+}
+
+DataType toInternalDtype(const H5::DataType &h5_type)
+{
+ if (h5_type == H5::PredType::IEEE_F32BE || h5_type == H5::PredType::IEEE_F32LE)
+ {
+ return DataType::FLOAT32;
+ }
+ if (h5_type == H5::PredType::STD_I32BE || h5_type == H5::PredType::STD_I32LE)
+ {
+ return DataType::S32;
+ }
+ if (h5_type == H5::PredType::STD_I64BE || h5_type == H5::PredType::STD_I64LE)
+ {
+ return DataType::S64;
+ }
+ // Only support three datatypes for now
+ return DataType::Unknown;
+}
+
+void readTensorData(H5::DataSet &tensor, uint8_t *buffer)
+{
+ tensor.read(buffer, H5::PredType::NATIVE_UINT8);
+}
+
+void readTensorData(H5::DataSet &tensor, float *buffer)
+{
+ tensor.read(buffer, H5::PredType::NATIVE_FLOAT);
+}
+
+void readTensorData(H5::DataSet &tensor, int32_t *buffer)
+{
+ tensor.read(buffer, H5::PredType::NATIVE_INT);
+}
+
+void readTensorData(H5::DataSet &tensor, int64_t *buffer)
+{
+ tensor.read(buffer, H5::PredType::NATIVE_LONG);
+}
+
+} // namespace
+
+namespace record_minmax
+{
+
+int32_t HDF5Importer::numInputs(int32_t record_idx)
+{
+ auto records = _value_grp.openGroup(std::to_string(record_idx));
+ return records.getNumObjs();
+}
+
+void HDF5Importer::readTensor(int32_t record_idx, int32_t input_idx, void *buffer)
+{
+ auto record = _value_grp.openGroup(std::to_string(record_idx));
+ auto tensor = record.openDataSet(std::to_string(input_idx));
+
+ readTensorData(tensor, static_cast<uint8_t *>(buffer));
+}
+
+void HDF5Importer::readTensor(int32_t record_idx, int32_t input_idx, DataType *dtype, Shape *shape,
+ void *buffer)
+{
+ auto record = _value_grp.openGroup(std::to_string(record_idx));
+ auto tensor = record.openDataSet(std::to_string(input_idx));
+
+ auto tensor_dtype = tensor.getDataType();
+ *dtype = toInternalDtype(tensor_dtype);
+
+ auto tensor_shape = tensor.getSpace();
+ *shape = toInternalShape(tensor_shape);
+
+ switch (*dtype)
+ {
+ case DataType::FLOAT32:
+ readTensorData(tensor, static_cast<float *>(buffer));
+ break;
+ case DataType::S32:
+ readTensorData(tensor, static_cast<int32_t *>(buffer));
+ break;
+ case DataType::S64:
+ readTensorData(tensor, static_cast<int64_t *>(buffer));
+ break;
+ default:
+ throw std::runtime_error{"Unsupported data type for input data (.h5)"};
+ }
+}
+
+} // namespace record_minmax
diff --git a/compiler/record-minmax/src/HDF5Importer.h b/compiler/record-minmax/src/HDF5Importer.h
new file mode 100644
index 000000000..9e98c7752
--- /dev/null
+++ b/compiler/record-minmax/src/HDF5Importer.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __RECORD_MINMAX_HDF5IMPORTER_H__
+#define __RECORD_MINMAX_HDF5IMPORTER_H__
+
+#include <luci_interpreter/core/Tensor.h>
+
+#include <H5Cpp.h>
+
+#include <stdexcept>
+
+using Shape = luci_interpreter::Shape;
+using DataType = luci_interpreter::DataType;
+
+namespace record_minmax
+{
+
+// HDF5Importer reads an input data saved in the hdf5 file in the given path
+// The hierarchy of the hdf5 file is as follows.
+// Group "/"
+// > Group "value"
+// > Group <record_idx>
+// > Dataset <input_idx>
+// record_idx : index of the record (dataset file can contain multiple records)
+// input_idx : index of the input (DNN model can have multiple inputs)
+// Ex: the j'th input of the i'th record can be accessed by "/value/i/j"
+class HDF5Importer
+{
+public:
+ explicit HDF5Importer(const std::string &path)
+ {
+ if (_file.isHdf5(path) == false)
+ throw std::runtime_error("Given data file is not HDF5");
+
+ _file = H5::H5File(path, H5F_ACC_RDONLY);
+ }
+
+public:
+ /**
+ * @brief importGroup has to be called before readTensor is called
+ * Otherwise, readTensor will throw an exception
+ */
+ void importGroup() { _value_grp = _file.openGroup("value"); }
+
+ /**
+ * @brief Read tensor data from file and store it into buffer
+ * @details A tensor in the file can be retrieved with (record_idx, input_idx)
+ * @param record_idx : index of the record
+ * @param input_idx : index of the input
+ * @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
+ */
+ void readTensor(int32_t record_idx, int32_t input_idx, DataType *dtype, Shape *shape,
+ void *buffer);
+
+ // Read a raw tensor (no type/shape is specified)
+ void readTensor(int32_t record_idx, int32_t input_idx, void *buffer);
+
+ bool isRawData() { return _value_grp.attrExists("rawData"); }
+
+ int32_t numRecords() { return _value_grp.getNumObjs(); }
+
+ int32_t numInputs(int32_t record_idx);
+
+private:
+ H5::H5File _file;
+ H5::Group _value_grp;
+};
+
+} // namespace record_minmax
+
+#endif // __RECORD_MINMAX_HDF5IMPORTER_H__
diff --git a/compiler/record-minmax/src/MinMaxObserver.cpp b/compiler/record-minmax/src/MinMaxObserver.cpp
new file mode 100644
index 000000000..c22cb4132
--- /dev/null
+++ b/compiler/record-minmax/src/MinMaxObserver.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MinMaxObserver.h"
+
+#include <luci/IR/CircleOpcode.h>
+
+using DataType = luci_interpreter::DataType;
+
+namespace record_minmax
+{
+
+// postTensorWrite is only called for a node producing a tensor
+void MinMaxObserver::postTensorWrite(const luci::CircleNode *node,
+ const luci_interpreter::Tensor *tensor)
+{
+ // CircleOutput does not produce a tensor
+ assert(node->opcode() != luci::CircleOpcode::CIRCLEOUTPUT);
+
+ // Operators with multiple outputs
+ assert(node->opcode() != luci::CircleOpcode::IF);
+ assert(node->opcode() != luci::CircleOpcode::SPLIT);
+ assert(node->opcode() != luci::CircleOpcode::SPLIT_V);
+ assert(node->opcode() != luci::CircleOpcode::TOPK_V2);
+ assert(node->opcode() != luci::CircleOpcode::UNPACK);
+ assert(node->opcode() != luci::CircleOpcode::WHILE);
+
+ if (node->opcode() == luci::CircleOpcode::CIRCLECONST)
+ {
+ // node is not activation. Do nothing.
+ return;
+ }
+
+ if (node->opcode() == luci::CircleOpcode::ARG_MAX)
+ {
+ // Output of arg_max is the index of the largest value across axes of a tensor
+ // this should not be quantized
+ return;
+ }
+
+ // Only support recording of float32 values
+ if (tensor->element_type() != DataType::FLOAT32)
+ throw std::runtime_error("Tensor's data type is not float");
+
+ const auto data = tensor->data<float>();
+ const auto num_elements = tensor->shape().num_elements();
+
+ std::vector<float> buf(data, data + num_elements);
+ auto minmax = std::minmax_element(buf.begin(), buf.end());
+ float min = *minmax.first;
+ float max = *minmax.second;
+
+ _minmax_data.recordMinMax(node, min, max);
+}
+
+} // namespace record_minmax
diff --git a/compiler/record-minmax/src/RecordMinMax.cpp b/compiler/record-minmax/src/RecordMinMax.cpp
new file mode 100644
index 000000000..cd5f29352
--- /dev/null
+++ b/compiler/record-minmax/src/RecordMinMax.cpp
@@ -0,0 +1,214 @@
+/*
+ * 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 "RecordMinMax.h"
+#include "RecordFunction.h"
+#include "MinMaxObserver.h"
+#include "HDF5Importer.h"
+
+#include <luci/Importer.h>
+#include <luci/CircleExporter.h>
+#include <luci/CircleFileExpContract.h>
+#include <luci/IR/CircleQuantParam.h>
+
+#include <algorithm>
+#include <cmath>
+#include <fstream>
+#include <numeric>
+#include <stdexcept>
+#include <iostream>
+
+using Shape = luci_interpreter::Shape;
+using DataType = luci_interpreter::DataType;
+
+namespace
+{
+
+/**
+ * @brief getTensorSize will return size in bytes
+ */
+template <typename NodeT> size_t getTensorSize(const NodeT *node)
+{
+ uint32_t tensor_size = loco::size(node->dtype());
+ for (uint32_t i = 0; i < node->rank(); ++i)
+ tensor_size *= node->dim(i).value();
+ return tensor_size;
+}
+
+/**
+ * @brief verifyTypeShape checks the type and the shape of CircleInput
+ * This throws an exception if type or shape does not match
+ */
+void verifyTypeShape(const luci::CircleInput *input_node, const DataType &dtype, const Shape &shape)
+{
+ // Type check
+ if (dtype != input_node->dtype())
+ throw std::runtime_error("Wrong input type.");
+
+ if (shape.num_dims() != input_node->rank())
+ throw std::runtime_error("Input rank mismatch.");
+
+ for (uint32_t i = 0; i < shape.num_dims(); i++)
+ {
+ if (shape.dim(i) != input_node->dim(i).value())
+ throw std::runtime_error("Input shape mismatch.");
+ }
+}
+
+} // namespace
+
+namespace record_minmax
+{
+
+void RecordMinMax::initialize(const std::string &input_model_path)
+{
+ // Load model from the file
+ std::ifstream fs(input_model_path, std::ifstream::binary);
+ if (fs.fail())
+ {
+ throw std::runtime_error("Cannot open model file \"" + input_model_path + "\".\n");
+ }
+ std::vector<char> model_data((std::istreambuf_iterator<char>(fs)),
+ std::istreambuf_iterator<char>());
+
+ // Verify flatbuffers
+ flatbuffers::Verifier verifier{reinterpret_cast<const uint8_t *>(model_data.data()),
+ model_data.size()};
+ if (!circle::VerifyModelBuffer(verifier))
+ {
+ throw std::runtime_error("ERROR: Failed to verify circle '" + input_model_path + "'");
+ }
+
+ _module = luci::Importer().importModule(circle::GetModel(model_data.data()));
+
+ if (_module == nullptr)
+ {
+ throw std::runtime_error("ERROR: Failed to load '" + input_model_path + "'");
+ }
+
+ // Initialize interpreter
+ _interpreter = std::make_unique<luci_interpreter::Interpreter>(_module.get());
+
+ _observer = std::make_unique<MinMaxObserver>();
+
+ _interpreter->attachObserver(_observer.get());
+}
+
+void RecordMinMax::profileData(const std::string &mode, const std::string &input_data_path,
+ float min_percentile, float max_percentile)
+{
+ try
+ {
+ HDF5Importer importer(input_data_path);
+ importer.importGroup();
+
+ bool is_raw_data = importer.isRawData();
+
+ const auto num_records = importer.numRecords();
+ if (num_records == 0)
+ throw std::runtime_error("The input data file does not contain any record.");
+
+ const auto input_nodes = loco::input_nodes(_module->graph());
+ const auto num_inputs = input_nodes.size();
+
+ for (int32_t record_idx = 0; record_idx < num_records; record_idx++)
+ {
+ if (num_inputs != importer.numInputs(record_idx))
+ throw std::runtime_error("Wrong number of inputs.");
+
+ if (record_idx % 100 == 0)
+ std::cout << "Recording " << record_idx << "'th data" << std::endl;
+
+ for (int32_t input_idx = 0; input_idx < num_inputs; input_idx++)
+ {
+ const auto *input_node = loco::must_cast<const luci::CircleInput *>(input_nodes[input_idx]);
+ assert(input_node->index() == input_idx);
+ std::vector<char> input_data(getTensorSize(input_node));
+
+ if (!is_raw_data)
+ {
+ DataType dtype;
+ Shape shape(input_node->rank());
+ importer.readTensor(record_idx, input_idx, &dtype, &shape, input_data.data());
+
+ // Check the type and the shape of the input data is valid
+ verifyTypeShape(input_node, dtype, shape);
+ }
+ else
+ {
+ // Skip type/shape check for raw data
+ importer.readTensor(record_idx, input_idx, input_data.data());
+ }
+
+ // TODO: Input data is copied twice (file -> buffer (input_data) -> interpreter inputs)
+ // We can redcue the copy by directly writing data from file to interpreter inputs
+ _interpreter->writeInputTensor(input_node, input_data.data(), input_data.size());
+ }
+
+ _interpreter->interpret();
+ }
+
+ std::cout << "Recording finished. Number of recorded data: " << num_records << std::endl;
+ }
+ catch (const H5::Exception &e)
+ {
+ H5::Exception::printErrorStack();
+ throw std::runtime_error("HDF5 error occurred.");
+ }
+
+ 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 = getNthPercentile(minmax.min_vector, min_percentile);
+ max = getNthPercentile(minmax.max_vector, max_percentile);
+ }
+ else if (mode == "moving_average")
+ {
+ min = getMovingAverage(minmax.min_vector, 0.9, 16, true);
+ max = getMovingAverage(minmax.max_vector, 0.9, 16, false);
+ }
+ assert(mode == "percentile" || mode == "moving_average");
+ auto quantparam = std::make_unique<luci::CircleQuantParam>();
+ quantparam->min.push_back(min);
+ quantparam->max.push_back(max);
+
+ assert(node->quantparam() == nullptr);
+
+ auto mutable_node = const_cast<luci::CircleNode *>(node);
+ mutable_node->quantparam(std::move(quantparam));
+ }
+}
+
+void RecordMinMax::saveModel(const std::string &output_model_path)
+{
+ // Export to output Circle file
+ luci::CircleExporter exporter;
+
+ luci::CircleFileExpContract contract(_module.get(), output_model_path);
+
+ if (!exporter.invoke(&contract))
+ {
+ throw std::runtime_error("ERROR: Failed to export '" + output_model_path + "'");
+ }
+}
+
+} // namespace record_minmax
diff --git a/compiler/record-minmax/tests/RecordFunction.test.cpp b/compiler/record-minmax/tests/RecordFunction.test.cpp
new file mode 100644
index 000000000..e2f135a4e
--- /dev/null
+++ b/compiler/record-minmax/tests/RecordFunction.test.cpp
@@ -0,0 +1,118 @@
+/*
+ * 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 "RecordFunction.h"
+
+#include <vector>
+#include <cmath>
+
+#include <gtest/gtest.h>
+
+namespace record_minmax
+{
+
+#define EXPECT_FLOAT_NEAR(exp, val) EXPECT_NEAR(exp, val, 1e-5 + 1e-5 * std::abs(exp))
+
+TEST(GetNthPercentileTest, Edge)
+{
+ std::vector<float> input{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+ EXPECT_FLOAT_NEAR(0, getNthPercentile(input, 0));
+ EXPECT_FLOAT_NEAR(9, getNthPercentile(input, 100));
+
+ SUCCEED();
+}
+
+TEST(GetNthPercentileTest, Simple)
+{
+ std::vector<float> input{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+ for (float i = 1; i <= 99; i++)
+ {
+ EXPECT_FLOAT_NEAR(0.09 * i, getNthPercentile(input, i));
+ }
+
+ for (float i = 0.5; i <= 99.5; i++)
+ {
+ EXPECT_FLOAT_NEAR(0.09 * std::floor(i) + 0.045, getNthPercentile(input, i));
+ }
+
+ SUCCEED();
+}
+
+TEST(GetNthPercentileTest, Float)
+{
+ std::vector<float> input{8.48424583, 89.39998456, 65.83323245, 87.85243858, 68.85414866,
+ 98.40591775, 16.74266565, 25.09415131, 74.54084952, 29.70536481,
+ 49.26803928, 79.49602425, 53.69395631, 73.73140271, 99.81245733,
+ 46.76997646, 78.37688474, 10.43076744, 30.39480496, 14.30875609,
+ 86.72073486, 17.97364969, 14.66724564, 0.47818459, 17.77138025,
+ 85.68981239, 22.18322696, 78.81541331, 93.04085581, 40.2147895};
+
+ EXPECT_FLOAT_NEAR(2.799942346802177, getNthPercentile(input, 1));
+ EXPECT_FLOAT_NEAR(7.768503955476342, getNthPercentile(input, 3.14));
+ EXPECT_FLOAT_NEAR(99.40456084968194, getNthPercentile(input, 99));
+
+ SUCCEED();
+}
+
+TEST(GetNthPercentileTest, FloatWithNegative)
+{
+ std::vector<float> input{-41.51575417, 39.39998456, 15.83323245, 37.85243858, 18.85414866,
+ 48.40591775, -33.25733435, -24.90584869, 24.54084952, -20.29463519,
+ -0.73196072, 29.49602425, 3.69395631, 23.73140271, 49.81245733,
+ -3.23002354, 28.37688474, -39.56923256, -19.60519504, -35.69124391,
+ 36.72073486, -32.02635031, -35.33275436, -49.52181541, -32.22861975,
+ 35.68981239, -27.81677304, 28.81541331, 43.04085581, -9.7852105};
+
+ EXPECT_FLOAT_NEAR(-47.20005765319782, getNthPercentile(input, 1));
+ EXPECT_FLOAT_NEAR(-42.23149604452366, getNthPercentile(input, 3.14));
+ EXPECT_FLOAT_NEAR(49.40456084968194, getNthPercentile(input, 99));
+
+ SUCCEED();
+}
+
+TEST(GetNthPercentileTest, SigleElement)
+{
+ std::vector<float> input{33};
+
+ EXPECT_FLOAT_NEAR(33, getNthPercentile(input, 0));
+ EXPECT_FLOAT_NEAR(33, getNthPercentile(input, 50));
+ EXPECT_FLOAT_NEAR(33, getNthPercentile(input, 100));
+
+ SUCCEED();
+}
+
+TEST(GetNthPercentileTest, OutOfBoundary_NEG)
+{
+ std::vector<float> input{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+ EXPECT_THROW(getNthPercentile(input, -1), std::runtime_error);
+ EXPECT_THROW(getNthPercentile(input, 101), std::runtime_error);
+
+ SUCCEED();
+}
+
+TEST(GetNthPercentileTest, EmptyVector_NEG)
+{
+ std::vector<float> input;
+
+ EXPECT_THROW(getNthPercentile(input, 10), std::runtime_error);
+
+ SUCCEED();
+}
+
+} // namespace record_minmax
diff --git a/compiler/safemain/README.md b/compiler/safemain/README.md
new file mode 100644
index 000000000..18447048e
--- /dev/null
+++ b/compiler/safemain/README.md
@@ -0,0 +1 @@
+# safemain
diff --git a/compiler/souschef/CMakeLists.txt b/compiler/souschef/CMakeLists.txt
new file mode 100644
index 000000000..ca7eddc6f
--- /dev/null
+++ b/compiler/souschef/CMakeLists.txt
@@ -0,0 +1,13 @@
+nnas_find_package(Protobuf QUIET)
+
+if(NOT Protobuf_FOUND)
+ message(STATUS "Build souschef: FAILED (missing Protobuf")
+ return()
+endif(NOT Protobuf_FOUND)
+
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+add_library(souschef STATIC ${SOURCES})
+set_target_properties(souschef PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(souschef PUBLIC include)
+target_link_libraries(souschef PUBLIC libprotobuf)
diff --git a/compiler/souschef/README.md b/compiler/souschef/README.md
new file mode 100644
index 000000000..a475ce418
--- /dev/null
+++ b/compiler/souschef/README.md
@@ -0,0 +1,3 @@
+# souschef
+
+_souschef_ is common library for _tflchef_ and _circlechef_
diff --git a/compiler/souschef/include/souschef/Arguments.h b/compiler/souschef/include/souschef/Arguments.h
new file mode 100644
index 000000000..4556ce797
--- /dev/null
+++ b/compiler/souschef/include/souschef/Arguments.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SOUSCHEF_ARGUMENTS_H__
+#define __SOUSCHEF_ARGUMENTS_H__
+
+#include <cstdint>
+#include <string>
+
+namespace souschef
+{
+
+/**
+ * @brief Read-only string sequence view
+ */
+struct Arguments
+{
+ virtual ~Arguments() = default;
+
+ virtual uint32_t count(void) const = 0;
+ virtual const std::string &value(uint32_t n) const = 0;
+};
+
+} // namespace souschef
+
+#endif // __SOUSCHEF_ARGUMENTS_H__
diff --git a/compiler/souschef/include/souschef/Data/Constant.h b/compiler/souschef/include/souschef/Data/Constant.h
new file mode 100644
index 000000000..6bb7bbab1
--- /dev/null
+++ b/compiler/souschef/include/souschef/Data/Constant.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SOUSCHEF_DATA_CONSTANT_H__
+#define __SOUSCHEF_DATA_CONSTANT_H__
+
+#include "souschef/DataChef.h"
+#include "souschef/LexicalCast.h"
+
+namespace souschef
+{
+
+template <typename T> class ConstantDataChef final : public DataChef
+{
+public:
+ ConstantDataChef(const T &value) : _value{value}
+ {
+ // DO NOTHING
+ }
+
+public:
+ std::vector<uint8_t> generate(int32_t count) const override
+ {
+ std::vector<uint8_t> res;
+
+ for (uint32_t n = 0; n < count; ++n)
+ {
+ const uint8_t *arr = reinterpret_cast<const uint8_t *>(&_value);
+
+ for (uint32_t b = 0; b < sizeof(T); ++b)
+ {
+ res.emplace_back(arr[b]);
+ }
+ }
+
+ return res;
+ }
+
+private:
+ T _value;
+};
+
+template <typename T> struct ConstantDataChefFactory : public DataChefFactory
+{
+ std::unique_ptr<DataChef> create(const Arguments &args) const
+ {
+ auto const value = to_number<T>(args.value(0));
+ return std::unique_ptr<DataChef>{new ConstantDataChef<T>{value}};
+ }
+};
+
+} // namespace souschef
+
+#endif // __SOUSCHEF_DATA_CONSTANT_H__
diff --git a/compiler/souschef/include/souschef/Data/Explicit.h b/compiler/souschef/include/souschef/Data/Explicit.h
new file mode 100644
index 000000000..6e5ee819e
--- /dev/null
+++ b/compiler/souschef/include/souschef/Data/Explicit.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SOUSCHEF_DATA_EXPLICIT_H__
+#define __SOUSCHEF_DATA_EXPLICIT_H__
+
+#include "souschef/DataChef.h"
+#include "souschef/LexicalCast.h"
+
+#include <vector>
+
+namespace souschef
+{
+
+template <typename T> class ExplicitDataChef final : public DataChef
+{
+public:
+ ExplicitDataChef()
+ {
+ // DO NOTHING
+ }
+
+public:
+ std::vector<uint8_t> generate(int32_t count) const override
+ {
+ std::vector<uint8_t> res;
+
+ for (uint32_t n = 0; n < count; ++n)
+ {
+ T const value = (n < _values.size()) ? _values.at(n) : T{};
+ const uint8_t *arr = reinterpret_cast<const uint8_t *>(&value);
+
+ for (uint32_t b = 0; b < sizeof(T); ++b)
+ {
+ res.emplace_back(arr[b]);
+ }
+ }
+
+ return res;
+ }
+
+public:
+ void insert(const T &value) { _values.emplace_back(value); }
+
+private:
+ std::vector<T> _values;
+};
+
+template <typename T> struct ExplicitDataChefFactory : public DataChefFactory
+{
+ std::unique_ptr<DataChef> create(const Arguments &args) const
+ {
+ std::unique_ptr<ExplicitDataChef<T>> res{new ExplicitDataChef<T>};
+
+ for (uint32_t n = 0; n < args.count(); ++n)
+ {
+ auto const value = to_number<T>(args.value(n));
+ res->insert(value);
+ }
+
+ return std::move(res);
+ }
+};
+
+} // namespace souschef
+
+#endif // __SOUSCHEF_DATA_EXPLICIT_H__
diff --git a/compiler/souschef/include/souschef/Data/Gaussian.h b/compiler/souschef/include/souschef/Data/Gaussian.h
new file mode 100644
index 000000000..75570e0b8
--- /dev/null
+++ b/compiler/souschef/include/souschef/Data/Gaussian.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SOUSCHEF_DATA_GAUSSIAN_H__
+#define __SOUSCHEF_DATA_GAUSSIAN_H__
+
+#include "souschef/DataChef.h"
+
+namespace souschef
+{
+
+/**
+ * @brief Generate a sequence of random values according to the gaussian(=normal) distribution
+ */
+class GaussianFloat32DataChef final : public DataChef
+{
+public:
+ GaussianFloat32DataChef(float mean, float stddev) : _mean{mean}, _stddev{stddev}
+ {
+ // DO NOTHING
+ }
+
+public:
+ std::vector<uint8_t> generate(int32_t count) const override;
+
+private:
+ float _mean;
+ float _stddev;
+};
+
+class GaussianInt32DataChef final : public DataChef
+{
+public:
+ GaussianInt32DataChef(float mean, float stddev) : _mean{mean}, _stddev{stddev}
+ {
+ // DO NOTHING
+ }
+
+public:
+ std::vector<uint8_t> generate(int32_t count) const override;
+
+private:
+ float _mean;
+ float _stddev;
+};
+
+class GaussianUint8DataChef final : public DataChef
+{
+public:
+ GaussianUint8DataChef(float mean, float stddev) : _mean{mean}, _stddev{stddev}
+ {
+ // DO NOTHING
+ }
+
+public:
+ std::vector<uint8_t> generate(int32_t count) const override;
+
+private:
+ float _mean;
+ float _stddev;
+};
+
+struct GaussianFloat32DataChefFactory : public DataChefFactory
+{
+ std::unique_ptr<DataChef> create(const Arguments &args) const;
+};
+
+struct GaussianInt32DataChefFactory : public DataChefFactory
+{
+ std::unique_ptr<DataChef> create(const Arguments &args) const;
+};
+
+struct GaussianUint8DataChefFactory : public DataChefFactory
+{
+ std::unique_ptr<DataChef> create(const Arguments &args) const;
+};
+
+} // namespace souschef
+
+#endif // __SOUSCHEF_DATA_GAUSSIAN_H__
diff --git a/compiler/souschef/include/souschef/DataChef.def b/compiler/souschef/include/souschef/DataChef.def
new file mode 100644
index 000000000..28901db18
--- /dev/null
+++ b/compiler/souschef/include/souschef/DataChef.def
@@ -0,0 +1,19 @@
+#ifndef DATA_CHEF
+#error "Define DATA_CHEF first"
+#endif // DATA_CHEF
+
+// DATA_CHEF(TYPE, NAME, FACTORY_CLASS)
+// "TYPE" SHOULD BE an enum tag of tflchef::TensorType
+DATA_CHEF(FLOAT32, constant, ConstantDataChefFactory<float>)
+DATA_CHEF(BOOL, constant, ConstantDataChefFactory<bool>)
+DATA_CHEF(UINT8, constant, ConstantDataChefFactory<uint8_t>)
+DATA_CHEF(INT32, constant, ConstantDataChefFactory<int32_t>)
+DATA_CHEF(INT64, constant, ConstantDataChefFactory<int64_t>)
+DATA_CHEF(INT64, explicit, ExplicitDataChefFactory<int64_t>)
+DATA_CHEF(INT32, explicit, ExplicitDataChefFactory<int32_t>)
+DATA_CHEF(UINT8, explicit, ExplicitDataChefFactory<uint8_t>)
+DATA_CHEF(BOOL, explicit, ExplicitDataChefFactory<bool>)
+DATA_CHEF(FLOAT32, explicit, ExplicitDataChefFactory<float>)
+DATA_CHEF(FLOAT32, gaussian, GaussianFloat32DataChefFactory)
+DATA_CHEF(INT32, gaussian, GaussianInt32DataChefFactory)
+DATA_CHEF(UINT8, gaussian, GaussianUint8DataChefFactory)
diff --git a/compiler/souschef/include/souschef/DataChef.h b/compiler/souschef/include/souschef/DataChef.h
new file mode 100644
index 000000000..4a65dfc08
--- /dev/null
+++ b/compiler/souschef/include/souschef/DataChef.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SOUSCHEF_DATA_CHEF_H__
+#define __SOUSCHEF_DATA_CHEF_H__
+
+#include "Arguments.h"
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+namespace souschef
+{
+
+using Data = std::vector<uint8_t>;
+
+/**
+ * @brief Data Generator
+ */
+struct DataChef
+{
+ virtual ~DataChef() = default;
+
+ // TODO Allow users to query the type of elements that this DataChef generates
+
+ /**
+ * @brief Generate a sequence of 'count' elements as a byte sequence
+ *
+ * Let D be the return value of generate(N).
+ * Then, D.size() == N * sizeof(T) where T is the element type.
+ */
+ virtual Data generate(int32_t count) const = 0;
+};
+
+/**
+ * @brief Data Generator Factory
+ */
+struct DataChefFactory
+{
+ virtual ~DataChefFactory() = default;
+
+ virtual std::unique_ptr<DataChef> create(const Arguments &args) const = 0;
+};
+
+} // namespace souschef
+
+#endif // __SOUSCHEF_DATA_CHEF_H__
diff --git a/compiler/souschef/include/souschef/DataChefs.h b/compiler/souschef/include/souschef/DataChefs.h
new file mode 100644
index 000000000..7a86a2c2e
--- /dev/null
+++ b/compiler/souschef/include/souschef/DataChefs.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SOUSCHEF_DATA_CHEFS_H__
+#define __SOUSCHEF_DATA_CHEFS_H__
+
+#include "Data/Constant.h"
+#include "Data/Explicit.h"
+#include "Data/Gaussian.h"
+
+#endif // __SOUSCHEF_DATA_CHEFS_H__
diff --git a/compiler/souschef/include/souschef/Dataset.h b/compiler/souschef/include/souschef/Dataset.h
new file mode 100644
index 000000000..ef67a7316
--- /dev/null
+++ b/compiler/souschef/include/souschef/Dataset.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SOUSCHEF_DATASET_H__
+#define __SOUSCHEF_DATASET_H__
+
+#include <vector>
+
+#include <google/protobuf/repeated_field.h>
+
+namespace souschef
+{
+
+template <typename T> class Dataset
+{
+public:
+ Dataset(const std::vector<T> &vec) : _vec{vec}
+ {
+ // DO NOTHING
+ }
+
+public:
+ Dataset(std::vector<T> &&vec) : _vec{std::move(vec)}
+ {
+ // DO NOTHING
+ }
+
+public:
+ template <typename Func> auto map(Func f) const -> Dataset<decltype(f(std::declval<T>()))>
+ {
+ using U = decltype(f(std::declval<T>()));
+ std::vector<U> res;
+
+ for (const auto &elem : _vec)
+ {
+ res.emplace_back(f(elem));
+ }
+
+ return Dataset<U>(std::move(res));
+ }
+
+public:
+ const std::vector<T> &vectorize(void) const { return _vec; }
+
+private:
+ std::vector<T> _vec;
+};
+
+template <typename T> std::vector<T> as_vector(const ::google::protobuf::RepeatedPtrField<T> &field)
+{
+ std::vector<T> res;
+ for (const auto &elem : field)
+ {
+ res.emplace_back(elem);
+ }
+ return res;
+}
+
+template <typename T> Dataset<T> as_dataset(const ::google::protobuf::RepeatedPtrField<T> &field)
+{
+ return Dataset<T>(as_vector<T>(field));
+}
+
+} // namespace souschef
+
+#endif // __SOUSCHEF_DATASET_H__
diff --git a/compiler/souschef/include/souschef/Dims.h b/compiler/souschef/include/souschef/Dims.h
new file mode 100644
index 000000000..fabbf3f95
--- /dev/null
+++ b/compiler/souschef/include/souschef/Dims.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SOUSCHEF_DIMS_H__
+#define __SOUSCHEF_DIMS_H__
+
+#include <cstdint>
+#include <functional>
+#include <numeric>
+#include <vector>
+
+namespace souschef
+{
+
+template <typename T> using Dims = std::vector<T>;
+
+template <typename SHAPETYPE> Dims<int32_t> as_dims(const SHAPETYPE &shape)
+{
+ std::vector<int32_t> res;
+
+ for (auto &dim : shape.dim())
+ {
+ res.emplace_back(static_cast<int32_t>(dim));
+ }
+
+ return res;
+}
+
+int32_t element_count(const Dims<int32_t> &dims)
+{
+ return std::accumulate(dims.begin(), dims.end(), 1, std::multiplies<int32_t>());
+}
+
+} // namespace souschef
+
+#endif // __SOUSCHEF_DIMS_H__
diff --git a/compiler/souschef/include/souschef/LexicalCast.h b/compiler/souschef/include/souschef/LexicalCast.h
new file mode 100644
index 000000000..d83cb2ab4
--- /dev/null
+++ b/compiler/souschef/include/souschef/LexicalCast.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @brief This file provides string <-> number cast helpers
+ */
+#ifndef __SOUSCHEF_LEXICAL_CAST_H__
+#define __SOUSCHEF_LEXICAL_CAST_H__
+
+#include <string>
+
+namespace souschef
+{
+
+/**
+ * @brief Return a numeric value that corresponds to a given string
+ *
+ * @note This function will throw an exception on casting failure
+ */
+template <typename Number> Number to_number(const std::string &s);
+
+} // namespace souschef
+
+#endif // __SOUSCHEF_LEXICAL_CAST_H__
diff --git a/compiler/souschef/include/souschef/RangedArguments.h b/compiler/souschef/include/souschef/RangedArguments.h
new file mode 100644
index 000000000..dd50f593e
--- /dev/null
+++ b/compiler/souschef/include/souschef/RangedArguments.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SOUSCHEF_RANGED_ARGUMENTS_H__
+#define __SOUSCHEF_RANGED_ARGUMENTS_H__
+
+#include "Arguments.h"
+
+#include <string>
+
+namespace souschef
+{
+
+template <typename InputIt> class RangedArguments : public Arguments
+{
+public:
+ RangedArguments(InputIt beg, InputIt end) : _beg{beg}, _end{end}
+ {
+ // DO NOTHING
+ }
+
+public:
+ uint32_t count(void) const override { return _end - _beg; }
+
+public:
+ const std::string &value(uint32_t n) const override { return *(_beg + n); }
+
+private:
+ InputIt _beg;
+ InputIt _end;
+};
+
+template <typename InputIt> RangedArguments<InputIt> ranged_arguments(InputIt beg, InputIt end)
+{
+ return RangedArguments<InputIt>{beg, end};
+}
+
+} // namespace souschef
+
+#endif // __SOUSCHEF_RANGED_ARGUMENTS_H__
diff --git a/compiler/souschef/include/souschef/Registry.h b/compiler/souschef/include/souschef/Registry.h
new file mode 100644
index 000000000..9457b6a0f
--- /dev/null
+++ b/compiler/souschef/include/souschef/Registry.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SOUSCHEF_REGISTRY_H__
+#define __SOUSCHEF_REGISTRY_H__
+
+#include <map>
+#include <memory>
+#include <string>
+
+namespace souschef
+{
+
+template <typename T> class Registry
+{
+public:
+ void add(const std::string &name, std::unique_ptr<T> &&entry)
+ {
+ _content[name] = std::move(entry);
+ }
+
+ const T &lookup(const std::string &name) const { return *(_content.at(name)); }
+
+private:
+ std::map<std::string, std::unique_ptr<T>> _content;
+};
+
+} // namespace souschef
+
+#endif // __SOUSCHEF_REGISTRY_H__
diff --git a/compiler/souschef/include/souschef/TensorFiller.h b/compiler/souschef/include/souschef/TensorFiller.h
new file mode 100644
index 000000000..1d87f1372
--- /dev/null
+++ b/compiler/souschef/include/souschef/TensorFiller.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SOUSCHEF_TENSOR_FILLER_H__
+#define __SOUSCHEF_TENSOR_FILLER_H__
+
+#include <map>
+#include <vector>
+
+namespace souschef
+{
+
+class TensorFiller
+{
+public:
+ virtual ~TensorFiller() = default;
+
+ /**
+ * @brief This will record the tensor by index, if it needs filler option,
+ * such as kernel, bias.
+ */
+ void set_tensor_filler(uint32_t tensor_index) { _tensor_filler[tensor_index] = true; }
+
+ /**
+ * @brief This will store int32 filler values such as reshape information for the tensor
+ */
+ void set_tensor_filler(uint32_t tensor_index, std::vector<int32_t> &expvalues)
+ {
+ _tensor_filler_vint32[tensor_index] = expvalues;
+ }
+
+ void set_tensor_filler(uint32_t tensor_index, std::vector<float> &expvalues)
+ {
+ _tensor_filler_vfloat[tensor_index] = expvalues;
+ }
+
+ /**
+ * @brief This will return true if the tensor by index, needs a filler option.
+ */
+ bool get_tensor_filler(uint32_t tensor_index)
+ {
+ auto it = _tensor_filler.find(tensor_index);
+ if (it != _tensor_filler.end())
+ {
+ return it->second;
+ }
+ return false;
+ }
+
+ /**
+ * @brief This will return true if the tensor by index, needs a int array filler option.
+ */
+ bool get_tensor_filler(uint32_t tensor_index, std::vector<int32_t> &expvalues)
+ {
+ auto it = _tensor_filler_vint32.find(tensor_index);
+ if (it != _tensor_filler_vint32.end())
+ {
+ expvalues = it->second;
+ return true;
+ }
+ return false;
+ }
+
+ bool get_tensor_filler(uint32_t tensor_index, std::vector<float> &expvalues)
+ {
+ auto it = _tensor_filler_vfloat.find(tensor_index);
+ if (it != _tensor_filler_vfloat.end())
+ {
+ expvalues = it->second;
+ return true;
+ }
+ return false;
+ }
+
+private:
+ std::map<uint32_t, bool> _tensor_filler{};
+ std::map<uint32_t, std::vector<int32_t>> _tensor_filler_vint32{};
+ std::map<uint32_t, std::vector<float>> _tensor_filler_vfloat{};
+};
+
+} // namespace souschef
+
+#endif // __SOUSCHEF_TENSOR_FILLER_H__
diff --git a/compiler/souschef/src/Dims.cpp b/compiler/souschef/src/Dims.cpp
new file mode 100644
index 000000000..fba4813fc
--- /dev/null
+++ b/compiler/souschef/src/Dims.cpp
@@ -0,0 +1,19 @@
+/*
+ * 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 "souschef/Dims.h"
+
+// NOTE Do NOT delete this file; this file checks the completeness of 'Dims.h'
diff --git a/compiler/souschef/src/Gaussian.cpp b/compiler/souschef/src/Gaussian.cpp
new file mode 100644
index 000000000..4a5083d8e
--- /dev/null
+++ b/compiler/souschef/src/Gaussian.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "souschef/Data/Gaussian.h"
+#include "souschef/LexicalCast.h"
+
+#include <random>
+#include <chrono>
+
+#include <cassert>
+#include <stdexcept>
+
+namespace souschef
+{
+
+std::vector<uint8_t> GaussianFloat32DataChef::generate(int32_t count) const
+{
+ // TODO Support seed value override
+ auto seed = std::chrono::system_clock::now().time_since_epoch().count();
+
+ std::minstd_rand rand{static_cast<std::minstd_rand::result_type>(seed)};
+ std::normal_distribution<float> dist{_mean, _stddev};
+
+ std::vector<uint8_t> res;
+
+ for (uint32_t n = 0; n < count; ++n)
+ {
+ auto const value = dist(rand);
+ auto const arr = reinterpret_cast<const uint8_t *>(&value);
+
+ for (uint32_t b = 0; b < sizeof(float); ++b)
+ {
+ res.emplace_back(arr[b]);
+ }
+ }
+
+ return res;
+}
+
+std::vector<uint8_t> GaussianInt32DataChef::generate(int32_t count) const
+{
+ // TODO Support seed value override
+ auto seed = std::chrono::system_clock::now().time_since_epoch().count();
+
+ std::minstd_rand rand{static_cast<std::minstd_rand::result_type>(seed)};
+ std::normal_distribution<float> dist{_mean, _stddev};
+
+ std::vector<uint8_t> res;
+
+ for (uint32_t n = 0; n < count; ++n)
+ {
+ auto const value = static_cast<int32_t>(dist(rand));
+ auto const arr = reinterpret_cast<const uint8_t *>(&value);
+
+ for (uint32_t b = 0; b < sizeof(int32_t); ++b)
+ {
+ res.emplace_back(arr[b]);
+ }
+ }
+
+ return res;
+}
+
+std::vector<uint8_t> GaussianUint8DataChef::generate(int32_t count) const
+{
+ // TODO Support seed value override
+ auto seed = std::chrono::system_clock::now().time_since_epoch().count();
+
+ std::minstd_rand rand{static_cast<std::minstd_rand::result_type>(seed)};
+ std::normal_distribution<float> dist{_mean, _stddev};
+
+ std::vector<uint8_t> res;
+
+ for (uint32_t n = 0; n < count; ++n)
+ {
+ auto const value = static_cast<uint8_t>(dist(rand)); // uint8_t for data type
+ auto const arr = reinterpret_cast<const uint8_t *>(&value); // uint8_t for byte streaming
+
+ for (uint32_t b = 0; b < sizeof(uint8_t); ++b)
+ {
+ res.emplace_back(arr[b]);
+ }
+ }
+
+ return res;
+}
+
+std::unique_ptr<DataChef> GaussianFloat32DataChefFactory::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<float>(args.value(0));
+ auto const stddev = to_number<float>(args.value(1));
+
+ return std::unique_ptr<DataChef>{new GaussianFloat32DataChef{mean, stddev}};
+}
+
+std::unique_ptr<DataChef> GaussianInt32DataChefFactory::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<float>(args.value(0));
+ auto const stddev = to_number<float>(args.value(1));
+
+ return std::unique_ptr<DataChef>{new GaussianInt32DataChef{mean, stddev}};
+}
+
+std::unique_ptr<DataChef> GaussianUint8DataChefFactory::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<float>(args.value(0));
+ auto const stddev = to_number<float>(args.value(1));
+
+ return std::unique_ptr<DataChef>{new GaussianUint8DataChef{mean, stddev}};
+}
+
+} // namespace souschef
diff --git a/compiler/souschef/src/LexicalCast.cpp b/compiler/souschef/src/LexicalCast.cpp
new file mode 100644
index 000000000..8e3d4cbbb
--- /dev/null
+++ b/compiler/souschef/src/LexicalCast.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "souschef/LexicalCast.h"
+
+#include <cassert>
+#include <limits>
+
+namespace souschef
+{
+
+template <> float to_number(const std::string &s) { return std::stof(s); }
+template <> int to_number(const std::string &s) { return std::stoi(s); }
+template <> int64_t to_number(const std::string &s) { return std::stoll(s); }
+template <> uint8_t to_number(const std::string &s)
+{
+ int temp = std::stoi(s);
+ assert(temp >= 0);
+ assert(temp <= std::numeric_limits<uint8_t>::max());
+ return static_cast<uint8_t>(temp);
+}
+template <> bool to_number(const std::string &s)
+{
+ if (std::stoi(s) || s == "T" || s == "t" || s == "TRUE" || s == "true")
+ return true;
+ return false;
+}
+
+} // namespace souschef
diff --git a/compiler/stdex/CMakeLists.txt b/compiler/stdex/CMakeLists.txt
index 9e9a7876f..91f07e69f 100644
--- a/compiler/stdex/CMakeLists.txt
+++ b/compiler/stdex/CMakeLists.txt
@@ -3,11 +3,12 @@ file(GLOB_RECURSE TESTS "src/*.test.cpp")
add_library(stdex INTERFACE)
target_include_directories(stdex INTERFACE include)
-nncc_find_package(GTest QUIET)
-
-if(NOT GTest_FOUND)
+if(NOT ENABLE_TEST)
return()
-endif(NOT GTest_FOUND)
+endif(NOT ENABLE_TEST)
+
+# Google Test is mandatory for test
+nnas_find_package(GTest REQUIRED)
add_executable(stdex_test ${TESTS})
target_link_libraries(stdex_test stdex)
diff --git a/compiler/stdex/include/stdex/Memory.h b/compiler/stdex/include/stdex/Memory.h
index b7c0a7c12..86751f073 100644
--- a/compiler/stdex/include/stdex/Memory.h
+++ b/compiler/stdex/include/stdex/Memory.h
@@ -22,11 +22,7 @@
namespace stdex
{
-template <typename T, typename... Args> std::unique_ptr<T> make_unique(Args &&... args)
-{
- // NOTE std::make_unique is missing in C++11 standard
- return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-}
+using std::make_unique;
} // namespace stdex
diff --git a/compiler/tf2circle-conversion-test/.gitignore b/compiler/tf2circle-conversion-test/.gitignore
new file mode 100644
index 000000000..8dbfa9012
--- /dev/null
+++ b/compiler/tf2circle-conversion-test/.gitignore
@@ -0,0 +1 @@
+/test.local.lst
diff --git a/compiler/tf2circle-conversion-test/CMakeLists.txt b/compiler/tf2circle-conversion-test/CMakeLists.txt
new file mode 100644
index 000000000..27f2463f3
--- /dev/null
+++ b/compiler/tf2circle-conversion-test/CMakeLists.txt
@@ -0,0 +1,138 @@
+nnas_include(TargetRequire)
+
+unset(REQUIRED_TARGETS)
+list(APPEND REQUIRED_TARGETS tf2circle)
+list(APPEND REQUIRED_TARGETS tfkit)
+TargetRequire_Return(${REQUIRED_TARGETS})
+
+message(STATUS "tf2circle-conversion-test: run tests")
+
+nncc_find_resource(TensorFlowTests)
+
+#
+# Copy [PREFIX]/test.pbtxt to PREFIX.pbtxt in binary folder
+# Copy [PREFIX]/test.info to PREFIX.info in binary folder
+# Copy [PREFIX]/customop.conf to PREFIX_customop.conf in binary folder
+# Encode PREFIX.pbtxt to PREFIX.pb
+#
+set(TEST_REPO "${TensorFlowTests_DIR}")
+set(TEST_PBTXT_FILENAME "test.pbtxt")
+set(TEST_INFO_FILENAME "test.info")
+set(TEST_CUSTOMOP_CONF_FILENAME "customop.conf")
+
+unset(TESTCASES)
+
+macro(add NAME)
+ list(APPEND TESTCASES ${NAME})
+endmacro(add)
+
+# Read "test.lst"
+include("test.lst")
+# Read "test.local.lst" if exists
+include("test.local.lst" OPTIONAL)
+
+unset(TEST_DEPS)
+unset(TEST_NAMES)
+
+foreach(PREFIX IN ITEMS ${TESTCASES})
+ if(NOT IS_DIRECTORY "${TEST_REPO}/${PREFIX}")
+ message(FATAL_ERROR "Missing '${PREFIX}' test")
+ endif()
+
+ set(PBTXT_SOURCE_PATH "${TEST_REPO}/${PREFIX}/${TEST_PBTXT_FILENAME}")
+ set(INFO_SOURCE_PATH "${TEST_REPO}/${PREFIX}/${TEST_INFO_FILENAME}")
+ set(CUSTOMOP_CONF_SOURCE_PATH "${TEST_REPO}/${PREFIX}/${TEST_CUSTOMOP_CONF_FILENAME}")
+
+ set(PBTXT_FILE "${PREFIX}.pbtxt")
+ set(PBTXT_PATH "${CMAKE_CURRENT_BINARY_DIR}/${PBTXT_FILE}")
+
+ set(INFO_FILE "${PREFIX}.info")
+ set(INFO_PATH "${CMAKE_CURRENT_BINARY_DIR}/${INFO_FILE}")
+
+ set(CUSTOMOP_CONF_FILE "${PREFIX}.${TEST_CUSTOMOP_CONF_FILENAME}") # ex) CustomOp_001.customop.conf
+ set(CUSTOMOP_CONF_PATH "${CMAKE_CURRENT_BINARY_DIR}/${CUSTOMOP_CONF_FILE}")
+
+ set(PB_FILE "${PREFIX}.pb")
+ set(PB_PATH "${CMAKE_CURRENT_BINARY_DIR}/${PB_FILE}")
+
+ # Copy .pbtxt
+ add_custom_command(OUTPUT ${PBTXT_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${PBTXT_SOURCE_PATH}" "${PBTXT_PATH}"
+ DEPENDS ${PBTXT_SOURCE_PATH}
+ COMMENT "Generate ${PBTXT_FILE}"
+ )
+
+ # Copy .info
+ add_custom_command(OUTPUT ${INFO_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${INFO_SOURCE_PATH}" "${INFO_PATH}"
+ DEPENDS ${INFO_SOURCE_PATH}
+ COMMENT "Generate ${INFO_FILE}"
+ )
+
+ # Generate .pb from .pbtxt
+ add_custom_command(OUTPUT ${PB_PATH}
+ COMMAND $<TARGET_FILE:tfkit> encode ${PBTXT_PATH} ${PB_PATH}
+ DEPENDS ${PBTXT_PATH}
+ COMMENT "Generate ${PB_FILE}"
+ )
+
+ list(APPEND TEST_DEPS ${INFO_PATH} ${PB_PATH})
+
+ if (EXISTS "${CUSTOMOP_CONF_SOURCE_PATH}")
+
+ # Copy customop.conf
+ add_custom_command(OUTPUT ${CUSTOMOP_CONF_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${CUSTOMOP_CONF_SOURCE_PATH}" "${CUSTOMOP_CONF_PATH}"
+ DEPENDS ${CUSTOMOP_CONF_SOURCE_PATH}
+ COMMENT "Generate ${CUSTOMOP_CONF_FILE}"
+ )
+
+ list(APPEND TEST_DEPS ${CUSTOMOP_CONF_PATH})
+
+ endif (EXISTS "${CUSTOMOP_CONF_SOURCE_PATH}")
+
+ list(APPEND TEST_NAMES ${PREFIX})
+endforeach(PREFIX)
+
+##
+## Copy testall
+##
+set(TEST_RUNNER "${CMAKE_CURRENT_BINARY_DIR}/testall.sh")
+set(TEST_RUNNER_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/testall.sh")
+
+add_custom_command(
+ OUTPUT ${TEST_RUNNER}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TEST_RUNNER_SOURCE}" "${TEST_RUNNER}"
+ DEPENDS ${TEST_RUNNER_SOURCE}
+ COMMENT "Generate test runner"
+)
+
+list(APPEND TEST_DEPS "${TEST_RUNNER}")
+
+###
+### Generate test.config
+###
+set(TEST_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/test.config")
+
+add_custom_command(
+ OUTPUT ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TF2CIRCLE_PATH=\"$<TARGET_FILE:tf2circle>\"' >> ${TEST_CONFIG}
+ DEPENDS
+ tf2circle
+ COMMENT "Generate test configuration"
+)
+
+list(APPEND TEST_DEPS "${TEST_CONFIG}")
+
+# This "tf2circle_conversion_test_deps" target enforces CMake to generate all the dependencies during "build" phase
+add_custom_target(tf2circle_conversion_test_deps ALL DEPENDS ${TEST_DEPS})
+
+# Run tests
+add_test(
+ NAME tf2circle_conversion_test
+ COMMAND "${TEST_RUNNER}"
+ "${TEST_CONFIG}"
+ "${CMAKE_CURRENT_BINARY_DIR}"
+ ${TEST_NAMES}
+)
diff --git a/compiler/tf2circle-conversion-test/README.md b/compiler/tf2circle-conversion-test/README.md
new file mode 100644
index 000000000..bee7aec18
--- /dev/null
+++ b/compiler/tf2circle-conversion-test/README.md
@@ -0,0 +1,3 @@
+# tf2circle-conversion-test
+
+Run `tf2circle` to `test.lst` and check whether given TF model is able to be converted into Circle model. Write `test.local.lst` for local test list.
diff --git a/compiler/tf2circle-conversion-test/requires.cmake b/compiler/tf2circle-conversion-test/requires.cmake
new file mode 100644
index 000000000..b1f872669
--- /dev/null
+++ b/compiler/tf2circle-conversion-test/requires.cmake
@@ -0,0 +1,2 @@
+require("tf2circle")
+require("tfkit")
diff --git a/compiler/tf2circle-conversion-test/test.lst b/compiler/tf2circle-conversion-test/test.lst
new file mode 100644
index 000000000..f24de50c0
--- /dev/null
+++ b/compiler/tf2circle-conversion-test/test.lst
@@ -0,0 +1,103 @@
+# TODO Enable skipped tests
+
+add(NET_0000)
+add(NET_0001)
+add(NET_0002)
+add(NET_0003)
+add(NET_0004)
+add(NET_0005)
+add(NET_0006)
+add(NET_0007)
+add(NET_0008)
+add(NET_0009)
+add(NET_0010)
+add(NET_0011)
+add(NET_0012)
+add(NET_0013)
+add(NET_0014)
+add(NET_0015)
+add(NET_0016)
+add(NET_0017)
+add(NET_0018)
+add(NET_0019)
+add(NET_0020)
+add(NET_0021)
+add(NET_0022)
+add(NET_0023)
+add(NET_0024)
+add(NET_0025)
+add(NET_0028)
+add(NET_0029)
+add(NET_0030)
+add(NET_0031)
+add(NET_0032)
+add(NET_0033)
+add(NET_0034)
+add(NET_0035)
+add(NET_0036)
+add(NET_0037)
+add(NET_0038)
+add(NET_0039)
+add(NET_0040)
+add(NET_0041)
+add(REGRESSION_0000)
+add(REGRESSION_0001)
+add(REGRESSION_0002)
+add(UNIT_Add_000)
+add(UNIT_Add_001)
+add(UNIT_Add_002)
+add(UNIT_Add_004)
+add(UNIT_Add_005)
+add(UNIT_AvgPool_000)
+add(UNIT_AvgPool_001)
+#add(UNIT_BiasAdd_000)
+#add(UNIT_BiasAdd_001)
+add(UNIT_BiasAdd_002)
+#add(UNIT_ConcatV2_000)
+#add(UNIT_ConcatV2_001)
+add(UNIT_ConcatV2_002)
+add(UNIT_Const_000)
+#add(UNIT_Const_001)
+add(UNIT_Conv2D_000)
+add(UNIT_Conv2DBackpropInput_000)
+add(UNIT_Conv2DBackpropInput_001)
+#add(UNIT_CustomOp_000)
+add(UNIT_CustomOp_001)
+add(UNIT_DepthwiseConv2dNative_000)
+add(UNIT_DepthwiseConv2dNative_001)
+add(UNIT_Maximum_000)
+add(UNIT_Maximum_001)
+add(UNIT_Maximum_002)
+add(UNIT_MaxPool_000)
+add(UNIT_MaxPool_001)
+add(UNIT_Mean_000)
+add(UNIT_Mean_001)
+add(UNIT_Mean_002)
+add(UNIT_Mean_003)
+add(UNIT_Mul_000)
+add(UNIT_Mul_001)
+add(UNIT_Mul_002)
+add(UNIT_Pad_000)
+add(UNIT_Placeholder_000)
+add(UNIT_Placeholder_001)
+add(UNIT_Placeholder_002)
+add(UNIT_Placeholder_003)
+add(UNIT_RealDiv_000)
+add(UNIT_RealDiv_001)
+add(UNIT_Relu_000)
+add(UNIT_Relu6_000)
+add(UNIT_Reshape_000)
+add(UNIT_Rsqrt_000)
+add(UNIT_Softmax_001)
+add(UNIT_Sqrt_000)
+add(UNIT_SquaredDifference_000)
+add(UNIT_SquaredDifference_001)
+add(UNIT_Squeeze_000)
+add(UNIT_Squeeze_001)
+add(UNIT_Squeeze_002)
+add(UNIT_Squeeze_003)
+add(UNIT_StopGradient_000)
+add(UNIT_StopGradient_001)
+add(UNIT_Sub_000)
+add(UNIT_Sub_001)
+add(UNIT_Tanh_000)
diff --git a/compiler/tf2circle-conversion-test/testall.sh b/compiler/tf2circle-conversion-test/testall.sh
new file mode 100755
index 000000000..a7e8037d5
--- /dev/null
+++ b/compiler/tf2circle-conversion-test/testall.sh
@@ -0,0 +1,90 @@
+#!/bin/bash
+
+# Need at least 2 arguments
+if [[ $# -lt 2 ]]; then
+ echo "USAGE: $0 ..."
+ echo
+ echo "ARGUMENTS:"
+ echo " [test.config path]"
+ echo " [WORKDIR]"
+ echo " [Prefix1]"
+ echo " [Prefix2]"
+ echo " ..."
+ exit 255
+fi
+
+CONFIG_PATH="$1"; shift
+WORKDIR="$1"; shift
+
+source "${CONFIG_PATH}"
+
+echo "-- Found TF2CIRCLE: ${TF2CIRCLE_PATH}"
+echo "-- Found workdir: ${WORKDIR}"
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+pushd "${WORKDIR}"
+while [[ $# -ne 0 ]]; do
+ PREFIX="$1"; shift
+
+ TESTED+=("${PREFIX}")
+
+ PASSED_TAG="${PREFIX}.passed"
+
+ rm -f "${PASSED_TAG}"
+
+ cat > "${PREFIX}.log" <(
+ exec 2>&1
+
+ echo "-- Found pb: ${PREFIX}.pb"
+
+ # Exit immediately if any command fails
+ set -e
+ # Show commands
+ set -x
+
+ # tflite is generated both for COMPARE and EXPORT actions
+ if [ -f "${WORKDIR}/${PREFIX}.customop.conf" ]; then
+
+ # Generate tflite
+ "${TF2CIRCLE_PATH}" \
+ "${WORKDIR}/${PREFIX}.info" \
+ "${WORKDIR}/${PREFIX}.pb" \
+ "${WORKDIR}/${PREFIX}.circle" \
+ "--customop" "${WORKDIR}/${PREFIX}.customop.conf"
+ else
+
+ # Generate circle
+ "${TF2CIRCLE_PATH}" \
+ "${WORKDIR}/${PREFIX}.info" \
+ "${WORKDIR}/${PREFIX}.pb" \
+ "${WORKDIR}/${PREFIX}.circle"
+
+ fi
+
+ if [[ $? -eq 0 ]]; then
+ touch "${PASSED_TAG}"
+ fi
+ )
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ PASSED+=("$PREFIX")
+ else
+ FAILED+=("$PREFIX")
+ 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/tf2circle-dredd-pb-test/.gitignore b/compiler/tf2circle-dredd-pb-test/.gitignore
new file mode 100644
index 000000000..23c7c1bb3
--- /dev/null
+++ b/compiler/tf2circle-dredd-pb-test/.gitignore
@@ -0,0 +1 @@
+/contrib.lst
diff --git a/compiler/tf2circle-dredd-pb-test/CMakeLists.txt b/compiler/tf2circle-dredd-pb-test/CMakeLists.txt
new file mode 100644
index 000000000..48b098e24
--- /dev/null
+++ b/compiler/tf2circle-dredd-pb-test/CMakeLists.txt
@@ -0,0 +1,141 @@
+nnas_include(TargetRequire)
+
+unset(REQUIRED_TARGETS)
+list(APPEND REQUIRED_TARGETS circle-inspect)
+list(APPEND REQUIRED_TARGETS circle-verify)
+list(APPEND REQUIRED_TARGETS tf2circle)
+list(APPEND REQUIRED_TARGETS dredd_rule_lib)
+TargetRequire_Return(${REQUIRED_TARGETS})
+
+set(PB_MODEL_REPO "${CMAKE_CURRENT_SOURCE_DIR}/contrib") # Where to find models to test
+
+unset(KEYS)
+unset(DEPS)
+
+function(check_file_exist)
+
+ foreach(FILE_PATH IN LISTS ARGV)
+ if(NOT EXISTS "${FILE_PATH}")
+ message(FATAL_ERROR "${FILE_PATH} does not exist." )
+ endif()
+ endforeach()
+
+endfunction()
+
+#
+# processing models in contrib.lst
+#
+# Example)
+#
+# Add(Inception_v3 RULE circle_1.0_rel_requirement.rule)
+# -> Read compiler/tf2circle-dredd-pb-test/contrib/Inception_v3/model.pb and generate
+# "Inception_v3.circle". Then rule file is tested for the generated circle file.
+#
+macro(Add MODEL_DIR)
+
+ set(ARG_OPTION)
+ set(ARG_ONE_VALUE RULE) # rule file name
+ set(ARG_MULTI_VALUE)
+ cmake_parse_arguments(ARG "${ARG_OPTION}" "${ARG_ONE_VALUE}" "${ARG_MULTI_VALUE}" ${ARGN})
+
+ if(NOT ARG_RULE )
+ message( FATAL_ERROR "RULE is mandadatory arg" )
+ endif()
+
+ set(RULE_FILENAME ${ARG_RULE})
+
+ set(MODEL_SOURCE_DIR "${PB_MODEL_REPO}/${MODEL_DIR}")
+
+ set(PB_PATH "${MODEL_SOURCE_DIR}/model.pb")
+ set(INFO_PATH "${MODEL_SOURCE_DIR}/model.info")
+ set(RULE_PATH "${MODEL_SOURCE_DIR}/${RULE_FILENAME}")
+
+ check_file_exist(${PB_PATH} ${INFO_PATH} ${RULE_PATH})
+
+ # Generate .test file which declares path of target pb, info, rule files
+ set(TARGET_TESTNAME "${MODEL_DIR}")
+ set(TEST_CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_TESTNAME}.test")
+
+ add_custom_command(
+ OUTPUT ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E echo 'MODEL_PB_PATH="${PB_PATH}"' >> ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E echo 'MODEL_INFO_PATH="${INFO_PATH}"' >> ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E echo 'MODEL_RULE_PATH="${RULE_PATH}"' >> ${TEST_CONFIG_FILE}
+ DEPENDS
+ ${PB_PATH}
+ COMMENT "Generate ${TARGET_TESTNAME} configuration for BIN"
+ )
+
+ list(APPEND KEYS "${TARGET_TESTNAME}")
+ list(APPEND DEPS "${TEST_CONFIG_FILE}")
+
+endmacro(Add)
+
+include(contrib.lst OPTIONAL)
+
+#
+# Generate toolchain.config
+#
+set(TOOLCHAIN_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/toolchain.config")
+
+add_custom_command(
+ OUTPUT ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'CIRCLE_INSPECT_PATH=\"$<TARGET_FILE:circle-inspect>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'CIRCLE_VERIFY_PATH=\"$<TARGET_FILE:circle-verify>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TF2CIRCLE_PATH=\"$<TARGET_FILE:tf2circle>\"' >> ${TOOLCHAIN_CONFIG}
+ # add more if new excutable file is needed in runner.sh and rule-lib.sh
+ DEPENDS
+ circle-inspect
+ circle-verify
+ tf2circle
+ COMMENT "Generate toolchin configuration"
+)
+
+list(APPEND DEPS "${TOOLCHAIN_CONFIG}")
+
+#
+# Generate quality test runner
+#
+set(SOURCE_RUNNER "${CMAKE_CURRENT_SOURCE_DIR}/runner.sh")
+set(TARGET_RUNNER "${CMAKE_CURRENT_BINARY_DIR}/runner.sh")
+
+add_custom_command(
+ OUTPUT ${TARGET_RUNNER}
+ COMMAND ${CMAKE_COMMAND} -E copy "${SOURCE_RUNNER}" "${TARGET_RUNNER}"
+ DEPENDS ${SOURCE_RUNNER}
+ COMMENT "Generate test runner"
+)
+
+list(APPEND DEPS "${TARGET_RUNNER}")
+
+#
+# copy rule-lib.sh (a library of shell script functions)
+#
+
+# getting path for rule-lib.sh in dredd-rule-lib
+get_target_property(DREDD_RULE_LIB_DIR dredd_rule_lib BINARY_DIR)
+
+set(SOURCE_RULE_LIB "${DREDD_RULE_LIB_DIR}/rule-lib.sh")
+set(TARGET_RULE_LIB "${CMAKE_CURRENT_BINARY_DIR}/rule-lib.sh")
+
+add_custom_command(
+ OUTPUT ${TARGET_RULE_LIB}
+ COMMAND ${CMAKE_COMMAND} -E copy "${SOURCE_RULE_LIB}" "${TARGET_RULE_LIB}"
+ DEPENDS ${SOURCE_RULE_LIB}
+ COMMENT "Generate rule lib"
+)
+
+list(APPEND DEPS "${TARGET_RULE_LIB}")
+
+# Generate dependencies
+add_custom_target(tf2circle_dredd_pb_deps ALL DEPENDS ${DEPS})
+
+add_test(
+ NAME tf2circle_dredd_pb_test
+ COMMAND
+ "${TARGET_RUNNER}"
+ "${TOOLCHAIN_CONFIG}"
+ ${KEYS}
+)
diff --git a/compiler/tf2circle-dredd-pb-test/README.md b/compiler/tf2circle-dredd-pb-test/README.md
new file mode 100644
index 000000000..65b4cd1fa
--- /dev/null
+++ b/compiler/tf2circle-dredd-pb-test/README.md
@@ -0,0 +1,3 @@
+# tf2circle-dredd-pb-test
+
+TODO write content
diff --git a/compiler/tf2circle-dredd-pb-test/contrib/.gitignore b/compiler/tf2circle-dredd-pb-test/contrib/.gitignore
new file mode 100644
index 000000000..968c34510
--- /dev/null
+++ b/compiler/tf2circle-dredd-pb-test/contrib/.gitignore
@@ -0,0 +1,3 @@
+/*
+# Exclude all except below
+!.gitignore
diff --git a/compiler/tf2circle-dredd-pb-test/requires.cmake b/compiler/tf2circle-dredd-pb-test/requires.cmake
new file mode 100644
index 000000000..0fb6cde85
--- /dev/null
+++ b/compiler/tf2circle-dredd-pb-test/requires.cmake
@@ -0,0 +1,4 @@
+require("tf2circle")
+require("circle-inspect")
+require("circle-verify")
+require("dredd-rule-lib")
diff --git a/compiler/tf2circle-dredd-pb-test/runner.sh b/compiler/tf2circle-dredd-pb-test/runner.sh
new file mode 100755
index 000000000..7c7b123c4
--- /dev/null
+++ b/compiler/tf2circle-dredd-pb-test/runner.sh
@@ -0,0 +1,121 @@
+#!/bin/bash
+
+# This script checks circle file generated by tf2circle
+
+# exit if unknown var is used
+set -u
+
+WORKDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+
+# Need at least toolchain.config
+if [[ $# -lt 1 ]]; then
+ echo "USAGE: $0 ..."
+ echo
+ echo "ARGUMENTS:"
+ echo " [toolchain.config path]"
+ echo " [Prefix1]"
+ echo " [Prefix2]"
+ echo " ..."
+ exit 255
+fi
+
+CONFIG_PATH="$1"; shift
+
+source "${CONFIG_PATH}"
+
+echo "-- Found circle-inspect: ${CIRCLE_INSPECT_PATH}"
+echo "-- Found circle-verify: ${CIRCLE_VERIFY_PATH}"
+echo "-- Found tf2circle: ${TF2CIRCLE_PATH}"
+echo "-- Found workdir: ${WORKDIR}"
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+pushd "${WORKDIR}"
+
+# running each rule file
+
+while [[ $# -ne 0 ]]; do
+ PREFIX="$1"; shift
+
+ echo "[ RUN ] ${PREFIX}"
+
+ TESTED+=("${PREFIX}")
+
+ PASSED_TAG="${PREFIX}.passed"
+
+ rm -f "${PASSED_TAG}"
+
+ cat > "${PREFIX}.log" <(
+ exec 2>&1
+
+ source "${PREFIX}.test"
+
+ echo "-- Use '${MODEL_PB_PATH}', '${MODEL_INFO_PATH}', and '${MODEL_RULE_PATH}'"
+
+ # Exit immediately if any command fails
+ set -e
+ # Show commands
+ set -x
+
+ # Generate circle
+ "${TF2CIRCLE_PATH}" \
+ "${MODEL_INFO_PATH}" \
+ "${MODEL_PB_PATH}" \
+ "${WORKDIR}/${PREFIX}.circle"
+
+ #
+ # Run rule prepared to check circle file
+ #
+
+ # set vars needed by rule file
+ CIRCLE_PATH="${WORKDIR}/${PREFIX}.circle"
+
+ # Note: turn off 'command printing'. Otherwise printing will be so messy
+ set +x
+
+ # set vars required by rule-lib.sh and rule file
+ COMPILED_FILE=${CIRCLE_PATH}
+ INSPECT_PROG_PATH=${CIRCLE_INSPECT_PATH}
+ VERIFY_PROG_PATH=${CIRCLE_VERIFY_PATH}
+ ERROR_LOG="${PREFIX}.error"
+
+ rm -f "${ERROR_LOG}"
+
+ # in case error while running ${MODEL_RULE_PATH}, prints error msg
+ trap 'echo "** ERROR **" ; cat "${ERROR_LOG}"' ERR
+
+ source rule-lib.sh
+ source "${MODEL_RULE_PATH}"
+
+ # unset
+ trap - ERR
+ set -x
+
+ # At this point, the exit code of all commands is 0
+ # If not 0, execution of this script ends because of "set -e"
+ touch "${PASSED_TAG}"
+ )
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ echo "[ OK ] ${PREFIX}"
+ PASSED+=("$PREFIX")
+ else
+ echo "[ FAIL] ${PREFIX}"
+ FAILED+=("$PREFIX")
+ 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/tf2circle-dredd-pbtxt-test/.gitignore b/compiler/tf2circle-dredd-pbtxt-test/.gitignore
new file mode 100644
index 000000000..8dbfa9012
--- /dev/null
+++ b/compiler/tf2circle-dredd-pbtxt-test/.gitignore
@@ -0,0 +1 @@
+/test.local.lst
diff --git a/compiler/tf2circle-dredd-pbtxt-test/CMakeLists.txt b/compiler/tf2circle-dredd-pbtxt-test/CMakeLists.txt
new file mode 100644
index 000000000..789e58535
--- /dev/null
+++ b/compiler/tf2circle-dredd-pbtxt-test/CMakeLists.txt
@@ -0,0 +1,184 @@
+nnas_include(TargetRequire)
+
+unset(REQUIRED_TARGETS)
+list(APPEND REQUIRED_TARGETS circle-inspect)
+list(APPEND REQUIRED_TARGETS circle-verify)
+list(APPEND REQUIRED_TARGETS tf2circle)
+list(APPEND REQUIRED_TARGETS tfkit)
+list(APPEND REQUIRED_TARGETS dredd_rule_lib)
+TargetRequire_Return(${REQUIRED_TARGETS})
+
+nncc_find_resource(TensorFlowTests)
+
+set(MODEL_REPO "${TensorFlowTests_DIR}") # Where to find text models to test
+
+unset(KEYS)
+unset(DEPS)
+
+#
+# processing models in test.lst and test.local.lst
+#
+# Example)
+#
+# Add(NET_0025 RULE test.rule)
+# -> Read test.pbtxt file under res/TensorFlowTests/NET_0025 and create "NET_0025.circle"
+# Then the circle is tested against rules in test.rule file.
+#
+macro(Add MODEL_DIR)
+
+ set(ARG_OPTION)
+ set(ARG_ONE_VALUE RULE) # rule file name
+ set(ARG_MULTI_VALUE)
+ cmake_parse_arguments(ARG "${ARG_OPTION}" "${ARG_ONE_VALUE}" "${ARG_MULTI_VALUE}" ${ARGN})
+
+ if(NOT ARG_RULE)
+ message( FATAL_ERROR "RULE is mandadatory arg" )
+ endif()
+
+ set(RULE_FILENAME ${ARG_RULE})
+
+ set(TARGET_TESTNAME "${MODEL_DIR}")
+
+ set(MODEL_SOURCE_DIR "${MODEL_REPO}/${MODEL_DIR}")
+
+ set(TXT_SOURCE_PBTXT_PATH "${MODEL_SOURCE_DIR}/test.pbtxt")
+ set(TXT_SOURCE_INFO_PATH "${MODEL_SOURCE_DIR}/test.info")
+ set(TXT_SOURCE_RULE_PATH "${MODEL_SOURCE_DIR}/${RULE_FILENAME}")
+
+ set(TXT_TARGET_PB_PATH "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_TESTNAME}.pb")
+ set(TXT_TARGET_PBTXT_PATH "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_TESTNAME}.pbtxt")
+ set(TXT_TARGET_INFO_PATH "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_TESTNAME}.info")
+ set(TXT_TARGET_RULE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_TESTNAME}.rule")
+
+ if(NOT EXISTS "${TXT_SOURCE_PBTXT_PATH}")
+ message(FATAL_ERROR "${TXT_SOURCE_PBTXT_PATH} - pbtxt file does not exist")
+ endif(NOT EXISTS "${TXT_SOURCE_PBTXT_PATH}")
+
+ if(NOT EXISTS "${TXT_SOURCE_INFO_PATH}")
+ message(FATAL_ERROR "${TXT_SOURCE_INFO_PATH} - info file does not exist")
+ endif(NOT EXISTS "${TXT_SOURCE_INFO_PATH}")
+
+ if(NOT EXISTS "${TXT_SOURCE_RULE_PATH}")
+ message(FATAL_ERROR "${TXT_SOURCE_RULE_PATH} - rule file does not exist")
+ endif(NOT EXISTS "${TXT_SOURCE_RULE_PATH}")
+
+ # Copy .pbtxt
+ add_custom_command(OUTPUT ${TXT_TARGET_PBTXT_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TXT_SOURCE_PBTXT_PATH}" "${TXT_TARGET_PBTXT_PATH}"
+ DEPENDS ${TXT_SOURCE_PBTXT_PATH}
+ COMMENT "Generate ${TXT_TARGET_PBTXT_PATH}"
+ )
+
+ # Copy .info
+ add_custom_command(OUTPUT ${TXT_TARGET_INFO_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TXT_SOURCE_INFO_PATH}" "${TXT_TARGET_INFO_PATH}"
+ DEPENDS ${TXT_SOURCE_INFO_PATH}
+ COMMENT "Generate ${TXT_TARGET_INFO_PATH}"
+ )
+
+ # Copy .rule
+ add_custom_command(OUTPUT ${TXT_TARGET_RULE_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TXT_SOURCE_RULE_PATH}" "${TXT_TARGET_RULE_PATH}"
+ DEPENDS ${TXT_SOURCE_RULE_PATH}
+ COMMENT "Generate ${TXT_TARGET_RULE_PATH}"
+ )
+
+ # Generate .pb from .pbtxt
+ add_custom_command(OUTPUT ${TXT_TARGET_PB_PATH}
+ COMMAND $<TARGET_FILE:tfkit> encode ${TXT_TARGET_PBTXT_PATH} ${TXT_TARGET_PB_PATH}
+ DEPENDS ${TXT_TARGET_PBTXT_PATH}
+ COMMENT "Generate ${TXT_TARGET_PB_PATH}"
+ )
+
+ # Generate .test file which declares path of target pb, info, rule files
+ # this file is used inside runner.sh
+ set(TEST_CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_TESTNAME}.test")
+
+ add_custom_command(
+ OUTPUT ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E echo 'MODEL_PB_PATH="${TXT_TARGET_PB_PATH}"' >> ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E echo 'MODEL_INFO_PATH="${TXT_TARGET_INFO_PATH}"' >> ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E echo 'MODEL_RULE_PATH="${TXT_TARGET_RULE_PATH}"' >> ${TEST_CONFIG_FILE}
+ DEPENDS
+ ${TXT_TARGET_PB_PATH}
+ ${TXT_TARGET_INFO_PATH}
+ ${TXT_TARGET_RULE_PATH}
+ COMMENT "Generate ${TARGET_TESTNAME} configuration for TXT"
+ )
+
+ list(APPEND DEPS "${TEST_CONFIG_FILE}")
+ list(APPEND KEYS "${TARGET_TESTNAME}")
+
+endmacro(Add)
+
+# Read "test.lst"
+include("test.lst")
+# Read "test.local.lst" if exists
+include("test.local.lst" OPTIONAL)
+
+#
+# Generate toolchain.config
+#
+set(TOOLCHAIN_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/toolchain.config")
+
+add_custom_command(
+ OUTPUT ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'CIRCLE_INSPECT_PATH=\"$<TARGET_FILE:circle-inspect>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'CIRCLE_VERIFY_PATH=\"$<TARGET_FILE:circle-verify>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TF2CIRCLE_PATH=\"$<TARGET_FILE:tf2circle>\"' >> ${TOOLCHAIN_CONFIG}
+ # add more if new excutable file is needed in runner.sh and rule-lib.sh
+ DEPENDS
+ circle-inspect
+ circle-verify
+ tf2circle
+ COMMENT "Generate toolchin configuration"
+)
+
+list(APPEND DEPS "${TOOLCHAIN_CONFIG}")
+
+#
+# copy runner.sh
+#
+set(SOURCE_RUNNER "${CMAKE_CURRENT_SOURCE_DIR}/runner.sh")
+set(TARGET_RUNNER "${CMAKE_CURRENT_BINARY_DIR}/runner.sh")
+
+add_custom_command(
+ OUTPUT ${TARGET_RUNNER}
+ COMMAND ${CMAKE_COMMAND} -E copy "${SOURCE_RUNNER}" "${TARGET_RUNNER}"
+ DEPENDS ${SOURCE_RUNNER}
+ COMMENT "Generate test runner"
+)
+
+list(APPEND DEPS "${TARGET_RUNNER}")
+
+#
+# copy rule-lib.sh (a library of shell script functions)
+#
+
+# getting path for rule-lib.sh in dredd-rule-lib
+get_target_property(DREDD_RULE_LIB_DIR dredd_rule_lib BINARY_DIR)
+
+set(SOURCE_RULE_LIB "${DREDD_RULE_LIB_DIR}/rule-lib.sh")
+set(TARGET_RULE_LIB "${CMAKE_CURRENT_BINARY_DIR}/rule-lib.sh")
+
+add_custom_command(
+ OUTPUT ${TARGET_RULE_LIB}
+ COMMAND ${CMAKE_COMMAND} -E copy "${SOURCE_RULE_LIB}" "${TARGET_RULE_LIB}"
+ DEPENDS ${SOURCE_RULE_LIB}
+ COMMENT "Generate rule lib"
+)
+
+list(APPEND DEPS "${TARGET_RULE_LIB}")
+
+# Generate dependencies
+add_custom_target(tf2circle_dredd_pbtxt_deps ALL DEPENDS ${DEPS})
+
+add_test(
+ NAME tf2circle_dredd_pbtxt_test
+ COMMAND
+ "${TARGET_RUNNER}"
+ "${TOOLCHAIN_CONFIG}"
+ ${KEYS}
+)
diff --git a/compiler/tf2circle-dredd-pbtxt-test/README.md b/compiler/tf2circle-dredd-pbtxt-test/README.md
new file mode 100644
index 000000000..8eb906185
--- /dev/null
+++ b/compiler/tf2circle-dredd-pbtxt-test/README.md
@@ -0,0 +1,3 @@
+# tf2circle-dredd-pbtxt-test
+
+TODO write content.
diff --git a/compiler/tf2circle-dredd-pbtxt-test/requires.cmake b/compiler/tf2circle-dredd-pbtxt-test/requires.cmake
new file mode 100644
index 000000000..747d8ab3c
--- /dev/null
+++ b/compiler/tf2circle-dredd-pbtxt-test/requires.cmake
@@ -0,0 +1,5 @@
+require("tfkit")
+require("tf2circle")
+require("circle-inspect")
+require("circle-verify")
+require("dredd-rule-lib")
diff --git a/compiler/tf2circle-dredd-pbtxt-test/runner.sh b/compiler/tf2circle-dredd-pbtxt-test/runner.sh
new file mode 100755
index 000000000..7c7b123c4
--- /dev/null
+++ b/compiler/tf2circle-dredd-pbtxt-test/runner.sh
@@ -0,0 +1,121 @@
+#!/bin/bash
+
+# This script checks circle file generated by tf2circle
+
+# exit if unknown var is used
+set -u
+
+WORKDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+
+# Need at least toolchain.config
+if [[ $# -lt 1 ]]; then
+ echo "USAGE: $0 ..."
+ echo
+ echo "ARGUMENTS:"
+ echo " [toolchain.config path]"
+ echo " [Prefix1]"
+ echo " [Prefix2]"
+ echo " ..."
+ exit 255
+fi
+
+CONFIG_PATH="$1"; shift
+
+source "${CONFIG_PATH}"
+
+echo "-- Found circle-inspect: ${CIRCLE_INSPECT_PATH}"
+echo "-- Found circle-verify: ${CIRCLE_VERIFY_PATH}"
+echo "-- Found tf2circle: ${TF2CIRCLE_PATH}"
+echo "-- Found workdir: ${WORKDIR}"
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+pushd "${WORKDIR}"
+
+# running each rule file
+
+while [[ $# -ne 0 ]]; do
+ PREFIX="$1"; shift
+
+ echo "[ RUN ] ${PREFIX}"
+
+ TESTED+=("${PREFIX}")
+
+ PASSED_TAG="${PREFIX}.passed"
+
+ rm -f "${PASSED_TAG}"
+
+ cat > "${PREFIX}.log" <(
+ exec 2>&1
+
+ source "${PREFIX}.test"
+
+ echo "-- Use '${MODEL_PB_PATH}', '${MODEL_INFO_PATH}', and '${MODEL_RULE_PATH}'"
+
+ # Exit immediately if any command fails
+ set -e
+ # Show commands
+ set -x
+
+ # Generate circle
+ "${TF2CIRCLE_PATH}" \
+ "${MODEL_INFO_PATH}" \
+ "${MODEL_PB_PATH}" \
+ "${WORKDIR}/${PREFIX}.circle"
+
+ #
+ # Run rule prepared to check circle file
+ #
+
+ # set vars needed by rule file
+ CIRCLE_PATH="${WORKDIR}/${PREFIX}.circle"
+
+ # Note: turn off 'command printing'. Otherwise printing will be so messy
+ set +x
+
+ # set vars required by rule-lib.sh and rule file
+ COMPILED_FILE=${CIRCLE_PATH}
+ INSPECT_PROG_PATH=${CIRCLE_INSPECT_PATH}
+ VERIFY_PROG_PATH=${CIRCLE_VERIFY_PATH}
+ ERROR_LOG="${PREFIX}.error"
+
+ rm -f "${ERROR_LOG}"
+
+ # in case error while running ${MODEL_RULE_PATH}, prints error msg
+ trap 'echo "** ERROR **" ; cat "${ERROR_LOG}"' ERR
+
+ source rule-lib.sh
+ source "${MODEL_RULE_PATH}"
+
+ # unset
+ trap - ERR
+ set -x
+
+ # At this point, the exit code of all commands is 0
+ # If not 0, execution of this script ends because of "set -e"
+ touch "${PASSED_TAG}"
+ )
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ echo "[ OK ] ${PREFIX}"
+ PASSED+=("$PREFIX")
+ else
+ echo "[ FAIL] ${PREFIX}"
+ FAILED+=("$PREFIX")
+ 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/tf2circle-dredd-pbtxt-test/test.lst b/compiler/tf2circle-dredd-pbtxt-test/test.lst
new file mode 100644
index 000000000..51bc4f21e
--- /dev/null
+++ b/compiler/tf2circle-dredd-pbtxt-test/test.lst
@@ -0,0 +1,4 @@
+# TODO add tests like the following:
+# Add(NET_0030 RULE circle_1.0_rel_requirement.rule) # Concat
+Add(NET_0028 RULE circle_1.0_rel_requirement.rule) # Instance Norm
+Add(UNIT_SquaredDifference_000 RULE circle.rule)
diff --git a/compiler/tf2circle-model-test/.gitignore b/compiler/tf2circle-model-test/.gitignore
new file mode 100644
index 000000000..23c7c1bb3
--- /dev/null
+++ b/compiler/tf2circle-model-test/.gitignore
@@ -0,0 +1 @@
+/contrib.lst
diff --git a/compiler/tf2circle-model-test/CMakeLists.txt b/compiler/tf2circle-model-test/CMakeLists.txt
new file mode 100644
index 000000000..2fb82236a
--- /dev/null
+++ b/compiler/tf2circle-model-test/CMakeLists.txt
@@ -0,0 +1,110 @@
+nnas_include(TargetRequire)
+
+unset(REQUIRED_TARGETS)
+list(APPEND REQUIRED_TARGETS tf2circle)
+list(APPEND REQUIRED_TARGETS tfkit)
+TargetRequire_Return(${REQUIRED_TARGETS})
+
+unset(KEYS)
+unset(DEPS)
+
+###
+### Add "Contrib" tests
+###
+macro(Add PREFIX)
+ # Let's use CONTRIB prefix to avoid name conflicts with official models
+ set(TEST_KEY "CONTRIB.${PREFIX}")
+
+ set(PACKAGE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/contrib/${PREFIX}")
+
+ set(MODEL_DOWNLOAD_SCRIPT "${PACKAGE_DIR}/model.download")
+ set(MODEL_PB_FILE "${PACKAGE_DIR}/model.pb")
+ set(MODEL_INFO_FILE "${PACKAGE_DIR}/model.info")
+ set(MODEL_MD5SUM_FILE "${PACKAGE_DIR}/model.md5sum")
+
+ # Try to download a model if it is missing
+ if(NOT EXISTS "${MODEL_PB_FILE}")
+ # TODO Extract this routine as a helper function
+ if(NOT EXISTS "${MODEL_DOWNLOAD_SCRIPT}")
+ message(FATAL_ERROR "${TEST_KEY} - Download script is missing")
+ endif(NOT EXISTS "${MODEL_DOWNLOAD_SCRIPT}")
+
+ execute_process(
+ COMMAND ${CMAKE_COMMAND} -D OUTPUT_PATH=${MODEL_PB_FILE} -P "${MODEL_DOWNLOAD_SCRIPT}"
+ RESULT_VARIABLE EXITCODE
+ )
+
+ if(NOT EXITCODE EQUAL 0)
+ message(FATAL_ERROR "${TEST_KEY} - Download fails")
+ endif(NOT EXITCODE EQUAL 0)
+ endif()
+
+ if(EXISTS "${MODEL_MD5SUM_FILE}")
+ # TODO Extract this routine as a helper function
+ file(STRINGS "${MODEL_MD5SUM_FILE}" EXPECTED_MD5SUM)
+ file(MD5 "${MODEL_PB_FILE}" OBTAINED_MD5SUM)
+
+ if(NOT "${EXPECTED_MD5SUM}" STREQUAL "${OBTAINED_MD5SUM}")
+ message(FATAL_ERROR "${TEST_KEY} - Checksum mismatches")
+ endif()
+ endif()
+
+ # Generate .test file which declares MODEL_PB_PATH and MODEL_INFO_PATH
+ set(TEST_CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/${TEST_KEY}.test")
+
+ add_custom_command(
+ OUTPUT ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E echo 'MODEL_PB_PATH="${MODEL_PB_FILE}"' >> ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E echo 'MODEL_INFO_PATH="${MODEL_INFO_FILE}"' >> ${TEST_CONFIG_FILE}
+ COMMENT "Generate ${TEST_KEY} configuration"
+ )
+
+ list(APPEND KEYS "${TEST_KEY}")
+ list(APPEND DEPS "${TEST_CONFIG_FILE}")
+endmacro(Add)
+
+include(contrib.lst OPTIONAL)
+
+###
+### Generate toolchain.config
+###
+set(TOOLCHAIN_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/toolchain.config")
+
+add_custom_command(
+ OUTPUT ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TF2CIRCLE_PATH=\"$<TARGET_FILE:tf2circle>\"' >> ${TOOLCHAIN_CONFIG}
+ DEPENDS
+ tf2circle
+ COMMENT "Generate toolchin configuration"
+)
+
+list(APPEND DEPS "${TOOLCHAIN_CONFIG}")
+
+##
+## Generate test runner
+##
+set(TEST_RUNNER_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/runner.sh")
+set(TEST_RUNNER_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/run")
+
+add_custom_command(
+ OUTPUT ${TEST_RUNNER_SCRIPT}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TEST_RUNNER_SOURCE}" "${TEST_RUNNER_SCRIPT}"
+ DEPENDS ${TEST_RUNNER_SOURCE}
+ COMMENT "Generate test runner"
+)
+
+list(APPEND DEPS "${TEST_RUNNER_SCRIPT}")
+
+### Generate dependencies
+add_custom_target(tf2circle_model_test_deps ALL DEPENDS ${DEPS})
+
+# NOTE This target is not built by default
+add_test(
+ NAME tf2circle_model_test
+ COMMAND
+ "${TEST_RUNNER_SCRIPT}"
+ "${TOOLCHAIN_CONFIG}"
+ ${KEYS}
+)
diff --git a/compiler/tf2circle-model-test/README.md b/compiler/tf2circle-model-test/README.md
new file mode 100644
index 000000000..fb5343700
--- /dev/null
+++ b/compiler/tf2circle-model-test/README.md
@@ -0,0 +1 @@
+# tf2circle-model-test
diff --git a/compiler/tf2circle-model-test/contrib/.gitignore b/compiler/tf2circle-model-test/contrib/.gitignore
new file mode 100644
index 000000000..968c34510
--- /dev/null
+++ b/compiler/tf2circle-model-test/contrib/.gitignore
@@ -0,0 +1,3 @@
+/*
+# Exclude all except below
+!.gitignore
diff --git a/compiler/tf2circle-model-test/requires.cmake b/compiler/tf2circle-model-test/requires.cmake
new file mode 100644
index 000000000..b1f872669
--- /dev/null
+++ b/compiler/tf2circle-model-test/requires.cmake
@@ -0,0 +1,2 @@
+require("tf2circle")
+require("tfkit")
diff --git a/compiler/tf2circle-model-test/runner.sh b/compiler/tf2circle-model-test/runner.sh
new file mode 100755
index 000000000..9e3b75c26
--- /dev/null
+++ b/compiler/tf2circle-model-test/runner.sh
@@ -0,0 +1,83 @@
+#!/bin/bash
+
+WORKDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+
+# Need at least toolchain.config
+if [[ $# -lt 1 ]]; then
+ echo "USAGE: $0 ..."
+ echo
+ echo "ARGUMENTS:"
+ echo " [toolchain.config path]"
+ echo " [Prefix1]"
+ echo " [Prefix2]"
+ echo " ..."
+ exit 255
+fi
+
+CONFIG_PATH="$1"; shift
+
+source "${CONFIG_PATH}"
+
+echo "-- Found TF2CIRCLE: ${TF2CIRCLE_PATH}"
+echo "-- Found workdir: ${WORKDIR}"
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+pushd "${WORKDIR}"
+while [[ $# -ne 0 ]]; do
+ PREFIX="$1"; shift
+
+ echo "[ RUN ] ${PREFIX}"
+
+ TESTED+=("${PREFIX}")
+
+ PASSED_TAG="${PREFIX}.passed"
+
+ rm -f "${PASSED_TAG}"
+
+ cat > "${PREFIX}.log" <(
+ exec 2>&1
+
+ source "${PREFIX}.test"
+
+ echo "-- Use '${MODEL_PB_PATH}' and '${MODEL_INFO_PATH}'"
+
+ # Exit immediately if any command fails
+ set -e
+ # Show commands
+ set -x
+
+ # Generate circle
+ "${TF2CIRCLE_PATH}" \
+ "${MODEL_INFO_PATH}" \
+ "${MODEL_PB_PATH}" \
+ "${WORKDIR}/${PREFIX}.circle"
+
+ if [[ $? -eq 0 ]]; then
+ touch "${PASSED_TAG}"
+ fi
+ )
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ echo "[ OK ] ${PREFIX}"
+ PASSED+=("$PREFIX")
+ else
+ echo "[ FAIL] ${PREFIX}"
+ FAILED+=("$PREFIX")
+ 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/tf2circle-ui-check/.gitignore b/compiler/tf2circle-ui-check/.gitignore
new file mode 100644
index 000000000..a178c13cd
--- /dev/null
+++ b/compiler/tf2circle-ui-check/.gitignore
@@ -0,0 +1 @@
+/test.lst
diff --git a/compiler/tf2circle-ui-check/CMakeLists.txt b/compiler/tf2circle-ui-check/CMakeLists.txt
new file mode 100644
index 000000000..863f1cf70
--- /dev/null
+++ b/compiler/tf2circle-ui-check/CMakeLists.txt
@@ -0,0 +1,44 @@
+nnas_include(TargetRequire)
+
+unset(REQUIRED_TARGETS)
+list(APPEND REQUIRED_TARGETS tfkit)
+list(APPEND REQUIRED_TARGETS tf2circle)
+TargetRequire_Return(${REQUIRED_TARGETS})
+
+nncc_find_resource(TensorFlowTests)
+
+message(STATUS "Build tf2circle-ui-check: TRUE")
+unset(TESTCASES)
+
+macro(Add NAME)
+ list(APPEND TESTCASES "${NAME}")
+endmacro(Add)
+
+include("test.lst" OPTIONAL)
+
+##
+## Generate configuration
+##
+set(CONFIG_PATH "${CMAKE_CURRENT_BINARY_DIR}/check.config")
+
+add_custom_command(
+ OUTPUT ${CONFIG_PATH}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${CONFIG_PATH}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TESTCASE_BASE="${TensorFlowTests_DIR}"' >> ${CONFIG_PATH}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TFKIT_PATH=\"$<TARGET_FILE:tfkit>\"' >> ${CONFIG_PATH}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TF2CIRCLE_PATH=\"$<TARGET_FILE:tf2circle>\"' >> ${CONFIG_PATH}
+ DEPENDS
+ tfkit
+ tf2circle
+ COMMENT "Generate configuration"
+)
+
+## NOTE tf2circle_ui_check is not built by default
+add_custom_target(tf2circle_ui_check
+ COMMAND
+ "${CMAKE_CURRENT_SOURCE_DIR}/checkall.sh"
+ "${CONFIG_PATH}"
+ ${TESTCASES}
+ DEPENDS "${CONFIG_PATH}"
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+)
diff --git a/compiler/tf2circle-ui-check/README.md b/compiler/tf2circle-ui-check/README.md
new file mode 100644
index 000000000..aea870e6f
--- /dev/null
+++ b/compiler/tf2circle-ui-check/README.md
@@ -0,0 +1,21 @@
+# tf2circle-ui-check
+
+tf2circle-ui-check makes it easy to check what ``tf2circle`` shows for selected TensorFlow testcases.
+
+## HOW TO USE
+
+First of all, create "test.lst" file and add tests of interest. Here is an example of "test.lst"
+```
+Add(NET_0000)
+Add(NET_0001)
+```
+
+Run "nncc configure". You may find the below messages if ``tf2circle-ui-check`` is configured properly:
+```
+-- Configure TF2CIRCLE-UI-CHECK
+-- Build tf2circle-ui-check: TRUE
+-- Configure TF2CIRCLE-UI-CHECK - Done
+```
+
+Finally, build ``tf2circle_ui_check`` target and see what happens!
+If CMake uses "make" as a generator, you may build ``tf2circle_ui_check`` target via running ``./nncc build tf2circle_ui_check``.
diff --git a/compiler/tf2circle-ui-check/checkall.sh b/compiler/tf2circle-ui-check/checkall.sh
new file mode 100755
index 000000000..447bf041e
--- /dev/null
+++ b/compiler/tf2circle-ui-check/checkall.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+# USAGE: check_all.sh [CONFIG] [TEST 1] [TEST 2] ...
+CONFIG_PATH="$1"; shift
+
+source "${CONFIG_PATH}"
+
+echo "###"
+echo "### tf2circle UI check"
+echo "###"
+echo
+
+echo "Get each test from '${TESTCASE_BASE}/'"
+echo "Use tfkit at '${TFKIT_PATH}'"
+echo "Use tf2circle at '${TF2CIRCLE_PATH}'"
+echo
+
+while [[ $# -ne 0 ]]; do
+ NAME="$1"; shift
+ TESTCASE_DIR="${TESTCASE_BASE}/${NAME}"
+
+ INFO_FILE="${TESTCASE_DIR}/test.info"
+ PBTXT_FILE="${TESTCASE_DIR}/test.pbtxt"
+ MANIFEST_FILE="${TESTCASE_DIR}/test.manifest"
+
+ PB_FILE="${NAME}.pb"
+
+
+ echo "Running '${NAME}'"
+ if [[ -f ${MANIFEST_FILE} ]]; then
+ # TODO Only dump SUMMARY
+ cat ${MANIFEST_FILE}
+ fi
+ echo
+
+ # Create a pb model
+ "${TFKIT_PATH}" encode "${PBTXT_FILE}" "${PB_FILE}"
+
+ echo "OUTPUT:"
+ echo "---------------------------------------------------------"
+ # Generate circle
+ "${TF2CIRCLE_PATH}" "${INFO_FILE}" "${PB_FILE}" "${NAME}.circle"
+ EXITCODE=$?
+ echo "---------------------------------------------------------"
+
+ echo
+ echo "EXITCODE: ${EXITCODE}"
+
+ echo "Running '${NAME}' - Done"
+done
+
+echo
+echo "###"
+echo "### tf2circle UI check (done)"
+echo "###"
+
+exit 0
diff --git a/compiler/tf2circle-ui-check/requires.cmake b/compiler/tf2circle-ui-check/requires.cmake
new file mode 100644
index 000000000..2f87c6a91
--- /dev/null
+++ b/compiler/tf2circle-ui-check/requires.cmake
@@ -0,0 +1,2 @@
+require("tfkit")
+require("tf2circle")
diff --git a/compiler/tf2circle-value-pbtxt-remote-test/.gitignore b/compiler/tf2circle-value-pbtxt-remote-test/.gitignore
new file mode 100644
index 000000000..a178c13cd
--- /dev/null
+++ b/compiler/tf2circle-value-pbtxt-remote-test/.gitignore
@@ -0,0 +1 @@
+/test.lst
diff --git a/compiler/tf2circle-value-pbtxt-remote-test/CMakeLists.txt b/compiler/tf2circle-value-pbtxt-remote-test/CMakeLists.txt
new file mode 100644
index 000000000..852018e64
--- /dev/null
+++ b/compiler/tf2circle-value-pbtxt-remote-test/CMakeLists.txt
@@ -0,0 +1,169 @@
+nnas_include(TargetRequire)
+
+unset(REQUIRED_TARGETS)
+list(APPEND REQUIRED_TARGETS tf2circle)
+list(APPEND REQUIRED_TARGETS tfkit)
+list(APPEND REQUIRED_TARGETS nnkit-run)
+list(APPEND REQUIRED_TARGETS nnkit_tf_backend)
+list(APPEND REQUIRED_TARGETS nnkit_randomize_action)
+list(APPEND REQUIRED_TARGETS nnkit_HDF5_export_action)
+list(APPEND REQUIRED_TARGETS nnkit_HDF5_import_action)
+TargetRequire_Return(${REQUIRED_TARGETS})
+
+message(STATUS "tf2circle-value_pbtxt-remote-test: run tests")
+
+nncc_find_resource(TensorFlowTests)
+
+#
+# Copy [PREFIX]/test.pbtxt to PREFIX.pbtxt in binary folder
+# Copy [PREFIX]/test.info to PREFIX.info in binary folder
+# Copy [PREFIX]/customop.conf to PREFIX.customop.conf in binary folder
+# Encode PREFIX.pbtxt to PREFIX.pb
+#
+set(TEST_REPO "${TensorFlowTests_DIR}")
+set(TEST_PBTXT_FILENAME "test.pbtxt")
+set(TEST_INFO_FILENAME "test.info")
+set(TEST_CUSTOMOP_CONF_FILENAME "customop.conf")
+
+unset(TESTCASES)
+
+macro(add NAME)
+ list(APPEND TESTCASES ${NAME})
+endmacro(add)
+
+# Read "test.lst" if exists
+include("test.lst" OPTIONAL)
+
+# Do not make test if there are no remote machine information
+if(NOT REMOTE_IP)
+ return()
+endif(NOT REMOTE_IP)
+
+if(NOT REMOTE_USER)
+ return()
+endif(NOT REMOTE_USER)
+
+# Do not make test if there are no testcases
+if(NOT TESTCASES)
+ return()
+endif(NOT TESTCASES)
+
+unset(TEST_DEPS)
+unset(TEST_NAMES)
+
+foreach(PREFIX IN ITEMS ${TESTCASES})
+ if(NOT IS_DIRECTORY "${TEST_REPO}/${PREFIX}")
+ message(FATAL_ERROR "Missing '${PREFIX}' test")
+ endif()
+
+ set(PBTXT_SOURCE_PATH "${TEST_REPO}/${PREFIX}/${TEST_PBTXT_FILENAME}")
+ set(INFO_SOURCE_PATH "${TEST_REPO}/${PREFIX}/${TEST_INFO_FILENAME}")
+ set(CUSTOMOP_CONF_SOURCE_PATH "${TEST_REPO}/${PREFIX}/${TEST_CUSTOMOP_CONF_FILENAME}")
+
+ set(PBTXT_FILE "${PREFIX}.pbtxt")
+ set(PBTXT_PATH "${CMAKE_CURRENT_BINARY_DIR}/${PBTXT_FILE}")
+
+ set(INFO_FILE "${PREFIX}.info")
+ set(INFO_PATH "${CMAKE_CURRENT_BINARY_DIR}/${INFO_FILE}")
+
+ set(CUSTOMOP_CONF_FILE "${PREFIX}.${TEST_CUSTOMOP_CONF_FILENAME}") # ex) CustomOp_001.customop.conf
+ set(CUSTOMOP_CONF_PATH "${CMAKE_CURRENT_BINARY_DIR}/${CUSTOMOP_CONF_FILE}")
+
+ set(PB_FILE "${PREFIX}.pb")
+ set(PB_PATH "${CMAKE_CURRENT_BINARY_DIR}/${PB_FILE}")
+
+ # Copy .pbtxt
+ add_custom_command(OUTPUT ${PBTXT_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${PBTXT_SOURCE_PATH}" "${PBTXT_PATH}"
+ DEPENDS ${PBTXT_SOURCE_PATH}
+ COMMENT "Generate ${PBTXT_FILE}"
+ )
+
+ # Copy .info
+ add_custom_command(OUTPUT ${INFO_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${INFO_SOURCE_PATH}" "${INFO_PATH}"
+ DEPENDS ${INFO_SOURCE_PATH}
+ COMMENT "Generate ${INFO_FILE}"
+ )
+
+ # Generate .pb from .pbtxt
+ add_custom_command(OUTPUT ${PB_PATH}
+ COMMAND $<TARGET_FILE:tfkit> encode ${PBTXT_PATH} ${PB_PATH}
+ DEPENDS ${PBTXT_PATH}
+ COMMENT "Generate ${PB_FILE}"
+ )
+
+ list(APPEND TEST_DEPS ${INFO_PATH} ${PB_PATH})
+
+ if (EXISTS "${CUSTOMOP_CONF_SOURCE_PATH}")
+
+ # Copy customop.conf
+ add_custom_command(OUTPUT ${CUSTOMOP_CONF_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${CUSTOMOP_CONF_SOURCE_PATH}" "${CUSTOMOP_CONF_PATH}"
+ DEPENDS ${CUSTOMOP_CONF_SOURCE_PATH}
+ COMMENT "Generate ${CUSTOMOP_CONF_FILE}"
+ )
+
+ list(APPEND TEST_DEPS ${CUSTOMOP_CONF_PATH})
+
+ endif (EXISTS "${CUSTOMOP_CONF_SOURCE_PATH}")
+
+ list(APPEND TEST_NAMES ${PREFIX})
+endforeach(PREFIX)
+
+##
+## Copy testall
+##
+set(TEST_RUNNER "${CMAKE_CURRENT_BINARY_DIR}/testall.sh")
+set(TEST_RUNNER_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/testall.sh")
+
+add_custom_command(
+ OUTPUT ${TEST_RUNNER}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TEST_RUNNER_SOURCE}" "${TEST_RUNNER}"
+ DEPENDS ${TEST_RUNNER_SOURCE}
+ COMMENT "Generate test runner"
+)
+
+list(APPEND TEST_DEPS "${TEST_RUNNER}")
+
+###
+### Generate test.config
+###
+set(TEST_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/test.config")
+
+add_custom_command(
+ OUTPUT ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'NNKIT_RUN_PATH=\"$<TARGET_FILE:nnkit-run>\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TF_BACKEND_PATH=\"$<TARGET_FILE:nnkit_tf_backend>\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TF2CIRCLE_PATH=\"$<TARGET_FILE:tf2circle>\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'RANDOMIZE_ACTION_PATH=\"$<TARGET_FILE:nnkit_randomize_action>\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'HDF5_EXPORT_ACTION_PATH=\"$<TARGET_FILE:nnkit_HDF5_export_action>\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'HDF5_IMPORT_ACTION_PATH=\"$<TARGET_FILE:nnkit_HDF5_import_action>\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'MODEL2NNPKG_PATH=\"${NNAS_PROJECT_SOURCE_DIR}/tools/nnpackage_tool/model2nnpkg/model2nnpkg.sh\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'RUNTIME_LIBRARY_PATH=\"${NNAS_PROJECT_SOURCE_DIR}/Product/out/\"' >> ${TEST_CONFIG}
+ DEPENDS
+ nnkit-run
+ nnkit_tf_backend
+ tf2circle
+ nnkit_randomize_action
+ nnkit_HDF5_export_action
+ nnkit_HDF5_import_action
+ COMMENT "Generate test configuration"
+)
+
+list(APPEND TEST_DEPS "${TEST_CONFIG}")
+
+# This "tf2circle_value_pbtxt_remote_test_deps" target enforces CMake to generate all the dependencies during "build" phase
+add_custom_target(tf2circle_value_pbtxt_remote_test_deps ALL DEPENDS ${TEST_DEPS})
+
+# Run tests
+add_test(
+ NAME tf2circle_value_pbtxt_remote_test
+ COMMAND "${TEST_RUNNER}"
+ "${TEST_CONFIG}"
+ "${CMAKE_CURRENT_BINARY_DIR}"
+ "${REMOTE_IP}"
+ "${REMOTE_USER}"
+ ${TEST_NAMES}
+)
diff --git a/compiler/tf2circle-value-pbtxt-remote-test/README.md b/compiler/tf2circle-value-pbtxt-remote-test/README.md
new file mode 100644
index 000000000..0d41b0a48
--- /dev/null
+++ b/compiler/tf2circle-value-pbtxt-remote-test/README.md
@@ -0,0 +1,138 @@
+# tf2circle-value-pbtxt-remote-test
+
+`tf2circle-value-pbtxt-remote-test` does random value test for `.circle` file using remote machine, normally Odroid, which `nnfw` runs on.
+
+### Prerequisites
+
+1. Tensorflow library
+ - Make sure that Tensorflow library could be found at `nncc configure` step. If there is no Tensorflow library, this test will not be created.
+ - If CMake reports TensorFlow library is not found in configure step, even when the library exists, set [`TENSORFLOW_PREFIX`](../../infra/cmake/packages/TensorFlowConfig.cmake#1) to include Tensorflow library like below.
+ ```sh
+ $ ./nncc configure -DTENSORFLOW_PREFIX=/path/to/Tensorflow/library
+ ```
+ - `TENSORFLOW_PREFIX` should contain Tensorflow library as shown below.
+ ```
+ TENSORFLOW_PREFIX
+ ├ include
+ | ├ tensorflow
+ | | └ c
+ | | ├ c_api.h
+ | ├ ...
+ |
+ ├ lib
+ | ├ libtensorflow.so
+ | ├ ...
+ ├ ...
+ ```
+1. Runtime Library and Binary files
+ - Detailed information is located in [here](../../docs/nnfw/howto/CrossBuildForArm.md)
+ - If you build runtime, related files will be produced in `Product/out`. Do not rename or move it.
+ - (TBD) Support native build option
+1. Remote machine information and test list
+ - You should create `test.lst` file first as shown below.
+ - Set IP address and username of remote machine using `set` command.
+ - Add Tensorflow models which you want to verify, which are in `/res/TensorflowTests/`
+ ```cmake
+ #--------------- Remote Machine Setting ---------------#
+ set(REMOTE_IP "xxx.xxx.xxx.xxx")
+ set(REMOTE_USER "remote_username")
+
+ #--------------------- Tests list ---------------------#
+ add(UNIT_Add_000)
+ add(UNIT_Add_001)
+ ...
+ ```
+ - If any Tensorflow model is added, or if `REMOTE_IP` and `REMOTE_USER` is not given, `tf2circle-value-pbtxt-remote-test` will not be created.
+1. (Optional) ssh authentication
+ - This test uses `ssh` and `scp` commands, and those commands require a password of remote machine whenever they are called. This means that you should enter the password everytime when `ssh` and `scp` require.
+ - This test resolves the problem by using `ssh-copy-id`, which copies the public key of host machine to `authorized_keys` of remote machine. Because of that, this test will ask the password of remote machine only once, at the first time. This is the only user interaction while running this test.
+ - If you do not want to interact with system, just do `ssh-copy-id ${REMOTE_USER}@${REMOTE_IP}` in advance, before running this test. Once `ssh-copy-id` is done, there will be no user-interaction action while running the test.
+
+### Running
+
+- If you finished prerequisites properly, configuring -> building -> testing steps create cmake test automatically.
+- All the related materials will be sent to `REMOTE_WORKDIR` in remote machine. Default value of `REMOTE_WORKDIR` is `CVT_YYMMDD_hhmmss`, which means Circle Value Test done on YY/MM/DD at hh:mm:ss.
+- `REMOTE_WORKDIR` will not be removed automatically after this test finish.
+ ```sh
+ $ ./nncc configure && ./nncc build
+
+ # Default REMOTE_WORKDIR is CVT_YYMMDD_hhmmss folder
+ $ ./nncc test -R tf2circle_value_pbtxt_remote_test
+
+ # You can set REMOTE_WORKDIR where you have write privilege
+ $ REMOTE_WORKDIR=/path/you/want/ ./nncc test -R tf2circle_value_pbtxt_remote_test
+ ```
+
+### Generated Files While Running
+
+- All related files(`pb`, `circle`, `h5` ... etc.) are created in `build/compiler/tf2circle-value-pbtxt-remote-test` folder.
+ ```
+ build/compiler/tf2circle-value-pbtxt-remote-test
+ ├ Result_latest -> Result_YYMMDD_hhmmss.csv
+ ├ Result_YYMMDD_hhmmss.csv
+ ├ ...
+ |
+ ├ UNIT_Add_000
+ | ├ metadata
+ | | ├ MANIFEST
+ | | └ tc
+ | | ├ expected.h5
+ | | └ input.h5
+ | └ UNIT_Add_000.circle
+ |
+ ├ UNIT_Add_000.circle
+ ├ UNIT_Add_000.expected.h5
+ ├ UNIT_Add_000.info
+ ├ UNIT_Add_000.input.h5
+ ├ UNIT_Add_000.log
+ ├ UNIT_Add_000.passed
+ ├ UNIT_Add_000.pb
+ ├ UNIT_Add_000.pbtxt
+ |
+ ├ ...
+ ```
+- Runtime products and each nnpackage are sent to `REMOTE_WORKDIR` in remote machine.
+- (TBD) Modify script not to remove obtained h5 file.
+ ```
+ REMOTE_WORKDIR
+ |
+ ├ Product
+ | └ out
+ | ├ bin
+ | ├ lib
+ | ├ test
+ | ├ ...
+ |
+ ├ UNIT_Add_000
+ | ├ metadata
+ | | ├ MANIFEST
+ | | └ tc
+ | | ├ expected.h5
+ | | ├ input.h5
+ | | └ UNIT_Add_000.out.h5
+ | | (Only when comparing with expected.h5 fails)
+ | |
+ | └ UNIT_Add_000.circle
+ ├ ...
+ ```
+
+### Check Test Result
+
+- Summary of test result will be created as csv file in host.
+ ```sh
+ # Result_latest is symbolic link to the latest csv result file
+ # Print the latest test result
+ $ cat build/compiler/tf2circle-value-pbtxt-remote-test/Result_latest
+ TEST_NAME, TF2CIRCLE, CIRCLE_VALUE_TEST
+ UNIT_Add_000, TRUE, TRUE
+ ...
+
+ # List all result csv files
+ $ ls build/compiler/tf2circle-value-pbtxt-remote-test/Result_*.csv
+ Result_20191119_212521.csv
+ ...
+ ```
+- Detailed log file for each test cases is also created.
+ ```sh
+ $ cat build/compiler/tf2circle-value-pbtxt-remote-test/*.log
+ ```
diff --git a/compiler/tf2circle-value-pbtxt-remote-test/requires.cmake b/compiler/tf2circle-value-pbtxt-remote-test/requires.cmake
new file mode 100644
index 000000000..8d05cb50b
--- /dev/null
+++ b/compiler/tf2circle-value-pbtxt-remote-test/requires.cmake
@@ -0,0 +1,3 @@
+require("tf2circle")
+require("nnkit")
+require("tfkit")
diff --git a/compiler/tf2circle-value-pbtxt-remote-test/testall.sh b/compiler/tf2circle-value-pbtxt-remote-test/testall.sh
new file mode 100755
index 000000000..c80b00a14
--- /dev/null
+++ b/compiler/tf2circle-value-pbtxt-remote-test/testall.sh
@@ -0,0 +1,152 @@
+#!/bin/bash
+
+# Need at least 4 arguments
+if [[ $# -lt 4 ]]; then
+ echo "USAGE: $0 ..."
+ echo
+ echo "ARGUMENTS:"
+ echo " [test.config path]"
+ echo " [WORKDIR]"
+ echo " [REMOTE_IP]"
+ echo " [REMOTE_USER]"
+ echo " [Prefix1]"
+ echo " [Prefix2]"
+ echo " ..."
+ exit 255
+fi
+
+CONFIG_PATH="$1"; shift
+WORKDIR="$1"; shift
+REMOTE_IP="$1"; shift
+REMOTE_USER="$1"; shift
+
+CURRENT_DATETIME=$(date +'%Y%m%d_%H%M%S')
+REMOTE_WORKDIR=${REMOTE_WORKDIR:-"CVT_${CURRENT_DATETIME}"}
+RESULT_CSV="${WORKDIR}/Result_${CURRENT_DATETIME}.csv"
+
+source "${CONFIG_PATH}"
+
+echo "-- Found nnkit-run: ${NNKIT_RUN_PATH}"
+echo "-- Found TF backend: ${TF_BACKEND_PATH}"
+echo "-- Found TF2CIRCLE: ${TF2CIRCLE_PATH}"
+echo "-- Found MODEL2NNPKG: ${MODEL2NNPKG_PATH}"
+echo "-- Found Runtime library: ${RUNTIME_LIBRARY_PATH}"
+echo "-- Found randomize action: ${RANDOMIZE_ACTION_PATH}"
+echo "-- Found HDF5 export action: ${HDF5_EXPORT_ACTION_PATH}"
+echo "-- Found HDF5 import action: ${HDF5_IMPORT_ACTION_PATH}"
+echo "-- Found workdir: ${WORKDIR}"
+
+if [ -z ${MODEL2NNPKG_PATH} ] || [ ! -f ${MODEL2NNPKG_PATH} ]; then
+ echo "MODEL2NNPKG is not found"
+ exit 3
+fi
+
+# Register remote machine ssh information
+cat /dev/zero | ssh-keygen -q -N ""
+ssh-copy-id -o ConnectTimeout=5 "${REMOTE_USER}@${REMOTE_IP}"
+
+# Odroid IP address validation
+if [[ $? -ne 0 ]]; then
+ echo "Cannot reach to given remote machine. Check IP address or username."
+ exit 5
+fi
+
+# Send runtime library files
+ssh "${REMOTE_USER}@${REMOTE_IP}" "mkdir -p ${REMOTE_WORKDIR}/Product/"
+scp -r "${RUNTIME_LIBRARY_PATH}" "${REMOTE_USER}@${REMOTE_IP}:${REMOTE_WORKDIR}/Product/"
+
+TESTED=()
+PASSED=()
+FAILED=()
+echo "TEST_NAME, TF2CIRCLE, CIRCLE_VALUE_TEST" >> ${RESULT_CSV}
+
+pushd "${WORKDIR}"
+while [[ $# -ne 0 ]]; do
+ PREFIX="$1"; shift
+
+ TESTED+=("${PREFIX}")
+
+ PASSED_TAG="${PREFIX}.passed"
+
+ rm -f "${PASSED_TAG}" "${PREFIX}.circle"
+
+ # Information to be recorded
+ TF2CIRCLE_PASSED=FALSE
+ CIRCLE_VALUE_PASSED=FALSE
+
+ cat > "${PREFIX}.log" <(
+ exec 2>&1
+
+ echo "-- Found pb: ${PREFIX}.pb"
+
+ # Exit immediately if any command fails
+ set -e
+ # Show commands
+ set -x
+
+ # Generate circle
+ "${TF2CIRCLE_PATH}" \
+ "${WORKDIR}/${PREFIX}.info" \
+ "${WORKDIR}/${PREFIX}.pb" \
+ "${WORKDIR}/${PREFIX}.circle"
+
+ # Run TensorFlow
+ "${NNKIT_RUN_PATH}" \
+ --backend "${TF_BACKEND_PATH}" \
+ --backend-arg "${WORKDIR}/${PREFIX}.pb" \
+ --backend-arg "${WORKDIR}/${PREFIX}.info" \
+ --pre "${RANDOMIZE_ACTION_PATH}" \
+ --pre "${HDF5_EXPORT_ACTION_PATH}" \
+ --pre-arg "${WORKDIR}/${PREFIX}.input.h5" \
+ --post "${HDF5_EXPORT_ACTION_PATH}" \
+ --post-arg "${WORKDIR}/${PREFIX}.expected.h5"
+
+ # Generate nnpackage model
+ "${MODEL2NNPKG_PATH}" -o "${WORKDIR}" "${WORKDIR}/${PREFIX}.circle"
+
+ # Copy h5 files into nnpackage
+ mkdir -p "${WORKDIR}/${PREFIX}/metadata/tc"
+ cp "${WORKDIR}/${PREFIX}.input.h5" "${WORKDIR}/${PREFIX}/metadata/tc/input.h5"
+ cp "${WORKDIR}/${PREFIX}.expected.h5" "${WORKDIR}/${PREFIX}/metadata/tc/expected.h5"
+
+ # Run test_arm_nnpkg in remote machine
+ scp -r "${WORKDIR}/${PREFIX}/" "${REMOTE_USER}@${REMOTE_IP}:${REMOTE_WORKDIR}/${PREFIX}/"
+ ssh "${REMOTE_USER}@${REMOTE_IP}" "cd ${REMOTE_WORKDIR}; ./Product/out/test/onert-test nnpkg-test -i . -o ${PREFIX}/metadata/tc ${PREFIX}"
+
+ if [[ $? -eq 0 ]]; then
+ touch "${PASSED_TAG}"
+ fi
+ )
+
+ if [[ -f "${PREFIX}.circle" ]]; then
+ TF2CIRCLE_PASSED=TRUE
+ else
+ TF2CIRCLE_PASSED=FALSE
+ fi
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ PASSED+=("$PREFIX")
+ CIRCLE_VALUE_PASSED=TRUE
+ else
+ FAILED+=("$PREFIX")
+ CIRCLE_VALUE_PASSED=FALSE
+ fi
+
+ echo "${PREFIX}, ${TF2CIRCLE_PASSED}, ${CIRCLE_VALUE_PASSED}" >> ${RESULT_CSV}
+done
+popd
+
+rm -f Result_latest
+ln -s ${RESULT_CSV} Result_latest
+
+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/tf2circle/CMakeLists.txt b/compiler/tf2circle/CMakeLists.txt
new file mode 100644
index 000000000..549f731a4
--- /dev/null
+++ b/compiler/tf2circle/CMakeLists.txt
@@ -0,0 +1,47 @@
+# TODO Allow users to force tf2circle build
+if(NOT TARGET moco_tf_frontend)
+ return()
+endif(NOT TARGET moco_tf_frontend)
+
+if(NOT TARGET tfinfo)
+ return()
+endif(NOT TARGET tfinfo)
+
+if(NOT TARGET exo)
+ return()
+endif(NOT TARGET exo)
+
+nnas_find_package(Protobuf QUIET)
+
+if(NOT Protobuf_FOUND)
+ return()
+endif(NOT Protobuf_FOUND)
+
+# generating and building schema for customop.conf
+Protobuf_Generate(CIRCLE_CUSTOMOP_INFO_PROTO
+ "${CMAKE_CURRENT_BINARY_DIR}/generated"
+ "./proto"
+ CustomOpInfo.proto)
+
+add_library(tf2circle_customop_info_proto STATIC ${CIRCLE_CUSTOMOP_INFO_PROTO_SOURCES})
+set_target_properties(tf2circle_customop_info_proto PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(tf2circle_customop_info_proto PUBLIC ${CIRCLE_CUSTOMOP_INFO_PROTO_INCLUDE_DIRS})
+target_link_libraries(tf2circle_customop_info_proto PUBLIC libprotobuf)
+install(TARGETS tf2circle_customop_info_proto DESTINATION lib)
+
+message(STATUS "Build tf2circle: TRUE")
+
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+add_executable(tf2circle ${SOURCES})
+target_link_libraries(tf2circle PRIVATE moco_log)
+target_link_libraries(tf2circle PRIVATE moco_tf_frontend)
+target_link_libraries(tf2circle PRIVATE tfinfo)
+target_link_libraries(tf2circle PRIVATE exo)
+target_link_libraries(tf2circle PRIVATE locop)
+target_link_libraries(tf2circle PRIVATE hermes_std)
+target_link_libraries(tf2circle PRIVATE stdex)
+target_link_libraries(tf2circle PRIVATE angkor cwrap)
+target_link_libraries(tf2circle PRIVATE tf2circle_customop_info_proto)
+
+install(TARGETS tf2circle DESTINATION bin)
diff --git a/compiler/tf2circle/README.md b/compiler/tf2circle/README.md
new file mode 100644
index 000000000..421431294
--- /dev/null
+++ b/compiler/tf2circle/README.md
@@ -0,0 +1,3 @@
+# tf2circle
+
+_tf2circle_ is a TensorFlow-to-Circle model converter.
diff --git a/compiler/tf2circle/proto/CustomOpInfo.proto b/compiler/tf2circle/proto/CustomOpInfo.proto
new file mode 100644
index 000000000..753370aea
--- /dev/null
+++ b/compiler/tf2circle/proto/CustomOpInfo.proto
@@ -0,0 +1,57 @@
+syntax = "proto3";
+
+package tf2circle;
+option cc_enable_arenas = true;
+
+/* example of prototxt file
+ custom_op {
+ name: "my/customOp/000"
+ op: "new_custom_op"
+ attr {
+ key: "output_shape"
+ value {
+ shape {
+ dim { size: 1 }
+ dim { size: 2 }
+ dim { size: 1 }
+ dim { size: 2 }
+ }
+ }
+ }
+ }
+*/
+
+enum DataType {
+ // Not a legal value for DataType. Used to indicate a DataType field
+ // has not been set.
+ DT_INVALID = 0;
+
+ DT_FLOAT = 1;
+ DT_INT32 = 15; // Set to 15, considering possibility for reordering. 10 for INT, 10+N for INT 2^N
+ // TODO Support more types
+}
+
+message ShapeProto {
+ message Dim {
+ int64 size = 1; // tensorflow uses int64
+ };
+
+ repeated Dim dim = 2;
+}
+
+message AttrValue {
+ oneof value {
+ ShapeProto shape = 1;
+ DataType type = 2;
+ }
+}
+
+message CustomOpDef {
+ string name = 1;
+ string op = 2;
+ map<string, AttrValue> attr = 3;
+}
+
+message CustomOpInfoDef {
+ repeated CustomOpDef custom_op = 1;
+}
diff --git a/compiler/tf2circle/requires.cmake b/compiler/tf2circle/requires.cmake
new file mode 100644
index 000000000..68d45bf3a
--- /dev/null
+++ b/compiler/tf2circle/requires.cmake
@@ -0,0 +1,8 @@
+require("stdex")
+require("hermes-std")
+require("moco-tf")
+require("exo")
+require("locop")
+require("loco")
+require("cwrap")
+require("angkor")
diff --git a/compiler/tf2circle/src/CustomopConfLoader.cpp b/compiler/tf2circle/src/CustomopConfLoader.cpp
new file mode 100644
index 000000000..412405893
--- /dev/null
+++ b/compiler/tf2circle/src/CustomopConfLoader.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CustomopConfLoader.h"
+
+#include <loco.h>
+#include <cwrap/Fildes.h>
+#include <angkor/TensorShape.h>
+
+#include <CustomOpInfo.pb.h>
+
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/text_format.h>
+
+#include <fcntl.h>
+
+namespace
+{
+bool load_text(const cwrap::Fildes &fildes, tf2circle::CustomOpInfoDef &def)
+{
+ google::protobuf::io::FileInputStream fis(fildes.get());
+
+ return google::protobuf::TextFormat::Parse(&fis, &def);
+}
+
+angkor::TensorShape convert_shape(const tf2circle::ShapeProto &shape)
+{
+ angkor::TensorShape to_shape;
+
+ int64_t rank64 = shape.dim_size();
+ assert(rank64 < std::numeric_limits<uint32_t>::max());
+
+ int32_t rank = static_cast<int32_t>(rank64);
+ to_shape.resize(rank);
+
+ for (int32_t d = 0; d < rank; d++)
+ {
+ int64_t dim_value = shape.dim(d).size();
+ assert(dim_value >= 0ULL);
+ assert(dim_value < std::numeric_limits<uint32_t>::max());
+
+ uint32_t dim_value32 = static_cast<uint32_t>(dim_value);
+ to_shape.dim(d) = dim_value32;
+ }
+
+ return to_shape;
+}
+
+loco::DataType convert_dtype(const tf2circle::DataType &dtype)
+{
+ if (dtype == tf2circle::DT_FLOAT)
+ return loco::DataType::FLOAT32;
+ else if (dtype == tf2circle::DT_INT32)
+ return loco::DataType::S32;
+ else
+ throw std::runtime_error("Not yet supported datatype. Cannot convert.");
+}
+
+// Note : the following functions look similar with plier::tf::Convert.h.
+// However, the schema is different.(not "tensorflow::..." but "tf2circle::...")
+// So, plier::tf cannot be used.
+loco::DataType get_dtype_attr(const tf2circle::CustomOpDef &custom_op)
+{
+ std::string type_attr_name("dtype");
+
+ assert(custom_op.attr().count(type_attr_name) > 0);
+ const auto &attr = custom_op.attr().at(type_attr_name);
+ assert(attr.value_case() == tf2circle::AttrValue::kType);
+ auto dtype_def = attr.type();
+
+ return convert_dtype(dtype_def);
+}
+
+angkor::TensorShape get_shape_attr(const tf2circle::CustomOpDef &custom_op)
+{
+ std::string shape_attr_name("output_shape");
+
+ assert(custom_op.attr().count(shape_attr_name) > 0);
+ const auto &attr = custom_op.attr().at(shape_attr_name);
+ assert(attr.value_case() == tf2circle::AttrValue::kShape);
+ auto shape_def = attr.shape();
+
+ return convert_shape(shape_def);
+}
+
+void add_customop(tf2circle::CustomOpInfoDef &def, moco::ModelSignature &sig)
+{
+ for (const auto &custom_op : def.custom_op())
+ {
+ sig.add_customop(custom_op.op());
+
+ auto name = custom_op.name();
+
+ // setting dtype and shape to ModelSignature
+ sig.dtype(name, get_dtype_attr(custom_op));
+ sig.shape(name, get_shape_attr(custom_op));
+ }
+}
+
+} // namespace
+
+namespace tf2circle
+{
+
+void load_customop_conf(const std::string &path, moco::ModelSignature &sig)
+{
+ CustomOpInfoDef def;
+
+ // TODO Consider Windows
+ cwrap::Fildes fildes{open(path.c_str(), O_RDONLY)};
+
+ if (fildes.get() < 0)
+ {
+ throw std::runtime_error{"Error: " + path + " not found"};
+ }
+
+ if (!load_text(fildes, def))
+ {
+ throw std::runtime_error{"Error: Failed to parse prototxt " + path};
+ }
+
+ add_customop(def, sig);
+}
+
+} // namespace tf2circle
diff --git a/compiler/tf2circle/src/CustomopConfLoader.h b/compiler/tf2circle/src/CustomopConfLoader.h
new file mode 100644
index 000000000..500b6acdf
--- /dev/null
+++ b/compiler/tf2circle/src/CustomopConfLoader.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUSTOMOP_CONF_LOADER_H__
+#define __CUSTOMOP_CONF_LOADER_H__
+
+#include <moco/tf/Frontend.h>
+
+#include <string>
+
+namespace tf2circle
+{
+
+/// @brief Loads customop.conf into ModelSignature
+void load_customop_conf(const std::string &path, moco::ModelSignature &sig);
+
+} // namespace tf2circle
+
+#endif // __CUSTOMOP_CONF_LOADER_H__
diff --git a/compiler/tf2circle/src/tf2circle.cpp b/compiler/tf2circle/src/tf2circle.cpp
new file mode 100644
index 000000000..a1160e968
--- /dev/null
+++ b/compiler/tf2circle/src/tf2circle.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CustomopConfLoader.h"
+
+#include <moco/LoggingContext.h>
+#include <moco/tf/Frontend.h>
+#include <exo/LoggingContext.h>
+#include <exo/CircleExporter.h>
+
+#include <nnkit/support/tftestinfo/TensorInfoParser.h>
+
+#include <locop/FormattedGraph.h>
+
+#include <hermes/ConsoleReporter.h>
+#include <hermes/EnvConfig.h>
+
+#include <stdex/Memory.h>
+
+#include <cassert>
+
+#include <iostream>
+#include <stdexcept>
+#include <string>
+
+namespace
+{
+
+std::unique_ptr<loco::Graph> import(const moco::ModelSignature &sig, const std::string &path)
+{
+ moco::tf::Frontend frontend;
+ return frontend.load(sig, path.c_str(), moco::tf::Frontend::FileType::Binary);
+}
+
+} // namespace
+
+//
+// Logging Support
+//
+namespace
+{
+
+struct Logger final : public hermes::Source
+{
+ Logger(hermes::Context *ctx) { activate(ctx->sources(), ctx->bus()); }
+ ~Logger() { deactivate(); }
+};
+
+struct LoggingContext
+{
+ static hermes::Context *get(void)
+ {
+ using EnvConfig = hermes::EnvConfig<hermes::EnvFormat::BooleanNumber>;
+
+ static hermes::Context *ctx = nullptr;
+
+ if (ctx == nullptr)
+ {
+ ctx = new hermes::Context;
+ ctx->sinks()->append(stdex::make_unique<hermes::ConsoleReporter>());
+ ctx->config(stdex::make_unique<EnvConfig>("TF2CIRCLE_Log"));
+ }
+
+ return ctx;
+ }
+};
+
+void print_help()
+{
+ std::cerr << "Usage: tf2circle <path/to/info> <path/to/pb> <path/to/circle/model> " << std::endl
+ << "Options: --customop <path/to/customop.conf>" << std::endl;
+}
+
+} // namespace
+
+#define LOGGER(name) \
+ ::Logger name { ::LoggingContext::get() }
+
+#define INFO(name) HERMES_INFO(name)
+
+namespace
+{
+
+void internal_error(void)
+{
+ std::cerr << "tf2circle: internal compiler error" << std::endl;
+
+ // TODO Explain how to report a bug
+}
+
+} // namespace
+
+namespace
+{
+
+class EntryFunctor
+{
+public:
+ EntryFunctor();
+
+public:
+ ~EntryFunctor();
+
+public:
+ int operator()(int argc, char **argv) const;
+};
+
+EntryFunctor::EntryFunctor()
+{
+ // NOTE Implement initialization here
+}
+
+EntryFunctor::~EntryFunctor()
+{
+ // NOTE Implement finialization here
+}
+
+int EntryFunctor::operator()(int argc, char **argv) const
+{
+ using EnvConfig = hermes::EnvConfig<hermes::EnvFormat::BooleanNumber>;
+
+ // This line allows users to control all the moco-tf loggers via TF2CIRCLE_Log_Frontend
+ moco::LoggingContext::get()->config(stdex::make_unique<EnvConfig>("TF2CIRCLE_Log_Frontend"));
+ // This line allows users to control all the exo-circle loggers via TF2CIRCLE_Log_Backend
+ exo::LoggingContext::get()->config(stdex::make_unique<EnvConfig>("TF2CIRCLE_Log_Backend"));
+
+ LOGGER(l);
+
+ // TODO We need better args parsing in future
+ if (!(argc == 4 or argc == 6))
+ {
+ print_help();
+ return 255;
+ }
+
+ std::string info_path{argv[1]};
+ std::string tf_path{argv[2]}; // .pb file
+ std::string circle_path{argv[3]};
+
+ std::cout << "Read '" << info_path << "'" << std::endl;
+
+ moco::ModelSignature sig;
+ {
+ for (const auto &info : nnkit::support::tftestinfo::parse(info_path.c_str()))
+ {
+ switch (info->kind())
+ {
+ case nnkit::support::tftestinfo::ParsedTensor::Kind::Input:
+ sig.add_input(moco::TensorName{info->name()});
+ sig.shape(info->name(), info->shape());
+ break;
+
+ case nnkit::support::tftestinfo::ParsedTensor::Kind::Output:
+ sig.add_output(moco::TensorName{info->name()});
+ sig.shape(info->name(), info->shape());
+ break;
+
+ default:
+ throw std::runtime_error{"Unknown kind"};
+ }
+ }
+ }
+
+ if (argc == 6) // optional parameter: path of customop.conf
+ {
+ if (std::string{argv[4]} == "--customop")
+ {
+ tf2circle::load_customop_conf(argv[5], sig);
+ }
+ else
+ {
+ print_help();
+ return 255;
+ }
+ }
+
+ std::cout << "Read '" << info_path << "' - Done" << std::endl;
+
+ std::cout << "Import from '" << tf_path << "'" << std::endl;
+ auto g = import(sig, tf_path);
+ std::cout << "Import from '" << tf_path << "' - Done" << std::endl;
+
+ INFO(l) << "Import Graph" << std::endl;
+ INFO(l) << locop::fmt<locop::Formatter::LinearV1>(g) << std::endl;
+
+ std::cout << "Export into '" << circle_path << "'" << std::endl;
+ exo::CircleExporter(g.get()).dumpToFile(circle_path.c_str());
+ std::cout << "Export into '" << circle_path << "' - Done" << std::endl;
+
+ return 0;
+}
+
+} // namespace
+
+int main(int argc, char **argv)
+{
+ // TODO Add "signal" handler here
+
+ try
+ {
+ EntryFunctor entry;
+ return entry(argc, argv);
+ }
+ catch (...)
+ {
+ // Catch all the exception and print the default error message.
+ internal_error();
+ }
+
+ // EX_SOFTWARE defined in "sysexits.h"
+ return 70;
+}
diff --git a/compiler/tf2nnpackage-value-remote-test/CMakeLists.txt b/compiler/tf2nnpackage-value-remote-test/CMakeLists.txt
new file mode 100644
index 000000000..255806ce8
--- /dev/null
+++ b/compiler/tf2nnpackage-value-remote-test/CMakeLists.txt
@@ -0,0 +1,93 @@
+include("test.lst")
+
+# Do not make test if there are no remote machine information
+if(NOT REMOTE_IP)
+ message(STATUS "tf2nnpackage-value-remote-test: need to set REMOTE IP")
+ return()
+endif(NOT REMOTE_IP)
+
+if(NOT REMOTE_USER)
+ message(STATUS "tf2nnpackage-value-remote-test: need to set REMOTE_USER")
+ return()
+endif(NOT REMOTE_USER)
+
+nnas_include(TargetRequire)
+
+unset(REQUIRED_TARGETS)
+list(APPEND REQUIRED_TARGETS testDataGenerator)
+TargetRequire_Return(${REQUIRED_TARGETS})
+
+message(STATUS "tf2nnpackage-value-remote-test: run tests")
+
+unset(TEST_NAMES)
+
+nncc_find_resource(TensorFlowLiteRecipes)
+set(TFLITE_RECIPE_REPO "${TensorFlowLiteRecipes_DIR}")
+
+file(GLOB SUBDIR RELATIVE ${TFLITE_RECIPE_REPO} ${TFLITE_RECIPE_REPO}/*)
+foreach(DIR IN ITEMS ${SUBDIR})
+ if(IS_DIRECTORY ${TFLITE_RECIPE_REPO}/${DIR})
+ list(APPEND TEST_NAMES ${DIR})
+ endif()
+endforeach()
+
+get_target_property(ARTIFACTS_SRC_PATH testDataGenerator SOURCE_DIR)
+
+# In this test, only the runtime test is performed because the test from tf to
+# nnpackage is done in common-artifacts, and for this runtime test, generation of
+# test data is required. And, tcgenerate in ${ARTIFACTS_SRC_PATH}/exclude.lst
+# means it won't generate test data, which is why below "tcgenerate" macro excludes
+# specific opearators from runtime test.
+# Also, since circlize and optimize macro included in `exclude.lst` file is only
+# needed in common-artifacts, it has no function here.
+macro(circlize)
+endmacro()
+macro(optimize)
+endmacro()
+
+macro(tcgenerate NAME)
+ list(REMOVE_ITEM TEST_NAMES ${NAME})
+endmacro()
+
+include("${ARTIFACTS_SRC_PATH}/exclude.lst")
+
+# Copy testall
+set(TEST_RUNNER_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/testall.sh")
+set(TEST_RUNNER "${CMAKE_CURRENT_BINARY_DIR}/testall.sh")
+
+add_custom_command(
+ OUTPUT ${TEST_RUNNER}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TEST_RUNNER_SOURCE}" "${TEST_RUNNER}"
+ DEPENDS ${TEST_RUNNER_SOURCE}
+ COMMENT "Generate test runner"
+)
+
+list(APPEND TEST_DEPS "${TEST_RUNNER}")
+
+get_target_property(ARTIFACTS_BIN_PATH testDataGenerator BINARY_DIR)
+
+# Generate test.config
+set(TEST_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/test.config")
+
+add_custom_command(
+ OUTPUT ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'RUNTIME_LIBRARY_PATH=\"${NNAS_PROJECT_SOURCE_DIR}/Product/out/\"' >> ${TEST_CONFIG}
+ COMMENT "Generate test configuration"
+)
+
+list(APPEND TEST_DEPS "${TEST_CONFIG}")
+
+# This "tf2nnpackage_value_remote_test_deps" target enforces CMake to generate all the dependencies during "build" phase
+add_custom_target(tf2nnpackage_value_remote_test_deps ALL DEPENDS ${TEST_DEPS})
+
+# Run tests
+add_test(
+ NAME tf2nnpackage_value_remote_test
+ COMMAND "${TEST_RUNNER}"
+ "${TEST_CONFIG}"
+ "${ARTIFACTS_BIN_PATH}"
+ "${REMOTE_IP}"
+ "${REMOTE_USER}"
+ ${TEST_NAMES}
+)
diff --git a/compiler/tf2nnpackage-value-remote-test/README.md b/compiler/tf2nnpackage-value-remote-test/README.md
new file mode 100644
index 000000000..65f307b13
--- /dev/null
+++ b/compiler/tf2nnpackage-value-remote-test/README.md
@@ -0,0 +1,60 @@
+# tf2nnpackage-value-remote-test
+
+`tf2nnpackage-value-remote-test` does random value test for nnpackage file using remote machine, normally Odroid, which `onert` runs on.
+
+### Prerequisites
+
+1. Runtime Library and Binary files
+ - Detailed information is located in [here](../../docs/howto/how-to-cross-build-runtime-for-arm.md)
+ - If you build runtime, related files will be produced in `Product/out`. Do not rename or move it.
+1. Remote machine information and test list
+ - You should create `test.lst` file first as shown below.
+ - Set IP address and username of remote machine using `set` command.
+ ```cmake
+ #--------------- Remote Machine Setting ---------------#
+ set(REMOTE_IP "xxx.xxx.xxx.xxx")
+ set(REMOTE_USER "remote_username")
+ ```
+ - If any recipe is added, or if `REMOTE_IP` and `REMOTE_USER` is not given, `tf2nnpackage-value-remote-test` will not be created.
+1. (Optional) ssh authentication
+ - This test uses `ssh` and `scp` commands, and those commands require a password of remote machine whenever they are called. This means that you should enter the password everytime when `ssh` and `scp` require.
+ - This test resolves the problem by using `ssh-copy-id`, which copies the public key of host machine to `authorized_keys` of remote machine. Because of that, this test will ask the password of remote machine only once, at the first time. This is the only user interaction while running this test.
+ - If you do not want to interact with system, just do `ssh-copy-id ${REMOTE_USER}@${REMOTE_IP}` in advance, before running this test. Once `ssh-copy-id` is done, there will be no user-interaction action while running the test.
+
+### Running
+
+- If you finished prerequisites properly, configuring -> building -> testing steps create cmake test automatically.
+- All the related materials will be sent to `REMOTE_WORKDIR` in remote machine. Default value of `REMOTE_WORKDIR` is `CVT_YYMMDD_hhmmss`, which means Circle Value Test done on YY/MM/DD at hh:mm:ss.
+- `REMOTE_WORKDIR` will not be removed automatically after this test finish.
+ ```sh
+ $ ./nncc configure && ./nncc build
+
+ # Default REMOTE_WORKDIR is CVT_YYMMDD_hhmmss folder
+ $ ./nncc test -R tf2nnpackage_value_remote_test
+
+ # You can set REMOTE_WORKDIR where you have write privilege
+ $ REMOTE_WORKDIR=/path/you/want/ ./nncc test -R tf2circle_value_pbtxt_remote_test
+ ```
+
+### Generated Files While Running
+
+- All related files(`pb`, `circle`, `h5` ... etc.) are taken from `build/compiler/common-artifacts` folder.
+- Runtime products and each nnpackage are sent to `REMOTE_WORKDIR` in remote machine.
+- Each test result is generated in `build/compiler/common-artifacts` with the name `${RECIPE}.log`
+
+### Check Test Result
+
+- Summary of test result will be created as csv file in host.
+ ```sh
+ # Result_latest is symbolic link to the latest csv result file
+ # Print the latest test result
+ $ cat build/compiler/tf2circle-value-pbtxt-remote-test/Result_latest
+ TEST_NAME, TF2CIRCLE, CIRCLE_VALUE_TEST
+ UNIT_Add_000, TRUE, TRUE
+ ...
+
+ # List all result csv files
+ $ ls build/compiler/tf2circle-value-pbtxt-remote-test/Result_*.csv
+ Result_20191119_212521.csv
+ ...
+ ```
diff --git a/compiler/tf2nnpackage-value-remote-test/requires.cmake b/compiler/tf2nnpackage-value-remote-test/requires.cmake
new file mode 100644
index 000000000..06a4a8a6a
--- /dev/null
+++ b/compiler/tf2nnpackage-value-remote-test/requires.cmake
@@ -0,0 +1 @@
+require("common-artifacts")
diff --git a/compiler/tf2nnpackage-value-remote-test/test.lst b/compiler/tf2nnpackage-value-remote-test/test.lst
new file mode 100644
index 000000000..10eb52d92
--- /dev/null
+++ b/compiler/tf2nnpackage-value-remote-test/test.lst
@@ -0,0 +1,3 @@
+#--------------- Remote Machine Setting ---------------#
+# set(REMOTE_IP "xxx.xxx.xxx.xxx")
+# set(REMOTE_USER "remote_username")
diff --git a/compiler/tf2nnpackage-value-remote-test/testall.sh b/compiler/tf2nnpackage-value-remote-test/testall.sh
new file mode 100755
index 000000000..ca672a3eb
--- /dev/null
+++ b/compiler/tf2nnpackage-value-remote-test/testall.sh
@@ -0,0 +1,110 @@
+#!/bin/bash
+
+# Need at least 4 arguments
+if [[ $# -lt 4 ]]; then
+ echo "USAGE: $0 ..."
+ echo
+ echo "ARGUMENTS:"
+ echo " [test.config path]"
+ echo " [WORKDIR]"
+ echo " [REMOTE_IP]"
+ echo " [REMOTE_USER]"
+ echo " [Prefix1]"
+ echo " [Prefix2]"
+ echo " ..."
+ exit 255
+fi
+
+CONFIG_PATH="$1"; shift
+WORKDIR="$1"; shift
+REMOTE_IP="$1"; shift
+REMOTE_USER="$1"; shift
+
+BINDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+CURRENT_DATETIME=$(date +'%Y%m%d_%H%M%S')
+REMOTE_WORKDIR=${REMOTE_WORKDIR:-"CVT_${CURRENT_DATETIME}"}
+RESULT_CSV="${BINDIR}/Result_${CURRENT_DATETIME}.csv"
+
+source "${CONFIG_PATH}"
+
+echo "-- Found Runtime library: ${RUNTIME_LIBRARY_PATH}"
+echo "-- Found workdir: ${WORKDIR}"
+
+# Register remote machine ssh information
+cat /dev/zero | ssh-keygen -q -N ""
+ssh-copy-id -o ConnectTimeout=5 "${REMOTE_USER}@${REMOTE_IP}"
+
+# Odroid IP address validation
+if [[ $? -ne 0 ]]; then
+ echo "Cannot reach to given remote machine. Check IP address or username."
+ exit 5
+fi
+
+# Send runtime library files
+ssh "${REMOTE_USER}@${REMOTE_IP}" "mkdir -p ${REMOTE_WORKDIR}/Product/"
+scp -r "${RUNTIME_LIBRARY_PATH}" "${REMOTE_USER}@${REMOTE_IP}:${REMOTE_WORKDIR}/Product/"
+
+TESTED=()
+PASSED=()
+FAILED=()
+echo "TEST_NAME, CIRCLE_VALUE_TEST" >> ${RESULT_CSV}
+
+pushd "${WORKDIR}"
+while [[ $# -ne 0 ]]; do
+ PREFIX="$1"; shift
+
+ TESTED+=("${PREFIX}")
+
+ PASSED_TAG="${PREFIX}.passed"
+
+ rm -f "${BINDIR}/${PASSED_TAG}"
+
+ # Information to be recorded
+ CIRCLE_VALUE_PASSED=FALSE
+
+ cat > "${BINDIR}/${PREFIX}.log" <(
+ exec 2>&1
+
+ # Exit immediately if any command fails
+ set -e
+ # Show commands
+ set -x
+
+ # Run nnpkg_test in remote machine
+ if [ ! -d "${PREFIX}" ] ; then
+ PREFIX=${PREFIX}.opt ;
+ fi
+ scp -r "${PREFIX}/" "${REMOTE_USER}@${REMOTE_IP}:${REMOTE_WORKDIR}/${PREFIX}/"
+ ssh "${REMOTE_USER}@${REMOTE_IP}" "cd ${REMOTE_WORKDIR}; ./Product/out/test/onert-test nnpkg-test ${PREFIX}"
+
+ if [[ $? -eq 0 ]]; then
+ touch "${BINDIR}/${PASSED_TAG}"
+ fi
+ )
+
+ if [[ -f "${BINDIR}/${PASSED_TAG}" ]]; then
+ PASSED+=("$PREFIX")
+ CIRCLE_VALUE_PASSED=TRUE
+ else
+ FAILED+=("$PREFIX")
+ CIRCLE_VALUE_PASSED=FALSE
+ fi
+
+ echo "${PREFIX}, ${CIRCLE_VALUE_PASSED}" >> ${RESULT_CSV}
+done
+popd
+
+rm -f Result_latest
+ln -s ${RESULT_CSV} Result_latest
+
+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/tf2nnpkg/CMakeLists.txt b/compiler/tf2nnpkg/CMakeLists.txt
new file mode 100644
index 000000000..8e1edf858
--- /dev/null
+++ b/compiler/tf2nnpkg/CMakeLists.txt
@@ -0,0 +1,35 @@
+if(NOT TARGET moco_tf_frontend)
+ return()
+endif(NOT TARGET moco_tf_frontend)
+
+if(NOT TARGET tfinfo)
+ return()
+endif(NOT TARGET tfinfo)
+
+if(NOT TARGET exo)
+ return()
+endif(NOT TARGET exo)
+
+message(STATUS "Build tf2nnpkg: TRUE")
+
+unset(SOURCES)
+list(APPEND SOURCES "src/tf2nnpkg.cpp")
+list(APPEND SOURCES "src/filesystem_common.cpp")
+
+option(TF2NNPKG_FOR_WINDOWS "Use Windows filesystem" OFF)
+if(TF2NNPKG_FOR_WINDOWS)
+ list(APPEND SOURCES "src/filesystem_windows.cpp")
+else()
+ list(APPEND SOURCES "src/filesystem_linux.cpp") # default
+endif()
+
+add_executable(tf2nnpkg ${SOURCES})
+target_link_libraries(tf2nnpkg PRIVATE moco_log)
+target_link_libraries(tf2nnpkg PRIVATE moco_tf_frontend)
+target_link_libraries(tf2nnpkg PRIVATE tfinfo)
+target_link_libraries(tf2nnpkg PRIVATE exo)
+target_link_libraries(tf2nnpkg PRIVATE locop)
+target_link_libraries(tf2nnpkg PRIVATE hermes_std)
+target_link_libraries(tf2nnpkg PRIVATE stdex)
+target_link_libraries(tf2nnpkg PRIVATE angkor cwrap)
+install(TARGETS tf2nnpkg DESTINATION bin)
diff --git a/compiler/tf2nnpkg/README.md b/compiler/tf2nnpkg/README.md
new file mode 100644
index 000000000..f8ec2c90f
--- /dev/null
+++ b/compiler/tf2nnpkg/README.md
@@ -0,0 +1 @@
+# tf2nnpkg
diff --git a/compiler/tf2nnpkg/requires.cmake b/compiler/tf2nnpkg/requires.cmake
new file mode 100644
index 000000000..68d45bf3a
--- /dev/null
+++ b/compiler/tf2nnpkg/requires.cmake
@@ -0,0 +1,8 @@
+require("stdex")
+require("hermes-std")
+require("moco-tf")
+require("exo")
+require("locop")
+require("loco")
+require("cwrap")
+require("angkor")
diff --git a/compiler/tf2nnpkg/src/filesystem.h b/compiler/tf2nnpkg/src/filesystem.h
new file mode 100644
index 000000000..0da608a6f
--- /dev/null
+++ b/compiler/tf2nnpkg/src/filesystem.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TF2NNPKG_FILESYSTEM_H__
+#define __TF2NNPKG_FILESYSTEM_H__
+
+/// @file OS-dependent filesystem functionalities
+
+#include <string>
+
+namespace filesystem
+{
+
+const std::string separator();
+
+/// @brief Normalize compatible separator in path to default separator
+std::string normalize_path(const std::string &path);
+
+bool is_dir(const std::string &path);
+
+bool mkdir(const std::string &path);
+
+// TODO use variadic argument
+std::string join(const std::string &path1, const std::string &path2);
+
+std::string basename(const std::string &path);
+
+} // namespace filesystem
+
+#endif // __TF2NNPKG_FILESYSTEM_H__
diff --git a/compiler/tf2nnpkg/src/filesystem_common.cpp b/compiler/tf2nnpkg/src/filesystem_common.cpp
new file mode 100644
index 000000000..e14528afb
--- /dev/null
+++ b/compiler/tf2nnpkg/src/filesystem_common.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "filesystem.h"
+
+namespace filesystem
+{
+
+std::string join(const std::string &path1, const std::string &path2)
+{
+ // TODO check path1 does not end with separator
+ // TODO check path2 does not start with separator
+ return path1 + separator() + path2;
+}
+
+std::string basename(const std::string &path)
+{
+ auto last_index = path.find_last_of(separator());
+
+ // No separator
+ if (last_index == std::string::npos)
+ return path;
+
+ // Trailing separator
+ if (last_index + separator().size() == path.size())
+ return basename(path.substr(0, last_index));
+
+ return path.substr(last_index + separator().size());
+}
+
+} // namespace filesystem
diff --git a/compiler/tf2nnpkg/src/filesystem_linux.cpp b/compiler/tf2nnpkg/src/filesystem_linux.cpp
new file mode 100644
index 000000000..a2fa96732
--- /dev/null
+++ b/compiler/tf2nnpkg/src/filesystem_linux.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "filesystem.h"
+
+#include <sys/stat.h>
+#include <dirent.h>
+
+namespace filesystem
+{
+
+const std::string separator() { return "/"; }
+
+std::string normalize_path(const std::string &path)
+{
+ // DO NOTHING
+ return path;
+}
+
+bool is_dir(const std::string &path)
+{
+ DIR *dir = opendir(path.c_str());
+ if (dir)
+ {
+ closedir(dir);
+ return true;
+ }
+ return false;
+}
+
+bool mkdir(const std::string &path) { return ::mkdir(path.c_str(), 0775) == 0; }
+
+} // namespace filesystem
diff --git a/compiler/tf2nnpkg/src/filesystem_windows.cpp b/compiler/tf2nnpkg/src/filesystem_windows.cpp
new file mode 100644
index 000000000..e7fba3352
--- /dev/null
+++ b/compiler/tf2nnpkg/src/filesystem_windows.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "filesystem.h"
+
+#include <direct.h>
+#include <windows.h>
+
+namespace filesystem
+{
+
+const std::string separator() { return "\\"; }
+
+std::string normalize_path(const std::string &path)
+{
+ std::string ret = path;
+
+ std::string candidate = "/";
+ size_t start_pos = 0;
+ while ((start_pos = ret.find(candidate, start_pos)) != std::string::npos)
+ {
+ ret.replace(start_pos, candidate.length(), separator());
+ start_pos += separator().length();
+ }
+ return ret;
+}
+
+bool is_dir(const std::string &path)
+{
+ DWORD ftyp = GetFileAttributesA(path.c_str());
+ if (ftyp == INVALID_FILE_ATTRIBUTES)
+ return false; // something is wrong with path
+
+ if (ftyp & FILE_ATTRIBUTE_DIRECTORY)
+ return true; // this is a directory
+
+ return false; // this is not a directory
+}
+
+bool mkdir(const std::string &path) { return _mkdir(path.c_str()) == 0; }
+
+} // namespace filesystem
diff --git a/compiler/tf2nnpkg/src/tf2nnpkg.cpp b/compiler/tf2nnpkg/src/tf2nnpkg.cpp
new file mode 100644
index 000000000..d9a0d9d2f
--- /dev/null
+++ b/compiler/tf2nnpkg/src/tf2nnpkg.cpp
@@ -0,0 +1,300 @@
+/*
+ * 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 "filesystem.h"
+
+#include <moco/LoggingContext.h>
+#include <moco/tf/Frontend.h>
+#include <exo/LoggingContext.h>
+#include <exo/CircleExporter.h>
+
+#include <nnkit/support/tftestinfo/TensorInfoParser.h>
+
+#include <locop/FormattedGraph.h>
+
+#include <hermes/ConsoleReporter.h>
+#include <hermes/EnvConfig.h>
+
+#include <stdex/Memory.h>
+
+#include <iostream>
+#include <fstream>
+#include <functional>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+namespace
+{
+
+std::unique_ptr<loco::Graph> import(const moco::ModelSignature &sig, const std::string &path)
+{
+ moco::tf::Frontend frontend;
+ return frontend.load(sig, path.c_str(), moco::tf::Frontend::FileType::Binary);
+}
+
+} // namespace
+
+//
+// Logging Support
+//
+namespace
+{
+
+struct Logger final : public hermes::Source
+{
+ Logger(hermes::Context *ctx) { activate(ctx->sources(), ctx->bus()); }
+ ~Logger() { deactivate(); }
+};
+
+struct LoggingContext
+{
+ static hermes::Context *get(void)
+ {
+ using EnvConfig = hermes::EnvConfig<hermes::EnvFormat::BooleanNumber>;
+
+ static hermes::Context *ctx = nullptr;
+
+ if (ctx == nullptr)
+ {
+ ctx = new hermes::Context;
+ ctx->sinks()->append(stdex::make_unique<hermes::ConsoleReporter>());
+ ctx->config(stdex::make_unique<EnvConfig>("TF2NNPKG_Log"));
+ }
+
+ return ctx;
+ }
+};
+
+void print_help()
+{
+ std::cerr << "Usage:" << std::endl;
+ std::cerr << " tf2nnpkg --info <path/to/info>" << std::endl;
+ std::cerr << " --graphdef <path/to/pb>" << std::endl;
+ std::cerr << " -o <path/to/package/dir>" << std::endl;
+}
+
+} // namespace
+
+#define LOGGER(name) \
+ ::Logger name { ::LoggingContext::get() }
+
+#define INFO(name) HERMES_INFO(name)
+
+namespace
+{
+
+void internal_error(void)
+{
+ std::cerr << "tf2nnpkg: internal compiler error" << std::endl;
+
+ // TODO Explain how to report a bug
+}
+
+} // namespace
+
+namespace
+{
+
+std::string extract_modelname(std::string tf_path)
+{
+ auto filename = filesystem::basename(tf_path);
+ // TODO Find better way
+ const std::string key = ".pb";
+ auto suffix_index = filename.find(key);
+ assert(suffix_index != std::string::npos);
+ assert(suffix_index + key.size() == filename.size());
+
+ return filename.substr(0, suffix_index);
+}
+
+class EntryFunctor
+{
+public:
+ EntryFunctor();
+
+public:
+ ~EntryFunctor();
+
+public:
+ int operator()(int argc, char **argv) const;
+};
+
+EntryFunctor::EntryFunctor()
+{
+ // NOTE Implement initialization here
+}
+
+EntryFunctor::~EntryFunctor()
+{
+ // NOTE Implement finialization here
+}
+
+int EntryFunctor::operator()(int argc, char **argv) const
+{
+ using EnvConfig = hermes::EnvConfig<hermes::EnvFormat::BooleanNumber>;
+
+ // This line allows users to control all the moco-tf loggers via TF2NNPKG_Log_Frontend
+ moco::LoggingContext::get()->config(stdex::make_unique<EnvConfig>("TF2NNPKG_Log_Frontend"));
+ // This line allows users to control all the exo-circle loggers via TF2NNPKG_Log_Backend
+ exo::LoggingContext::get()->config(stdex::make_unique<EnvConfig>("TF2NNPKG_Log_Backend"));
+
+ LOGGER(l);
+
+ // Simple argument parser (based on map)
+ std::map<std::string, std::function<void(const std::string &arg)>> argparse;
+
+ std::string arg_info;
+ std::string arg_graphdef;
+ std::string arg_output;
+
+ argparse["--info"] = [&](const std::string &arg) { arg_info = arg; };
+ argparse["--graphdef"] = [&](const std::string &arg) { arg_graphdef = arg; };
+ argparse["-o"] = [&](const std::string &arg) { arg_output = arg; };
+
+ // TODO We need better args parsing in future
+
+ for (int n = 1; n < argc; n += 2)
+ {
+ const std::string tag{argv[n]};
+ const std::string arg{argv[n + 1]};
+
+ auto it = argparse.find(tag);
+ if (it == argparse.end())
+ {
+ std::cerr << "Option '" << tag << "' is not supported" << std::endl;
+ print_help();
+ return 255;
+ }
+
+ it->second(arg);
+ }
+ if (arg_info.empty() || arg_graphdef.empty() || arg_output.empty())
+ {
+ print_help();
+ return 255;
+ }
+
+ // Input paths
+ std::string info_path = arg_info;
+ std::string tf_path = arg_graphdef; // .pb file
+
+ // Output paths
+ std::string outdir_path = arg_output;
+ std::string modelname = extract_modelname(filesystem::normalize_path(tf_path));
+ std::string nnpkg_path = filesystem::join(outdir_path, modelname);
+ std::string model_filename = modelname + ".circle";
+ std::string metadata_path = filesystem::join(nnpkg_path, "metadata");
+ std::string circle_path = filesystem::join(nnpkg_path, model_filename);
+ std::string manifest_path = filesystem::join(metadata_path, "MANIFEST");
+
+ std::cout << "Read '" << info_path << "'" << std::endl;
+
+ moco::ModelSignature sig;
+ {
+ for (const auto &info : nnkit::support::tftestinfo::parse(info_path.c_str()))
+ {
+ switch (info->kind())
+ {
+ case nnkit::support::tftestinfo::ParsedTensor::Kind::Input:
+ sig.add_input(moco::TensorName{info->name()});
+ sig.shape(info->name(), info->shape());
+ break;
+
+ case nnkit::support::tftestinfo::ParsedTensor::Kind::Output:
+ sig.add_output(moco::TensorName{info->name()});
+ sig.shape(info->name(), info->shape());
+ break;
+
+ default:
+ throw std::runtime_error{"Unknown kind"};
+ }
+ }
+ }
+
+ std::cout << "Read '" << info_path << "' - Done" << std::endl;
+
+ std::cout << "Import from '" << tf_path << "'" << std::endl;
+ auto g = import(sig, tf_path);
+ std::cout << "Import from '" << tf_path << "' - Done" << std::endl;
+
+ INFO(l) << "Import Graph" << std::endl;
+ INFO(l) << locop::fmt<locop::Formatter::LinearV1>(g) << std::endl;
+
+ if (not filesystem::is_dir(outdir_path))
+ {
+ std::cout << "Make output directory '" << outdir_path << "'" << std::endl;
+ if (not filesystem::mkdir(outdir_path))
+ throw std::runtime_error("Fail to make directory " + outdir_path);
+ std::cout << "Make output directory '" << outdir_path << "' - Done" << std::endl;
+ }
+
+ if (not filesystem::is_dir(nnpkg_path))
+ {
+ std::cout << "Make package directory '" << nnpkg_path << "'" << std::endl;
+ if (not filesystem::mkdir(nnpkg_path))
+ throw std::runtime_error("Fail to make directory " + nnpkg_path);
+ std::cout << "Make package directory '" << nnpkg_path << "' - Done" << std::endl;
+ }
+
+ std::cout << "Export into '" << circle_path << "'" << std::endl;
+ exo::CircleExporter(g.get()).dumpToFile(circle_path.c_str());
+ std::cout << "Export into '" << circle_path << "' - Done" << std::endl;
+
+ if (not filesystem::is_dir(metadata_path))
+ {
+ std::cout << "Make metadata directory '" << metadata_path << "'" << std::endl;
+ if (not filesystem::mkdir(metadata_path))
+ throw std::runtime_error("Fail to make directory " + metadata_path);
+ std::cout << "Make metadata directory '" << metadata_path << "' - Done" << std::endl;
+ }
+
+ std::cout << "Make manifest file '" << manifest_path << "'" << std::endl;
+ std::ofstream manifest_file;
+ manifest_file.open(manifest_path, std::ios::out | std::ios::binary);
+ manifest_file << "{\n";
+ manifest_file << " \"major-version\" : \"1\",\n";
+ manifest_file << " \"minor-version\" : \"0\",\n";
+ manifest_file << " \"patch-version\" : \"0\",\n";
+ manifest_file << " \"models\" : [ \"" + model_filename + "\" ],\n";
+ manifest_file << " \"model-types\" : [ \"circle\" ]\n";
+ manifest_file << "}";
+ manifest_file.close();
+ std::cout << "Make manifest file '" << manifest_path << "' - Done" << std::endl;
+
+ return 0;
+}
+
+} // namespace
+
+int main(int argc, char **argv)
+{
+ // TODO Add "signal" handler here
+
+ try
+ {
+ EntryFunctor entry;
+ return entry(argc, argv);
+ }
+ catch (...)
+ {
+ // Catch all the exception and print the default error message.
+ internal_error();
+ }
+
+ // EX_SOFTWARE defined in "sysexits.h"
+ return 70;
+}
diff --git a/compiler/tf2tflite-dredd-pb-test/.gitignore b/compiler/tf2tflite-dredd-pb-test/.gitignore
new file mode 100644
index 000000000..23c7c1bb3
--- /dev/null
+++ b/compiler/tf2tflite-dredd-pb-test/.gitignore
@@ -0,0 +1 @@
+/contrib.lst
diff --git a/compiler/tf2tflite-dredd-pb-test/CMakeLists.txt b/compiler/tf2tflite-dredd-pb-test/CMakeLists.txt
new file mode 100644
index 000000000..b75c50772
--- /dev/null
+++ b/compiler/tf2tflite-dredd-pb-test/CMakeLists.txt
@@ -0,0 +1,141 @@
+nnas_include(TargetRequire)
+
+unset(REQUIRED_TARGETS)
+list(APPEND REQUIRED_TARGETS tfl-inspect)
+list(APPEND REQUIRED_TARGETS tfl-verify)
+list(APPEND REQUIRED_TARGETS tf2tflite)
+list(APPEND REQUIRED_TARGETS dredd_rule_lib)
+TargetRequire_Return(${REQUIRED_TARGETS})
+
+set(PB_MODEL_REPO "${CMAKE_CURRENT_SOURCE_DIR}/contrib") # Where to find models to test
+
+unset(KEYS)
+unset(DEPS)
+
+function(check_file_exist)
+
+ foreach(FILE_PATH IN LISTS ARGV)
+ if(NOT EXISTS "${FILE_PATH}")
+ message(FATAL_ERROR "${FILE_PATH} does not exist." )
+ endif()
+ endforeach()
+
+endfunction()
+
+#
+# processing models in contrib.lst
+#
+# Example)
+#
+# Add(Inception_v3 RULE tflite_1.0_rel_requirement.rule)
+# -> Read compiler/tf2tflite-dredd-pb-test/contrib/Inception_v3/model.pb and generate
+# "Inception_v3.tflite". Then rule file is tested for the generated tflite file.
+#
+macro(Add MODEL_DIR)
+
+ set(ARG_OPTION)
+ set(ARG_ONE_VALUE RULE) # rule file name
+ set(ARG_MULTI_VALUE)
+ cmake_parse_arguments(ARG "${ARG_OPTION}" "${ARG_ONE_VALUE}" "${ARG_MULTI_VALUE}" ${ARGN})
+
+ if(NOT ARG_RULE )
+ message( FATAL_ERROR "RULE is mandadatory arg" )
+ endif()
+
+ set(RULE_FILENAME ${ARG_RULE})
+
+ set(MODEL_SOURCE_DIR "${PB_MODEL_REPO}/${MODEL_DIR}")
+
+ set(PB_PATH "${MODEL_SOURCE_DIR}/model.pb")
+ set(INFO_PATH "${MODEL_SOURCE_DIR}/model.info")
+ set(RULE_PATH "${MODEL_SOURCE_DIR}/${RULE_FILENAME}")
+
+ check_file_exist(${PB_PATH} ${INFO_PATH} ${RULE_PATH})
+
+ # Generate .test file which declares path of target pb, info, rule files
+ set(TARGET_TESTNAME "${MODEL_DIR}")
+ set(TEST_CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_TESTNAME}.test")
+
+ add_custom_command(
+ OUTPUT ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E echo 'MODEL_PB_PATH="${PB_PATH}"' >> ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E echo 'MODEL_INFO_PATH="${INFO_PATH}"' >> ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E echo 'MODEL_RULE_PATH="${RULE_PATH}"' >> ${TEST_CONFIG_FILE}
+ DEPENDS
+ ${PB_PATH}
+ COMMENT "Generate ${TARGET_TESTNAME} configuration for BIN"
+ )
+
+ list(APPEND KEYS "${TARGET_TESTNAME}")
+ list(APPEND DEPS "${TEST_CONFIG_FILE}")
+
+endmacro(Add)
+
+include(contrib.lst OPTIONAL)
+
+#
+# Generate toolchain.config
+#
+set(TOOLCHAIN_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/toolchain.config")
+
+add_custom_command(
+ OUTPUT ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TFL_INSPECT_PATH=\"$<TARGET_FILE:tfl-inspect>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TFL_VERIFY_PATH=\"$<TARGET_FILE:tfl-verify>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TF2TFLITE_PATH=\"$<TARGET_FILE:tf2tflite>\"' >> ${TOOLCHAIN_CONFIG}
+ # add more if new excutable file is needed in runner.sh and rule-lib.sh
+ DEPENDS
+ tfl-inspect
+ tfl-verify
+ tf2tflite
+ COMMENT "Generate toolchin configuration"
+)
+
+list(APPEND DEPS "${TOOLCHAIN_CONFIG}")
+
+#
+# Generate quality test runner
+#
+set(SOURCE_RUNNER "${CMAKE_CURRENT_SOURCE_DIR}/runner.sh")
+set(TARGET_RUNNER "${CMAKE_CURRENT_BINARY_DIR}/runner.sh")
+
+add_custom_command(
+ OUTPUT ${TARGET_RUNNER}
+ COMMAND ${CMAKE_COMMAND} -E copy "${SOURCE_RUNNER}" "${TARGET_RUNNER}"
+ DEPENDS ${SOURCE_RUNNER}
+ COMMENT "Generate test runner"
+)
+
+list(APPEND DEPS "${TARGET_RUNNER}")
+
+#
+# copy rule-lib.sh (a library of shell script functions)
+#
+
+# getting path for rule-lib.sh in dredd-rule-lib
+get_target_property(DREDD_RULE_LIB_DIR dredd_rule_lib BINARY_DIR)
+
+set(SOURCE_RULE_LIB "${DREDD_RULE_LIB_DIR}/rule-lib.sh")
+set(TARGET_RULE_LIB "${CMAKE_CURRENT_BINARY_DIR}/rule-lib.sh")
+
+add_custom_command(
+ OUTPUT ${TARGET_RULE_LIB}
+ COMMAND ${CMAKE_COMMAND} -E copy "${SOURCE_RULE_LIB}" "${TARGET_RULE_LIB}"
+ DEPENDS ${SOURCE_RULE_LIB}
+ COMMENT "Generate rule lib"
+)
+
+list(APPEND DEPS "${TARGET_RULE_LIB}")
+
+# Generate dependencies
+add_custom_target(tf2tflite_dredd_pb_deps ALL DEPENDS ${DEPS})
+
+add_test(
+ NAME tf2tflite_dredd_pb_test
+ COMMAND
+ "${TARGET_RUNNER}"
+ "${TOOLCHAIN_CONFIG}"
+ ${KEYS}
+)
diff --git a/compiler/tf2tflite-dredd-pb-test/README.md b/compiler/tf2tflite-dredd-pb-test/README.md
new file mode 100644
index 000000000..87bd18dd7
--- /dev/null
+++ b/compiler/tf2tflite-dredd-pb-test/README.md
@@ -0,0 +1,6 @@
+# tf2tflite-dredd-pb-test
+
+*tf2tflite-dredd-pb-test* validates non-functional aspects of `.tflite` files, which are compiled
+from `.pb` files.
+
+For more information, please refer to `README.md` in *dredd-rule-lib*.
diff --git a/compiler/tf2tflite-dredd-pb-test/contrib/.gitignore b/compiler/tf2tflite-dredd-pb-test/contrib/.gitignore
new file mode 100644
index 000000000..968c34510
--- /dev/null
+++ b/compiler/tf2tflite-dredd-pb-test/contrib/.gitignore
@@ -0,0 +1,3 @@
+/*
+# Exclude all except below
+!.gitignore
diff --git a/compiler/tf2tflite-dredd-pb-test/requires.cmake b/compiler/tf2tflite-dredd-pb-test/requires.cmake
new file mode 100644
index 000000000..da019550f
--- /dev/null
+++ b/compiler/tf2tflite-dredd-pb-test/requires.cmake
@@ -0,0 +1,4 @@
+require("tf2tflite")
+require("tfl-inspect")
+require("tfl-verify")
+require("dredd-rule-lib")
diff --git a/compiler/tf2tflite-dredd-pb-test/runner.sh b/compiler/tf2tflite-dredd-pb-test/runner.sh
new file mode 100755
index 000000000..4eef372b0
--- /dev/null
+++ b/compiler/tf2tflite-dredd-pb-test/runner.sh
@@ -0,0 +1,121 @@
+#!/bin/bash
+
+# This script checks tflite file generated by tf2tflite
+
+# exit if unknown var is used
+set -u
+
+WORKDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+
+# Need at least toolchain.config
+if [[ $# -lt 1 ]]; then
+ echo "USAGE: $0 ..."
+ echo
+ echo "ARGUMENTS:"
+ echo " [toolchain.config path]"
+ echo " [Prefix1]"
+ echo " [Prefix2]"
+ echo " ..."
+ exit 255
+fi
+
+CONFIG_PATH="$1"; shift
+
+source "${CONFIG_PATH}"
+
+echo "-- Found tfl-inspect: ${TFL_INSPECT_PATH}"
+echo "-- Found tfl-verify: ${TFL_VERIFY_PATH}"
+echo "-- Found tf2tflite: ${TF2TFLITE_PATH}"
+echo "-- Found workdir: ${WORKDIR}"
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+pushd "${WORKDIR}"
+
+# running each rule file
+
+while [[ $# -ne 0 ]]; do
+ PREFIX="$1"; shift
+
+ echo "[ RUN ] ${PREFIX}"
+
+ TESTED+=("${PREFIX}")
+
+ PASSED_TAG="${PREFIX}.passed"
+
+ rm -f "${PASSED_TAG}"
+
+ cat > "${PREFIX}.log" <(
+ exec 2>&1
+
+ source "${PREFIX}.test"
+
+ echo "-- Use '${MODEL_PB_PATH}', '${MODEL_INFO_PATH}', and '${MODEL_RULE_PATH}'"
+
+ # Exit immediately if any command fails
+ set -e
+ # Show commands
+ set -x
+
+ # Generate tflite
+ "${TF2TFLITE_PATH}" \
+ "${MODEL_INFO_PATH}" \
+ "${MODEL_PB_PATH}" \
+ "${WORKDIR}/${PREFIX}.tflite"
+
+ #
+ # Run rule prepared to check tflite file
+ #
+
+ # set vars needed by rule file
+ TFLITE_PATH="${WORKDIR}/${PREFIX}.tflite"
+
+ # Note: turn off 'command printing'. Otherwise printing will be so messy
+ set +x
+
+ # set vars required by rule-lib.sh and rule file
+ COMPILED_FILE=${TFLITE_PATH}
+ INSPECT_PROG_PATH=${TFL_INSPECT_PATH}
+ VERIFY_PROG_PATH=${TFL_VERIFY_PATH}
+ ERROR_LOG="${PREFIX}.error"
+
+ rm -f "${ERROR_LOG}"
+
+ # in case error while running ${MODEL_RULE_PATH}, prints error msg
+ trap 'echo "** ERROR **" ; cat "${ERROR_LOG}"' ERR
+
+ source rule-lib.sh
+ source "${MODEL_RULE_PATH}"
+
+ # unset
+ trap - ERR
+ set -x
+
+ # At this point, the exit code of all commands is 0
+ # If not 0, execution of this script ends because of "set -e"
+ touch "${PASSED_TAG}"
+ )
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ echo "[ OK ] ${PREFIX}"
+ PASSED+=("$PREFIX")
+ else
+ echo "[ FAIL] ${PREFIX}"
+ FAILED+=("$PREFIX")
+ 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/tf2tflite-dredd-pbtxt-test/.gitignore b/compiler/tf2tflite-dredd-pbtxt-test/.gitignore
new file mode 100644
index 000000000..8dbfa9012
--- /dev/null
+++ b/compiler/tf2tflite-dredd-pbtxt-test/.gitignore
@@ -0,0 +1 @@
+/test.local.lst
diff --git a/compiler/tf2tflite-dredd-pbtxt-test/CMakeLists.txt b/compiler/tf2tflite-dredd-pbtxt-test/CMakeLists.txt
new file mode 100644
index 000000000..87cf7836f
--- /dev/null
+++ b/compiler/tf2tflite-dredd-pbtxt-test/CMakeLists.txt
@@ -0,0 +1,184 @@
+nnas_include(TargetRequire)
+
+unset(REQUIRED_TARGETS)
+list(APPEND REQUIRED_TARGETS tfl-verify)
+list(APPEND REQUIRED_TARGETS tfl-inspect)
+list(APPEND REQUIRED_TARGETS tf2tflite)
+list(APPEND REQUIRED_TARGETS tfkit)
+list(APPEND REQUIRED_TARGETS dredd_rule_lib)
+TargetRequire_Return(${REQUIRED_TARGETS})
+
+nncc_find_resource(TensorFlowTests)
+
+set(MODEL_REPO "${TensorFlowTests_DIR}") # Where to find text models to test
+
+unset(KEYS)
+unset(DEPS)
+
+#
+# processing models in test.lst and test.local.lst
+#
+# Example)
+#
+# Add(NET_0025 RULE test.rule)
+# -> Read test.pbtxt file under res/TensorFlowTests/NET_0025 and create "NET_0025.tflite"
+# Then the tflite is tested against rules in test.rule file.
+#
+macro(Add MODEL_DIR)
+
+ set(ARG_OPTION)
+ set(ARG_ONE_VALUE RULE) # rule file name
+ set(ARG_MULTI_VALUE)
+ cmake_parse_arguments(ARG "${ARG_OPTION}" "${ARG_ONE_VALUE}" "${ARG_MULTI_VALUE}" ${ARGN})
+
+ if(NOT ARG_RULE)
+ message( FATAL_ERROR "RULE is mandadatory arg" )
+ endif()
+
+ set(RULE_FILENAME ${ARG_RULE})
+
+ set(TARGET_TESTNAME "${MODEL_DIR}")
+
+ set(MODEL_SOURCE_DIR "${MODEL_REPO}/${MODEL_DIR}")
+
+ set(TXT_SOURCE_PBTXT_PATH "${MODEL_SOURCE_DIR}/test.pbtxt")
+ set(TXT_SOURCE_INFO_PATH "${MODEL_SOURCE_DIR}/test.info")
+ set(TXT_SOURCE_RULE_PATH "${MODEL_SOURCE_DIR}/${RULE_FILENAME}")
+
+ set(TXT_TARGET_PB_PATH "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_TESTNAME}.pb")
+ set(TXT_TARGET_PBTXT_PATH "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_TESTNAME}.pbtxt")
+ set(TXT_TARGET_INFO_PATH "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_TESTNAME}.info")
+ set(TXT_TARGET_RULE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_TESTNAME}.rule")
+
+ if(NOT EXISTS "${TXT_SOURCE_PBTXT_PATH}")
+ message(FATAL_ERROR "${TXT_SOURCE_PBTXT_PATH} - pbtxt file does not exist")
+ endif(NOT EXISTS "${TXT_SOURCE_PBTXT_PATH}")
+
+ if(NOT EXISTS "${TXT_SOURCE_INFO_PATH}")
+ message(FATAL_ERROR "${TXT_SOURCE_INFO_PATH} - info file does not exist")
+ endif(NOT EXISTS "${TXT_SOURCE_INFO_PATH}")
+
+ if(NOT EXISTS "${TXT_SOURCE_RULE_PATH}")
+ message(FATAL_ERROR "${TXT_SOURCE_RULE_PATH} - rule file does not exist")
+ endif(NOT EXISTS "${TXT_SOURCE_RULE_PATH}")
+
+ # Copy .pbtxt
+ add_custom_command(OUTPUT ${TXT_TARGET_PBTXT_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TXT_SOURCE_PBTXT_PATH}" "${TXT_TARGET_PBTXT_PATH}"
+ DEPENDS ${TXT_SOURCE_PBTXT_PATH}
+ COMMENT "Generate ${TXT_TARGET_PBTXT_PATH}"
+ )
+
+ # Copy .info
+ add_custom_command(OUTPUT ${TXT_TARGET_INFO_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TXT_SOURCE_INFO_PATH}" "${TXT_TARGET_INFO_PATH}"
+ DEPENDS ${TXT_SOURCE_INFO_PATH}
+ COMMENT "Generate ${TXT_TARGET_INFO_PATH}"
+ )
+
+ # Copy .rule
+ add_custom_command(OUTPUT ${TXT_TARGET_RULE_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TXT_SOURCE_RULE_PATH}" "${TXT_TARGET_RULE_PATH}"
+ DEPENDS ${TXT_SOURCE_RULE_PATH}
+ COMMENT "Generate ${TXT_TARGET_RULE_PATH}"
+ )
+
+ # Generate .pb from .pbtxt
+ add_custom_command(OUTPUT ${TXT_TARGET_PB_PATH}
+ COMMAND $<TARGET_FILE:tfkit> encode ${TXT_TARGET_PBTXT_PATH} ${TXT_TARGET_PB_PATH}
+ DEPENDS ${TXT_TARGET_PBTXT_PATH}
+ COMMENT "Generate ${TXT_TARGET_PB_PATH}"
+ )
+
+ # Generate .test file which declares path of target pb, info, rule files
+ # this file is used inside runner.sh
+ set(TEST_CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_TESTNAME}.test")
+
+ add_custom_command(
+ OUTPUT ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E echo 'MODEL_PB_PATH="${TXT_TARGET_PB_PATH}"' >> ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E echo 'MODEL_INFO_PATH="${TXT_TARGET_INFO_PATH}"' >> ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E echo 'MODEL_RULE_PATH="${TXT_TARGET_RULE_PATH}"' >> ${TEST_CONFIG_FILE}
+ DEPENDS
+ ${TXT_TARGET_PB_PATH}
+ ${TXT_TARGET_INFO_PATH}
+ ${TXT_TARGET_RULE_PATH}
+ COMMENT "Generate ${TARGET_TESTNAME} configuration for TXT"
+ )
+
+ list(APPEND DEPS "${TEST_CONFIG_FILE}")
+ list(APPEND KEYS "${TARGET_TESTNAME}")
+
+endmacro(Add)
+
+# Read "test.lst"
+include("test.lst")
+# Read "test.local.lst" if exists
+include("test.local.lst" OPTIONAL)
+
+#
+# Generate toolchain.config
+#
+set(TOOLCHAIN_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/toolchain.config")
+
+add_custom_command(
+ OUTPUT ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TFL_INSPECT_PATH=\"$<TARGET_FILE:tfl-inspect>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TFL_VERIFY_PATH=\"$<TARGET_FILE:tfl-verify>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TF2TFLITE_PATH=\"$<TARGET_FILE:tf2tflite>\"' >> ${TOOLCHAIN_CONFIG}
+ # add more if new excutable file is needed in runner.sh and rule-lib.sh
+ DEPENDS
+ tfl-inspect
+ tfl-verify
+ tf2tflite
+ COMMENT "Generate toolchin configuration"
+)
+
+list(APPEND DEPS "${TOOLCHAIN_CONFIG}")
+
+#
+# copy runner.sh
+#
+set(SOURCE_RUNNER "${CMAKE_CURRENT_SOURCE_DIR}/runner.sh")
+set(TARGET_RUNNER "${CMAKE_CURRENT_BINARY_DIR}/runner.sh")
+
+add_custom_command(
+ OUTPUT ${TARGET_RUNNER}
+ COMMAND ${CMAKE_COMMAND} -E copy "${SOURCE_RUNNER}" "${TARGET_RUNNER}"
+ DEPENDS ${SOURCE_RUNNER}
+ COMMENT "Generate test runner"
+)
+
+list(APPEND DEPS "${TARGET_RUNNER}")
+
+#
+# copy rule-lib.sh (a library of shell script functions)
+#
+
+# getting path for rule-lib.sh in dredd-rule-lib
+get_target_property(DREDD_RULE_LIB_DIR dredd_rule_lib BINARY_DIR)
+
+set(SOURCE_RULE_LIB "${DREDD_RULE_LIB_DIR}/rule-lib.sh")
+set(TARGET_RULE_LIB "${CMAKE_CURRENT_BINARY_DIR}/rule-lib.sh")
+
+add_custom_command(
+ OUTPUT ${TARGET_RULE_LIB}
+ COMMAND ${CMAKE_COMMAND} -E copy "${SOURCE_RULE_LIB}" "${TARGET_RULE_LIB}"
+ DEPENDS ${SOURCE_RULE_LIB}
+ COMMENT "Generate rule lib"
+)
+
+list(APPEND DEPS "${TARGET_RULE_LIB}")
+
+# Generate dependencies
+add_custom_target(tf2tflite_dredd_pbtxt_deps ALL DEPENDS ${DEPS})
+
+add_test(
+ NAME tf2tflite_dredd_pbtxt_test
+ COMMAND
+ "${TARGET_RUNNER}"
+ "${TOOLCHAIN_CONFIG}"
+ ${KEYS}
+)
diff --git a/compiler/tf2tflite-dredd-pbtxt-test/README.md b/compiler/tf2tflite-dredd-pbtxt-test/README.md
new file mode 100644
index 000000000..091867987
--- /dev/null
+++ b/compiler/tf2tflite-dredd-pbtxt-test/README.md
@@ -0,0 +1 @@
+# tf2tflite-dredd-pbtxt-test
diff --git a/compiler/tf2tflite-dredd-pbtxt-test/requires.cmake b/compiler/tf2tflite-dredd-pbtxt-test/requires.cmake
new file mode 100644
index 000000000..94fb442af
--- /dev/null
+++ b/compiler/tf2tflite-dredd-pbtxt-test/requires.cmake
@@ -0,0 +1,5 @@
+require("tfkit")
+require("tf2tflite")
+require("tfl-verify")
+require("tfl-inspect")
+require("dredd-rule-lib")
diff --git a/compiler/tf2tflite-dredd-pbtxt-test/runner.sh b/compiler/tf2tflite-dredd-pbtxt-test/runner.sh
new file mode 100755
index 000000000..8575bc282
--- /dev/null
+++ b/compiler/tf2tflite-dredd-pbtxt-test/runner.sh
@@ -0,0 +1,121 @@
+#!/bin/bash
+
+# exit if unknown var is used
+set -u
+
+# This script checks tflite file generated by tf2tflite
+
+WORKDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+
+# Need at least toolchain.config
+if [[ $# -lt 1 ]]; then
+ echo "USAGE: $0 ..."
+ echo
+ echo "ARGUMENTS:"
+ echo " [toolchain.config path]"
+ echo " [Prefix1]"
+ echo " [Prefix2]"
+ echo " ..."
+ exit 255
+fi
+
+CONFIG_PATH="$1"; shift
+
+source "${CONFIG_PATH}"
+
+echo "-- Found tfl-inspect: ${TFL_INSPECT_PATH}"
+echo "-- Found tfl-verify: ${TFL_VERIFY_PATH}"
+echo "-- Found tf2tflite: ${TF2TFLITE_PATH}"
+echo "-- Found workdir: ${WORKDIR}"
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+pushd "${WORKDIR}"
+
+# running each rule file
+
+while [[ $# -ne 0 ]]; do
+ PREFIX="$1"; shift
+
+ echo "[ RUN ] ${PREFIX}"
+
+ TESTED+=("${PREFIX}")
+
+ PASSED_TAG="${PREFIX}.passed"
+
+ rm -f "${PASSED_TAG}"
+
+ cat > "${PREFIX}.log" <(
+ exec 2>&1
+
+ source "${PREFIX}.test"
+
+ echo "-- Use '${MODEL_PB_PATH}', '${MODEL_INFO_PATH}', and '${MODEL_RULE_PATH}'"
+
+ # Exit immediately if any command fails
+ set -e
+ # Show commands
+ set -x
+
+ # Generate tflite
+ "${TF2TFLITE_PATH}" \
+ "${MODEL_INFO_PATH}" \
+ "${MODEL_PB_PATH}" \
+ "${WORKDIR}/${PREFIX}.tflite"
+
+ #
+ # Run rule prepared to check tflite file
+ #
+
+ # set vars needed by rule file
+ TFLITE_PATH="${WORKDIR}/${PREFIX}.tflite"
+
+ # Note: turn off 'command printing'. Otherwise printing will be so messy
+ set +x
+
+ # set vars required by rule-lib.sh and rule file
+ COMPILED_FILE=${TFLITE_PATH}
+ INSPECT_PROG_PATH=${TFL_INSPECT_PATH}
+ VERIFY_PROG_PATH=${TFL_VERIFY_PATH}
+ ERROR_LOG="${PREFIX}.error"
+
+ rm -f "${ERROR_LOG}"
+
+ # in case error while running ${MODEL_RULE_PATH}, prints error msg
+ trap 'echo "** ERROR **" ; cat "${ERROR_LOG}"' ERR
+
+ source rule-lib.sh
+ source "${MODEL_RULE_PATH}"
+
+ # unset
+ trap - ERR
+ set -x
+
+ # At this point, the exit code of all commands is 0
+ # If not 0, execution of this script ends because of "set -e"
+ touch "${PASSED_TAG}"
+ )
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ echo "[ OK ] ${PREFIX}"
+ PASSED+=("$PREFIX")
+ else
+ echo "[ FAIL] ${PREFIX}"
+ FAILED+=("$PREFIX")
+ 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/tf2tflite-dredd-pbtxt-test/test.lst b/compiler/tf2tflite-dredd-pbtxt-test/test.lst
new file mode 100644
index 000000000..2fed541d4
--- /dev/null
+++ b/compiler/tf2tflite-dredd-pbtxt-test/test.lst
@@ -0,0 +1 @@
+Add(NET_0025 RULE tflite_1.0_rel_requirement.rule) # CBR
diff --git a/compiler/tf2tflite-value-pb-test/.gitignore b/compiler/tf2tflite-value-pb-test/.gitignore
new file mode 100644
index 000000000..23c7c1bb3
--- /dev/null
+++ b/compiler/tf2tflite-value-pb-test/.gitignore
@@ -0,0 +1 @@
+/contrib.lst
diff --git a/compiler/tf2tflite-value-pb-test/CMakeLists.txt b/compiler/tf2tflite-value-pb-test/CMakeLists.txt
new file mode 100644
index 000000000..41974f72c
--- /dev/null
+++ b/compiler/tf2tflite-value-pb-test/CMakeLists.txt
@@ -0,0 +1,131 @@
+nnas_include(TargetRequire)
+
+unset(REQUIRED_TARGETS)
+list(APPEND REQUIRED_TARGETS tf2tflite)
+list(APPEND REQUIRED_TARGETS tfkit)
+list(APPEND REQUIRED_TARGETS nnkit-run)
+list(APPEND REQUIRED_TARGETS nnkit_tf_backend)
+list(APPEND REQUIRED_TARGETS nnkit_tflite_backend)
+list(APPEND REQUIRED_TARGETS nnkit_randomize_action)
+list(APPEND REQUIRED_TARGETS nnkit_HDF5_export_action)
+list(APPEND REQUIRED_TARGETS nnkit_HDF5_import_action)
+list(APPEND REQUIRED_TARGETS i5diff)
+TargetRequire_Return(${REQUIRED_TARGETS})
+
+unset(KEYS)
+unset(DEPS)
+
+###
+### Add "Contrib" tests
+###
+macro(Add PREFIX)
+ # Let's use CONTRIB prefix to avoid name conflicts with official models
+ set(TEST_KEY "CONTRIB.${PREFIX}")
+
+ set(PACKAGE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/contrib/${PREFIX}")
+
+ set(MODEL_DOWNLOAD_SCRIPT "${PACKAGE_DIR}/model.download")
+ set(MODEL_PB_FILE "${PACKAGE_DIR}/model.pb")
+ set(MODEL_INFO_FILE "${PACKAGE_DIR}/model.info")
+ set(MODEL_MD5SUM_FILE "${PACKAGE_DIR}/model.md5sum")
+
+ # Try to download a model if it is missing
+ if(NOT EXISTS "${MODEL_PB_FILE}")
+ # TODO Extract this routine as a helper function
+ if(NOT EXISTS "${MODEL_DOWNLOAD_SCRIPT}")
+ message(FATAL_ERROR "${TEST_KEY} - Download script is missing")
+ endif(NOT EXISTS "${MODEL_DOWNLOAD_SCRIPT}")
+
+ execute_process(
+ COMMAND ${CMAKE_COMMAND} -D OUTPUT_PATH=${MODEL_PB_FILE} -P "${MODEL_DOWNLOAD_SCRIPT}"
+ RESULT_VARIABLE EXITCODE
+ )
+
+ if(NOT EXITCODE EQUAL 0)
+ message(FATAL_ERROR "${TEST_KEY} - Download fails")
+ endif(NOT EXITCODE EQUAL 0)
+ endif()
+
+ if(EXISTS "${MODEL_MD5SUM_FILE}")
+ # TODO Extract this routine as a helper function
+ file(STRINGS "${MODEL_MD5SUM_FILE}" EXPECTED_MD5SUM)
+ file(MD5 "${MODEL_PB_FILE}" OBTAINED_MD5SUM)
+
+ if(NOT "${EXPECTED_MD5SUM}" STREQUAL "${OBTAINED_MD5SUM}")
+ message(FATAL_ERROR "${TEST_KEY} - Checksum mismatches")
+ endif()
+ endif()
+
+ # Generate .test file which declares MODEL_PB_PATH and MODEL_INFO_PATH
+ set(TEST_CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/${TEST_KEY}.test")
+
+ add_custom_command(
+ OUTPUT ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E echo 'MODEL_PB_PATH="${MODEL_PB_FILE}"' >> ${TEST_CONFIG_FILE}
+ COMMAND ${CMAKE_COMMAND} -E echo 'MODEL_INFO_PATH="${MODEL_INFO_FILE}"' >> ${TEST_CONFIG_FILE}
+ COMMENT "Generate ${TEST_KEY} configuration"
+ )
+
+ list(APPEND KEYS "${TEST_KEY}")
+ list(APPEND DEPS "${TEST_CONFIG_FILE}")
+endmacro(Add)
+
+include(contrib.lst OPTIONAL)
+
+###
+### Generate toolchain.config
+###
+set(TOOLCHAIN_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/toolchain.config")
+
+add_custom_command(
+ OUTPUT ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'NNKIT_RUN_PATH=\"$<TARGET_FILE:nnkit-run>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TF_BACKEND_PATH=\"$<TARGET_FILE:nnkit_tf_backend>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TFLITE_BACKEND_PATH=\"$<TARGET_FILE:nnkit_tflite_backend>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TF2TFLITE_PATH=\"$<TARGET_FILE:tf2tflite>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'RANDOMIZE_ACTION_PATH=\"$<TARGET_FILE:nnkit_randomize_action>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'HDF5_EXPORT_ACTION_PATH=\"$<TARGET_FILE:nnkit_HDF5_export_action>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'HDF5_IMPORT_ACTION_PATH=\"$<TARGET_FILE:nnkit_HDF5_import_action>\"' >> ${TOOLCHAIN_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'I5DIFF_PATH=\"$<TARGET_FILE:i5diff>\"' >> ${TOOLCHAIN_CONFIG}
+ DEPENDS
+ nnkit-run
+ nnkit_tf_backend
+ nnkit_tflite_backend
+ tf2tflite
+ nnkit_randomize_action
+ nnkit_HDF5_export_action
+ nnkit_HDF5_import_action
+ i5diff
+ COMMENT "Generate toolchin configuration"
+)
+
+list(APPEND DEPS "${TOOLCHAIN_CONFIG}")
+
+##
+## Generate test runner
+##
+set(TEST_RUNNER_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/runner.sh")
+set(TEST_RUNNER_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/runner.sh")
+
+add_custom_command(
+ OUTPUT ${TEST_RUNNER_SCRIPT}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TEST_RUNNER_SOURCE}" "${TEST_RUNNER_SCRIPT}"
+ DEPENDS ${TEST_RUNNER_SOURCE}
+ COMMENT "Generate test runner"
+)
+
+list(APPEND DEPS "${TEST_RUNNER_SCRIPT}")
+
+### Generate dependencies
+add_custom_target(tf2tflite_value_pb_test_deps ALL DEPENDS ${DEPS})
+
+# NOTE This target is not built by default
+add_test(
+ NAME tf2tflite_value_pb_test
+ COMMAND
+ "${TEST_RUNNER_SCRIPT}"
+ "${TOOLCHAIN_CONFIG}"
+ ${KEYS}
+)
diff --git a/compiler/tf2tflite-value-pb-test/README.md b/compiler/tf2tflite-value-pb-test/README.md
new file mode 100644
index 000000000..6c4a68cc5
--- /dev/null
+++ b/compiler/tf2tflite-value-pb-test/README.md
@@ -0,0 +1 @@
+# tf2tflite-value-pb-test
diff --git a/compiler/tf2tflite-value-pb-test/contrib/.gitignore b/compiler/tf2tflite-value-pb-test/contrib/.gitignore
new file mode 100644
index 000000000..968c34510
--- /dev/null
+++ b/compiler/tf2tflite-value-pb-test/contrib/.gitignore
@@ -0,0 +1,3 @@
+/*
+# Exclude all except below
+!.gitignore
diff --git a/compiler/tf2tflite-value-pb-test/requires.cmake b/compiler/tf2tflite-value-pb-test/requires.cmake
new file mode 100644
index 000000000..471025e5e
--- /dev/null
+++ b/compiler/tf2tflite-value-pb-test/requires.cmake
@@ -0,0 +1,6 @@
+require("i5diff")
+require("nnkit-tf")
+require("nnkit-tflite")
+require("nnkit")
+require("tf2tflite")
+require("tfkit")
diff --git a/compiler/tf2tflite-value-pb-test/runner.sh b/compiler/tf2tflite-value-pb-test/runner.sh
new file mode 100755
index 000000000..faec12521
--- /dev/null
+++ b/compiler/tf2tflite-value-pb-test/runner.sh
@@ -0,0 +1,112 @@
+#!/bin/bash
+
+WORKDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+
+# Need at least toolchain.config
+if [[ $# -lt 1 ]]; then
+ echo "USAGE: $0 ..."
+ echo
+ echo "ARGUMENTS:"
+ echo " [toolchain.config path]"
+ echo " [Prefix1]"
+ echo " [Prefix2]"
+ echo " ..."
+ exit 255
+fi
+
+CONFIG_PATH="$1"; shift
+
+source "${CONFIG_PATH}"
+
+echo "-- Found nnkit-run: ${NNKIT_RUN_PATH}"
+echo "-- Found TF backend: ${TF_BACKEND_PATH}"
+echo "-- Found TFLITE backend: ${TFLITE_BACKEND_PATH}"
+echo "-- Found TF2TFLITE: ${TF2TFLITE_PATH}"
+echo "-- Found randomize action: ${RANDOMIZE_ACTION_PATH}"
+echo "-- Found HDF5 export action: ${HDF5_EXPORT_ACTION_PATH}"
+echo "-- Found HDF5 import action: ${HDF5_IMPORT_ACTION_PATH}"
+echo "-- Found i5diff: ${I5DIFF_PATH}"
+echo "-- Found workdir: ${WORKDIR}"
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+pushd "${WORKDIR}"
+while [[ $# -ne 0 ]]; do
+ PREFIX="$1"; shift
+
+ echo "[ RUN ] ${PREFIX}"
+
+ TESTED+=("${PREFIX}")
+
+ PASSED_TAG="${PREFIX}.passed"
+
+ rm -f "${PASSED_TAG}"
+
+ cat > "${PREFIX}.log" <(
+ exec 2>&1
+
+ source "${PREFIX}.test"
+
+ echo "-- Use '${MODEL_PB_PATH}' and '${MODEL_INFO_PATH}'"
+
+ # Exit immediately if any command fails
+ set -e
+ # Show commands
+ set -x
+
+ # Generate tflite
+ "${TF2TFLITE_PATH}" \
+ "${MODEL_INFO_PATH}" \
+ "${MODEL_PB_PATH}" \
+ "${WORKDIR}/${PREFIX}.tflite"
+
+ # Run TensorFlow
+ "${NNKIT_RUN_PATH}" \
+ --backend "${TF_BACKEND_PATH}" \
+ --backend-arg "${MODEL_PB_PATH}" \
+ --backend-arg "${MODEL_INFO_PATH}" \
+ --pre "${RANDOMIZE_ACTION_PATH}" \
+ --pre "${HDF5_EXPORT_ACTION_PATH}" \
+ --pre-arg "${WORKDIR}/${PREFIX}.input.h5" \
+ --post "${HDF5_EXPORT_ACTION_PATH}" \
+ --post-arg "${WORKDIR}/${PREFIX}.expected.h5"
+
+ # Run TensorFlow Lite
+ "${NNKIT_RUN_PATH}" \
+ --backend "${TFLITE_BACKEND_PATH}" \
+ --backend-arg "${WORKDIR}/${PREFIX}.tflite" \
+ --pre "${HDF5_IMPORT_ACTION_PATH}" \
+ --pre-arg "${WORKDIR}/${PREFIX}.input.h5" \
+ --post "${HDF5_EXPORT_ACTION_PATH}" \
+ --post-arg "${WORKDIR}/${PREFIX}.obtained.h5"
+
+ "${I5DIFF_PATH}" -d 0.001 "${PREFIX}.expected.h5" "${PREFIX}.obtained.h5"
+
+ if [[ $? -eq 0 ]]; then
+ touch "${PASSED_TAG}"
+ fi
+ )
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ echo "[ OK ] ${PREFIX}"
+ PASSED+=("$PREFIX")
+ else
+ echo "[ FAIL] ${PREFIX}"
+ FAILED+=("$PREFIX")
+ 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/tf2tflite-value-pbtxt-test/.gitignore b/compiler/tf2tflite-value-pbtxt-test/.gitignore
new file mode 100644
index 000000000..8dbfa9012
--- /dev/null
+++ b/compiler/tf2tflite-value-pbtxt-test/.gitignore
@@ -0,0 +1 @@
+/test.local.lst
diff --git a/compiler/tf2tflite-value-pbtxt-test/CMakeLists.txt b/compiler/tf2tflite-value-pbtxt-test/CMakeLists.txt
new file mode 100644
index 000000000..2e76e21d3
--- /dev/null
+++ b/compiler/tf2tflite-value-pbtxt-test/CMakeLists.txt
@@ -0,0 +1,159 @@
+nnas_include(TargetRequire)
+
+unset(REQUIRED_TARGETS)
+list(APPEND REQUIRED_TARGETS tf2tflite)
+list(APPEND REQUIRED_TARGETS tfkit)
+list(APPEND REQUIRED_TARGETS nnkit-run)
+list(APPEND REQUIRED_TARGETS nnkit_tf_backend)
+list(APPEND REQUIRED_TARGETS nnkit_tflite_backend)
+list(APPEND REQUIRED_TARGETS nnkit_randomize_action)
+list(APPEND REQUIRED_TARGETS nnkit_HDF5_export_action)
+list(APPEND REQUIRED_TARGETS nnkit_HDF5_import_action)
+list(APPEND REQUIRED_TARGETS i5diff)
+TargetRequire_Return(${REQUIRED_TARGETS})
+
+message(STATUS "tf2tflite-value-pbtxt-test: run tests")
+
+nncc_find_resource(TensorFlowTests)
+
+#
+# Copy [PREFIX]/test.pbtxt to PREFIX.pbtxt in binary folder
+# Copy [PREFIX]/test.info to PREFIX.info in binary folder
+# Copy [PREFIX]/customop.conf to PREFIX_customop.conf in binary folder
+# Encode PREFIX.pbtxt to PREFIX.pb
+#
+set(TEST_REPO "${TensorFlowTests_DIR}")
+set(TEST_PBTXT_FILENAME "test.pbtxt")
+set(TEST_INFO_FILENAME "test.info")
+set(TEST_CUSTOMOP_CONF_FILENAME "customop.conf")
+
+unset(TESTCASES)
+
+macro(add NAME)
+ list(APPEND TESTCASES ${NAME})
+endmacro(add)
+
+# Read "test.lst"
+include("test.lst")
+# Read "test.local.lst" if exists
+include("test.local.lst" OPTIONAL)
+
+unset(TEST_DEPS)
+unset(TEST_NAMES)
+
+foreach(PREFIX IN ITEMS ${TESTCASES})
+ if(NOT IS_DIRECTORY "${TEST_REPO}/${PREFIX}")
+ message(FATAL_ERROR "Missing '${PREFIX}' test")
+ endif()
+
+ set(PBTXT_SOURCE_PATH "${TEST_REPO}/${PREFIX}/${TEST_PBTXT_FILENAME}")
+ set(INFO_SOURCE_PATH "${TEST_REPO}/${PREFIX}/${TEST_INFO_FILENAME}")
+ set(CUSTOMOP_CONF_SOURCE_PATH "${TEST_REPO}/${PREFIX}/${TEST_CUSTOMOP_CONF_FILENAME}")
+
+ set(PBTXT_FILE "${PREFIX}.pbtxt")
+ set(PBTXT_PATH "${CMAKE_CURRENT_BINARY_DIR}/${PBTXT_FILE}")
+
+ set(INFO_FILE "${PREFIX}.info")
+ set(INFO_PATH "${CMAKE_CURRENT_BINARY_DIR}/${INFO_FILE}")
+
+ set(CUSTOMOP_CONF_FILE "${PREFIX}.${TEST_CUSTOMOP_CONF_FILENAME}") # ex) CustomOp_001.customop.conf
+ set(CUSTOMOP_CONF_PATH "${CMAKE_CURRENT_BINARY_DIR}/${CUSTOMOP_CONF_FILE}")
+
+ set(PB_FILE "${PREFIX}.pb")
+ set(PB_PATH "${CMAKE_CURRENT_BINARY_DIR}/${PB_FILE}")
+
+ # Copy .pbtxt
+ add_custom_command(OUTPUT ${PBTXT_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${PBTXT_SOURCE_PATH}" "${PBTXT_PATH}"
+ DEPENDS ${PBTXT_SOURCE_PATH}
+ COMMENT "Generate ${PBTXT_FILE}"
+ )
+
+ # Copy .info
+ add_custom_command(OUTPUT ${INFO_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${INFO_SOURCE_PATH}" "${INFO_PATH}"
+ DEPENDS ${INFO_SOURCE_PATH}
+ COMMENT "Generate ${INFO_FILE}"
+ )
+
+ # Generate .pb from .pbtxt
+ add_custom_command(OUTPUT ${PB_PATH}
+ COMMAND $<TARGET_FILE:tfkit> encode ${PBTXT_PATH} ${PB_PATH}
+ DEPENDS ${PBTXT_PATH}
+ COMMENT "Generate ${PB_FILE}"
+ )
+
+ list(APPEND TEST_DEPS ${INFO_PATH} ${PB_PATH})
+
+ if (EXISTS "${CUSTOMOP_CONF_SOURCE_PATH}")
+
+ # Copy customop.conf
+ add_custom_command(OUTPUT ${CUSTOMOP_CONF_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${CUSTOMOP_CONF_SOURCE_PATH}" "${CUSTOMOP_CONF_PATH}"
+ DEPENDS ${CUSTOMOP_CONF_SOURCE_PATH}
+ COMMENT "Generate ${CUSTOMOP_CONF_FILE}"
+ )
+
+ list(APPEND TEST_DEPS ${CUSTOMOP_CONF_PATH})
+
+ endif (EXISTS "${CUSTOMOP_CONF_SOURCE_PATH}")
+
+ list(APPEND TEST_NAMES ${PREFIX})
+endforeach(PREFIX)
+
+##
+## Copy testall
+##
+set(TEST_RUNNER "${CMAKE_CURRENT_BINARY_DIR}/testall.sh")
+set(TEST_RUNNER_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/testall.sh")
+
+add_custom_command(
+ OUTPUT ${TEST_RUNNER}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TEST_RUNNER_SOURCE}" "${TEST_RUNNER}"
+ DEPENDS ${TEST_RUNNER_SOURCE}
+ COMMENT "Generate test runner"
+)
+
+list(APPEND TEST_DEPS "${TEST_RUNNER}")
+
+###
+### Generate test.config
+###
+set(TEST_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/test.config")
+
+add_custom_command(
+ OUTPUT ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'NNKIT_RUN_PATH=\"$<TARGET_FILE:nnkit-run>\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TF_BACKEND_PATH=\"$<TARGET_FILE:nnkit_tf_backend>\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TFLITE_BACKEND_PATH=\"$<TARGET_FILE:nnkit_tflite_backend>\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TF2TFLITE_PATH=\"$<TARGET_FILE:tf2tflite>\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'RANDOMIZE_ACTION_PATH=\"$<TARGET_FILE:nnkit_randomize_action>\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'HDF5_EXPORT_ACTION_PATH=\"$<TARGET_FILE:nnkit_HDF5_export_action>\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'HDF5_IMPORT_ACTION_PATH=\"$<TARGET_FILE:nnkit_HDF5_import_action>\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'I5DIFF_PATH=\"$<TARGET_FILE:i5diff>\"' >> ${TEST_CONFIG}
+ DEPENDS
+ nnkit-run
+ nnkit_tf_backend
+ nnkit_tflite_backend
+ tf2tflite
+ nnkit_randomize_action
+ nnkit_HDF5_export_action
+ nnkit_HDF5_import_action
+ i5diff
+ COMMENT "Generate test configuration"
+)
+
+list(APPEND TEST_DEPS "${TEST_CONFIG}")
+
+# This "tf2tflite_value_pbtxt_test_deps" target enforces CMake to generate all the dependencies during "build" phase
+add_custom_target(tf2tflite_value_pbtxt_test_deps ALL DEPENDS ${TEST_DEPS})
+
+# Run tests
+add_test(
+ NAME tf2tflite_value_pbtxt_test
+ COMMAND "${TEST_RUNNER}"
+ "${TEST_CONFIG}"
+ "${CMAKE_CURRENT_BINARY_DIR}"
+ ${TEST_NAMES}
+)
diff --git a/compiler/tf2tflite-value-pbtxt-test/README.md b/compiler/tf2tflite-value-pbtxt-test/README.md
new file mode 100644
index 000000000..30429549a
--- /dev/null
+++ b/compiler/tf2tflite-value-pbtxt-test/README.md
@@ -0,0 +1,3 @@
+# tf2tflite-value-pbtxt-test
+
+Run `tf2tflite` to `test.lst` and do random value test using `nnkit`. Write `test.local.lst` for local test list.
diff --git a/compiler/tf2tflite-value-pbtxt-test/requires.cmake b/compiler/tf2tflite-value-pbtxt-test/requires.cmake
new file mode 100644
index 000000000..bb783e254
--- /dev/null
+++ b/compiler/tf2tflite-value-pbtxt-test/requires.cmake
@@ -0,0 +1,4 @@
+require("tf2tflite")
+require("nnkit")
+require("tfkit")
+require("i5diff")
diff --git a/compiler/tf2tflite-value-pbtxt-test/test.lst b/compiler/tf2tflite-value-pbtxt-test/test.lst
new file mode 100644
index 000000000..419918b26
--- /dev/null
+++ b/compiler/tf2tflite-value-pbtxt-test/test.lst
@@ -0,0 +1,101 @@
+# TODO Enable skipped tests
+
+add(NET_0000)
+add(NET_0001)
+add(NET_0002)
+add(NET_0003)
+add(NET_0004)
+add(NET_0005)
+add(NET_0006)
+add(NET_0007)
+add(NET_0008)
+add(NET_0009)
+add(NET_0010)
+add(NET_0011)
+add(NET_0012)
+add(NET_0013)
+add(NET_0014)
+add(NET_0015)
+add(NET_0016)
+add(NET_0017)
+add(NET_0018)
+add(NET_0019)
+add(NET_0020)
+add(NET_0021)
+add(NET_0022)
+add(NET_0023)
+add(NET_0024)
+add(NET_0025)
+add(NET_0028)
+add(NET_0029)
+add(NET_0030)
+add(NET_0031)
+add(NET_0032)
+add(NET_0033)
+add(NET_0034)
+add(NET_0035)
+add(NET_0036)
+add(NET_0037)
+add(NET_0038)
+add(NET_0039)
+add(NET_0040)
+add(NET_0041)
+add(REGRESSION_0000)
+add(REGRESSION_0001)
+add(REGRESSION_0002)
+add(UNIT_Add_000)
+add(UNIT_Add_001)
+add(UNIT_Add_002)
+add(UNIT_Add_004)
+add(UNIT_Add_005)
+add(UNIT_AvgPool_000)
+add(UNIT_AvgPool_001)
+#add(UNIT_BiasAdd_000)
+#add(UNIT_BiasAdd_001)
+add(UNIT_BiasAdd_002)
+#add(UNIT_ConcatV2_000)
+#add(UNIT_ConcatV2_001)
+add(UNIT_ConcatV2_002)
+add(UNIT_Const_000)
+#add(UNIT_Const_001)
+add(UNIT_Conv2D_000)
+add(UNIT_Conv2DBackpropInput_000)
+add(UNIT_Conv2DBackpropInput_001)
+add(UNIT_DepthwiseConv2dNative_000)
+add(UNIT_DepthwiseConv2dNative_001)
+add(UNIT_Maximum_000)
+add(UNIT_Maximum_001)
+add(UNIT_Maximum_002)
+add(UNIT_MaxPool_000)
+add(UNIT_MaxPool_001)
+add(UNIT_Mean_000)
+add(UNIT_Mean_001)
+add(UNIT_Mean_002)
+add(UNIT_Mean_003)
+add(UNIT_Mul_000)
+add(UNIT_Mul_001)
+add(UNIT_Mul_002)
+add(UNIT_Pad_000)
+add(UNIT_Placeholder_000)
+add(UNIT_Placeholder_001)
+add(UNIT_Placeholder_002)
+add(UNIT_Placeholder_003)
+add(UNIT_RealDiv_000)
+add(UNIT_RealDiv_001)
+add(UNIT_Relu_000)
+add(UNIT_Relu6_000)
+add(UNIT_Reshape_000)
+add(UNIT_Rsqrt_000)
+add(UNIT_Softmax_001)
+add(UNIT_Sqrt_000)
+add(UNIT_SquaredDifference_000)
+add(UNIT_SquaredDifference_001)
+add(UNIT_Squeeze_000)
+add(UNIT_Squeeze_001)
+add(UNIT_Squeeze_002)
+add(UNIT_Squeeze_003)
+add(UNIT_StopGradient_000)
+add(UNIT_StopGradient_001)
+add(UNIT_Sub_000)
+add(UNIT_Sub_001)
+add(UNIT_Tanh_000)
diff --git a/compiler/tf2tflite/testall.sh b/compiler/tf2tflite-value-pbtxt-test/testall.sh
index ff18b6058..ff18b6058 100755
--- a/compiler/tf2tflite/testall.sh
+++ b/compiler/tf2tflite-value-pbtxt-test/testall.sh
diff --git a/compiler/tf2tflite/CMakeLists.txt b/compiler/tf2tflite/CMakeLists.txt
index 05447d656..663563e00 100644
--- a/compiler/tf2tflite/CMakeLists.txt
+++ b/compiler/tf2tflite/CMakeLists.txt
@@ -1,19 +1,17 @@
-nncc_include(TargetRequire)
-
# TODO Allow users to force tf2tflite build
if(NOT TARGET moco_tf_frontend)
return()
endif(NOT TARGET moco_tf_frontend)
-if(NOT TARGET nnkit_support_tftestinfo)
+if(NOT TARGET tfinfo)
return()
-endif(NOT TARGET nnkit_support_tftestinfo)
+endif(NOT TARGET tfinfo)
-if(NOT TARGET exo_tflite)
+if(NOT TARGET exo)
return()
-endif(NOT TARGET exo_tflite)
+endif(NOT TARGET exo)
-nncc_find_package(Protobuf QUIET)
+nnas_find_package(Protobuf QUIET)
if(NOT Protobuf_FOUND)
return()
@@ -25,177 +23,22 @@ Protobuf_Generate(CUSTOMOP_INFO_PROTO
"./proto"
CustomOpInfo.proto)
-add_library(tf2tflite_customop_info_proto STATIC ${CUSTOMOP_INFO_PROTO_SOURCES})
+add_library(tf2tflite_customop_info_proto SHARED ${CUSTOMOP_INFO_PROTO_SOURCES})
set_target_properties(tf2tflite_customop_info_proto PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_include_directories(tf2tflite_customop_info_proto PUBLIC ${CUSTOMOP_INFO_PROTO_INCLUDE_DIRS})
target_link_libraries(tf2tflite_customop_info_proto PUBLIC libprotobuf)
+install(TARGETS tf2tflite_customop_info_proto DESTINATION lib)
file(GLOB_RECURSE SOURCES "src/*.cpp")
add_executable(tf2tflite ${SOURCES})
target_link_libraries(tf2tflite PRIVATE moco_log)
target_link_libraries(tf2tflite PRIVATE moco_tf_frontend)
-target_link_libraries(tf2tflite PRIVATE nnkit_support_tftestinfo)
-target_link_libraries(tf2tflite PRIVATE exo_tflite)
+target_link_libraries(tf2tflite PRIVATE tfinfo)
+target_link_libraries(tf2tflite PRIVATE exo)
target_link_libraries(tf2tflite PRIVATE locop)
target_link_libraries(tf2tflite PRIVATE hermes_std)
target_link_libraries(tf2tflite PRIVATE stdex)
target_link_libraries(tf2tflite PRIVATE angkor cwrap)
target_link_libraries(tf2tflite PRIVATE tf2tflite_customop_info_proto)
-
-nncc_find_resource(TensorFlowTests)
-
-unset(REQUIRED_TARGETS)
-list(APPEND REQUIRED_TARGETS tfkit)
-list(APPEND REQUIRED_TARGETS nnkit-run)
-list(APPEND REQUIRED_TARGETS nnkit_tf_backend)
-list(APPEND REQUIRED_TARGETS nnkit_tflite_backend)
-list(APPEND REQUIRED_TARGETS nnkit_randomize_action)
-list(APPEND REQUIRED_TARGETS nnkit_HDF5_export_action)
-list(APPEND REQUIRED_TARGETS nnkit_HDF5_import_action)
-list(APPEND REQUIRED_TARGETS i5diff)
-TargetRequire_Return(${REQUIRED_TARGETS})
-
-message(STATUS "tf2tflite: run tests")
-
-#
-# Copy [PREFIX]/test.pbtxt to PREFIX.pbtxt in binary folder
-# Copy [PREFIX]/test.info to PREFIX.info in binary folder
-# Copy [PREFIX]/customop.conf to PREFIX_customop.conf in binary folder
-# Encode PREFIX.pbtxt to PREFIX.pb
-#
-set(TEST_REPO "${TensorFlowTests_DIR}")
-set(TEST_PBTXT_FILENAME "test.pbtxt")
-set(TEST_INFO_FILENAME "test.info")
-set(TEST_CUSTOMOP_CONF_FILENAME "customop.conf")
-
-unset(TESTCASES)
-
-macro(add NAME)
- list(APPEND TESTCASES ${NAME})
-endmacro(add)
-
-# Read "test.lst"
-include("test.lst")
-# Read "test.local.lst" if exists
-include("test.local.lst" OPTIONAL)
-
-unset(TEST_DEPS)
-unset(TEST_NAMES)
-
-foreach(PREFIX IN ITEMS ${TESTCASES})
- if(NOT IS_DIRECTORY "${TEST_REPO}/${PREFIX}")
- message(FATAL_ERROR "Missing '${PREFIX}' test")
- endif()
-
- set(PBTXT_SOURCE_PATH "${TEST_REPO}/${PREFIX}/${TEST_PBTXT_FILENAME}")
- set(INFO_SOURCE_PATH "${TEST_REPO}/${PREFIX}/${TEST_INFO_FILENAME}")
- set(CUSTOMOP_CONF_SOURCE_PATH "${TEST_REPO}/${PREFIX}/${TEST_CUSTOMOP_CONF_FILENAME}")
-
- set(PBTXT_FILE "${PREFIX}.pbtxt")
- set(PBTXT_PATH "${CMAKE_CURRENT_BINARY_DIR}/${PBTXT_FILE}")
-
- set(INFO_FILE "${PREFIX}.info")
- set(INFO_PATH "${CMAKE_CURRENT_BINARY_DIR}/${INFO_FILE}")
-
- set(CUSTOMOP_CONF_FILE "${PREFIX}.${TEST_CUSTOMOP_CONF_FILENAME}") # ex) CustomOp_001.customop.conf
- set(CUSTOMOP_CONF_PATH "${CMAKE_CURRENT_BINARY_DIR}/${CUSTOMOP_CONF_FILE}")
-
- set(PB_FILE "${PREFIX}.pb")
- set(PB_PATH "${CMAKE_CURRENT_BINARY_DIR}/${PB_FILE}")
-
- # Copy .pbtxt
- add_custom_command(OUTPUT ${PBTXT_PATH}
- COMMAND ${CMAKE_COMMAND} -E copy "${PBTXT_SOURCE_PATH}" "${PBTXT_PATH}"
- DEPENDS ${PBTXT_SOURCE_PATH}
- COMMENT "Generate ${PBTXT_FILE}"
- )
-
- # Copy .info
- add_custom_command(OUTPUT ${INFO_PATH}
- COMMAND ${CMAKE_COMMAND} -E copy "${INFO_SOURCE_PATH}" "${INFO_PATH}"
- DEPENDS ${INFO_SOURCE_PATH}
- COMMENT "Generate ${INFO_FILE}"
- )
-
- # Generate .pb from .pbtxt
- add_custom_command(OUTPUT ${PB_PATH}
- COMMAND $<TARGET_FILE:tfkit> encode ${PBTXT_PATH} ${PB_PATH}
- DEPENDS ${PBTXT_PATH}
- COMMENT "Generate ${PB_FILE}"
- )
-
- list(APPEND TEST_DEPS ${INFO_PATH} ${PB_PATH})
-
- if (EXISTS "${CUSTOMOP_CONF_SOURCE_PATH}")
-
- # Copy customop.conf
- add_custom_command(OUTPUT ${CUSTOMOP_CONF_PATH}
- COMMAND ${CMAKE_COMMAND} -E copy "${CUSTOMOP_CONF_SOURCE_PATH}" "${CUSTOMOP_CONF_PATH}"
- DEPENDS ${CUSTOMOP_CONF_SOURCE_PATH}
- COMMENT "Generate ${CUSTOMOP_CONF_FILE}"
- )
-
- list(APPEND TEST_DEPS ${CUSTOMOP_CONF_PATH})
-
- endif (EXISTS "${CUSTOMOP_CONF_SOURCE_PATH}")
-
- list(APPEND TEST_NAMES ${PREFIX})
-endforeach(PREFIX)
-
-##
-## Copy testall
-##
-set(TEST_RUNNER "${CMAKE_CURRENT_BINARY_DIR}/testall.sh")
-set(TEST_RUNNER_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/testall.sh")
-
-add_custom_command(
- OUTPUT ${TEST_RUNNER}
- COMMAND ${CMAKE_COMMAND} -E copy "${TEST_RUNNER_SOURCE}" "${TEST_RUNNER}"
- DEPENDS ${TEST_RUNNER_SOURCE}
- COMMENT "Generate test runner"
-)
-
-list(APPEND TEST_DEPS "${TEST_RUNNER}")
-
-###
-### Generate test.config
-###
-set(TEST_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/test.config")
-
-add_custom_command(
- OUTPUT ${TEST_CONFIG}
- COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_CONFIG}
- COMMAND ${CMAKE_COMMAND} -E echo 'NNKIT_RUN_PATH=\"$<TARGET_FILE:nnkit-run>\"' >> ${TEST_CONFIG}
- COMMAND ${CMAKE_COMMAND} -E echo 'TF_BACKEND_PATH=\"$<TARGET_FILE:nnkit_tf_backend>\"' >> ${TEST_CONFIG}
- COMMAND ${CMAKE_COMMAND} -E echo 'TFLITE_BACKEND_PATH=\"$<TARGET_FILE:nnkit_tflite_backend>\"' >> ${TEST_CONFIG}
- COMMAND ${CMAKE_COMMAND} -E echo 'TF2TFLITE_PATH=\"$<TARGET_FILE:tf2tflite>\"' >> ${TEST_CONFIG}
- COMMAND ${CMAKE_COMMAND} -E echo 'RANDOMIZE_ACTION_PATH=\"$<TARGET_FILE:nnkit_randomize_action>\"' >> ${TEST_CONFIG}
- COMMAND ${CMAKE_COMMAND} -E echo 'HDF5_EXPORT_ACTION_PATH=\"$<TARGET_FILE:nnkit_HDF5_export_action>\"' >> ${TEST_CONFIG}
- COMMAND ${CMAKE_COMMAND} -E echo 'HDF5_IMPORT_ACTION_PATH=\"$<TARGET_FILE:nnkit_HDF5_import_action>\"' >> ${TEST_CONFIG}
- COMMAND ${CMAKE_COMMAND} -E echo 'I5DIFF_PATH=\"$<TARGET_FILE:i5diff>\"' >> ${TEST_CONFIG}
- DEPENDS
- nnkit-run
- nnkit_tf_backend
- nnkit_tflite_backend
- tf2tflite
- nnkit_randomize_action
- nnkit_HDF5_export_action
- nnkit_HDF5_import_action
- i5diff
- COMMENT "Generate test configuration"
-)
-
-list(APPEND TEST_DEPS "${TEST_CONFIG}")
-
-# This "tf2tflite_deps" target enforces CMake to generate all the dependencies during "build" phase
-add_custom_target(tf2tflite_deps ALL DEPENDS ${TEST_DEPS})
-
-# Run tests
-add_test(
- NAME tf2tflite_test
- COMMAND "${TEST_RUNNER}"
- "${TEST_CONFIG}"
- "${CMAKE_CURRENT_BINARY_DIR}"
- ${TEST_NAMES}
-)
+install(TARGETS tf2tflite DESTINATION bin)
diff --git a/compiler/tf2tflite/requires.cmake b/compiler/tf2tflite/requires.cmake
index ce98653d4..68d45bf3a 100644
--- a/compiler/tf2tflite/requires.cmake
+++ b/compiler/tf2tflite/requires.cmake
@@ -1,12 +1,8 @@
require("stdex")
require("hermes-std")
require("moco-tf")
-require("nnkit")
-require("exo-tflite")
+require("exo")
require("locop")
-require("tfkit")
-require("nnkit")
-require("i5diff")
require("loco")
require("cwrap")
require("angkor")
diff --git a/compiler/tf2tflite/src/CustomopConfLoader.cpp b/compiler/tf2tflite/src/CustomopConfLoader.cpp
index 726b06651..7399a432a 100644
--- a/compiler/tf2tflite/src/CustomopConfLoader.cpp
+++ b/compiler/tf2tflite/src/CustomopConfLoader.cpp
@@ -96,7 +96,7 @@ angkor::TensorShape get_shape_attr(const tf2tflite::CustomOpDef &custom_op)
return convert_shape(shape_def);
}
-void add_customop(tf2tflite::CustomOpInfoDef &def, moco::tf::ModelSignature &sig)
+void add_customop(tf2tflite::CustomOpInfoDef &def, moco::ModelSignature &sig)
{
for (const auto &custom_op : def.custom_op())
{
@@ -115,7 +115,7 @@ void add_customop(tf2tflite::CustomOpInfoDef &def, moco::tf::ModelSignature &sig
namespace tf2tflite
{
-void load_customop_conf(const std::string &path, moco::tf::ModelSignature &sig)
+void load_customop_conf(const std::string &path, moco::ModelSignature &sig)
{
CustomOpInfoDef def;
diff --git a/compiler/tf2tflite/src/CustomopConfLoader.h b/compiler/tf2tflite/src/CustomopConfLoader.h
index da3ab3dc7..4f5f3a6cb 100644
--- a/compiler/tf2tflite/src/CustomopConfLoader.h
+++ b/compiler/tf2tflite/src/CustomopConfLoader.h
@@ -25,7 +25,7 @@ namespace tf2tflite
{
/// @brief Loads customop.conf into ModelSignature
-void load_customop_conf(const std::string &path, moco::tf::ModelSignature &sig);
+void load_customop_conf(const std::string &path, moco::ModelSignature &sig);
} // namespace tf2tflite
diff --git a/compiler/tf2tflite/src/Driver.cpp b/compiler/tf2tflite/src/Driver.cpp
index 9ae080cb2..e43d30bb2 100644
--- a/compiler/tf2tflite/src/Driver.cpp
+++ b/compiler/tf2tflite/src/Driver.cpp
@@ -18,6 +18,7 @@
#include <moco/LoggingContext.h>
#include <moco/tf/Frontend.h>
+#include <exo/LoggingContext.h>
#include <exo/TFLExporter.h>
#include <nnkit/support/tftestinfo/TensorInfoParser.h>
@@ -38,7 +39,7 @@
namespace
{
-std::unique_ptr<loco::Graph> import(const moco::tf::ModelSignature &sig, const std::string &path)
+std::unique_ptr<loco::Graph> import(const moco::ModelSignature &sig, const std::string &path)
{
moco::tf::Frontend frontend;
return frontend.load(sig, path.c_str(), moco::tf::Frontend::FileType::Binary);
@@ -96,6 +97,8 @@ int main(int argc, char **argv)
// This line allows users to control all the moco-tf loggers via TF2TFLITE_Log_Frontend
moco::LoggingContext::get()->config(stdex::make_unique<EnvConfig>("TF2TFLITE_Log_Frontend"));
+ // This line allows users to control all the exo-tflite loggers via TF2TFLITE_Log_Backend
+ exo::LoggingContext::get()->config(stdex::make_unique<EnvConfig>("TF2TFLITE_Log_Backend"));
LOGGER(l);
@@ -112,18 +115,20 @@ int main(int argc, char **argv)
std::cout << "Read '" << info_path << "'" << std::endl;
- moco::tf::ModelSignature sig;
+ moco::ModelSignature sig;
{
for (const auto &info : nnkit::support::tftestinfo::parse(info_path.c_str()))
{
switch (info->kind())
{
case nnkit::support::tftestinfo::ParsedTensor::Kind::Input:
- sig.add_input(moco::tf::TensorName{info->name()});
+ sig.add_input(moco::TensorName{info->name()});
+ sig.shape(info->name(), info->shape());
break;
case nnkit::support::tftestinfo::ParsedTensor::Kind::Output:
- sig.add_output(moco::tf::TensorName{info->name()});
+ sig.add_output(moco::TensorName{info->name()});
+ sig.shape(info->name(), info->shape());
break;
default:
diff --git a/compiler/tf2tflite/test.lst b/compiler/tf2tflite/test.lst
deleted file mode 100644
index 085c940dd..000000000
--- a/compiler/tf2tflite/test.lst
+++ /dev/null
@@ -1,60 +0,0 @@
-# TODO Enable skipped tests
-
-add(NET_0000)
-add(NET_0001)
-add(NET_0002)
-add(NET_0003)
-add(NET_0004)
-add(NET_0005)
-add(NET_0006)
-add(NET_0007)
-add(NET_0008)
-add(NET_0009)
-add(NET_0010)
-add(NET_0011)
-add(NET_0012)
-add(NET_0013)
-add(NET_0014)
-add(NET_0015)
-add(NET_0017)
-add(NET_0018)
-add(NET_0019)
-add(NET_0020)
-add(NET_0021)
-add(NET_0022)
-add(NET_0023)
-add(NET_0024)
-add(REGRESSION_0000)
-add(UNIT_Add_000)
-add(UNIT_AvgPool_000)
-add(UNIT_AvgPool_001)
-#add(UNIT_BiasAdd_000)
-#add(UNIT_BiasAdd_001)
-add(UNIT_BiasAdd_002)
-#add(UNIT_Concat_000)
-#add(UNIT_Concat_001)
-add(UNIT_Concat_002)
-add(UNIT_Const_000)
-#add(UNIT_Const_001)
-add(UNIT_Conv2D_000)
-add(UNIT_DepthwiseConv2D_000)
-add(UNIT_DepthwiseConv2D_001)
-add(UNIT_MaxPool_000)
-add(UNIT_MaxPool_001)
-add(UNIT_Mul_000)
-add(UNIT_Placeholder_000)
-add(UNIT_RealDiv_000)
-add(UNIT_Relu_000)
-add(UNIT_Relu6_000)
-add(UNIT_Reshape_000)
-add(UNIT_Rsqrt_000)
-add(UNIT_Softmax_001)
-add(UNIT_Sqrt_000)
-add(UNIT_SquaredDifference_000)
-add(UNIT_Squeeze_000)
-add(UNIT_Squeeze_001)
-add(UNIT_Squeeze_002)
-add(UNIT_StopGradient_000)
-add(UNIT_StopGradient_001)
-add(UNIT_Sub_000)
-add(UNIT_Tanh_000)
diff --git a/compiler/tf2tfliteV2-conversion-test/CMakeLists.txt b/compiler/tf2tfliteV2-conversion-test/CMakeLists.txt
new file mode 100644
index 000000000..3e7e57747
--- /dev/null
+++ b/compiler/tf2tfliteV2-conversion-test/CMakeLists.txt
@@ -0,0 +1,109 @@
+nncc_find_resource(TensorFlowTests)
+
+#
+# Copy [PREFIX]/test.pbtxt to PREFIX.pbtxt in binary folder
+# Copy [PREFIX]/test.info to PREFIX.info in binary folder
+# Encode PREFIX.pbtxt to PREFIX.pb
+#
+set(TEST_REPO "${TensorFlowTests_DIR}")
+set(TEST_PBTXT_FILENAME "test.pbtxt")
+set(TEST_INFO_FILENAME "test.info")
+
+unset(TESTCASES)
+
+macro(add NAME)
+ list(APPEND TESTCASES ${NAME})
+endmacro(add)
+
+# Read "test.lst"
+include("test.lst")
+# Read "test.local.lst" if exists
+include("test.local.lst" OPTIONAL)
+
+unset(TEST_DEPS)
+unset(TEST_NAMES)
+
+foreach(PREFIX IN ITEMS ${TESTCASES})
+ if(NOT IS_DIRECTORY "${TEST_REPO}/${PREFIX}")
+ message(FATAL_ERROR "Missing '${PREFIX}' test")
+ endif()
+
+ set(PBTXT_SOURCE_PATH "${TEST_REPO}/${PREFIX}/${TEST_PBTXT_FILENAME}")
+ set(INFO_SOURCE_PATH "${TEST_REPO}/${PREFIX}/${TEST_INFO_FILENAME}")
+
+ set(PBTXT_FILE "${PREFIX}.pbtxt")
+ set(PBTXT_PATH "${CMAKE_CURRENT_BINARY_DIR}/${PBTXT_FILE}")
+
+ set(INFO_FILE "${PREFIX}.info")
+ set(INFO_PATH "${CMAKE_CURRENT_BINARY_DIR}/${INFO_FILE}")
+
+ # Copy .pbtxt
+ add_custom_command(OUTPUT ${PBTXT_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${PBTXT_SOURCE_PATH}" "${PBTXT_PATH}"
+ DEPENDS ${PBTXT_SOURCE_PATH}
+ COMMENT "Generate ${PBTXT_FILE}"
+ )
+
+ # Copy .info
+ add_custom_command(OUTPUT ${INFO_PATH}
+ COMMAND ${CMAKE_COMMAND} -E copy "${INFO_SOURCE_PATH}" "${INFO_PATH}"
+ DEPENDS ${INFO_SOURCE_PATH}
+ COMMENT "Generate ${INFO_FILE}"
+ )
+
+ list(APPEND TEST_DEPS ${INFO_PATH} ${PBTXT_PATH})
+ list(APPEND TEST_NAMES ${PREFIX})
+endforeach(PREFIX)
+
+##
+## Copy testall
+##
+set(TEST_RUNNER "${CMAKE_CURRENT_BINARY_DIR}/testall.sh")
+set(TEST_RUNNER_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/testall.sh")
+
+add_custom_command(
+ OUTPUT ${TEST_RUNNER}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TEST_RUNNER_SOURCE}" "${TEST_RUNNER}"
+ DEPENDS ${TEST_RUNNER_SOURCE}
+ COMMENT "Generate test runner"
+)
+
+list(APPEND TEST_DEPS "${TEST_RUNNER}")
+
+get_target_property(ARTIFACTS_BIN_PATH testDataGenerator BINARY_DIR)
+
+set(VIRTUALENV "${NNCC_OVERLAY_DIR}/venv_1_13_2")
+
+###
+### Generate test.config
+###
+set(TEST_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/test.config")
+
+# Get tf2tfliteV2 binary path
+get_target_property(TF2TFLITEV2_BIN_DIR tf2tfliteV2 BINARY_DIR)
+set(TF2TFLITEV2_PATH "${TF2TFLITEV2_BIN_DIR}/tf2tfliteV2.py")
+
+add_custom_command(
+ OUTPUT ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TF2TFLITEV2_PATH=\"${TF2TFLITEV2_PATH}\"' >> ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'VIRTUALENV=\"${VIRTUALENV}\"' >> ${TEST_CONFIG}
+ DEPENDS
+ tf2tfliteV2
+ COMMENT "Generate test configuration"
+)
+
+list(APPEND TEST_DEPS "${TEST_CONFIG}")
+
+# This "tf2tfliteV2_conversion_test_deps" target enforces CMake to generate all the dependencies during "build" phase
+add_custom_target(tf2tfliteV2_conversion_test_deps ALL DEPENDS ${TEST_DEPS})
+
+# TODO This test takes a long time and will only be tested once a day.
+# Run tests
+# add_test(
+# NAME tf2tfliteV2_conversion_test
+# COMMAND "${TEST_RUNNER}"
+# "${TEST_CONFIG}"
+# "${CMAKE_CURRENT_BINARY_DIR}"
+# ${TEST_NAMES}
+# )
diff --git a/compiler/tf2tfliteV2-conversion-test/README.md b/compiler/tf2tfliteV2-conversion-test/README.md
new file mode 100644
index 000000000..d4d2a975f
--- /dev/null
+++ b/compiler/tf2tfliteV2-conversion-test/README.md
@@ -0,0 +1,2 @@
+# tf2tfliteV2-conversion-test
+
diff --git a/compiler/tf2tfliteV2-conversion-test/requires.cmake b/compiler/tf2tfliteV2-conversion-test/requires.cmake
new file mode 100644
index 000000000..31b335471
--- /dev/null
+++ b/compiler/tf2tfliteV2-conversion-test/requires.cmake
@@ -0,0 +1,2 @@
+require("common-artifacts")
+require("tf2tfliteV2")
diff --git a/compiler/tf2tfliteV2-conversion-test/test.lst b/compiler/tf2tfliteV2-conversion-test/test.lst
new file mode 100644
index 000000000..2f771ba31
--- /dev/null
+++ b/compiler/tf2tfliteV2-conversion-test/test.lst
@@ -0,0 +1,124 @@
+# TODO Enable skipped tests
+
+add(NET_0000)
+add(NET_0001)
+add(NET_0002)
+add(NET_0003)
+add(NET_0004)
+add(NET_0005)
+add(NET_0006)
+add(NET_0007)
+add(NET_0008)
+add(NET_0009)
+add(NET_0010)
+add(NET_0011)
+add(NET_0012)
+add(NET_0013)
+add(NET_0014)
+add(NET_0015)
+add(NET_0016)
+add(NET_0017)
+add(NET_0018)
+add(NET_0019)
+add(NET_0020)
+add(NET_0021)
+add(NET_0022)
+#add(NET_0023)
+add(NET_0024)
+add(NET_0025)
+add(NET_0026)
+add(NET_0027)
+add(NET_0028)
+add(NET_0029)
+add(NET_0030)
+add(NET_0031)
+add(NET_0032)
+add(NET_0033)
+add(NET_0034)
+add(NET_0035)
+add(NET_0036)
+add(NET_0037)
+add(NET_0038)
+add(NET_0039)
+add(NET_0040)
+add(NET_0041)
+#add(NET_0042)
+add(REGRESSION_0000)
+add(REGRESSION_0001)
+add(REGRESSION_0002)
+add(UNIT_Add_000)
+add(UNIT_Add_001)
+add(UNIT_Add_002)
+#add(UNIT_Add_003)
+add(UNIT_Add_004)
+add(UNIT_Add_005)
+add(UNIT_AvgPool_000)
+add(UNIT_AvgPool_001)
+#add(UNIT_BiasAdd_000)
+#add(UNIT_BiasAdd_001)
+add(UNIT_BiasAdd_002)
+#add(UNIT_ConcatV2_000)
+#add(UNIT_ConcatV2_001)
+add(UNIT_ConcatV2_002)
+#add(UNIT_Const_000)
+#add(UNIT_Const_001)
+add(UNIT_Conv2D_000)
+add(UNIT_Conv2D_001)
+add(UNIT_Conv2D_002)
+add(UNIT_Conv2DBackpropInput_000)
+add(UNIT_Conv2DBackpropInput_001)
+add(UNIT_Conv2DBackpropInput_002)
+add(UNIT_CustomOp_000)
+add(UNIT_CustomOp_001)
+add(UNIT_DepthwiseConv2dNative_000)
+add(UNIT_DepthwiseConv2dNative_001)
+add(UNIT_FusedBatchNorm_000)
+add(UNIT_FusedBatchNorm_001)
+add(UNIT_Maximum_000)
+add(UNIT_Maximum_001)
+add(UNIT_Maximum_002)
+add(UNIT_MaxPool_000)
+add(UNIT_MaxPool_001)
+add(UNIT_Mean_000)
+add(UNIT_Mean_001)
+add(UNIT_Mean_002)
+add(UNIT_Mean_003)
+add(UNIT_MirrorPad_000)
+add(UNIT_Mul_000)
+add(UNIT_Mul_001)
+add(UNIT_Mul_002)
+#add(UNIT_Pack_000)
+#add(UNIT_Pack_001)
+#add(UNIT_Pack_002)
+#add(UNIT_Pack_003)
+#add(UNIT_Pack_004)
+add(UNIT_Pad_000)
+add(UNIT_PadV2_000)
+#add(UNIT_Placeholder_000)
+#add(UNIT_Placeholder_001)
+#add(UNIT_Placeholder_002)
+#add(UNIT_Placeholder_003)
+add(UNIT_RealDiv_000)
+add(UNIT_RealDiv_001)
+add(UNIT_Relu_000)
+add(UNIT_Relu6_000)
+add(UNIT_Reshape_000)
+add(UNIT_Rsqrt_000)
+#add(UNIT_Shape_000)
+add(UNIT_Softmax_000)
+add(UNIT_Softmax_001)
+add(UNIT_Softmax_002)
+add(UNIT_Softmax_003)
+add(UNIT_Sqrt_000)
+add(UNIT_SquaredDifference_000)
+add(UNIT_SquaredDifference_001)
+add(UNIT_Squeeze_000)
+add(UNIT_Squeeze_001)
+add(UNIT_Squeeze_002)
+add(UNIT_Squeeze_003)
+#add(UNIT_StopGradient_000)
+#add(UNIT_StopGradient_001)
+#add(UNIT_StridedSlice_000)
+add(UNIT_Sub_000)
+add(UNIT_Sub_001)
+add(UNIT_Tanh_000)
diff --git a/compiler/tf2tfliteV2-conversion-test/testall.sh b/compiler/tf2tfliteV2-conversion-test/testall.sh
new file mode 100755
index 000000000..2d9a423c5
--- /dev/null
+++ b/compiler/tf2tfliteV2-conversion-test/testall.sh
@@ -0,0 +1,81 @@
+#!/bin/bash
+
+# Need at least 2 arguments
+if [[ $# -lt 2 ]]; then
+ echo "USAGE: $0 ..."
+ echo
+ echo "ARGUMENTS:"
+ echo " [test.config path]"
+ echo " [WORKDIR]"
+ echo " [Prefix1]"
+ echo " [Prefix2]"
+ echo " ..."
+ exit 255
+fi
+
+CONFIG_PATH="$1"; shift
+WORKDIR="$1"; shift
+
+source "${CONFIG_PATH}"
+
+echo "-- Found TF2TFLITEV2: ${TF2TFLITEV2_PATH}"
+echo "-- Found python virtualenv: ${VIRTUALENV}"
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+pushd "${WORKDIR}"
+while [[ $# -ne 0 ]]; do
+ PREFIX="$1"; shift
+
+ TESTED+=("${PREFIX}")
+
+ PASSED_TAG="${PREFIX}.passed"
+
+ rm -f "${PASSED_TAG}"
+
+ cat > "${PREFIX}.log" <(
+ exec 2>&1
+
+ echo "-- Found pbtxt: ${PREFIX}.pbtxt"
+
+ # Exit immediately if any command fails
+ set -e
+ # Show commands
+ set -x
+
+ # Generate tflite
+ source "${VIRTUALENV}/bin/activate"
+ "${VIRTUALENV}/bin/python" "${TF2TFLITEV2_PATH}" \
+ --v1 \
+ --input_path "${WORKDIR}/${PREFIX}.pbtxt" \
+ --input_arrays "$(awk -F, '/^input/ { print $2 }' ${PREFIX}.info | cut -d: -f1 | tr -d ' ' | paste -d, -s)" \
+ --input_shapes "$(cat ${PREFIX}.info | grep '^input' | cut -d '[' -f2 | cut -d ']' -f1 | tr -d ' ' | xargs | tr ' ' ':')" \
+ --output_path "${WORKDIR}/${PREFIX}.tflite" \
+ --output_arrays "$(awk -F, '/^output/ { print $2 }' ${PREFIX}.info | cut -d: -f1 | tr -d ' ' | paste -d, -s)"
+
+ if [[ $? -eq 0 ]]; then
+ touch "${PASSED_TAG}"
+ fi
+ )
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ PASSED+=("$PREFIX")
+ else
+ FAILED+=("$PREFIX")
+ 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/tf2tfliteV2/CMakeLists.txt b/compiler/tf2tfliteV2/CMakeLists.txt
new file mode 100644
index 000000000..5591ac4e1
--- /dev/null
+++ b/compiler/tf2tfliteV2/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(tf2tfliteV2_FILE "tf2tfliteV2.py")
+set(tf2tfliteV2_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${tf2tfliteV2_FILE}")
+set(tf2tfliteV2_BIN "${CMAKE_CURRENT_BINARY_DIR}/${tf2tfliteV2_FILE}")
+
+add_custom_command(OUTPUT ${tf2tfliteV2_BIN}
+ COMMAND ${CMAKE_COMMAND} -E copy "${tf2tfliteV2_SRC}" "${tf2tfliteV2_BIN}"
+ DEPENDS ${tf2tfliteV2_SRC}
+ COMMENT "Generate ${tf2tfliteV2_BIN}"
+ )
+
+add_custom_target(tf2tfliteV2 ALL DEPENDS ${tf2tfliteV2_BIN})
+
+install(FILES ${tf2tfliteV2_BIN} DESTINATION bin)
diff --git a/compiler/tf2tfliteV2/README.md b/compiler/tf2tfliteV2/README.md
new file mode 100644
index 000000000..0a90735cb
--- /dev/null
+++ b/compiler/tf2tfliteV2/README.md
@@ -0,0 +1,65 @@
+# tf2tfliteV2
+
+_tf2tfliteV2_ is a TensorFlow to TensorFlow Lite model Converter.
+
+## Where does V2 come from?
+Even though we alreay have _tf2tflite_, we cannot cover all opeartors in TensorFlow. To expand coverage, we introduce _tf2tfliteV2_ which uses `TensorFlow Lite Converter`(by Google) internally.
+
+## Prerequisite
+- Frozen graph from TensorFlow 1.13.1 in binary(`*.pb`) or text(`*.pbtxt`) format
+- Desired version of TensorFlow(You can use python virtualenv, docker, etc.)
+
+## Example
+```
+python tf2tfliteV2.py \
+> --v1 \
+> -i frozen_graph.pb -o converted.tflite
+> -I model_inputs -O model_outputs
+```
+```
+python tf2tfliteV2.py \
+> --v1 \
+> --input_path=frozen_graph.pb \
+> --output_path=converted.tflite \
+> --input_arrays=model_inputs \
+> --output_arrays=model_outputs
+
+```
+```
+python tf2tfliteV2.py \
+> --v2 \
+> --input_path=frozen_graph.pbtxt \
+> --output_path=converted.tflite \
+> --input_arrays=model_inputs \
+> --output_arrays=model_outputs
+```
+```
+python tf2tfliteV2.py \
+> --v2 \
+> --input_path=multiple_output_graph.pb \
+> --output_path=converted.tflite \
+> --input_arrays=model_inputs \
+> --output_arrays=output,output:1,output:2
+```
+
+## optional argument
+```
+ -h, --help show this help message and exit
+ --v1 Use TensorFlow Lite Converter 1.x
+ --v2 Use TensorFlow Lite Converter 2.x
+ --graph_def Use graph def file(default)
+ --saved_model Use saved model
+ --keras_model Use keras model
+ -i INPUT_PATH, --input_path INPUT_PATH
+ Full filepath of the input file.
+ -o OUTPUT_PATH, --output_path OUTPUT_PATH
+ Full filepath of the output file.
+ -I INPUT_ARRAYS, --input_arrays INPUT_ARRAYS
+ Names of the input arrays, comma-separated.
+ -s INPUT_SHAPES, --input_shapes INPUT_SHAPES
+ Shapes corresponding to --input_arrays, colon-
+ separated.(ex:"1,4,4,3:1,20,20,3")
+ -O OUTPUT_ARRAYS, --output_arrays OUTPUT_ARRAYS
+ Names of the output arrays, comma-separated.
+
+```
diff --git a/compiler/tf2tfliteV2/tf2tfliteV2.py b/compiler/tf2tfliteV2/tf2tfliteV2.py
new file mode 100755
index 000000000..3fb988102
--- /dev/null
+++ b/compiler/tf2tfliteV2/tf2tfliteV2.py
@@ -0,0 +1,246 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+# Copyright (C) 2018 The TensorFlow Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import tensorflow as tf
+import argparse
+import sys
+
+from google.protobuf.message import DecodeError
+from google.protobuf import text_format as _text_format
+
+
+def wrap_frozen_graph(graph_def, inputs, outputs):
+ def _imports_graph_def():
+ tf.compat.v1.import_graph_def(graph_def, name="")
+
+ wrapped_import = tf.compat.v1.wrap_function(_imports_graph_def, [])
+ import_graph = wrapped_import.graph
+ return wrapped_import.prune(
+ tf.nest.map_structure(import_graph.as_graph_element, inputs),
+ tf.nest.map_structure(import_graph.as_graph_element, outputs))
+
+
+def _get_parser():
+ """
+ Returns an ArgumentParser for TensorFlow Lite Converter.
+ """
+ parser = argparse.ArgumentParser(
+ description=("Command line tool to run TensorFlow Lite Converter."))
+
+ # Converter version.
+ converter_version = parser.add_mutually_exclusive_group(required=True)
+ converter_version.add_argument(
+ "--v1", action="store_true", help="Use TensorFlow Lite Converter 1.x")
+ converter_version.add_argument(
+ "--v2", action="store_true", help="Use TensorFlow Lite Converter 2.x")
+
+ # Input model format
+ model_format_arg = parser.add_mutually_exclusive_group()
+ model_format_arg.add_argument(
+ "--graph_def",
+ action="store_const",
+ dest="model_format",
+ const="graph_def",
+ help="Use graph def file(default)")
+ model_format_arg.add_argument(
+ "--saved_model",
+ action="store_const",
+ dest="model_format",
+ const="saved_model",
+ help="Use saved model")
+ model_format_arg.add_argument(
+ "--keras_model",
+ action="store_const",
+ dest="model_format",
+ const="keras_model",
+ help="Use keras model")
+
+ # Input and output path.
+ parser.add_argument(
+ "-i",
+ "--input_path",
+ type=str,
+ help="Full filepath of the input file.",
+ required=True)
+ parser.add_argument(
+ "-o",
+ "--output_path",
+ type=str,
+ help="Full filepath of the output file.",
+ required=True)
+
+ # Input and output arrays.
+ parser.add_argument(
+ "-I",
+ "--input_arrays",
+ type=str,
+ help="Names of the input arrays, comma-separated.")
+ parser.add_argument(
+ "-s",
+ "--input_shapes",
+ type=str,
+ help=
+ "Shapes corresponding to --input_arrays, colon-separated.(ex:\"1,4,4,3:1,20,20,3\")"
+ )
+ parser.add_argument(
+ "-O",
+ "--output_arrays",
+ type=str,
+ help="Names of the output arrays, comma-separated.")
+
+ # Set default value
+ parser.set_defaults(model_format="graph_def")
+ return parser
+
+
+def _check_flags(flags):
+ """
+ Checks the parsed flags to ensure they are valid.
+ """
+ if flags.v1:
+ invalid = ""
+ # To be filled
+
+ if invalid:
+ raise ValueError(invalid + " options must be used with v2")
+
+ if flags.v2:
+ if tf.__version__.find("2.") != 0:
+ raise ValueError(
+ "Imported TensorFlow should have version >= 2.0 but you have " +
+ tf.__version__)
+
+ invalid = ""
+ # To be filled
+
+ if invalid:
+ raise ValueError(invalid + " options must be used with v1")
+
+ if flags.input_shapes:
+ if not flags.input_arrays:
+ raise ValueError("--input_shapes must be used with --input_arrays")
+ if flags.input_shapes.count(":") != flags.input_arrays.count(","):
+ raise ValueError("--input_shapes and --input_arrays must have the same "
+ "number of items")
+
+
+def _parse_array(arrays, type_fn=str):
+ return list(map(type_fn, arrays.split(",")))
+
+
+def _v1_convert(flags):
+ if flags.model_format == "graph_def":
+ if not flags.input_arrays:
+ raise ValueError("--input_arrays must be provided")
+ if not flags.output_arrays:
+ raise ValueError("--output_arrays must be provided")
+ input_shapes = None
+ if flags.input_shapes:
+ input_arrays = _parse_array(flags.input_arrays)
+ input_shapes_list = [
+ _parse_array(shape, type_fn=int)
+ for shape in flags.input_shapes.split(":")
+ ]
+ input_shapes = dict(list(zip(input_arrays, input_shapes_list)))
+
+ converter = tf.compat.v1.lite.TFLiteConverter.from_frozen_graph(
+ flags.input_path, _parse_array(flags.input_arrays),
+ _parse_array(flags.output_arrays), input_shapes)
+
+ if flags.model_format == "saved_model":
+ converter = tf.compat.v1.lite.TFLiteConverter.from_saved_model(flags.input_path)
+
+ if flags.model_format == "keras_model":
+ converter = tf.compat.v1.lite.TFLiteConverter.from_keras_model_file(
+ flags.input_path)
+
+ converter.allow_custom_ops = True
+
+ tflite_model = converter.convert()
+ open(flags.output_path, "wb").write(tflite_model)
+
+
+def _v2_convert(flags):
+ if flags.model_format == "graph_def":
+ if not flags.input_arrays:
+ raise ValueError("--input_arrays must be provided")
+ if not flags.output_arrays:
+ raise ValueError("--output_arrays must be provided")
+ file_content = open(flags.input_path, 'rb').read()
+ try:
+ graph_def = tf.compat.v1.GraphDef()
+ graph_def.ParseFromString(file_content)
+ except (_text_format.ParseError, DecodeError):
+ try:
+ _text_format.Merge(file_content, graph_def)
+ except (_text_format.ParseError, DecodeError):
+ raise IOError("Unable to parse input file '{}'.".format(flags.input_path))
+
+ wrap_func = wrap_frozen_graph(
+ graph_def,
+ inputs=[
+ _str + ":0" if len(_str.split(":")) == 1 else _str
+ for _str in _parse_array(flags.input_arrays)
+ ],
+ outputs=[
+ _str + ":0" if len(_str.split(":")) == 1 else _str
+ for _str in _parse_array(flags.output_arrays)
+ ])
+ converter = tf.lite.TFLiteConverter.from_concrete_functions([wrap_func])
+
+ if flags.model_format == "saved_model":
+ converter = tf.lite.TFLiteConverter.from_saved_model(flags.input_path)
+
+ if flags.model_format == "keras_model":
+ keras_model = tf.keras.models.load_model(flags.input_path)
+ converter = tf.lite.TFLiteConverter.from_keras_model(keras_model)
+
+ converter.allow_custom_ops = True
+ converter.experimental_new_converter = True
+
+ converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS]
+
+ tflite_model = converter.convert()
+ open(flags.output_path, "wb").write(tflite_model)
+
+
+def _convert(flags):
+ if (flags.v1):
+ _v1_convert(flags)
+ else:
+ _v2_convert(flags)
+
+
+"""
+Input frozen graph must be from TensorFlow 1.13.1
+"""
+
+
+def main():
+ # Parse argument.
+ parser = _get_parser()
+
+ # Check if the flags are valid.
+ flags = parser.parse_known_args(args=sys.argv[1:])
+ _check_flags(flags[0])
+
+ # Convert
+ _convert(flags[0])
+
+
+if __name__ == "__main__":
+ main()
diff --git a/compiler/tfgraph-xform/CMakeLists.txt b/compiler/tfgraph-xform/CMakeLists.txt
deleted file mode 100644
index 6c6737da4..000000000
--- a/compiler/tfgraph-xform/CMakeLists.txt
+++ /dev/null
@@ -1,328 +0,0 @@
-macro(require_package PKGNAME)
- nncc_find_package(${PKGNAME} ${ARGN} QUIET)
- if(NOT ${PKGNAME}_FOUND)
- message(STATUS "Build tfgraph-xform: FALSE (${PKGNAME} is missing)")
- return()
- endif(NOT ${PKGNAME}_FOUND)
-endmacro(require_package)
-
-require_package(Abseil)
-require_package(Protobuf)
-require_package(EigenSource-fd6845384b86)
-require_package(GoogleDoubleConversion)
-require_package(GoogleNSync)
-require_package(TensorFlowSource EXACT 1.12)
-require_package(TensorFlowProtoText EXACT 1.12)
-
-message(STATUS "Build tfgraph-xform: TRUE")
-
-#
-# Set "SOURCE_FILES"
-#
-unset(SOURCE_FILES)
-
-macro(Source_Add RPATH)
- list(APPEND SOURCE_FILES "${TensorFlowSource_DIR}/tensorflow/${RPATH}")
-endmacro(Source_Add)
-
-# TensorFlow "core"
-Source_Add(core/lib/core/status.cc)
-Source_Add(core/lib/core/coding.cc)
-Source_Add(core/lib/core/arena.cc)
-Source_Add(core/lib/core/threadpool.cc)
-
-Source_Add(core/lib/strings/scanner.cc)
-Source_Add(core/lib/strings/str_util.cc)
-Source_Add(core/lib/strings/numbers.cc)
-Source_Add(core/lib/strings/stringprintf.cc)
-Source_Add(core/lib/strings/strcat.cc)
-Source_Add(core/lib/strings/proto_text_util.cc)
-Source_Add(core/lib/strings/proto_serialization.cc)
-Source_Add(core/lib/strings/ordered_code.cc)
-Source_Add(core/lib/hash/hash.cc)
-Source_Add(core/lib/hash/crc32c.cc)
-Source_Add(core/lib/hash/crc32c_accelerate.cc)
-Source_Add(core/lib/io/iterator.cc)
-Source_Add(core/lib/io/two_level_iterator.cc)
-Source_Add(core/lib/io/format.cc)
-Source_Add(core/lib/io/block.cc)
-Source_Add(core/lib/io/table.cc)
-Source_Add(core/lib/random/random.cc)
-Source_Add(core/lib/io/path.cc)
-
-Source_Add(core/platform/cpu_info.cc)
-Source_Add(core/platform/abi.cc)
-Source_Add(core/platform/env.cc)
-Source_Add(core/platform/env_time.cc)
-Source_Add(core/platform/file_system.cc)
-Source_Add(core/platform/file_system_helper.cc)
-Source_Add(core/platform/tensor_coding.cc)
-Source_Add(core/platform/tracing.cc)
-Source_Add(core/platform/setround.cc)
-Source_Add(core/platform/denormal.cc)
-Source_Add(core/platform/protobuf_util.cc)
-
-Source_Add(core/platform/default/mutex.cc)
-Source_Add(core/platform/default/logging.cc)
-Source_Add(core/platform/default/string_coding.cc)
-
-Source_Add(core/platform/posix/error.cc)
-Source_Add(core/platform/posix/env.cc)
-Source_Add(core/platform/posix/env_time.cc)
-Source_Add(core/platform/posix/port.cc)
-Source_Add(core/platform/posix/load_library.cc)
-Source_Add(core/platform/posix/posix_file_system.cc)
-
-Source_Add(core/util/env_var.cc)
-Source_Add(core/util/padding.cc)
-Source_Add(core/util/mirror_pad_mode.cc)
-Source_Add(core/util/command_line_flags.cc)
-Source_Add(core/util/tensor_format.cc)
-Source_Add(core/util/tensor_slice_set.cc)
-Source_Add(core/util/tensor_slice_reader.cc)
-Source_Add(core/util/tensor_slice_reader_cache.cc)
-Source_Add(core/util/saved_tensor_slice_util.cc)
-Source_Add(core/util/equal_graph_def.cc)
-Source_Add(core/util/device_name_utils.cc)
-Source_Add(core/util/work_sharder.cc)
-Source_Add(core/util/use_cudnn.cc)
-Source_Add(core/util/strided_slice_op.cc)
-Source_Add(core/util/bcast.cc)
-
-Source_Add(core/graph/tensor_id.cc)
-Source_Add(core/graph/algorithm.cc)
-Source_Add(core/graph/node_builder.cc)
-Source_Add(core/graph/subgraph.cc)
-Source_Add(core/graph/graph.cc)
-Source_Add(core/graph/graph_constructor.cc)
-Source_Add(core/graph/edgeset.cc)
-Source_Add(core/graph/while_context.cc)
-Source_Add(core/graph/control_flow.cc)
-Source_Add(core/graph/gradients.cc)
-Source_Add(core/graph/optimizer_cse.cc)
-
-Source_Add(core/framework/versions.cc)
-Source_Add(core/framework/types.cc)
-Source_Add(core/framework/function.cc)
-Source_Add(core/framework/op.cc)
-Source_Add(core/framework/op_def_builder.cc)
-Source_Add(core/framework/op_kernel.cc)
-Source_Add(core/framework/op_segment.cc)
-Source_Add(core/framework/resource_handle.cc)
-Source_Add(core/framework/tensor.cc)
-Source_Add(core/framework/tensor_shape.cc)
-Source_Add(core/framework/tensor_reference.cc)
-Source_Add(core/framework/tensor_slice.cc)
-Source_Add(core/framework/tensor_util.cc)
-Source_Add(core/framework/unique_tensor_references.cc)
-Source_Add(core/framework/allocator.cc)
-Source_Add(core/framework/allocator_registry.cc)
-Source_Add(core/framework/tracking_allocator.cc)
-Source_Add(core/framework/variant.cc)
-Source_Add(core/framework/variant_op_registry.cc)
-Source_Add(core/framework/variant_tensor_data.cc)
-Source_Add(core/framework/memory_types.cc)
-Source_Add(core/framework/log_memory.cc)
-Source_Add(core/framework/node_def_builder.cc)
-
-Source_Add(core/framework/common_shape_fns.cc)
-Source_Add(core/framework/shape_inference.cc)
-
-Source_Add(core/framework/resource_mgr.cc)
-Source_Add(core/framework/device_base.cc)
-Source_Add(core/framework/rendezvous.cc)
-Source_Add(core/framework/cancellation.cc)
-
-Source_Add(core/framework/attr_value_util.cc)
-Source_Add(core/framework/attr_value_util.cc)
-Source_Add(core/framework/op_def_util.cc)
-Source_Add(core/framework/node_def_util.cc)
-Source_Add(core/framework/kernel_def_builder.cc)
-Source_Add(core/framework/kernel_def_util.cc)
-
-Source_Add(core/common_runtime/device.cc)
-Source_Add(core/common_runtime/device_mgr.cc)
-Source_Add(core/common_runtime/function.cc)
-Source_Add(core/common_runtime/memory_types.cc)
-Source_Add(core/common_runtime/copy_tensor.cc)
-Source_Add(core/common_runtime/shape_refiner.cc)
-Source_Add(core/common_runtime/constant_folding.cc)
-Source_Add(core/common_runtime/eval_const_tensor.cc)
-Source_Add(core/common_runtime/graph_optimizer.cc)
-Source_Add(core/common_runtime/graph_runner.cc)
-Source_Add(core/common_runtime/rendezvous_mgr.cc)
-Source_Add(core/common_runtime/rendezvous_util.cc)
-Source_Add(core/common_runtime/process_function_library_runtime.cc)
-Source_Add(core/common_runtime/executor.cc)
-Source_Add(core/common_runtime/executor_factory.cc)
-
-# TensorFlow - Operations
-Source_Add(core/ops/no_op.cc)
-Source_Add(core/ops/sendrecv_ops.cc)
-Source_Add(core/ops/array_ops.cc)
-Source_Add(core/ops/math_ops.cc)
-Source_Add(core/ops/image_ops.cc)
-Source_Add(core/ops/nn_ops.cc)
-
-# TensorFlow - OpKernel Implementations
-Source_Add(core/kernels/ops_util.cc)
-Source_Add(core/kernels/cwise_ops_common.cc)
-Source_Add(core/kernels/cwise_op_add_1.cc)
-Source_Add(core/kernels/cwise_op_sub.cc)
-Source_Add(core/kernels/cwise_op_mul_1.cc)
-Source_Add(core/kernels/strided_slice_op.cc)
-Source_Add(core/kernels/strided_slice_op_inst_0.cc)
-Source_Add(core/kernels/strided_slice_op_inst_1.cc)
-Source_Add(core/kernels/strided_slice_op_inst_2.cc)
-Source_Add(core/kernels/strided_slice_op_inst_3.cc)
-Source_Add(core/kernels/strided_slice_op_inst_4.cc)
-Source_Add(core/kernels/strided_slice_op_inst_5.cc)
-Source_Add(core/kernels/strided_slice_op_inst_6.cc)
-Source_Add(core/kernels/strided_slice_op_inst_7.cc)
-Source_Add(core/kernels/relu_op.cc)
-Source_Add(core/kernels/conv_ops.cc)
-Source_Add(core/kernels/conv_grad_ops.cc)
-Source_Add(core/kernels/conv_grad_input_ops.cc)
-Source_Add(core/kernels/bias_op.cc)
-Source_Add(core/kernels/pad_op.cc)
-Source_Add(core/kernels/cast_op_impl_bool.cc)
-Source_Add(core/kernels/cast_op_impl_int8.cc)
-Source_Add(core/kernels/cast_op_impl_uint8.cc)
-Source_Add(core/kernels/cast_op_impl_int16.cc)
-Source_Add(core/kernels/cast_op_impl_uint16.cc)
-Source_Add(core/kernels/cast_op_impl_int32.cc)
-Source_Add(core/kernels/cast_op_impl_uint32.cc)
-Source_Add(core/kernels/cast_op_impl_int64.cc)
-Source_Add(core/kernels/cast_op_impl_uint64.cc)
-Source_Add(core/kernels/cast_op_impl_half.cc)
-Source_Add(core/kernels/cast_op_impl_bfloat.cc)
-Source_Add(core/kernels/cast_op_impl_float.cc)
-Source_Add(core/kernels/cast_op_impl_double.cc)
-Source_Add(core/kernels/cast_op_impl_complex64.cc)
-Source_Add(core/kernels/cast_op_impl_complex128.cc)
-Source_Add(core/kernels/cast_op.cc)
-Source_Add(core/kernels/split_op.cc)
-Source_Add(core/kernels/concat_lib_cpu.cc)
-Source_Add(core/kernels/concat_op.cc)
-Source_Add(core/kernels/resize_bilinear_op.cc)
-Source_Add(core/kernels/constant_op.cc)
-Source_Add(core/kernels/pack_op.cc)
-Source_Add(core/kernels/reshape_op.cc)
-Source_Add(core/kernels/shape_ops.cc)
-Source_Add(core/kernels/fill_functor.cc)
-Source_Add(core/kernels/fused_batch_norm_op.cc)
-Source_Add(core/kernels/identity_op.cc)
-Source_Add(core/kernels/split_lib_cpu.cc)
-Source_Add(core/kernels/unpack_op.cc)
-Source_Add(core/kernels/pooling_ops_common.cc)
-Source_Add(core/kernels/maxpooling_op.cc)
-Source_Add(core/kernels/deep_conv2d.cc)
-Source_Add(core/kernels/no_op.cc)
-Source_Add(core/kernels/sendrecv_ops.cc)
-
-# TensorFlow "transform_graph" - Basic Infrastructure
-Source_Add(tools/graph_transforms/file_utils.cc)
-Source_Add(tools/graph_transforms/transform_utils.cc)
-Source_Add(tools/graph_transforms/transform_graph.cc)
-Source_Add(tools/graph_transforms/transform_graph_main.cc)
-
-# TensorFlow "trasnform_graph" - Transfrom Implementations
-Source_Add(tools/graph_transforms/fold_constants_lib.cc)
-Source_Add(tools/graph_transforms/fold_old_batch_norms.cc)
-Source_Add(tools/graph_transforms/strip_unused_nodes.cc)
-
-#
-# Set "PROTO_FILES"
-#
-unset(PROTO_FILES)
-
-macro(Proto_Add RPATH)
- list(APPEND PROTO_FILES "${RPATH}")
-endmacro(Proto_Add)
-
-Proto_Add(tensorflow/core/lib/core/error_codes.proto)
-
-# Minimal Protocol Buffer Specification to read GraphDef
-Proto_Add(tensorflow/core/framework/versions.proto)
-Proto_Add(tensorflow/core/framework/resource_handle.proto)
-Proto_Add(tensorflow/core/framework/types.proto)
-Proto_Add(tensorflow/core/framework/tensor.proto)
-Proto_Add(tensorflow/core/framework/tensor_shape.proto)
-Proto_Add(tensorflow/core/framework/tensor_slice.proto)
-Proto_Add(tensorflow/core/framework/attr_value.proto)
-Proto_Add(tensorflow/core/framework/op_def.proto)
-Proto_Add(tensorflow/core/framework/node_def.proto)
-Proto_Add(tensorflow/core/framework/function.proto)
-Proto_Add(tensorflow/core/framework/graph.proto)
-
-Proto_Add(tensorflow/core/framework/api_def.proto)
-# "tensorflow/core/framework/tensor.cc" requires these headers
-Proto_Add(tensorflow/core/framework/allocation_description.proto)
-Proto_Add(tensorflow/core/framework/tensor_description.proto)
-Proto_Add(tensorflow/core/framework/log_memory.proto)
-Proto_Add(tensorflow/core/framework/kernel_def.proto)
-Proto_Add(tensorflow/core/framework/device_attributes.proto)
-Proto_Add(tensorflow/core/framework/cost_graph.proto)
-Proto_Add(tensorflow/core/framework/step_stats.proto)
-
-Proto_Add(tensorflow/core/protobuf/cluster.proto)
-Proto_Add(tensorflow/core/protobuf/config.proto)
-Proto_Add(tensorflow/core/protobuf/debug.proto)
-Proto_Add(tensorflow/core/protobuf/rewriter_config.proto)
-
-Proto_Add(tensorflow/core/util/saved_tensor_slice.proto)
-
-#
-# Set "PROTO_TEXT_FILES"
-#
-unset(PROTO_TEXT_FILES)
-
-macro(ProtoText_Add RPATH)
- list(APPEND PROTO_TEXT_FILES "${RPATH}")
-endmacro(ProtoText_Add)
-
-ProtoText_Add(tensorflow/core/framework/versions.proto)
-ProtoText_Add(tensorflow/core/framework/attr_value.proto)
-ProtoText_Add(tensorflow/core/framework/resource_handle.proto)
-ProtoText_Add(tensorflow/core/framework/types.proto)
-ProtoText_Add(tensorflow/core/framework/tensor_shape.proto)
-ProtoText_Add(tensorflow/core/framework/tensor_description.proto)
-ProtoText_Add(tensorflow/core/framework/allocation_description.proto)
-ProtoText_Add(tensorflow/core/framework/tensor.proto)
-ProtoText_Add(tensorflow/core/framework/op_def.proto)
-ProtoText_Add(tensorflow/core/framework/node_def.proto)
-ProtoText_Add(tensorflow/core/framework/function.proto)
-ProtoText_Add(tensorflow/core/framework/graph.proto)
-ProtoText_Add(tensorflow/core/framework/kernel_def.proto)
-ProtoText_Add(tensorflow/core/framework/log_memory.proto)
-ProtoText_Add(tensorflow/core/framework/device_attributes.proto)
-
-#
-# Build "tfgraph-xform" executable
-#
-Protobuf_Generate(TF_PROTO
- "${CMAKE_CURRENT_BINARY_DIR}/gen/tensorflow-proto" # OUTPUT ROOT
- "${TensorFlowSource_DIR}" # BASE DIRECTORY
- ${PROTO_FILES} # .proto path (relative to the BASE)
-)
-
-ProtoText_Generate(TF_PROTO_TEXT
- "${CMAKE_CURRENT_BINARY_DIR}/gen/tensorflow-prototext" # OUTPUT ROOT
- ${PROTO_FILES}
-)
-
-add_executable(tfgraph-xform
- ${SOURCE_FILES} # TensorFlow Source Files
- ${TF_PROTO_SOURCES} # Source Files generated by Protocol Buffer
- ${TF_PROTO_TEXT_SOURCES} # Source Files generated by Proto Text
-)
-
-target_include_directories(tfgraph-xform PRIVATE ${TensorFlowSource_DIR})
-target_include_directories(tfgraph-xform PRIVATE ${TF_PROTO_INCLUDE_DIRS})
-target_include_directories(tfgraph-xform PRIVATE ${TF_PROTO_TEXT_INCLUDE_DIRS})
-target_include_directories(tfgraph-xform PRIVATE ${EigenSource_DIR})
-target_link_libraries(tfgraph-xform PRIVATE ${TF_PROTO_LIBRARIES})
-target_link_libraries(tfgraph-xform PRIVATE abseil)
-target_link_libraries(tfgraph-xform PRIVATE dl)
-target_link_libraries(tfgraph-xform PRIVATE Google::DoubleConversion)
-target_link_libraries(tfgraph-xform PRIVATE Google::NSync)
diff --git a/compiler/tfgraph-xform/README.md b/compiler/tfgraph-xform/README.md
deleted file mode 100644
index 41fb88530..000000000
--- a/compiler/tfgraph-xform/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# tfgraph-xform
-
-Let's build TensorFlow "transform-graph" tool without Bazel.
-
-**DISCLAIMER** Not every transformation is supported.
diff --git a/compiler/tfinfo-v2/CMakeLists.txt b/compiler/tfinfo-v2/CMakeLists.txt
new file mode 100644
index 000000000..cf438ea29
--- /dev/null
+++ b/compiler/tfinfo-v2/CMakeLists.txt
@@ -0,0 +1,36 @@
+nnas_find_package(Protobuf QUIET)
+
+if(NOT Protobuf_FOUND)
+ return()
+endif(NOT Protobuf_FOUND)
+
+# generating and building schema
+Protobuf_Generate(TFINFO_PROTO
+ "${CMAKE_CURRENT_BINARY_DIR}/generated"
+ "./proto"
+ tfinfo-v2.proto)
+
+add_library(tfinfo_v2_proto STATIC ${TFINFO_PROTO_SOURCES})
+set_target_properties(tfinfo_v2_proto PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(tfinfo_v2_proto PUBLIC ${TFINFO_PROTO_INCLUDE_DIRS})
+target_link_libraries(tfinfo_v2_proto PUBLIC libprotobuf)
+
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(tfinfo_v2 STATIC ${SOURCES})
+set_target_properties(tfinfo_v2 PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(tfinfo_v2 PUBLIC include)
+target_link_libraries(tfinfo_v2 PRIVATE tfinfo_v2_proto)
+target_link_libraries(tfinfo_v2 PRIVATE oops)
+target_link_libraries(tfinfo_v2 PRIVATE stdex)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(tfinfo_v2_test ${TESTS})
+target_link_libraries(tfinfo_v2_test tfinfo_v2)
diff --git a/compiler/tfinfo-v2/README.md b/compiler/tfinfo-v2/README.md
new file mode 100644
index 000000000..72fbdb06a
--- /dev/null
+++ b/compiler/tfinfo-v2/README.md
@@ -0,0 +1 @@
+# tfinfo-v2
diff --git a/compiler/tfinfo-v2/include/tfinfo-v2/TensorInfoLoader.h b/compiler/tfinfo-v2/include/tfinfo-v2/TensorInfoLoader.h
new file mode 100644
index 000000000..ee3348e85
--- /dev/null
+++ b/compiler/tfinfo-v2/include/tfinfo-v2/TensorInfoLoader.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFINFO_TENSOR_INFO_LOADER_H__
+#define __TFINFO_TENSOR_INFO_LOADER_H__
+
+#include "TensorSignature.h"
+
+#include <memory>
+#include <vector>
+
+namespace tfinfo
+{
+inline namespace v2
+{
+
+/**
+ * @brief Function to create TensorSignatures defined in info file
+ */
+TensorSignatures load(const char *info_path);
+
+/**
+ * @brief Function to create TensorSignatures from stream
+ */
+TensorSignatures load(std::istream *stream, const char *path_for_error_msg);
+
+} // namespace v2
+} // namespace tfinfo
+
+#endif // __TFINFO_TENSOR_INFO_LOADER_H__
diff --git a/compiler/tfinfo-v2/include/tfinfo-v2/TensorSignature.h b/compiler/tfinfo-v2/include/tfinfo-v2/TensorSignature.h
new file mode 100644
index 000000000..f26d0354a
--- /dev/null
+++ b/compiler/tfinfo-v2/include/tfinfo-v2/TensorSignature.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFINFO_V2_TENSORSIGNATURE_H__
+#define __TFINFO_V2_TENSORSIGNATURE_H__
+
+#include <map>
+#include <vector>
+#include <memory>
+#include <string>
+#include <stdexcept>
+
+namespace tfinfo
+{
+inline namespace v2
+{
+
+/**
+ * @brief Supported Data Types
+ */
+enum class DataType
+{
+ UNKNOWN,
+
+ FLOAT32, // IEEE 32-bit floating point
+ /* To be added */
+};
+
+/**
+ * @brief Class to represent axis and size of dims.
+ * User should enter axis and size of dim(s) when input tensor(s) contain(s) unknown dim(s).
+ * Such axis and size of dim(s) will be stored in ShapeHint.
+ */
+class ShapeHint
+{
+ using AxisHint = uint32_t;
+ using SizeHint = uint64_t;
+
+public:
+ ShapeHint() = default;
+
+ void add(AxisHint axis, SizeHint size)
+ {
+ if (_dims.find(axis) != _dims.end())
+ throw std::runtime_error("dim value already exists");
+
+ _dims[axis] = size;
+ }
+
+ std::map<AxisHint, SizeHint>::const_iterator cbegin() const { return _dims.cbegin(); }
+
+ std::map<AxisHint, SizeHint>::const_iterator cend() const { return _dims.cend(); }
+
+ bool empty() { return _dims.size() == 0; }
+
+ size_t size() { return _dims.size(); }
+
+private:
+ std::map<AxisHint, SizeHint> _dims;
+};
+
+using TensorName = std::string;
+
+/**
+ * @brief Class to store input and output tensor information
+ */
+class TensorSignature final
+{
+public:
+ enum class Kind
+ {
+ Input,
+ Output
+ };
+
+ TensorSignature(const Kind kind, const std::string &name) : _kind(kind), _tensor_name()
+ {
+ // tensor name can be a form of "placeholder:0" or "placeholder".
+ // If tensor index is omitted, ":0" is appended
+ auto pos = name.find(":");
+ if (pos == std::string::npos)
+ _tensor_name.assign(name + ":0");
+ else
+ _tensor_name.assign(name);
+ }
+
+ TensorSignature(const Kind kind, const std::string &name, const ShapeHint &shape_hint)
+ : TensorSignature(kind, name)
+ {
+ _shape_hint = shape_hint;
+ }
+
+public:
+ Kind kind() const { return _kind; }
+
+ const TensorName &name() { return _tensor_name; }
+
+ ShapeHint &shapeHint() { return _shape_hint; }
+
+private:
+ Kind _kind;
+ std::string _tensor_name;
+ ShapeHint _shape_hint;
+};
+
+using TensorSignatures = std::vector<std::unique_ptr<TensorSignature>>;
+
+} // namespace v2
+} // namespace tfinfo
+
+#endif // __TFINFO_V2_TENSORSIGNATURE_H__
diff --git a/compiler/tfinfo-v2/proto/tfinfo-v2.proto b/compiler/tfinfo-v2/proto/tfinfo-v2.proto
new file mode 100644
index 000000000..4f7c47b4a
--- /dev/null
+++ b/compiler/tfinfo-v2/proto/tfinfo-v2.proto
@@ -0,0 +1,46 @@
+syntax = "proto3";
+
+package tfinfo_v2_proto;
+option cc_enable_arenas = true;
+
+/*
+Example of prototxt file is as follows:
+
+input {
+ name : "placeholder:0"
+}
+output {
+ name : "relu:0"
+}
+
+When a model has unknown dim in its input,
+value of all unknowm dims must be provided like the following:
+
+input {
+ name : "placeholder:0"
+ dim { axis: 0 size: 8 }
+ dim { axis: 3 size: 4 }
+}
+output {
+ name : "relu:0"
+}
+*/
+
+message Dim {
+ int32 axis = 1;
+ int64 size = 2; // tensorflow uses int64
+}
+
+message OutputDef {
+ string name = 1;
+}
+
+message InputDef {
+ string name = 1;
+ repeated Dim dim = 2;
+}
+
+message InfoDef {
+ repeated InputDef input = 1;
+ repeated OutputDef output = 2;
+}
diff --git a/compiler/tfinfo-v2/requires.cmake b/compiler/tfinfo-v2/requires.cmake
new file mode 100644
index 000000000..e7efab4fb
--- /dev/null
+++ b/compiler/tfinfo-v2/requires.cmake
@@ -0,0 +1,2 @@
+require("oops")
+require("stdex")
diff --git a/compiler/tfinfo-v2/src/TFInfo_v2.test.cpp b/compiler/tfinfo-v2/src/TFInfo_v2.test.cpp
new file mode 100644
index 000000000..02a2d9199
--- /dev/null
+++ b/compiler/tfinfo-v2/src/TFInfo_v2.test.cpp
@@ -0,0 +1,246 @@
+/*
+ * 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 "tfinfo-v2/TensorInfoLoader.h"
+
+#include "tfinfo-v2/TensorSignature.h"
+
+#include <gtest/gtest.h>
+
+#include <sstream>
+#include <map>
+
+#define TC_CASE(content) #content
+
+using namespace tfinfo::v2;
+
+namespace
+{
+
+// clang-format off
+const std::vector<std::string> success_cases =
+{
+ TC_CASE(
+ output {
+ name : "relu:0"
+ }
+ ),
+
+ TC_CASE(
+ input {
+ name : "placeholder:0"
+ }
+
+ input {
+ name : "placeholder:1"
+ dim { axis:0 size: 1 }
+ dim { axis:2 size: 4 }
+ }
+
+ output {
+ name : "relu:0"
+ }
+ ),
+ // clang-format on
+};
+
+} // namespace
+
+TEST(TFINFO_V2, success_0)
+{
+ std::stringstream ss{success_cases[0]};
+
+ auto tensors = load(&ss, "tfinfo_v2_test");
+
+ std::map<std::string, tfinfo::v2::TensorSignature *> m;
+
+ for (auto &tensor : tensors)
+ {
+ m[tensor->name()] = tensor.get();
+ }
+
+ ASSERT_EQ(m.size(), 1);
+
+ auto t1 = m["relu:0"];
+ {
+ ASSERT_EQ(t1->kind(), tfinfo::v2::TensorSignature::Kind::Output);
+ ASSERT_TRUE(t1->shapeHint().empty());
+ }
+}
+
+TEST(TFINFO_V2, success_1)
+{
+ std::stringstream ss{success_cases[1]};
+
+ auto tensors = load(&ss, "tfinfo_v2_test");
+
+ std::map<std::string, tfinfo::v2::TensorSignature *> m;
+
+ for (auto &tensor : tensors)
+ {
+ m[tensor->name()] = tensor.get();
+ }
+
+ ASSERT_EQ(m.size(), 3);
+
+ auto t1 = m["placeholder:0"];
+ {
+ ASSERT_EQ(t1->kind(), tfinfo::v2::TensorSignature::Kind::Input);
+ ASSERT_TRUE(t1->shapeHint().empty());
+ }
+
+ auto t2 = m["placeholder:1"];
+ {
+ ASSERT_EQ(t2->kind(), tfinfo::v2::TensorSignature::Kind::Input);
+ ASSERT_FALSE(t2->shapeHint().empty());
+
+ auto iter = t2->shapeHint().cbegin();
+
+ ASSERT_TRUE(iter != t2->shapeHint().cend());
+ ASSERT_EQ(iter->first, 0); // axis
+ ASSERT_EQ(iter->second, 1); // size
+
+ iter++;
+
+ ASSERT_TRUE(iter != t2->shapeHint().cend());
+ ASSERT_EQ(iter->first, 2); // axis
+ ASSERT_EQ(iter->second, 4); // size
+
+ iter++;
+
+ ASSERT_TRUE(iter == t2->shapeHint().cend());
+ }
+
+ auto t3 = m["relu:0"];
+ {
+ ASSERT_EQ(t3->kind(), tfinfo::v2::TensorSignature::Kind::Output);
+ ASSERT_TRUE(t3->shapeHint().empty());
+ }
+}
+
+namespace
+{
+
+// clang-format off
+const std::vector<std::string> fail_cases =
+ {
+ // no output
+ TC_CASE(
+ input {
+ name : "relu:0"
+ }
+ ),
+
+ // no name in input
+ TC_CASE(
+ input {
+ shape {
+ dim { size: 1 }
+ dim { size: 2 }
+ }
+ }
+ output {
+ name : "relu:0"
+ }
+ ),
+
+ // wrong name format - no tensor index
+ TC_CASE(
+ output {
+ name : "name_with_no_index"
+ }
+ ),
+
+ // wrong name format - no name but numbers
+ TC_CASE(
+ output {
+ name : "1"
+ }
+ ),
+
+ // duplicated node def - input, input
+ TC_CASE(
+ input {
+ name : "duplicated_name:0"
+ }
+
+ input {
+ name : "duplicated_name:0"
+ }
+ ),
+
+ // duplicated node def - input, output
+ TC_CASE(
+ input {
+ name : "duplicated_name:0"
+ }
+
+ output {
+ name : "duplicated_name:0"
+ }
+ ),
+
+ // wrong keyword ('in', 'out' instead of 'input', 'output')
+ TC_CASE(
+ in {
+ name : "a:0"
+ }
+
+ out {
+ name : "b:0"
+ }
+ ),
+
+ // wrong keyword ('input_name' instead of 'name')
+ TC_CASE(
+ input {
+ input_name : "a:0"
+ }
+
+ output {
+ name : "b:0"
+ }
+ ),
+
+ // using deprecated format
+ // (note that because of ",", macro TC_CASE cannot be used.)
+ R"(
+ input, a:0, TF_FLOAT, [2, 3 ,4]
+ output, b:0, TF_FLOAT, [2, 3 ,4]
+ )",
+ // clang-format on
+};
+
+} // namespace
+
+TEST(TFINFO_V2, failure)
+{
+ for (int i = 0; i < fail_cases.size(); i++)
+ {
+ std::stringstream ss{fail_cases[i]};
+
+ try
+ {
+ load(&ss, "tfinfo_v2_test");
+
+ FAIL();
+ }
+ catch (const std::exception &e)
+ {
+ std::cerr << ss.str() << std::endl << e.what() << '\n';
+ }
+ }
+}
diff --git a/compiler/tfinfo-v2/src/TensorInfoLoader.cpp b/compiler/tfinfo-v2/src/TensorInfoLoader.cpp
new file mode 100644
index 000000000..0bf828773
--- /dev/null
+++ b/compiler/tfinfo-v2/src/TensorInfoLoader.cpp
@@ -0,0 +1,179 @@
+/*
+ * 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 "tfinfo-v2/TensorInfoLoader.h"
+
+#include "tfinfo-v2/TensorSignature.h"
+
+#include <oops/UserExn.h>
+#include <stdex/Memory.h>
+
+#include <tfinfo-v2.pb.h>
+
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/text_format.h>
+
+#include <fstream>
+#include <fcntl.h>
+
+namespace
+{
+
+// for testing purpose
+bool load_text(std::istream *stream, tfinfo_v2_proto::InfoDef &info_def)
+{
+ google::protobuf::io::IstreamInputStream iis(stream);
+
+ return google::protobuf::TextFormat::Parse(&iis, &info_def);
+}
+
+bool is_num(const std::string &num)
+{
+ for (int i = 0; i < num.length(); i++)
+ if (!isdigit(num[i]))
+ return false;
+
+ return true;
+}
+
+void validate_tensor_name(const std::string &tensor_name, const char *path)
+{
+ // Note that Tensorflow tensor name format is
+ // operation name ":" index, e.g., "in/placeholder:0"
+ int pos = tensor_name.find(":");
+ if (pos == std::string::npos)
+ throw oops::UserExn("Missing index separator, ':'", "name", tensor_name, "file", path);
+
+ if (tensor_name.length() == pos + 1) // ':' is the last char
+ throw oops::UserExn("Missing tensor index after ':'", "name", tensor_name, "file", path);
+
+ // 1. Validating operation name.
+ // for naming format, refer to https://www.tensorflow.org/api_docs/python/tf/Operation#__init__
+ // First char is in the form of "[A-Za-z0-9.]"
+ // and the rest chars are in the form of "[A-Za-z0-9_.\\-/]*"
+ std::string op_name = tensor_name.substr(0, pos);
+
+ // first character
+ if (!(isalnum(op_name[0]) || op_name[0] == '.'))
+ throw oops::UserExn("Wrong tensor name format", "name", tensor_name, "file", path);
+
+ // and the rest chars
+ for (int i = 1; i < op_name.length(); i++)
+ if (!(isalnum(op_name[i]) || std::string("_.\\-/").find(op_name[i]) != std::string::npos))
+ throw oops::UserExn("Wrong tensor name format", "name", tensor_name, "file", path);
+
+ // 2. validating index after ":"
+ std::string index = tensor_name.substr(pos + 1, op_name.length() - pos - 1);
+
+ if (!is_num(index))
+ throw oops::UserExn("Wrong tensor name format", "name", tensor_name, "file", path);
+}
+
+void check_duplicate(tfinfo::v2::TensorSignatures &tensors, const char *path)
+{
+ std::map<std::string, bool> tool;
+ for (auto &tensor : tensors)
+ {
+ if (tool.find(tensor->name()) != tool.end())
+ throw oops::UserExn("Duplicate tensor definition", "name", tensor->name(), "file", path);
+ else
+ tool[tensor->name()] = true;
+ }
+}
+
+void convert(tfinfo_v2_proto::InfoDef &info_def, tfinfo::v2::TensorSignatures &tensors,
+ const char *path)
+{
+ // processing input. Note that there could be no input.
+ if (auto input_size = info_def.input_size())
+ {
+ for (int i = 0; i < input_size; i++)
+ {
+ auto input_def = info_def.input().Get(i);
+
+ auto name = input_def.name();
+ validate_tensor_name(name, path);
+
+ auto tensor = stdex::make_unique<tfinfo::v2::TensorSignature>(
+ tfinfo::v2::TensorSignature::Kind::Input, name);
+
+ // when there is dim attribute for unknown shape
+ if (input_def.dim_size() > 0)
+ {
+ for (int d = 0; d < input_def.dim().size(); d++)
+ {
+ auto dim = input_def.dim(d);
+ tensor->shapeHint().add(dim.axis(), dim.size());
+ }
+ }
+
+ tensors.emplace_back(std::move(tensor));
+ }
+ }
+
+ // processing output
+ auto output_size = info_def.output_size();
+ if (output_size == 0)
+ throw oops::UserExn("Missing output node. At least 1 output node must exist", "file", path);
+
+ if (auto output_node_size = info_def.output_size())
+ {
+ for (int i = 0; i < output_node_size; i++)
+ {
+ auto name = info_def.output().Get(i).name();
+ validate_tensor_name(name, path);
+
+ auto tensor = stdex::make_unique<tfinfo::v2::TensorSignature>(
+ tfinfo::v2::TensorSignature::Kind::Output, name);
+ tensors.emplace_back(std::move(tensor));
+ }
+ }
+
+ check_duplicate(tensors, path);
+}
+
+} // namespace
+
+namespace tfinfo
+{
+inline namespace v2
+{
+
+TensorSignatures load(const char *path)
+{
+ std::ifstream stream(path, std::ios::in | std::ios::binary);
+
+ return load(&stream, path);
+}
+
+TensorSignatures load(std::istream *stream, const char *path_for_error_msg)
+{
+ tfinfo_v2_proto::InfoDef info_def;
+
+ if (!load_text(stream, info_def))
+ {
+ throw oops::UserExn("Cannot parse the info file", "path", path_for_error_msg);
+ }
+
+ TensorSignatures tensors;
+
+ convert(info_def, tensors, path_for_error_msg);
+
+ return tensors;
+}
+
+} // namespace v2
+} // namespace tfinfo
diff --git a/compiler/tfinfo-v2/src/TensorSignature.cpp b/compiler/tfinfo-v2/src/TensorSignature.cpp
new file mode 100644
index 000000000..3107f33db
--- /dev/null
+++ b/compiler/tfinfo-v2/src/TensorSignature.cpp
@@ -0,0 +1,17 @@
+/*
+ * 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 "tfinfo-v2/TensorSignature.h"
diff --git a/compiler/tfinfo/CMakeLists.txt b/compiler/tfinfo/CMakeLists.txt
index 6c5644ee1..678912e6f 100644
--- a/compiler/tfinfo/CMakeLists.txt
+++ b/compiler/tfinfo/CMakeLists.txt
@@ -5,7 +5,7 @@ list(REMOVE_ITEM SOURCES ${TESTS})
add_library(tfinfo STATIC ${SOURCES})
set_target_properties(tfinfo PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_include_directories(tfinfo PUBLIC include)
-target_link_libraries(tfinfo stdex angkor)
+target_link_libraries(tfinfo stdex angkor oops)
# TODO Remove "nnkit_support_tftestinfo" later
add_library(nnkit_support_tftestinfo ALIAS tfinfo)
@@ -14,7 +14,7 @@ if(NOT ENABLE_TEST)
return()
endif(NOT ENABLE_TEST)
-nncc_find_package(GTest REQUIRED)
+nnas_find_package(GTest REQUIRED)
GTest_AddTest(tfinfo_test ${TESTS})
target_link_libraries(tfinfo_test tfinfo)
diff --git a/compiler/tfinfo/include/nnkit/support/tftestinfo/ParsedTensor.h b/compiler/tfinfo/include/nnkit/support/tftestinfo/ParsedTensor.h
index ea2e2689c..aec8c5e40 100644
--- a/compiler/tfinfo/include/nnkit/support/tftestinfo/ParsedTensor.h
+++ b/compiler/tfinfo/include/nnkit/support/tftestinfo/ParsedTensor.h
@@ -19,6 +19,8 @@
#include "nncc/core/ADT/tensor/Shape.h"
+#include <oops/UserExn.h>
+
#include <string>
#include <cassert>
#include <stdexcept>
@@ -72,6 +74,9 @@ public:
const std::string &name() { return _tensor_name; }
const nncc::core::ADT::tensor::Shape &shape() const { return _shape; }
+ // TODO This method is a bridge between testinfo and testinfo-v2. When testinfo-v2 is introduced,
+ // this method will be removed.
+ nncc::core::ADT::tensor::Shape &mutable_shape() { return _shape; }
const DataType &dtype() const { return _dtype; }
@@ -91,19 +96,22 @@ public:
// sanity check
if (separator == std::string::npos)
- throw std::runtime_error("wrong tensor name format : " + _tensor_name);
+ throw oops::UserExn("Tensor name in wrong format", "name", _tensor_name);
return std::stoi(_tensor_name.substr(separator + 1, _tensor_name.length() - separator - 1));
}
public:
bool isFloatTensor() const { return _dtype == DataType::FLOAT32; }
+ bool hasShape() const { return _has_shape; }
private:
Kind _kind;
std::string _tensor_name;
nncc::core::ADT::tensor::Shape _shape;
DataType _dtype;
+ // Now, every info file has a shape.
+ bool _has_shape = true;
};
} // namespace tftestinfo
diff --git a/compiler/tfinfo/requires.cmake b/compiler/tfinfo/requires.cmake
new file mode 100644
index 000000000..3b45c6458
--- /dev/null
+++ b/compiler/tfinfo/requires.cmake
@@ -0,0 +1,3 @@
+require("stdex")
+require("angkor")
+require("oops")
diff --git a/compiler/tfinfo/src/TensorInfoParser.cpp b/compiler/tfinfo/src/TensorInfoParser.cpp
index a28477efe..9eb3da296 100644
--- a/compiler/tfinfo/src/TensorInfoParser.cpp
+++ b/compiler/tfinfo/src/TensorInfoParser.cpp
@@ -20,6 +20,7 @@
// TODO Remove this file after code cleanup
#include "Compat.h"
+#include <oops/UserExn.h>
#include <stdex/Memory.h>
#include <nncc/core/ADT/tensor/Shape.h>
@@ -40,15 +41,26 @@ namespace
using nnkit::support::tftestinfo::ParsedTensor;
-// remove comment and whitespaces
-void compact(std::string &line)
+// remove comment
+void remove_comment(std::string &line)
{
int pos = line.find_first_of("#");
if (pos != std::string::npos)
line.erase(pos);
+}
+
+std::string trim(const std::string &str)
+{
+ static const std::string whitespace = " \t";
+ static const std::string empty = "";
- while ((pos = line.find_first_of(" \t\n\r")) != std::string::npos)
- line.erase(pos, 1);
+ const auto begin = str.find_first_not_of(whitespace);
+ if (begin == std::string::npos)
+ return empty;
+
+ const auto end = str.find_last_not_of(whitespace);
+
+ return str.substr(begin, end - begin + 1);
}
ParsedTensor::Kind get_kind(const std::string &tok)
@@ -58,7 +70,7 @@ ParsedTensor::Kind get_kind(const std::string &tok)
else if (tok == "output")
return ParsedTensor::Kind::Output;
else
- throw std::runtime_error("A line must be either 'input' or 'output' but : " + tok);
+ throw oops::UserExn("Unrecognizable token", "token", tok);
}
TF_DataType get_dtype(const std::string &tok)
@@ -66,7 +78,7 @@ TF_DataType get_dtype(const std::string &tok)
if (tok == "TF_FLOAT")
return TF_FLOAT;
else
- throw std::runtime_error("Unsupported tensor datatype: " + tok);
+ throw oops::UserExn("Unsupported tensor datatype", "data type", tok);
}
bool validate_num(const std::string &num)
@@ -121,7 +133,7 @@ namespace tftestinfo
#define CHECK_NOT_NULL(x) \
if (!(x)) \
- throw std::runtime_error("parsing error: cannot find token")
+ oops::UserExn("Cannot find required token")
/**
* @brief Function to parse a line of test.info file
@@ -137,29 +149,31 @@ std::unique_ptr<ParsedTensor> parse_line(std::string &line)
TF_DataType dtype;
std::vector<int32_t> shape;
- compact(line);
+ remove_comment(line);
if (line.length() == 0) // empty line or line with comment
return nullptr;
- std::string tok, dim;
+ std::string tok, trimmed, dim;
std::istringstream line_stream(line);
CHECK_NOT_NULL(std::getline(line_stream, tok, ',')); // kind
- kind = get_kind(tok);
+ kind = get_kind(trim(tok));
CHECK_NOT_NULL(std::getline(line_stream, tok, ',')); // tensor name
- if (!validate_name(tok))
- throw std::runtime_error("tensor name in wrong format: " + tok);
- name.assign(tok);
+ trimmed = trim(tok);
+ if (!validate_name(trimmed))
+ throw oops::UserExn("Tensor name in wrong format", "name", tok);
+ name.assign(trimmed);
CHECK_NOT_NULL(std::getline(line_stream, tok, ',')); // data type
- dtype = get_dtype(tok);
+ dtype = get_dtype(trim(tok));
CHECK_NOT_NULL(std::getline(line_stream, tok, '[')); // start of shape
- if (tok.length())
- throw std::runtime_error("unknown token between data type and shape: " + tok);
+ trimmed = trim(tok);
+ if (trimmed.length())
+ throw oops::UserExn("Unknown token between data type and shape", "token", tok);
CHECK_NOT_NULL(std::getline(line_stream, tok, ']'));
@@ -168,15 +182,17 @@ std::unique_ptr<ParsedTensor> parse_line(std::string &line)
bool first = true;
while (std::getline(shape_stream, dim, ',')) // each dim
{
+ dim = trim(dim);
+
if (first && dim.length() == 0)
continue; // scalar
first = false;
if (dim.length() == 0)
- throw std::runtime_error("empty dim in shape:" + tok);
+ throw oops::UserExn("Empty dim in shape", "shape", tok);
if (!validate_num(dim))
- throw std::runtime_error("shape must be number:" + dim);
+ throw oops::UserExn("Dim in shape must be a number", "dim", dim);
shape.emplace_back(std::stoi(dim));
}
@@ -193,7 +209,7 @@ std::vector<std::unique_ptr<ParsedTensor>> parse(const char *info_path)
if (infile.fail())
{
- throw std::runtime_error("stream failed : " + std::string(info_path));
+ throw oops::UserExn("Fail to open file", "path", info_path);
}
std::vector<std::unique_ptr<ParsedTensor>> tensors;
diff --git a/compiler/tfinfo/src/TensorInfoParser.test.cpp b/compiler/tfinfo/src/TensorInfoParser.test.cpp
index 0cd6039f4..73ec61c31 100644
--- a/compiler/tfinfo/src/TensorInfoParser.test.cpp
+++ b/compiler/tfinfo/src/TensorInfoParser.test.cpp
@@ -83,12 +83,23 @@ TEST(NNKIT_TF_PARSER, failure_case)
"input, aa, TF_FLOAT, [abc]", // wrong name
"input, a$a:0, TF_FLOAT, [abc]", // wrong name
"input, aa:a, TF_FLOAT, [abc]", // wrong name (wrong value index)
+ "input aa:a, TF_FLOAT, [1]", // missing comma, exception.what() is "A line must be either 'input' or 'output' but : input aa:a"
+ "input, aa:a TF_FLOAT, [1]", // missing comma
+ "input, aa:a, TF_FLOAT [1]", // missing comma,
};
// clang-format on
for (auto tc : exception_list)
{
- EXPECT_THROW(parse_line(tc), std::exception);
+ try
+ {
+ parse_line(tc);
+ FAIL();
+ }
+ catch (const std::exception &e)
+ {
+ std::cout << e.what() << '\n';
+ }
}
}
diff --git a/compiler/tfkit/CMakeLists.txt b/compiler/tfkit/CMakeLists.txt
index 4ea9eb6c8..b809658b1 100644
--- a/compiler/tfkit/CMakeLists.txt
+++ b/compiler/tfkit/CMakeLists.txt
@@ -1,43 +1,13 @@
-nncc_find_package(Protobuf QUIET)
-nncc_find_package(TensorFlowSource EXACT 1.12 QUIET)
-
-if(NOT Protobuf_FOUND)
- return()
-endif(NOT Protobuf_FOUND)
-
-if(NOT TensorFlowSource_FOUND)
+if(NOT TARGET mio_tf)
return()
-endif(NOT TensorFlowSource_FOUND)
+endif(NOT TARGET mio_tf)
message(STATUS "Build tfkit: TRUE")
-# Minimal Protocol Buffer specification for GraphDef file (.pb) encoding/decoding
-unset(PROTO_FILES)
-list(APPEND PROTO_FILES tensorflow/core/framework/versions.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/resource_handle.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/types.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/tensor.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/tensor_shape.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/attr_value.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/op_def.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/node_def.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/function.proto)
-list(APPEND PROTO_FILES tensorflow/core/framework/graph.proto)
-
-Protobuf_Generate(TENSORFLOW_GRAPHDEF_PROTO
- "${CMAKE_CURRENT_BINARY_DIR}/generated"
- "${TensorFlowSource_DIR}"
- ${PROTO_FILES})
-
file(GLOB_RECURSE SOURCES "src/*.cpp")
-add_library(tfkitproto STATIC ${TENSORFLOW_GRAPHDEF_PROTO_SOURCES})
-set_target_properties(tfkitproto PROPERTIES POSITION_INDEPENDENT_CODE ON)
-target_include_directories(tfkitproto PUBLIC ${TENSORFLOW_GRAPHDEF_PROTO_INCLUDE_DIRS})
-target_link_libraries(tfkitproto PUBLIC libprotobuf)
-
add_executable(tfkit ${SOURCES})
target_link_libraries(tfkit PRIVATE stdex)
target_link_libraries(tfkit PRIVATE cli)
-target_link_libraries(tfkit PRIVATE tfkitproto)
+target_link_libraries(tfkit PRIVATE mio_tf)
target_link_libraries(tfkit PRIVATE nncc_common)
diff --git a/compiler/tfkit/src/PackCommand.cpp b/compiler/tfkit/src/PackCommand.cpp
index 36bf5a71c..a1c4a6fc8 100644
--- a/compiler/tfkit/src/PackCommand.cpp
+++ b/compiler/tfkit/src/PackCommand.cpp
@@ -51,21 +51,18 @@ template <> void pack<float>(tensorflow::TensorProto *input_tensor)
}
else if (input_tensor->float_val().size() == input_flat_size)
{
- // clang-format off
- // TODO fix indentation
- input_tensor->clear_tensor_content();
+ input_tensor->clear_tensor_content();
- std::vector<float> tensor_content;
- for (int i = 0; i < input_flat_size; ++i)
- {
- tensor_content.push_back(input_tensor->float_val(i));
- }
+ std::vector<float> tensor_content;
+ for (int i = 0; i < input_flat_size; ++i)
+ {
+ tensor_content.push_back(input_tensor->float_val(i));
+ }
- input_tensor->set_tensor_content(std::string(
- reinterpret_cast<const char *>(tensor_content.data()), sizeof(float) * input_flat_size));
+ input_tensor->set_tensor_content(std::string(
+ reinterpret_cast<const char *>(tensor_content.data()), sizeof(float) * input_flat_size));
- input_tensor->clear_float_val();
- // clang-format on
+ input_tensor->clear_float_val();
}
else
{
diff --git a/compiler/tfkit/src/UnpackCommand.cpp b/compiler/tfkit/src/UnpackCommand.cpp
index 46253ee03..a6711f131 100644
--- a/compiler/tfkit/src/UnpackCommand.cpp
+++ b/compiler/tfkit/src/UnpackCommand.cpp
@@ -49,18 +49,15 @@ template <> void unpack<float>(tensorflow::TensorProto *input_tensor)
}
else if (input_tensor->tensor_content().size() == input_flat_size * sizeof(float))
{
- // clang-format off
- // TODO fix indentation
- input_tensor->clear_float_val();
+ input_tensor->clear_float_val();
- const float *tensor_content =
- reinterpret_cast<const float *>(input_tensor->tensor_content().data());
- for (int i = 0; i < input_flat_size; i++)
- {
- input_tensor->add_float_val(tensor_content[i]);
- }
- input_tensor->clear_tensor_content();
- // clang-format on
+ const float *tensor_content =
+ reinterpret_cast<const float *>(input_tensor->tensor_content().data());
+ for (int i = 0; i < input_flat_size; i++)
+ {
+ input_tensor->add_float_val(tensor_content[i]);
+ }
+ input_tensor->clear_tensor_content();
}
else
{
@@ -104,6 +101,76 @@ template <> void unpack<int32_t>(tensorflow::TensorProto *input_tensor)
}
}
+template <> void unpack<int8_t>(tensorflow::TensorProto *input_tensor)
+{
+ const auto &input_shape = input_tensor->tensor_shape();
+ assert(input_shape.dim_size() <= 6);
+ int input_flat_size = tfkit::tf::GetElementCount(input_shape);
+
+ // Adjust where shape is not set but actual value exist
+ if (input_tensor->tensor_content().size() > 0 && input_flat_size == -1)
+ {
+ input_flat_size = input_tensor->tensor_content().size() / sizeof(int8_t);
+ }
+
+ if (input_tensor->tensor_content().size() == 0)
+ {
+ // Do nothing as there is no tensor content to unpack
+ }
+ else if (input_tensor->tensor_content().size() == input_flat_size * sizeof(int8_t))
+ {
+ input_tensor->clear_int_val();
+
+ const int8_t *tensor_content =
+ reinterpret_cast<const int8_t *>(input_tensor->tensor_content().data());
+ for (int i = 0; i < input_flat_size; i++)
+ {
+ input_tensor->add_int_val(tensor_content[i]);
+ }
+ input_tensor->clear_tensor_content();
+ }
+ else
+ {
+ throw std::runtime_error{"Number of elements mismatch in unpack<int8_t>."};
+ // TODO: support for these
+ }
+}
+
+template <> void unpack<bool>(tensorflow::TensorProto *input_tensor)
+{
+ const auto &input_shape = input_tensor->tensor_shape();
+ assert(input_shape.dim_size() <= 6);
+ int input_flat_size = tfkit::tf::GetElementCount(input_shape);
+
+ // Adjust where shape is not set but actual value exist
+ if (input_tensor->tensor_content().size() > 0 && input_flat_size == -1)
+ {
+ input_flat_size = input_tensor->tensor_content().size() / sizeof(bool);
+ }
+
+ if (input_tensor->tensor_content().size() == 0)
+ {
+ // Do nothing as there is no tensor content to unpack
+ }
+ else if (input_tensor->tensor_content().size() == input_flat_size * sizeof(bool))
+ {
+ input_tensor->clear_bool_val();
+
+ const bool *tensor_content =
+ reinterpret_cast<const bool *>(input_tensor->tensor_content().data());
+ for (int i = 0; i < input_flat_size; i++)
+ {
+ input_tensor->add_bool_val(tensor_content[i]);
+ }
+ input_tensor->clear_tensor_content();
+ }
+ else
+ {
+ throw std::runtime_error{"Number of elements mismatch in unpack<bool>."};
+ // TODO: support for these
+ }
+}
+
void unpack(tensorflow::GraphDef &graph_def)
{
auto nodes = graph_def.mutable_node();
@@ -124,6 +191,12 @@ void unpack(tensorflow::GraphDef &graph_def)
case tensorflow::DT_INT32:
unpack<int32_t>(tensor);
break;
+ case tensorflow::DT_INT8:
+ unpack<int8_t>(tensor);
+ break;
+ case tensorflow::DT_BOOL:
+ unpack<bool>(tensor);
+ break;
default:
throw std::runtime_error{"Unsupported dtype"};
}
diff --git a/compiler/tfl-inspect/CMakeLists.txt b/compiler/tfl-inspect/CMakeLists.txt
new file mode 100644
index 000000000..ba019865f
--- /dev/null
+++ b/compiler/tfl-inspect/CMakeLists.txt
@@ -0,0 +1,14 @@
+if(NOT TARGET mio_tflite)
+ return()
+endif(NOT TARGET mio_tflite)
+
+set(DRIVER "driver/Driver.cpp")
+
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+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_tflite)
+target_link_libraries(tfl-inspect safemain)
diff --git a/compiler/tfl-inspect/README.md b/compiler/tfl-inspect/README.md
new file mode 100644
index 000000000..65dacc8a2
--- /dev/null
+++ b/compiler/tfl-inspect/README.md
@@ -0,0 +1,51 @@
+# tfl-inspect
+
+_tfl-inspect_ allows users to retrieve various information from a TensorFlow Lite model files
+
+## Information to inspect
+
+#### --operators
+
+Operators with `--operators`
+- show operator codes one line at a time in execution order
+
+Example
+```
+$ tfl_inspect --operators model.tflite
+```
+
+Result
+```
+RESHAPE
+DEPTHWISE_CONV_2D
+ADD
+```
+
+To get the count of specific operator, use other tools like sort, uniq, etc.
+
+Example
+```
+$ tfl-inspect --operators inception_v3.tflite | sort | uniq -c
+```
+Result
+```
+ 10 AVERAGE_POOL_2D
+ 15 CONCATENATION
+ 95 CONV_2D
+ 4 MAX_POOL_2D
+ 1 RESHAPE
+ 1 SOFTMAX
+```
+
+#### --conv2d_weight
+
+Conv2D series weight input node type with `--conv2d_weight`
+- shows Conv2D series node weight input node type
+- Conv2D series: CONV2D, DEPTHWISE_CONV_2D
+
+Example result
+```
+CONV2D,CONST
+DEPTHWISE_CONV_2D,RELU
+CONV2D,CONST
+```
diff --git a/compiler/tfl-inspect/driver/Driver.cpp b/compiler/tfl-inspect/driver/Driver.cpp
new file mode 100644
index 000000000..a48001169
--- /dev/null
+++ b/compiler/tfl-inspect/driver/Driver.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dump.h"
+
+#include <arser/arser.h>
+#include <foder/FileLoader.h>
+
+#include <functional>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <vector>
+#include <string>
+
+int entry(int argc, char **argv)
+{
+ arser::Arser arser{"tfl-inspect allows users to retrieve various information from a TensorFlow "
+ "Lite model files"};
+ arser.add_argument("--operators").nargs(0).help("Dump operators in tflite file");
+ arser.add_argument("--conv2d_weight")
+ .nargs(0)
+ .help("Dump Conv2D series weight operators in tflite file");
+ arser.add_argument("--op_version").nargs(0).help("Dump versions of the operators in tflite file");
+ arser.add_argument("tflite").type(arser::DataType::STR).help("TFLite file to inspect");
+
+ try
+ {
+ arser.parse(argc, argv);
+ }
+ catch (const std::runtime_error &err)
+ {
+ std::cout << err.what() << std::endl;
+ std::cout << arser;
+ return 255;
+ }
+
+ if (!arser["--operators"] && !arser["--conv2d_weight"] && !arser["--op_version"])
+ {
+ std::cout << "At least one option must be specified" << std::endl;
+ std::cout << arser;
+ return 255;
+ }
+
+ std::vector<std::unique_ptr<tflinspect::DumpInterface>> dumps;
+
+ if (arser["--operators"])
+ dumps.push_back(std::make_unique<tflinspect::DumpOperators>());
+ if (arser["--conv2d_weight"])
+ dumps.push_back(std::make_unique<tflinspect::DumpConv2DWeight>());
+ if (arser["--op_version"])
+ dumps.push_back(std::make_unique<tflinspect::DumpOperatorVersion>());
+
+ std::string model_file = arser.get<std::string>("tflite");
+
+ // Load TF lite model from a tflite file
+ foder::FileLoader fileLoader{model_file};
+ std::vector<char> modelData = fileLoader.load();
+ const tflite::Model *tfliteModel = tflite::GetModel(modelData.data());
+ if (tfliteModel == nullptr)
+ {
+ std::cerr << "ERROR: Failed to load tflite '" << model_file << "'" << std::endl;
+ return 255;
+ }
+
+ for (auto &dump : dumps)
+ {
+ dump->run(std::cout, tfliteModel);
+ }
+
+ return 0;
+}
diff --git a/compiler/tfl-inspect/requires.cmake b/compiler/tfl-inspect/requires.cmake
new file mode 100644
index 000000000..25857ad2b
--- /dev/null
+++ b/compiler/tfl-inspect/requires.cmake
@@ -0,0 +1,4 @@
+require("arser")
+require("foder")
+require("mio-tflite")
+require("safemain")
diff --git a/compiler/tfl-inspect/src/Dump.cpp b/compiler/tfl-inspect/src/Dump.cpp
new file mode 100644
index 000000000..78e77001c
--- /dev/null
+++ b/compiler/tfl-inspect/src/Dump.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dump.h"
+#include "Reader.h"
+
+#include <string>
+#include <ostream>
+#include <stdexcept>
+
+namespace tflinspect
+{
+
+void DumpOperators::run(std::ostream &os, const tflite::Model *model)
+{
+ tflinspect::Reader reader(model);
+
+ const uint32_t subgraph_size = reader.num_subgraph();
+
+ for (uint32_t g = 0; g < subgraph_size; g++)
+ {
+ reader.select_subgraph(g);
+ auto ops = reader.operators();
+
+ // dump operators
+ for (uint32_t i = 0; i < ops->Length(); ++i)
+ {
+ const auto op = ops->Get(i);
+
+ auto op_name = reader.opcode_name(op);
+
+ os << op_name << std::endl;
+ }
+ }
+}
+
+} // namespace tflinspect
+
+namespace
+{
+
+const tflite::Operator *operator_match_output(tflinspect::Reader &reader, const int32_t tensor)
+{
+ auto ops = reader.operators();
+
+ for (uint32_t i = 0; i < ops->Length(); ++i)
+ {
+ const auto op = ops->Get(i);
+
+ const std::vector<int32_t> &outputs = tflinspect::as_index_vector(op->outputs());
+
+ for (auto output : outputs)
+ {
+ if (output == tensor)
+ return op;
+ }
+ }
+ return nullptr;
+}
+
+size_t tensor_buffer_size(tflinspect::Reader &reader, const int32_t tensor_id)
+{
+ auto tensors = reader.tensors();
+
+ if (tensor_id < 0 || tensor_id >= tensors->Length())
+ {
+ throw std::runtime_error("Invalid Tensor ID");
+ }
+
+ auto tensor = tensors->Get(tensor_id);
+ auto buffer_id = tensor->buffer();
+
+ size_t size = reader.buffer_info(buffer_id, nullptr);
+
+ return size;
+}
+
+} // namespace
+
+namespace tflinspect
+{
+
+void DumpConv2DWeight::run(std::ostream &os, const tflite::Model *model)
+{
+ tflinspect::Reader reader(model);
+
+ const uint32_t subgraph_size = reader.num_subgraph();
+
+ for (uint32_t g = 0; g < subgraph_size; g++)
+ {
+ reader.select_subgraph(g);
+ auto ops = reader.operators();
+
+ // dump Conv2D, DepthwiseConv2D and its weight input operator
+ for (uint32_t i = 0; i < ops->Length(); ++i)
+ {
+ const auto op = ops->Get(i);
+ auto bc = reader.builtin_code(op);
+
+ if (bc == tflite::BuiltinOperator_CONV_2D || bc == tflite::BuiltinOperator_DEPTHWISE_CONV_2D)
+ {
+ const std::vector<int32_t> &inputs = tflinspect::as_index_vector(op->inputs());
+ if (inputs.size() < 2)
+ {
+ throw std::runtime_error("Operator has invalid input");
+ }
+ auto weight_input = inputs[1]; // Tensor ID of weight input
+
+ const auto op_weight = operator_match_output(reader, weight_input);
+ const auto buffer_size = tensor_buffer_size(reader, weight_input);
+
+ std::string weight_op_name = "?";
+
+ if (op_weight == nullptr && buffer_size > 0)
+ {
+ weight_op_name = "CONST";
+ }
+ else if (op_weight != nullptr)
+ {
+ weight_op_name = reader.opcode_name(op_weight);
+ }
+
+ auto op_name = reader.opcode_name(op);
+ os << op_name << "," << weight_op_name << std::endl;
+ }
+ }
+ }
+}
+
+} // namespace tflinspect
+
+namespace tflinspect
+{
+
+void DumpOperatorVersion::run(std::ostream &os, const tflite::Model *model)
+{
+ std::map<std::string, int32_t> op_version_map;
+
+ tflinspect::Reader reader(model);
+
+ const uint32_t subgraph_size = reader.num_subgraph();
+
+ for (uint32_t g = 0; g < subgraph_size; g++)
+ {
+ reader.select_subgraph(g);
+ auto ops = reader.operators();
+
+ // dump Conv2D, DepthwiseConv2D and its weight input operator
+ for (uint32_t i = 0; i < ops->Length(); ++i)
+ {
+ const auto op = ops->Get(i);
+
+ auto op_name = reader.opcode_name(op);
+ auto op_version = reader.opcodes().at(op->opcode_index())->version();
+
+ if (op_version_map.find(op_name) == op_version_map.end() ||
+ op_version_map[op_name] < op_version)
+ op_version_map[op_name] = op_version;
+ }
+ }
+
+ for (auto op : op_version_map)
+ {
+ os << op.first << "," << op.second << std::endl;
+ }
+}
+
+} // namespace tflinspect
diff --git a/compiler/tfl-inspect/src/Dump.h b/compiler/tfl-inspect/src/Dump.h
new file mode 100644
index 000000000..83397a2da
--- /dev/null
+++ b/compiler/tfl-inspect/src/Dump.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __DUMP_H__
+#define __DUMP_H__
+
+#include <mio/tflite/schema_generated.h>
+
+#include <ostream>
+
+namespace tflinspect
+{
+
+class DumpInterface
+{
+public:
+ virtual ~DumpInterface() = default;
+
+public:
+ virtual void run(std::ostream &os, const tflite::Model *model) = 0;
+};
+
+class DumpOperators final : public DumpInterface
+{
+public:
+ DumpOperators() = default;
+
+public:
+ void run(std::ostream &os, const tflite::Model *model);
+};
+
+class DumpConv2DWeight final : public DumpInterface
+{
+public:
+ DumpConv2DWeight() = default;
+
+public:
+ void run(std::ostream &os, const tflite::Model *model);
+};
+
+class DumpOperatorVersion final : public DumpInterface
+{
+public:
+ DumpOperatorVersion() = default;
+
+public:
+ void run(std::ostream &os, const tflite::Model *model);
+};
+
+} // namespace tflinspect
+
+#endif // __DUMP_H__
diff --git a/compiler/tfl-inspect/src/Reader.cpp b/compiler/tfl-inspect/src/Reader.cpp
new file mode 100644
index 000000000..5be289446
--- /dev/null
+++ b/compiler/tfl-inspect/src/Reader.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Reader.h"
+
+#include <sstream>
+#include <string>
+
+namespace tflinspect
+{
+
+bool is_valid(const tflite::OperatorCode *opcode)
+{
+ tflite::BuiltinOperator code = opcode->builtin_code();
+ return (tflite::BuiltinOperator_MIN <= code && code <= tflite::BuiltinOperator_MAX);
+}
+
+bool is_custom(const tflite::OperatorCode *opcode)
+{
+ tflite::BuiltinOperator code = opcode->builtin_code();
+ 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 = opcode->builtin_code();
+ 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;
+}
+
+Reader::Reader(const tflite::Model *model)
+{
+ _subgraphs = model->subgraphs();
+ _buffers = model->buffers();
+
+ auto opcodes = model->operator_codes();
+ for (const ::tflite::OperatorCode *opcode : *opcodes)
+ {
+ _op_codes.push_back(opcode);
+ }
+}
+
+size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data)
+{
+ if (buff_data != nullptr)
+ {
+ *buff_data = nullptr;
+ }
+
+ if (buf_idx == 0)
+ return 0;
+
+ if (auto *buffer = (*_buffers)[buf_idx])
+ {
+ if (auto *array = buffer->data())
+ {
+ if (size_t size = array->size())
+ {
+ if (buff_data != nullptr)
+ {
+ *buff_data = reinterpret_cast<const uint8_t *>(array->data());
+ }
+ return size;
+ }
+ }
+ }
+
+ return 0;
+}
+
+tflite::BuiltinOperator Reader::builtin_code(const tflite::Operator *op) const
+{
+ uint32_t index = op->opcode_index();
+ assert(index < _op_codes.size());
+ const tflite::OperatorCode *opcode = _op_codes.at(index);
+
+ return opcode->builtin_code();
+}
+
+std::string Reader::opcode_name(const tflite::Operator *op) const
+{
+ uint32_t index = op->opcode_index();
+ assert(index < _op_codes.size());
+ const tflite::OperatorCode *opcode = _op_codes.at(index);
+
+ if (!is_valid(opcode))
+ {
+ std::ostringstream oss;
+ oss << "(invalid: " << index << ")";
+ return oss.str();
+ }
+
+ return tflinspect::opcode_name(opcode);
+}
+
+bool Reader::select_subgraph(uint32_t sgindex)
+{
+ _tensors = nullptr;
+ _operators = nullptr;
+
+ _inputs.clear();
+ _outputs.clear();
+
+ if (_subgraphs->Length() <= sgindex)
+ {
+ assert(false);
+ return false;
+ }
+
+ const tflite::SubGraph *subgraph = (*_subgraphs)[sgindex];
+
+ _tensors = subgraph->tensors();
+ _operators = subgraph->operators();
+
+ _inputs = as_index_vector(subgraph->inputs());
+ _outputs = as_index_vector(subgraph->outputs());
+
+ return true;
+}
+
+} // namespace tflinspect
diff --git a/compiler/tfl-inspect/src/Reader.h b/compiler/tfl-inspect/src/Reader.h
new file mode 100644
index 000000000..e9e182a4b
--- /dev/null
+++ b/compiler/tfl-inspect/src/Reader.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __READER_H__
+#define __READER_H__
+
+#include <mio/tflite/schema_generated.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace tflinspect
+{
+
+template <typename T> std::vector<T> as_index_vector(const flatbuffers::Vector<T> *flat_array)
+{
+ std::vector<T> ret(flat_array->Length());
+ for (uint32_t i = 0; i < flat_array->Length(); i++)
+ {
+ ret[i] = flat_array->Get(i);
+ }
+ return ret;
+}
+
+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);
+
+/**
+ * @brief Loads TF lite file and provides helpers to access attributes
+ */
+class Reader
+{
+private:
+ using TFliteSubGraphs_t = flatbuffers::Vector<flatbuffers::Offset<tflite::SubGraph>>;
+ using TFliteBuffers_t = flatbuffers::Vector<flatbuffers::Offset<tflite::Buffer>>;
+ using TFliteTensors_t = flatbuffers::Vector<flatbuffers::Offset<tflite::Tensor>>;
+ using TFliteOperators_t = flatbuffers::Vector<flatbuffers::Offset<tflite::Operator>>;
+
+public:
+ Reader(const tflite::Model *model);
+
+ Reader() = delete;
+
+public:
+ const std::vector<const tflite::OperatorCode *> &opcodes() { return _op_codes; }
+ const TFliteBuffers_t *buffers() { return _buffers; }
+ const TFliteTensors_t *tensors() { return _tensors; }
+ const TFliteOperators_t *operators() { return _operators; }
+ const std::vector<int32_t> &inputs() const { return _inputs; }
+ const std::vector<int32_t> &outputs() const { return _outputs; }
+
+ uint32_t num_subgraph() const { return _subgraphs->Length(); }
+
+ size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data);
+ tflite::BuiltinOperator builtin_code(const tflite::Operator *op) const;
+ std::string opcode_name(const tflite::Operator *op) const;
+
+public:
+ bool select_subgraph(uint32_t subgraph);
+
+private:
+ const TFliteSubGraphs_t *_subgraphs{nullptr};
+ const TFliteBuffers_t *_buffers{nullptr};
+ const TFliteTensors_t *_tensors{nullptr};
+ const TFliteOperators_t *_operators{nullptr};
+
+ std::vector<const tflite::OperatorCode *> _op_codes;
+ std::vector<int32_t> _inputs;
+ std::vector<int32_t> _outputs;
+};
+
+} // namespace tflinspect
+
+#endif // __READER_H__
diff --git a/compiler/tfl-verify/CMakeLists.txt b/compiler/tfl-verify/CMakeLists.txt
new file mode 100644
index 000000000..4421a4660
--- /dev/null
+++ b/compiler/tfl-verify/CMakeLists.txt
@@ -0,0 +1,13 @@
+if(NOT TARGET mio_tflite)
+ return()
+endif(NOT TARGET mio_tflite)
+
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+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_tflite)
+target_link_libraries(tfl-verify safemain)
+target_link_libraries(tfl-verify cwrap)
diff --git a/compiler/tfl-verify/README.md b/compiler/tfl-verify/README.md
new file mode 100644
index 000000000..c50016873
--- /dev/null
+++ b/compiler/tfl-verify/README.md
@@ -0,0 +1,23 @@
+# tfl-verify
+
+_tfl-verify_ allows users to verify TF Lite models.
+
+## Usage
+
+Provide _tflite_ file as a parameter to verify validity.
+
+```
+$ tfl-verify tflitefile.tflite
+```
+
+Result for valid file
+```
+[ RUN ] Check tflitefile.tflite
+[ PASS ] Check tflitefile.tflite
+```
+
+Result for invalid file
+```
+[ RUN ] Check tflitefile.tflite
+[ FAIL ] Check tflitefile.tflite
+```
diff --git a/compiler/tfl-verify/requires.cmake b/compiler/tfl-verify/requires.cmake
new file mode 100644
index 000000000..79503f325
--- /dev/null
+++ b/compiler/tfl-verify/requires.cmake
@@ -0,0 +1,5 @@
+require("arser")
+require("foder")
+require("mio-tflite")
+require("safemain")
+require("cwrap")
diff --git a/compiler/tfl-verify/src/Driver.cpp b/compiler/tfl-verify/src/Driver.cpp
new file mode 100644
index 000000000..6d1897607
--- /dev/null
+++ b/compiler/tfl-verify/src/Driver.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VerifyFlatBuffers.h"
+
+#include <arser/arser.h>
+
+#include <iostream>
+#include <memory>
+#include <string>
+
+int entry(int argc, char **argv)
+{
+ arser::Arser arser;
+ arser.add_argument("tflite").type(arser::DataType::STR).help("TFLite file path to verify");
+
+ try
+ {
+ arser.parse(argc, argv);
+ }
+ catch (const std::runtime_error &err)
+ {
+ std::cout << err.what() << std::endl;
+ std::cout << arser;
+ return 255;
+ }
+
+ auto verifier = std::make_unique<VerifyFlatbuffers>();
+
+ std::string model_file = arser.get<std::string>("tflite");
+
+ std::cout << "[ RUN ] Check " << model_file << std::endl;
+
+ auto result = verifier->run(model_file);
+
+ if (result == 0)
+ {
+ std::cout << "[ PASS ] Check " << model_file << std::endl;
+ }
+ else
+ {
+ std::cout << "[ FAIL ] Check " << model_file << std::endl;
+ }
+
+ return result;
+}
diff --git a/compiler/tfl-verify/src/VerifyFlatBuffers.cpp b/compiler/tfl-verify/src/VerifyFlatBuffers.cpp
new file mode 100644
index 000000000..7fb48a71e
--- /dev/null
+++ b/compiler/tfl-verify/src/VerifyFlatBuffers.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VerifyFlatBuffers.h"
+
+#include <foder/FileLoader.h>
+#include <mio/tflite/schema_generated.h>
+
+int VerifyFlatbuffers::run(const std::string &model_file)
+{
+ foder::FileLoader fileLoader{model_file};
+ std::vector<char> modeldata = fileLoader.load();
+
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(modeldata.data());
+ flatbuffers::Verifier verifier{data, modeldata.size()};
+
+ if (!tflite::VerifyModelBuffer(verifier))
+ {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/compiler/tfl-verify/src/VerifyFlatBuffers.h b/compiler/tfl-verify/src/VerifyFlatBuffers.h
new file mode 100644
index 000000000..c301b5b10
--- /dev/null
+++ b/compiler/tfl-verify/src/VerifyFlatBuffers.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VERIFY_FLATBUFFERS_H__
+#define __VERIFY_FLATBUFFERS_H__
+
+#include <ostream>
+#include <string>
+
+class VerifyFlatbuffers
+{
+public:
+ VerifyFlatbuffers() = default;
+
+public:
+ int run(const std::string &model_file);
+};
+
+#endif // __VERIFY_FLATBUFFERS_H__
diff --git a/compiler/tflchef/CMakeLists.txt b/compiler/tflchef/CMakeLists.txt
index 2cec026f2..ebc873342 100644
--- a/compiler/tflchef/CMakeLists.txt
+++ b/compiler/tflchef/CMakeLists.txt
@@ -1,16 +1,19 @@
-nncc_find_package(FlatBuffers QUIET)
-nncc_find_package(Protobuf QUIET)
-
-if(NOT FlatBuffers_FOUND)
- return()
-endif(NOT FlatBuffers_FOUND)
+nnas_find_package(Protobuf QUIET)
if(NOT Protobuf_FOUND)
+ message(STATUS "Build tflchef: FAILED (missing Protobuf)")
return()
endif(NOT Protobuf_FOUND)
+if(NOT TARGET mio_tflite)
+ message(STATUS "Build tflchef: FAILED (missing mio_tflite)")
+ return()
+endif(NOT TARGET mio_tflite)
+
# Recipe Parser
add_subdirectory(proto)
+# Log
+add_subdirectory(log)
# Core Library
add_subdirectory(core)
# TFlite Library
diff --git a/compiler/tflchef/core/CMakeLists.txt b/compiler/tflchef/core/CMakeLists.txt
index a9bc82180..43f6b8b03 100644
--- a/compiler/tflchef/core/CMakeLists.txt
+++ b/compiler/tflchef/core/CMakeLists.txt
@@ -1,12 +1,9 @@
-FlatBuffers_Target(tflchef_flatbuffer
- OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/schema/tflite"
- SCHEMA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/schema"
- SCHEMA_FILES tflite.fbs)
-
file(GLOB_RECURSE SOURCES "src/*.cpp")
add_library(tflchef_core STATIC ${SOURCES})
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_flatbuffer)
+target_link_libraries(tflchef_core tflchef_log)
+target_link_libraries(tflchef_core mio_tflite)
+target_link_libraries(tflchef_core souschef)
diff --git a/compiler/tflchef/core/schema/tflite.fbs b/compiler/tflchef/core/schema/tflite.fbs
deleted file mode 100644
index 3da3188c3..000000000
--- a/compiler/tflchef/core/schema/tflite.fbs
+++ /dev/null
@@ -1,698 +0,0 @@
-// 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.
-
-// Revision History
-// Version 0: Initial version.
-// Version 1: Add subgraphs to schema.
-// Version 2: Rename operators to conform to NN API.
-// Version 3: Move buffer data from Model.Subgraph.Tensors to Model.Buffers.
-
-namespace tflite;
-
-// This corresponds to the version.
-file_identifier "TFL3";
-// File extension of any written files.
-file_extension "tflite";
-
-// The type of data stored in a tensor.
-enum TensorType : byte {
- FLOAT32 = 0,
- FLOAT16 = 1,
- INT32 = 2,
- UINT8 = 3,
- INT64 = 4,
- STRING = 5,
- BOOL = 6,
- INT16 = 7,
- COMPLEX64 = 8,
-}
-
-// Parameters for converting a quantized tensor back to float. Given a
-// quantized value q, the corresponding float value f should be:
-// f = scale * (q - zero_point)
-table QuantizationParameters {
- min:[float]; // For importing back into tensorflow.
- max:[float]; // For importing back into tensorflow.
- scale:[float]; // For dequantizing the tensor's values.
- zero_point:[long];
-}
-
-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
- // Tensorflow's NHWC).
- shape:[int];
- type:TensorType;
- // An index that refers to the buffers table at the root of the model. Or,
- // if there is no data buffer associated (i.e. intermediate results), then
- // this is 0 (which refers to an always existent empty buffer).
- //
- // The data_buffer itself is an opaque container, with the assumption that the
- // target device is little-endian. In addition, all builtin operators assume
- // the memory is ordered such that if `shape` is [4, 3, 2], then index
- // [i, j, k] maps to data_buffer[i*3*2 + j*2 + k].
- buffer:uint;
- name:string; // For debugging and importing back into tensorflow.
- quantization:QuantizationParameters; // Optional.
-
- is_variable:bool = false;
-}
-
-// A list of builtin operators. Builtin operators are slightly faster than custom
-// ones, but not by much. Moreover, while custom operators accept an opaque
-// object containing configuration parameters, builtins have a predetermined
-// set of acceptable options.
-enum BuiltinOperator : byte {
- ADD = 0,
- AVERAGE_POOL_2D = 1,
- CONCATENATION = 2,
- CONV_2D = 3,
- DEPTHWISE_CONV_2D = 4,
- // DEPTH_TO_SPACE = 5,
- DEQUANTIZE = 6,
- EMBEDDING_LOOKUP = 7,
- FLOOR = 8,
- FULLY_CONNECTED = 9,
- HASHTABLE_LOOKUP = 10,
- L2_NORMALIZATION = 11,
- L2_POOL_2D = 12,
- LOCAL_RESPONSE_NORMALIZATION = 13,
- LOGISTIC = 14,
- LSH_PROJECTION = 15,
- LSTM = 16,
- MAX_POOL_2D = 17,
- MUL = 18,
- RELU = 19,
- // NOTE(aselle): RELU_N1_TO_1 used to be called RELU1, but it was renamed
- // since different model developers use RELU1 in different ways. Never
- // create another op called RELU1.
- RELU_N1_TO_1 = 20,
- RELU6 = 21,
- RESHAPE = 22,
- RESIZE_BILINEAR = 23,
- RNN = 24,
- SOFTMAX = 25,
- SPACE_TO_DEPTH = 26,
- SVDF = 27,
- TANH = 28,
- // TODO(aselle): Consider rename to CONCATENATE_EMBEDDINGS
- CONCAT_EMBEDDINGS = 29,
- SKIP_GRAM = 30,
- CALL = 31,
- CUSTOM = 32,
- EMBEDDING_LOOKUP_SPARSE = 33,
- PAD = 34,
- UNIDIRECTIONAL_SEQUENCE_RNN = 35,
- GATHER = 36,
- BATCH_TO_SPACE_ND = 37,
- SPACE_TO_BATCH_ND = 38,
- TRANSPOSE = 39,
- MEAN = 40,
- SUB = 41,
- DIV = 42,
- SQUEEZE = 43,
- UNIDIRECTIONAL_SEQUENCE_LSTM = 44,
- STRIDED_SLICE = 45,
- BIDIRECTIONAL_SEQUENCE_RNN = 46,
- EXP = 47,
- TOPK_V2 = 48,
- SPLIT = 49,
- LOG_SOFTMAX = 50,
- // DELEGATE is a special op type for the operations which are delegated to
- // other backends.
- // WARNING: Experimental interface, subject to change
- DELEGATE = 51,
- BIDIRECTIONAL_SEQUENCE_LSTM = 52,
- CAST = 53,
- PRELU = 54,
- MAXIMUM = 55,
- ARG_MAX = 56,
- MINIMUM = 57,
- LESS = 58,
- NEG = 59,
- PADV2 = 60,
- GREATER = 61,
- GREATER_EQUAL = 62,
- LESS_EQUAL = 63,
- SELECT = 64,
- SLICE = 65,
- SIN = 66,
- TRANSPOSE_CONV = 67,
- SPARSE_TO_DENSE = 68,
- TILE = 69,
- EXPAND_DIMS = 70,
- EQUAL = 71,
- NOT_EQUAL = 72,
- LOG = 73,
- SUM = 74,
- SQRT = 75,
- RSQRT = 76,
- SHAPE = 77,
- POW = 78,
- ARG_MIN = 79,
- FAKE_QUANT = 80,
- REDUCE_PROD = 81,
- REDUCE_MAX = 82,
- PACK = 83,
- LOGICAL_OR = 84,
- ONE_HOT = 85,
- LOGICAL_AND = 86,
- LOGICAL_NOT = 87,
- UNPACK = 88,
- REDUCE_MIN = 89,
- FLOOR_DIV = 90,
- REDUCE_ANY = 91,
- SQUARE = 92,
- ZEROS_LIKE = 93,
- FILL = 94,
-}
-
-// Options for the builtin operators.
-union BuiltinOptions {
- 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,
-}
-
-enum Padding : byte { SAME, VALID }
-
-enum ActivationFunctionType : byte {
- NONE = 0,
- RELU = 1,
- RELU_N1_TO_1 = 2,
- RELU6 = 3,
- TANH = 4,
- SIGN_BIT = 5,
-}
-
-table Conv2DOptions {
- padding:Padding;
- stride_w:int;
- stride_h:int;
- fused_activation_function:ActivationFunctionType;
- dilation_w_factor:int = 1;
- dilation_h_factor:int = 1;
-}
-
-table Pool2DOptions {
- padding:Padding;
- stride_w:int;
- stride_h:int;
- filter_width:int;
- filter_height:int;
- fused_activation_function:ActivationFunctionType;
-}
-
-table DepthwiseConv2DOptions {
- // Parameters for DepthwiseConv version 1 or above.
- padding:Padding;
- stride_w:int;
- stride_h:int;
- depth_multiplier:int;
- fused_activation_function:ActivationFunctionType;
- // Parameters for DepthwiseConv version 2 or above.
- dilation_w_factor:int = 1;
- dilation_h_factor:int = 1;
-}
-
-table ConcatEmbeddingsOptions {
- num_channels:int;
- num_columns_per_channel:[int];
- embedding_dim_per_channel:[int]; // This could be inferred from parameters.
-}
-
-enum LSHProjectionType: byte {
- UNKNOWN = 0,
- SPARSE = 1,
- DENSE = 2,
-}
-
-table LSHProjectionOptions {
- type: LSHProjectionType;
-}
-
-table SVDFOptions {
- rank:int;
- fused_activation_function:ActivationFunctionType;
-}
-
-// An implementation of TensorFlow RNNCell.
-table RNNOptions {
- fused_activation_function:ActivationFunctionType;
-}
-
-// An implementation of TensorFlow dynamic_rnn with RNNCell.
-table SequenceRNNOptions {
- time_major:bool;
- fused_activation_function:ActivationFunctionType;
-}
-
-// An implementation of TensorFlow bidrectional_dynamic_rnn with RNNCell.
-table BidirectionalSequenceRNNOptions {
- time_major:bool;
- fused_activation_function:ActivationFunctionType;
-}
-
-enum FullyConnectedOptionsWeightsFormat: byte {
- DEFAULT = 0,
- SHUFFLED4x16INT8 = 1,
-}
-
-// An implementation of TensorFlow fully_connected (a.k.a Dense) layer.
-table FullyConnectedOptions {
- // Parameters for FullyConnected version 1 or above.
- fused_activation_function:ActivationFunctionType;
-
- // Parameters for FullyConnected version 2 or above.
- weights_format:FullyConnectedOptionsWeightsFormat = DEFAULT;
-}
-
-table SoftmaxOptions {
- beta: float;
-}
-
-// An implementation of TensorFlow concat.
-table ConcatenationOptions {
- axis:int;
- fused_activation_function:ActivationFunctionType;
-}
-
-table AddOptions {
- fused_activation_function:ActivationFunctionType;
-}
-
-table MulOptions {
- fused_activation_function:ActivationFunctionType;
-}
-
-table L2NormOptions {
- fused_activation_function:ActivationFunctionType;
-}
-
-table LocalResponseNormalizationOptions {
- radius:int;
- bias:float;
- alpha:float;
- beta:float;
-}
-
-enum LSTMKernelType : byte {
- // Full LSTM kernel which supports peephole and projection.
- FULL = 0,
- // Basic LSTM kernels. Equivalent to TensorFlow BasicLSTMCell.
- BASIC = 1,
-}
-
-// An implementation of TensorFlow LSTMCell and CoupledInputForgetGateLSTMCell
-table LSTMOptions {
- // Parameters for LSTM version 1 or above.
- fused_activation_function:ActivationFunctionType;
- cell_clip: float; // Optional, 0.0 means no clipping
- proj_clip: float; // Optional, 0.0 means no clipping
-
- // Parameters for LSTM version 2 or above.
- // Basic kernel is only supported in version 2 or above.
- kernel_type: LSTMKernelType = FULL;
-}
-
-table ResizeBilinearOptions {
- new_height: int (deprecated);
- new_width: int (deprecated);
- align_corners: bool;
-}
-
-// A call operation options
-table CallOptions {
- // The subgraph index that needs to be called.
- subgraph:uint;
-}
-
-table PadOptions {
-}
-
-table PadV2Options {
-}
-
-table ReshapeOptions {
- new_shape:[int];
-}
-
-table SpaceToBatchNDOptions {
-}
-
-table BatchToSpaceNDOptions {
-}
-
-table SkipGramOptions {
- ngram_size: int;
- max_skip_size: int;
- include_all_ngrams: bool;
-}
-
-table SpaceToDepthOptions {
- block_size: int;
-}
-
-table SubOptions {
- fused_activation_function:ActivationFunctionType;
-}
-
-table DivOptions {
- fused_activation_function:ActivationFunctionType;
-}
-
-table TopKV2Options {
-}
-
-enum CombinerType : byte {
- SUM = 0,
- MEAN = 1,
- SQRTN = 2,
-}
-
-table EmbeddingLookupSparseOptions {
- combiner:CombinerType;
-}
-
-table GatherOptions {
- axis: int;
-}
-
-table TransposeOptions {
-}
-
-table ExpOptions {
-}
-
-table ReducerOptions {
- keep_dims: bool;
-}
-
-table SqueezeOptions {
- squeeze_dims:[int];
-}
-
-table SplitOptions {
- num_splits: int;
-}
-
-table StridedSliceOptions {
- begin_mask: int;
- end_mask: int;
- ellipsis_mask: int;
- new_axis_mask: int;
- shrink_axis_mask: int;
-}
-
-table LogSoftmaxOptions {
-}
-
-table CastOptions {
- in_data_type: TensorType;
- out_data_type: TensorType;
-}
-
-table DequantizeOptions {
-}
-
-table MaximumMinimumOptions {
-}
-
-table TileOptions {
-}
-
-table ArgMaxOptions {
- output_type : TensorType;
-}
-
-table ArgMinOptions {
- output_type : TensorType;
-}
-
-table GreaterOptions {
-}
-
-table GreaterEqualOptions {
-}
-
-table LessOptions {
-}
-
-table LessEqualOptions {
-}
-
-table NegOptions {
-}
-
-table SelectOptions {
-}
-
-table SliceOptions {
-}
-
-table TransposeConvOptions {
- padding:Padding;
- stride_w:int;
- stride_h:int;
-}
-
-table ExpandDimsOptions {
-}
-
-table SparseToDenseOptions {
- validate_indices:bool;
-}
-
-table EqualOptions {
-}
-
-table NotEqualOptions {
-}
-
-table ShapeOptions {
- // Optional output type of the operation (int32 or int64). Defaults to int32.
- out_type : TensorType;
-}
-
-table PowOptions {
-}
-
-table FakeQuantOptions {
- // Parameters supported by version 1:
- min:float;
- max:float;
- num_bits:int;
-
- // Parameters supported by version 2:
- narrow_range:bool;
-}
-
-table PackOptions {
- values_count:int;
- axis:int;
-}
-
-table LogicalOrOptions {
-}
-
-table OneHotOptions {
- axis:int;
-}
-
-table LogicalAndOptions {
-}
-
-table LogicalNotOptions {
-}
-
-table UnpackOptions {
- num:int;
- axis:int;
-}
-
-table FloorDivOptions {
-}
-
-table SquareOptions {
-}
-
-table ZerosLikeOptions {
-}
-
-table FillOptions {
-}
-
-// An OperatorCode can be an enum value (BuiltinOperator) if the operator is a
-// builtin, or a string if the operator is custom.
-table OperatorCode {
- builtin_code:BuiltinOperator;
- custom_code:string;
-
- // The version of the operator. The version need to be bumped whenever new
- // parameters are introduced into an op.
- version:int = 1;
-}
-
-enum CustomOptionsFormat : byte {
- FLEXBUFFERS = 0,
-}
-
-// An operator takes tensors as inputs and outputs. The type of operation being
-// performed is determined by an index into the list of valid OperatorCodes,
-// while the specifics of each operations is configured using builtin_options
-// or custom_options.
-table Operator {
- // Index into the operator_codes array. Using an integer here avoids
- // complicate map lookups.
- opcode_index:uint;
-
- // Optional input and output tensors are indicated by -1.
- inputs:[int];
- outputs:[int];
-
- builtin_options:BuiltinOptions;
- custom_options:[ubyte];
- custom_options_format:CustomOptionsFormat;
-
- // A list of booleans indicating the input tensors which are being mutated by
- // this operator.(e.g. used by RNN and LSTM).
- // For example, if the "inputs" array refers to 5 tensors and the second and
- // fifth are mutable variables, then this list will contain
- // [false, true, false, false, true].
- //
- // If the list is empty, no variable is mutated in this operator.
- // The list either has the same length as `inputs`, or is empty.
- mutating_variable_inputs:[bool];
-}
-
-// The root type, defining a subgraph, which typically represents an entire
-// model.
-table SubGraph {
- // A list of all tensors used in this subgraph.
- tensors:[Tensor];
-
- // Indices of the tensors that are inputs into this subgraph. Note this is
- // the list of non-static tensors that feed into the subgraph for inference.
- inputs:[int];
-
- // Indices of the tensors that are outputs out of this subgraph. Note this is
- // the list of output tensors that are considered the product of the
- // subgraph's inference.
- outputs:[int];
-
- // All operators, in execution order.
- operators:[Operator];
-
- // Name of this subgraph (used for debugging).
- name:string;
-}
-
-// Table of raw data buffers (used for constant tensors). Referenced by tensors
-// by index. The generous alignment accommodates mmap-friendly data structures.
-table Buffer {
- data:[ubyte] (force_align: 16);
-}
-
-table Model {
- // Version of the schema.
- version:uint;
-
- // A list of all operator codes used in this model. This is
- // kept in order because operators carry an index into this
- // vector.
- operator_codes:[OperatorCode];
-
- // All the subgraphs of the model. The 0th is assumed to be the main
- // model.
- subgraphs:[SubGraph];
-
- // A description of the model.
- description:string;
-
- // Buffers of the model.
- // Note the 0th entry of this array must be an empty buffer (sentinel).
- // This is a convention so that tensors without a buffer can provide 0 as
- // their buffer.
- buffers:[Buffer];
-
- // Metadata about the model. Indirects into the existings buffers list.
- metadata_buffer:[int];
-}
-
-root_type Model;
diff --git a/compiler/tflchef/core/schema/tflite.meta b/compiler/tflchef/core/schema/tflite.meta
deleted file mode 100644
index cd90a3d63..000000000
--- a/compiler/tflchef/core/schema/tflite.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-URL="https://raw.githubusercontent.com/tensorflow/tensorflow/a6d8ffae097/tensorflow/contrib/lite/schema/schema.fbs"
-NOTE="TensorFlow Lite Schema from TensorFlow v1.12.0 release"
diff --git a/compiler/tflchef/core/src/Arguments.h b/compiler/tflchef/core/src/Arguments.h
deleted file mode 100644
index 341aea6c9..000000000
--- a/compiler/tflchef/core/src/Arguments.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __ARGUMENTS_H__
-#define __ARGUMENTS_H__
-
-#include <cstdint>
-#include <string>
-
-/**
- * @brief Read-only string sequence view
- */
-struct Arguments
-{
- virtual ~Arguments() = default;
-
- virtual uint32_t count(void) const = 0;
- virtual const std::string &value(uint32_t n) const = 0;
-};
-
-#endif // __ARGUMENTS_H__
diff --git a/compiler/tflchef/core/src/Convert.cpp b/compiler/tflchef/core/src/Convert.cpp
index 8e4f1d392..9602faa96 100644
--- a/compiler/tflchef/core/src/Convert.cpp
+++ b/compiler/tflchef/core/src/Convert.cpp
@@ -41,8 +41,14 @@ tflite::ActivationFunctionType as_tflite_activation(const tflchef::Activation &v
return tflite::ActivationFunctionType_NONE;
case tflchef::RELU:
return tflite::ActivationFunctionType_RELU;
+ case tflchef::RELU_N1_TO_1:
+ return tflite::ActivationFunctionType_RELU_N1_TO_1;
case tflchef::RELU6:
return tflite::ActivationFunctionType_RELU6;
+ case tflchef::TANH:
+ return tflite::ActivationFunctionType_TANH;
+ case tflchef::SIGN_BIT:
+ return tflite::ActivationFunctionType_SIGN_BIT;
default:
break;
}
@@ -58,9 +64,99 @@ tflite::TensorType as_tflite_tensortype(const tflchef::TensorType &value)
return tflite::TensorType_FLOAT32;
case tflchef::INT32:
return tflite::TensorType_INT32;
+ case tflchef::UINT8:
+ return tflite::TensorType_UINT8;
+ case tflchef::INT64:
+ return tflite::TensorType_INT64;
+ case tflchef::BOOL:
+ return tflite::TensorType_BOOL;
default:
break;
}
throw std::runtime_error{"Unknown tensor type"};
}
+
+tflite::MirrorPadMode as_tflite_mirrorpadmode(const tflchef::MirrorPadMode &value)
+{
+ switch (value)
+ {
+ case tflchef::REFLECT:
+ return tflite::MirrorPadMode_REFLECT;
+ case tflchef::SYMMETRIC:
+ return tflite::MirrorPadMode_SYMMETRIC;
+ default:
+ break;
+ }
+
+ throw std::runtime_error{"Unknown mirrorpad mode"};
+}
+
+tflite::DimensionType as_tflite_dimensiontype(const tflchef::DimensionType &value)
+{
+ switch (value)
+ {
+ case tflchef::DimensionType::DENSE:
+ return tflite::DimensionType_DENSE;
+ case tflchef::DimensionType::SPARSE_CSR:
+ return tflite::DimensionType_SPARSE_CSR;
+ default:
+ break;
+ }
+
+ throw std::runtime_error("Unknown dimension type");
+}
+
+tflite::SparseIndexVector as_tflite_sparse_idx_vec_type(const tflchef::SparseIndexVecType &value)
+{
+ switch (value)
+ {
+ case tflchef::SparseIndexVecType::SparseIdxVecType_NONE:
+ return tflite::SparseIndexVector_NONE;
+ case tflchef::SparseIndexVecType::INT32VEC:
+ return tflite::SparseIndexVector_Int32Vector;
+ case tflchef::SparseIndexVecType::UINT16VEC:
+ return tflite::SparseIndexVector_Uint16Vector;
+ case tflchef::SparseIndexVecType::UINT8VEC:
+ return tflite::SparseIndexVector_Uint8Vector;
+ default:
+ break;
+ }
+
+ throw std::runtime_error("Unknown SparseIndexVector type");
+}
+
+flatbuffers::Offset<void>
+as_tflite_sparse_index_vec(flatbuffers::FlatBufferBuilder &fb,
+ const ::tflchef::TensorSparsity_IndexVec &value)
+{
+ auto sparse_idx_type = value.type();
+
+ switch (sparse_idx_type)
+ {
+ case tflchef::SparseIndexVecType::SparseIdxVecType_NONE:
+ return flatbuffers::Offset<void>();
+ case tflchef::SparseIndexVecType::INT32VEC:
+ {
+ auto values_vec_int32 = std::vector<int32_t>{value.dim().begin(), value.dim().end()};
+ auto values_int32 = fb.CreateVector(values_vec_int32);
+ return tflite::CreateInt32Vector(fb, values_int32).Union();
+ }
+ case tflchef::SparseIndexVecType::UINT16VEC:
+ {
+ auto values_vec_uint16 = std::vector<uint16_t>{value.dim().begin(), value.dim().end()};
+ auto values_uint16 = fb.CreateVector(values_vec_uint16);
+ return tflite::CreateUint16Vector(fb, values_uint16).Union();
+ }
+ case tflchef::SparseIndexVecType::UINT8VEC:
+ {
+ auto values_vec_uint8 = std::vector<uint8_t>{value.dim().begin(), value.dim().end()};
+ auto values_uint8 = fb.CreateVector(values_vec_uint8);
+ return tflite::CreateUint8Vector(fb, values_uint8).Union();
+ }
+ default:
+ break;
+ }
+
+ throw std::runtime_error("Unknown SparseIndexVector type");
+}
diff --git a/compiler/tflchef/core/src/Convert.h b/compiler/tflchef/core/src/Convert.h
index 1685fcbc3..45c93d229 100644
--- a/compiler/tflchef/core/src/Convert.h
+++ b/compiler/tflchef/core/src/Convert.h
@@ -22,10 +22,16 @@
#define __CONVERT_H__
#include <tflchef.pb.h>
-#include <tflite_generated.h>
+#include <mio/tflite/schema_generated.h>
tflite::Padding as_tflite_padding(const tflchef::Padding &value);
tflite::ActivationFunctionType as_tflite_activation(const tflchef::Activation &value);
tflite::TensorType as_tflite_tensortype(const tflchef::TensorType &value);
+tflite::MirrorPadMode as_tflite_mirrorpadmode(const tflchef::MirrorPadMode &value);
+tflite::DimensionType as_tflite_dimensiontype(const tflchef::DimensionType &value);
+tflite::SparseIndexVector as_tflite_sparse_idx_vec_type(const tflchef::SparseIndexVecType &value);
+flatbuffers::Offset<void>
+as_tflite_sparse_index_vec(flatbuffers::FlatBufferBuilder &fb,
+ const ::tflchef::TensorSparsity_IndexVec &value);
#endif // __CONVERT_H__
diff --git a/compiler/tflchef/core/src/CustomOp/AddV2.cpp b/compiler/tflchef/core/src/CustomOp/AddV2.cpp
new file mode 100644
index 000000000..dffd336cd
--- /dev/null
+++ b/compiler/tflchef/core/src/CustomOp/AddV2.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020 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 "AddV2.h"
+
+#include "flatbuffers/flexbuffers.h"
+
+flatbuffers::Offset<void> AddV2Chef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ return flatbuffers::Offset<void>();
+}
+
+flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
+AddV2Chef::custom_value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.type() == "AddV2");
+
+ /**
+ * REGISTER_OP("AddV2")
+ .Input("x: T")
+ .Input("y: T")
+ .Output("z: T")
+ .Attr(
+ "T: {bfloat16, half, float, double, uint8, int8, int16, int32, int64, "
+ "complex64, complex128}")
+ .SetShapeFn(shape_inference::BroadcastBinaryOpShapeFn)
+ .SetIsAggregate()
+ .SetIsCommutative();
+ */
+
+ auto flex_buffers = std::make_unique<flexbuffers::Builder>();
+ 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<OpChef> AddV2ChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new AddV2Chef{operation}};
+}
diff --git a/compiler/tflchef/core/src/CustomOp/AddV2.h b/compiler/tflchef/core/src/CustomOp/AddV2.h
new file mode 100644
index 000000000..dbbaf5a62
--- /dev/null
+++ b/compiler/tflchef/core/src/CustomOp/AddV2.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_ADDV2_H__
+#define __OP_ADDV2_H__
+
+#include "OpChef.h"
+
+class AddV2Chef final : public OpChef
+{
+public:
+ explicit AddV2Chef(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<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+ flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
+ custom_value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct AddV2ChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_ADDV2_H__
diff --git a/compiler/tflchef/core/src/CustomOp/All.cpp b/compiler/tflchef/core/src/CustomOp/All.cpp
new file mode 100644
index 000000000..b3ae821a4
--- /dev/null
+++ b/compiler/tflchef/core/src/CustomOp/All.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020 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 "All.h"
+
+#include "flatbuffers/flexbuffers.h"
+
+flatbuffers::Offset<void> AllChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ return flatbuffers::Offset<void>();
+}
+
+flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
+AllChef::custom_value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.type() == "All");
+
+ /**
+ * REGISTER_OP("All")
+ .Input("input: bool")
+ .Input("reduction_indices: Tidx")
+ .Output("output: bool")
+ .Attr("keep_dims: bool = false")
+ .Attr("Tidx: {int32, int64} = DT_INT32")
+ .SetShapeFn(shape_inference::ReductionShape);
+ */
+
+ auto flex_buffers = std::make_unique<flexbuffers::Builder>();
+ size_t map_start = flex_buffers->StartMap();
+
+ // TODO Support more data types
+ flex_buffers->Int("Tidx", tflite::TensorType_INT32);
+ flex_buffers->Bool("keep_dims", operation.all_options().keep_dims());
+
+ flex_buffers->EndMap(map_start);
+ flex_buffers->Finish();
+
+ auto circle_custom_options = fbb.CreateVector(flex_buffers->GetBuffer());
+ return circle_custom_options;
+}
+
+std::unique_ptr<OpChef> AllChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new AllChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/CustomOp/All.h b/compiler/tflchef/core/src/CustomOp/All.h
new file mode 100644
index 000000000..f7949f3d2
--- /dev/null
+++ b/compiler/tflchef/core/src/CustomOp/All.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_ALL_H__
+#define __OP_ALL_H__
+
+#include "OpChef.h"
+
+class AllChef final : public OpChef
+{
+public:
+ explicit AllChef(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<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+ flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
+ custom_value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct AllChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_ALL_H__
diff --git a/compiler/tflchef/core/src/CustomOp/BatchMatMulV2.cpp b/compiler/tflchef/core/src/CustomOp/BatchMatMulV2.cpp
new file mode 100644
index 000000000..595f3b9bb
--- /dev/null
+++ b/compiler/tflchef/core/src/CustomOp/BatchMatMulV2.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 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 "BatchMatMulV2.h"
+
+#include "flatbuffers/flexbuffers.h"
+
+flatbuffers::Offset<void> BatchMatMulV2Chef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ return flatbuffers::Offset<void>();
+}
+
+flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
+BatchMatMulV2Chef::custom_value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.type() == "BatchMatMulV2");
+
+ /**
+ * REGISTER_OP("BatchMatMulV2")
+ .Input("x: T")
+ .Input("y: T")
+ .Output("output: T")
+ .Attr(
+ "T: {bfloat16, half, float, double, int32, int64, complex64, "
+ "complex128}")
+ .Attr("adj_x: bool = false")
+ .Attr("adj_y: bool = false")
+ .SetShapeFn(shape_inference::BatchMatMulV2Shape);
+ */
+
+ auto flex_buffers = std::make_unique<flexbuffers::Builder>();
+ size_t map_start = flex_buffers->StartMap();
+
+ flex_buffers->Bool("adj_x", operation.batch_matmul_options().adj_x());
+ flex_buffers->Bool("adj_y", operation.batch_matmul_options().adj_y());
+ // 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<OpChef> BatchMatMulV2ChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new BatchMatMulV2Chef{operation}};
+}
diff --git a/compiler/tflchef/core/src/CustomOp/BatchMatMulV2.h b/compiler/tflchef/core/src/CustomOp/BatchMatMulV2.h
new file mode 100644
index 000000000..d20f4d2a5
--- /dev/null
+++ b/compiler/tflchef/core/src/CustomOp/BatchMatMulV2.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_BATCH_MATMUL_V2_H__
+#define __OP_BATCH_MATMUL_V2_H__
+
+#include "OpChef.h"
+
+class BatchMatMulV2Chef final : public OpChef
+{
+public:
+ explicit BatchMatMulV2Chef(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<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+ flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
+ custom_value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct BatchMatMulV2ChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_BATCH_MATMUL_V2_H__
diff --git a/compiler/tflchef/core/src/CustomOp/MatMul.cpp b/compiler/tflchef/core/src/CustomOp/MatMul.cpp
new file mode 100644
index 000000000..ba34aa8db
--- /dev/null
+++ b/compiler/tflchef/core/src/CustomOp/MatMul.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020 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 "MatMul.h"
+
+#include "flatbuffers/flexbuffers.h"
+
+flatbuffers::Offset<void> MatMulChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ return flatbuffers::Offset<void>();
+}
+
+flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
+MatMulChef::custom_value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.type() == "MatMul");
+
+ /**
+ * REGISTER_OP("MatMul")
+ .Input("a: T")
+ .Input("b: T")
+ .Output("product: T")
+ .Attr("transpose_a: bool = false")
+ .Attr("transpose_b: bool = false")
+ .Attr("T: {half, float, double, int32, complex64, complex128}")
+ .SetShapeFn(shape_inference::MatMulShape)
+ */
+
+ auto flex_buffers = std::make_unique<flexbuffers::Builder>();
+ size_t map_start = flex_buffers->StartMap();
+
+ flex_buffers->Bool("transpose_a", operation.matmul_options().transpose_a());
+ flex_buffers->Bool("transpose_b", operation.matmul_options().transpose_b());
+ // TODO how do we support other 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<OpChef> MatMulChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new MatMulChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/CustomOp/MatMul.h b/compiler/tflchef/core/src/CustomOp/MatMul.h
new file mode 100644
index 000000000..b0307f977
--- /dev/null
+++ b/compiler/tflchef/core/src/CustomOp/MatMul.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_MATMUL_H__
+#define __OP_MATMUL_H__
+
+#include "OpChef.h"
+
+class MatMulChef final : public OpChef
+{
+public:
+ explicit MatMulChef(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<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+ flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
+ custom_value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct MatMulChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_MATMUL_H__
diff --git a/compiler/tflchef/core/src/CustomOp/MatrixBandPart.cpp b/compiler/tflchef/core/src/CustomOp/MatrixBandPart.cpp
new file mode 100644
index 000000000..d12597edb
--- /dev/null
+++ b/compiler/tflchef/core/src/CustomOp/MatrixBandPart.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020 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 "MatrixBandPart.h"
+
+#include "flatbuffers/flexbuffers.h"
+
+flatbuffers::Offset<void> MatrixBandPartChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ return flatbuffers::Offset<void>();
+}
+
+flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
+MatrixBandPartChef::custom_value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.type() == "MatrixBandPart");
+
+ /**
+ * REGISTER_OP("MatrixBandPart")
+ .Input("input: T")
+ .Input("num_lower: Tindex")
+ .Input("num_upper: Tindex")
+ .Output("band: T")
+ .Attr("T: type")
+ .Attr("Tindex: {int32, int64} = DT_INT64")
+ .SetShapeFn(shape_inference::UnchangedShape);
+ */
+
+ auto flex_buffers = std::make_unique<flexbuffers::Builder>();
+ size_t map_start = flex_buffers->StartMap();
+
+ // TODO Support more data types
+ flex_buffers->Int("T", tflite::TensorType_FLOAT32);
+ flex_buffers->Int("Tindex", tflite::TensorType_INT64);
+
+ flex_buffers->EndMap(map_start);
+ flex_buffers->Finish();
+
+ auto circle_custom_options = fbb.CreateVector(flex_buffers->GetBuffer());
+ return circle_custom_options;
+}
+
+std::unique_ptr<OpChef> MatrixBandPartChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new MatrixBandPartChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/CustomOp/MatrixBandPart.h b/compiler/tflchef/core/src/CustomOp/MatrixBandPart.h
new file mode 100644
index 000000000..54a8a3afb
--- /dev/null
+++ b/compiler/tflchef/core/src/CustomOp/MatrixBandPart.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_MATRIXBANDPART_H__
+#define __OP_MATRIXBANDPART_H__
+
+#include "OpChef.h"
+
+class MatrixBandPartChef final : public OpChef
+{
+public:
+ explicit MatrixBandPartChef(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<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+ flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
+ custom_value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct MatrixBandPartChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_MATRIXBANDPART_H__
diff --git a/compiler/tflchef/core/src/CustomOp/MaxPoolWithArgMax.cpp b/compiler/tflchef/core/src/CustomOp/MaxPoolWithArgMax.cpp
new file mode 100644
index 000000000..b1c92ecbd
--- /dev/null
+++ b/compiler/tflchef/core/src/CustomOp/MaxPoolWithArgMax.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2020 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 "MaxPoolWithArgMax.h"
+
+#include "flatbuffers/flexbuffers.h"
+
+flatbuffers::Offset<void> MaxPoolWithArgMaxChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ return flatbuffers::Offset<void>();
+}
+
+flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
+MaxPoolWithArgMaxChef::custom_value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.type() == "MaxPoolWithArgMax");
+
+ /**
+ * REGISTER_OP("MaxPoolWithArgmax")
+ .Attr("ksize: list(int) >= 4")
+ .Attr("strides: list(int) >= 4")
+ .Attr("Targmax: {int32, int64} = DT_INT64")
+ .Attr(GetPaddingAttrString())
+ .Attr("include_batch_in_index: bool = false")
+ .Input("input: T")
+ .Output("output: T")
+ .Output("argmax: Targmax")
+ .Attr("T: realnumbertype")
+ .SetShapeFn([](InferenceContext* c) {
+ TF_RETURN_IF_ERROR(shape_inference::MaxPoolShape(c));
+ c->set_output(1, c->output(0));
+ return Status::OK();
+ });
+ */
+
+ auto flex_buffers = std::make_unique<flexbuffers::Builder>();
+ size_t map_start = flex_buffers->StartMap();
+
+ auto start = flex_buffers->StartVector("ksize");
+ flex_buffers->Add(1);
+ flex_buffers->Add(operation.max_pool_with_argmax_options().filter_width());
+ flex_buffers->Add(operation.max_pool_with_argmax_options().filter_height());
+ flex_buffers->Add(1);
+ flex_buffers->EndVector(start, /*typed=*/true, /*fixed=*/false);
+ start = flex_buffers->StartVector("strides");
+ flex_buffers->Add(1);
+ flex_buffers->Add(operation.max_pool_with_argmax_options().stride_w());
+ flex_buffers->Add(operation.max_pool_with_argmax_options().stride_h());
+ flex_buffers->Add(1);
+ flex_buffers->EndVector(start, /*typed=*/true, /*fixed=*/false);
+ auto output_type = operation.max_pool_with_argmax_options().output_type();
+ assert(output_type == tflite::TensorType_INT64 || output_type == tflite::TensorType_INT32);
+ flex_buffers->Int("Targmax", output_type);
+ std::string padding = operation.max_pool_with_argmax_options().padding() ? "VALID" : "SAME";
+ flex_buffers->String("padding", padding);
+ flex_buffers->Bool("include_batch_in_index",
+ operation.max_pool_with_argmax_options().include_batch_in_index());
+ 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<OpChef>
+MaxPoolWithArgMaxChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new MaxPoolWithArgMaxChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/CustomOp/MaxPoolWithArgMax.h b/compiler/tflchef/core/src/CustomOp/MaxPoolWithArgMax.h
new file mode 100644
index 000000000..2123a86bd
--- /dev/null
+++ b/compiler/tflchef/core/src/CustomOp/MaxPoolWithArgMax.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_MAXPOOLWITHARGMAX_H__
+#define __OP_MAXPOOLWITHARGMAX_H__
+
+#include "OpChef.h"
+
+class MaxPoolWithArgMaxChef final : public OpChef
+{
+public:
+ explicit MaxPoolWithArgMaxChef(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<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+ flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
+ custom_value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct MaxPoolWithArgMaxChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_MAXPOOLWITHARGMAX_H__
diff --git a/compiler/tflchef/core/src/Data/Constant.h b/compiler/tflchef/core/src/Data/Constant.h
deleted file mode 100644
index ebe1f3d93..000000000
--- a/compiler/tflchef/core/src/Data/Constant.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __CONSTANT_FILLER_H__
-#define __CONSTANT_FILLER_H__
-
-#include "DataChef.h"
-#include "LexicalCast.h"
-
-template <typename T> class ConstantDataChef final : public DataChef
-{
-public:
- ConstantDataChef(const T &value) : _value{value}
- {
- // DO NOTHING
- }
-
-public:
- std::vector<uint8_t> generate(int32_t count) const override
- {
- std::vector<uint8_t> res;
-
- for (uint32_t n = 0; n < count; ++n)
- {
- const uint8_t *arr = reinterpret_cast<const uint8_t *>(&_value);
-
- for (uint32_t b = 0; b < sizeof(T); ++b)
- {
- res.emplace_back(arr[b]);
- }
- }
-
- return res;
- }
-
-private:
- T _value;
-};
-
-template <typename T> struct ConstantDataChefFactory : public DataChefFactory
-{
- std::unique_ptr<DataChef> create(const Arguments &args) const
- {
- auto const value = to_number<T>(args.value(0));
- return std::unique_ptr<DataChef>{new ConstantDataChef<T>{value}};
- }
-};
-
-#endif // __CONSTANT_FILLER_H__
diff --git a/compiler/tflchef/core/src/Data/Explicit.h b/compiler/tflchef/core/src/Data/Explicit.h
deleted file mode 100644
index f4175f485..000000000
--- a/compiler/tflchef/core/src/Data/Explicit.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __EXPLICIT_FILLER_H__
-#define __EXPLICIT_FILLER_H__
-
-#include "DataChef.h"
-#include "LexicalCast.h"
-
-#include <vector>
-
-/**
- * @brief Return the identity of arithmetic addition
- */
-template <typename T> T zero(void);
-
-template <> int zero(void) { return 0; }
-
-template <typename T> class ExplicitDataChef final : public DataChef
-{
-public:
- ExplicitDataChef()
- {
- // DO NOTHING
- }
-
-public:
- std::vector<uint8_t> generate(int32_t count) const override
- {
- std::vector<uint8_t> res;
-
- for (uint32_t n = 0; n < count; ++n)
- {
- T const value = (n < _values.size()) ? _values.at(n) : zero<T>();
- const uint8_t *arr = reinterpret_cast<const uint8_t *>(&value);
-
- for (uint32_t b = 0; b < sizeof(T); ++b)
- {
- res.emplace_back(arr[b]);
- }
- }
-
- return res;
- }
-
-public:
- void insert(const T &value) { _values.emplace_back(value); }
-
-private:
- std::vector<T> _values;
-};
-
-template <typename T> struct ExplicitDataChefFactory : public DataChefFactory
-{
- std::unique_ptr<DataChef> create(const Arguments &args) const
- {
- std::unique_ptr<ExplicitDataChef<T>> res{new ExplicitDataChef<T>};
-
- for (uint32_t n = 0; n < args.count(); ++n)
- {
- auto const value = to_number<T>(args.value(n));
- res->insert(value);
- }
-
- return std::move(res);
- }
-};
-
-#endif // __EXPLICIT_FILLER_H__
diff --git a/compiler/tflchef/core/src/Data/Gaussian.cpp b/compiler/tflchef/core/src/Data/Gaussian.cpp
deleted file mode 100644
index 411f7b048..000000000
--- a/compiler/tflchef/core/src/Data/Gaussian.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Gaussian.h"
-#include "LexicalCast.h"
-
-#include <random>
-#include <chrono>
-
-#include <cassert>
-#include <stdexcept>
-
-std::vector<uint8_t> GaussianFloat32DataChef::generate(int32_t count) const
-{
- // TODO Support seed value override
- int seed = std::chrono::system_clock::now().time_since_epoch().count();
-
- std::minstd_rand rand{seed};
- std::normal_distribution<float> dist{_mean, _stddev};
-
- std::vector<uint8_t> res;
-
- for (uint32_t n = 0; n < count; ++n)
- {
- auto const value = dist(rand);
- auto const arr = reinterpret_cast<const uint8_t *>(&value);
-
- for (uint32_t b = 0; b < sizeof(float); ++b)
- {
- res.emplace_back(arr[b]);
- }
- }
-
- return res;
-}
-
-std::unique_ptr<DataChef> GaussianFloat32DataChefFactory::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<float>(args.value(0));
- auto const stddev = to_number<float>(args.value(1));
-
- return std::unique_ptr<DataChef>{new GaussianFloat32DataChef{mean, stddev}};
-}
diff --git a/compiler/tflchef/core/src/Data/Gaussian.h b/compiler/tflchef/core/src/Data/Gaussian.h
deleted file mode 100644
index 588d3e1cf..000000000
--- a/compiler/tflchef/core/src/Data/Gaussian.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __GAUSSIAN_FILLER_H__
-#define __GAUSSIAN_FILLER_H__
-
-#include "DataChef.h"
-
-/**
- * @brief Generate a sequence of random values according to the gaussian(=normal) distribution
- */
-class GaussianFloat32DataChef final : public DataChef
-{
-public:
- GaussianFloat32DataChef(float mean, float stddev) : _mean{mean}, _stddev{stddev}
- {
- // DO NOTHING
- }
-
-public:
- std::vector<uint8_t> generate(int32_t count) const override;
-
-private:
- float _mean;
- float _stddev;
-};
-
-struct GaussianFloat32DataChefFactory : public DataChefFactory
-{
- std::unique_ptr<DataChef> create(const Arguments &args) const;
-};
-
-#endif // __GAUSSIAN_FILLER_H__
diff --git a/compiler/tflchef/core/src/DataChef.def b/compiler/tflchef/core/src/DataChef.def
deleted file mode 100644
index 9e894979b..000000000
--- a/compiler/tflchef/core/src/DataChef.def
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef DATA_CHEF
-#error "Define DATA_CHEF first"
-#endif // DATA_CHEF
-
-// DATA_CHEF(TYPE, NAME, FACTORY_CLASS)
-// "TYPE" SHOULD BE an enum tag of tflchef::TensorType
-DATA_CHEF(FLOAT32, constant, ConstantDataChefFactory<float>)
-DATA_CHEF(INT32, explicit, ExplicitDataChefFactory<int>)
-DATA_CHEF(FLOAT32, gaussian, GaussianFloat32DataChefFactory)
diff --git a/compiler/tflchef/core/src/DataChef.h b/compiler/tflchef/core/src/DataChef.h
deleted file mode 100644
index d0571028a..000000000
--- a/compiler/tflchef/core/src/DataChef.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __DATA_CHEF_H__
-#define __DATA_CHEF_H__
-
-#include "Arguments.h"
-
-#include <cstdint>
-#include <memory>
-#include <vector>
-
-using Data = std::vector<uint8_t>;
-
-/**
- * @brief Data Generator
- */
-struct DataChef
-{
- virtual ~DataChef() = default;
-
- // TODO Allow users to query the type of elements that this DataChef generates
-
- /**
- * @brief Generate a sequence of 'count' elements as a byte sequence
- *
- * Let D be the return value of generate(N).
- * Then, D.size() == N * sizeof(T) where T is the element type.
- */
- virtual Data generate(int32_t count) const = 0;
-};
-
-/**
- * @brief Data Generator Factory
- */
-struct DataChefFactory
-{
- virtual ~DataChefFactory() = default;
-
- virtual std::unique_ptr<DataChef> create(const Arguments &args) const = 0;
-};
-
-#endif // __DATA_CHEF_H__
diff --git a/compiler/tflchef/core/src/DataChefs.h b/compiler/tflchef/core/src/DataChefs.h
deleted file mode 100644
index 2310ae89d..000000000
--- a/compiler/tflchef/core/src/DataChefs.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __DATA_CHEFS_H__
-#define __DATA_CHEFS_H__
-
-#include "Data/Constant.h"
-#include "Data/Explicit.h"
-#include "Data/Gaussian.h"
-
-#endif // __DATA_CHEFS_H__
diff --git a/compiler/tflchef/core/src/Dataset.h b/compiler/tflchef/core/src/Dataset.h
deleted file mode 100644
index 9d5c7a43f..000000000
--- a/compiler/tflchef/core/src/Dataset.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __DATASET_H__
-#define __DATASET_H__
-
-#include <vector>
-
-template <typename T> class Dataset
-{
-public:
- Dataset(const std::vector<T> &vec) : _vec{vec}
- {
- // DO NOTHING
- }
-
-public:
- Dataset(std::vector<T> &&vec) : _vec{std::move(vec)}
- {
- // DO NOTHING
- }
-
-public:
- template <typename Func> auto map(Func f) const -> Dataset<decltype(f(std::declval<T>()))>
- {
- using U = decltype(f(std::declval<T>()));
- std::vector<U> res;
-
- for (const auto &elem : _vec)
- {
- res.emplace_back(f(elem));
- }
-
- return Dataset<U>(std::move(res));
- }
-
-public:
- const std::vector<T> &vectorize(void) const { return _vec; }
-
-private:
- std::vector<T> _vec;
-};
-
-#endif // __DATASET_H__
diff --git a/compiler/tflchef/core/src/LexicalCast.cpp b/compiler/tflchef/core/src/LexicalCast.cpp
deleted file mode 100644
index 334729dc4..000000000
--- a/compiler/tflchef/core/src/LexicalCast.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "LexicalCast.h"
-
-template <> float to_number(const std::string &s) { return std::stof(s); }
-template <> int to_number(const std::string &s) { return std::stoi(s); }
diff --git a/compiler/tflchef/core/src/LexicalCast.h b/compiler/tflchef/core/src/LexicalCast.h
deleted file mode 100644
index 4aeccb482..000000000
--- a/compiler/tflchef/core/src/LexicalCast.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @brief This file provides string <-> number cast helpers
- */
-#ifndef __LEXICAL_CAST_H__
-#define __LEXICAL_CAST_H__
-
-#include <string>
-
-/**
- * @brief Return a numeric value that corresponds to a given string
- *
- * @note This function will throw an exception on casting failure
- */
-template <typename Number> Number to_number(const std::string &s);
-
-#endif // __LEXICAL_CAST_H__
diff --git a/compiler/tflchef/core/src/ModelChef.cpp b/compiler/tflchef/core/src/ModelChef.cpp
index 95886874f..2f4d7eeb5 100644
--- a/compiler/tflchef/core/src/ModelChef.cpp
+++ b/compiler/tflchef/core/src/ModelChef.cpp
@@ -15,17 +15,20 @@
*/
#include "tflchef/ModelChef.h"
-#include "Arguments.h"
+#include <souschef/RangedArguments.h>
+#include <souschef/Registry.h>
#include "Convert.h"
-#include "DataChef.h"
-#include "DataChefs.h"
+#include <souschef/DataChefs.h>
#include "OpChef.h"
#include "OpChefs.h"
-#include "Dataset.h"
+#include <souschef/Dataset.h>
+#include <souschef/Dims.h>
+
+#include "Log.h"
#include <iterator>
#include <map>
@@ -36,80 +39,10 @@
#include <fstream>
#include <iostream>
#include <numeric>
+#include <sstream>
#include <stdexcept>
-namespace
-{
-
-template <typename InputIt> class RangedArguments : public Arguments
-{
-public:
- RangedArguments(InputIt beg, InputIt end) : _beg{beg}, _end{end}
- {
- // DO NOTHING
- }
-
-public:
- uint32_t count(void) const override { return _end - _beg; }
-
-public:
- const std::string &value(uint32_t n) const override { return *(_beg + n); }
-
-private:
- InputIt _beg;
- InputIt _end;
-};
-
-template <typename InputIt> RangedArguments<InputIt> ranged_arguments(InputIt beg, InputIt end)
-{
- return RangedArguments<InputIt>{beg, end};
-}
-
-} // namespace
-
-namespace
-{
-
-template <typename T> std::vector<T> as_vector(const ::google::protobuf::RepeatedPtrField<T> &field)
-{
- std::vector<T> res;
- for (const auto &elem : field)
- {
- res.emplace_back(elem);
- }
- return res;
-}
-
-template <typename T> Dataset<T> as_dataset(const ::google::protobuf::RepeatedPtrField<T> &field)
-{
- return Dataset<T>(as_vector<T>(field));
-}
-
-} // namespace
-
-namespace
-{
-
-template <typename T> using Dims = std::vector<T>;
-
-Dims<int32_t> as_dims(const tflchef::TensorShape &shape)
-{
- std::vector<int32_t> res;
-
- for (auto &dim : shape.dim())
- {
- res.emplace_back(static_cast<int32_t>(dim));
- }
-
- return res;
-}
-
-int32_t element_count(const Dims<int32_t> &dims)
-{
- return std::accumulate(dims.begin(), dims.end(), 1, std::multiplies<int32_t>());
-}
-
-} // namespace
+using namespace souschef;
namespace
{
@@ -146,20 +79,6 @@ private:
namespace
{
-template <typename T> class Registry
-{
-public:
- void add(const std::string &name, std::unique_ptr<T> &&entry)
- {
- _content[name] = std::move(entry);
- }
-
- const T &lookup(const std::string &name) const { return *(_content.at(name)); }
-
-private:
- std::map<std::string, std::unique_ptr<T>> _content;
-};
-
struct DataChefRegistry final : public Registry<DataChefFactory>
{
};
@@ -167,14 +86,23 @@ struct DataChefRegistry final : public Registry<DataChefFactory>
DataChefRegistry &data_chef_registry(const tflchef::TensorType &type)
{
static DataChefRegistry s32;
+ static DataChefRegistry s64;
static DataChefRegistry fp32;
+ static DataChefRegistry u8;
+ static DataChefRegistry boolean;
switch (type)
{
case tflchef::INT32:
return s32;
+ case tflchef::INT64:
+ return s64;
case tflchef::FLOAT32:
return fp32;
+ case tflchef::UINT8:
+ return u8;
+ case tflchef::BOOL:
+ return boolean;
default:
break;
}
@@ -192,93 +120,160 @@ OpChefRegistry &op_chef_registry(void)
return registry;
}
-// @brief This will prepare a set of unique operator codes in the mode recipe
-std::set<tflite::BuiltinOperator> gather_opcode_set(const ::tflchef::ModelRecipe &model_recipe)
+/// @brief This will prepare a map of unique builtin codes in the model recipe
+std::map<tflite::BuiltinOperator, int32_t>
+gather_builtincode_map(const ::tflchef::ModelRecipe &model_recipe)
{
- std::set<tflite::BuiltinOperator> opcode_set;
+ // Key and value of the map are BuiltinOperator and operator version
+ std::map<tflite::BuiltinOperator, int32_t> builtin_map;
+
for (const auto &operation : model_recipe.operation())
{
auto op_chef = op_chef_registry().lookup(operation.type()).create(&operation);
- opcode_set.insert(op_chef->code());
+ if (op_chef->code() == tflite::BuiltinOperator_CUSTOM)
+ continue;
+
+ // Various operation version is unified as the highest version among them
+ if (builtin_map.find(op_chef->code()) == builtin_map.end() ||
+ builtin_map[op_chef->code()] < operation.version())
+ builtin_map[op_chef->code()] = operation.version();
+ }
+
+ // Add ops used in Graphs(subgraphs)
+ for (int g = 0; g < model_recipe.graph_size(); ++g)
+ {
+ const auto &graph = model_recipe.graph(g);
+ for (const auto &operation : graph.operation())
+ {
+ auto op_chef = op_chef_registry().lookup(operation.type()).create(&operation);
+ if (op_chef->code() == tflite::BuiltinOperator_CUSTOM)
+ continue;
+
+ // Various operation version is unified as the highest version among them
+ if (builtin_map.find(op_chef->code()) == builtin_map.end() ||
+ builtin_map[op_chef->code()] < operation.version())
+ builtin_map[op_chef->code()] = operation.version();
+ }
}
- return opcode_set;
+
+ return builtin_map;
+}
+
+/// @brief This will prepare a set of unique custom codes in the mode recipe
+std::set<std::string> gather_customcode_set(const ::tflchef::ModelRecipe &model_recipe)
+{
+ std::set<std::string> customcode_set;
+ for (const auto &operation : model_recipe.operation())
+ {
+ auto op_chef = op_chef_registry().lookup(operation.type()).create(&operation);
+ if (op_chef->code() == tflite::BuiltinOperator_CUSTOM)
+ customcode_set.insert(operation.type());
+ }
+
+ // Add ops used in Graphs(subgraphs)
+ for (int g = 0; g < model_recipe.graph_size(); ++g)
+ {
+ const auto &graph = model_recipe.graph(g);
+ for (const auto &operation : graph.operation())
+ {
+ auto op_chef = op_chef_registry().lookup(operation.type()).create(&operation);
+ if (op_chef->code() == tflite::BuiltinOperator_CUSTOM)
+ customcode_set.insert(operation.type());
+ }
+ }
+
+ return customcode_set;
}
} // namespace
-namespace tflchef
+namespace
{
-/**
- * @brief Generate a (in-memory) TensorFlow Lite model from a given model recipe
- */
-GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe)
+struct CookParams
{
-// Initialize Op Chef Registry
-#define OP_CHEF(NAME, FACTORY_CLASS) \
- op_chef_registry().add(#NAME, std::unique_ptr<FACTORY_CLASS>(new FACTORY_CLASS()));
-#include "OpChef.def"
-#undef OP_CHEF
-
-// Initialize Data Chef Registry
-#define DATA_CHEF(TYPE, NAME, FACTORY_CLASS) \
- data_chef_registry(::tflchef::TYPE) \
- .add(#NAME, std::unique_ptr<FACTORY_CLASS>(new FACTORY_CLASS()));
-#include "DataChef.def"
-#undef DATA_CHEF
+ std::vector<flatbuffers::Offset<::tflite::Buffer>> &buffer_vec;
+ std::vector<flatbuffers::Offset<::tflite::OperatorCode>> &code_vec;
+ std::vector<flatbuffers::Offset<::tflite::SubGraph>> &subgraph_vec;
+ std::unique_ptr<flatbuffers::FlatBufferBuilder> &flatbuffer_builder;
+ std::map<tflite::BuiltinOperator, int32_t> &builtin_code_map;
+ std::string noname;
+};
- // Tensor Name -> Tensor ID mapping
- std::map<std::string, int32_t> symbol_table;
+template <typename T> void cook_graph(const T &graph, CookParams &cp)
+{
+ LOGGER(l);
- auto lookup = [&symbol_table](const std::string &name) { return symbol_table.at(name); };
-
- //
- // Create FlatBufferBuilder
- //
- auto flatbuffer_builder =
- std::unique_ptr<flatbuffers::FlatBufferBuilder>(new flatbuffers::FlatBufferBuilder(1024));
+ std::vector<flatbuffers::Offset<::tflite::Buffer>> &buffer_vec = cp.buffer_vec;
+ std::vector<flatbuffers::Offset<::tflite::OperatorCode>> &code_vec = cp.code_vec;
+ std::vector<flatbuffers::Offset<::tflite::SubGraph>> &subgraph_vec = cp.subgraph_vec;
+ std::unique_ptr<flatbuffers::FlatBufferBuilder> &flatbuffer_builder = cp.flatbuffer_builder;
+ std::map<tflite::BuiltinOperator, int32_t> &builtin_code_map = cp.builtin_code_map;
// Operand-related
- std::vector<flatbuffers::Offset<::tflite::Buffer>> buffer_vec;
std::vector<flatbuffers::Offset<::tflite::Tensor>> tensor_vec;
// Operation-related
- std::vector<flatbuffers::Offset<::tflite::OperatorCode>> code_vec;
std::vector<flatbuffers::Offset<::tflite::Operator>> operator_vec;
- // Create OperatorCode
- std::set<tflite::BuiltinOperator> opcode_set = gather_opcode_set(model_recipe);
- for (auto opcode : opcode_set)
+ // default name for graph
+ std::string graph_name = cp.noname;
+ if (graph.has_name())
+ graph_name = graph.name();
+
+ // Tensor Name -> Tensor ID mapping (per Graph)
+ std::map<std::string, int32_t> symbol_table;
+
+ auto lookup = [&symbol_table, &graph_name](const std::string &name) {
+ if (symbol_table.find(name) != symbol_table.end())
+ return symbol_table.at(name);
+ else if (name == "")
+ return -1; // -1 in TFLite means that optional input tensor is empty.
+ else
+ {
+ std::string msg = "tflchef : input not found in " + graph_name + " graph";
+ throw std::runtime_error(msg.c_str());
+ }
+ };
+
+ int32_t buffer_start = buffer_vec.size();
+ int32_t buffer_index = 0;
+
+ // Create buffer(s) 1~n(I) for input(s)
+ const auto size_input = graph.input_size();
+ for (int ci = 0; ci < size_input; ++ci)
{
- tflite::OperatorCodeBuilder code_builder{*flatbuffer_builder};
- code_builder.add_builtin_code(opcode);
- auto code = code_builder.Finish();
- // Update OperatorCode vector
- code_vec.emplace_back(code);
+ tflite::BufferBuilder buffer_builder{*flatbuffer_builder};
+ buffer_vec.emplace_back(buffer_builder.Finish());
}
-
- // Create an Empty Buffer
- //
- // Buffer 0 SHOULD be an empty buffer in TensorFlow Lite model file
- // (Please refer to the comment for Tensor.buffer field in schema)
+ // Create buffer(s) n(I)+1~n(I)+n(O) for output(s)
+ const auto size_output = graph.output_size();
+ for (int co = 0; co < size_output; ++co)
{
tflite::BufferBuilder buffer_builder{*flatbuffer_builder};
buffer_vec.emplace_back(buffer_builder.Finish());
}
- for (const auto &operand : model_recipe.operand())
+ auto input_names = as_dataset(graph.input()).vectorize();
+ auto output_names = as_dataset(graph.output()).vectorize();
+
+ for (const auto &operand : graph.operand())
{
assert(operand.has_name());
assert(operand.has_type());
- assert(operand.has_shape());
- std::vector<int32_t> dims = as_dims(operand.shape());
+ flatbuffers::Offset<flatbuffers::Vector<int32_t>> shape;
+ std::vector<int32_t> dims;
+ if (operand.has_shape())
+ {
+ dims = as_dims(operand.shape());
+ shape = flatbuffer_builder->CreateVector(dims);
+ }
- auto shape = flatbuffer_builder->CreateVector(dims);
auto name = flatbuffer_builder->CreateString(operand.name());
- int32_t buffer_index = 0;
+ buffer_index = 0;
// Create Buffer if filler is specified
if (operand.has_filler())
@@ -293,7 +288,8 @@ GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe)
assert(chef != nullptr);
// Create Data
- auto data_vec = chef->generate(element_count(dims));
+ int32_t count = (element_count(dims) > 0) ? element_count(dims) : filler.arg_size();
+ auto data_vec = chef->generate(count);
auto data = flatbuffer_builder->CreateVector(data_vec);
// Create Buffer
@@ -305,6 +301,40 @@ GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe)
buffer_index = buffer_vec.size();
buffer_vec.emplace_back(buffer);
}
+ else
+ {
+ // if this is input or output, assign to that buffer_index
+ int idx = 0;
+ for (auto it = input_names.begin(); it != input_names.end(); ++it, ++idx)
+ {
+ if (*it == operand.name())
+ {
+ buffer_index = buffer_start + idx;
+ break;
+ }
+ }
+ if (buffer_index == 0)
+ {
+ idx = 0;
+ for (auto it = output_names.begin(); it != output_names.end(); ++it, ++idx)
+ {
+ if (*it == operand.name())
+ {
+ buffer_index = buffer_start + size_input + idx;
+ break;
+ }
+ }
+ }
+ if (buffer_index == 0)
+ {
+ // we couldn't find the buffer; create an empty buffer for this tensor
+ buffer_index = buffer_vec.size();
+
+ tflite::BufferBuilder buffer_builder{*flatbuffer_builder};
+ buffer_vec.emplace_back(buffer_builder.Finish());
+ }
+ }
+ assert(buffer_index != 0);
flatbuffers::Offset<tflite::QuantizationParameters> quant_index;
@@ -321,13 +351,13 @@ GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe)
std::vector<int64_t> quant_zero_point_vec(quant.zero_point_size());
for (uint32_t i = 0; i < quant.max_size(); ++i)
- quant_max_vec.push_back(quant.max(i));
+ quant_max_vec.at(i) = quant.max(i);
for (uint32_t i = 0; i < quant.min_size(); ++i)
- quant_max_vec.push_back(quant.min(i));
+ quant_min_vec.at(i) = quant.min(i);
for (uint32_t i = 0; i < quant.scale_size(); ++i)
- quant_max_vec.push_back(quant.scale(i));
+ quant_scale_vec.at(i) = quant.scale(i);
for (uint32_t i = 0; i < quant.zero_point_size(); ++i)
- quant_max_vec.push_back(quant.zero_point(i));
+ quant_zero_point_vec.at(i) = quant.zero_point(i);
auto quant_max = flatbuffer_builder->CreateVector(quant_max_vec);
auto quant_min = flatbuffer_builder->CreateVector(quant_min_vec);
@@ -340,11 +370,66 @@ GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe)
quant_builder.add_min(quant_min);
quant_builder.add_scale(quant_scale);
quant_builder.add_zero_point(quant_zero_point);
+ quant_builder.add_quantized_dimension(quant.quantized_dimension());
// Update QuantizationParameters Index
quant_index = quant_builder.Finish();
}
+ flatbuffers::Offset<tflite::SparsityParameters> sparsity_index;
+
+ if (operand.has_sparsity())
+ {
+ const auto &sparsity = operand.sparsity();
+
+ // Create traversal order
+ std::vector<int> traversal_order_vec{sparsity.traversal_order().dim().begin(),
+ sparsity.traversal_order().dim().end()};
+ auto traversal_order = flatbuffer_builder->CreateVector(traversal_order_vec);
+
+ // Create block map
+ std::vector<int> block_map_vec{sparsity.block_map().dim().begin(),
+ sparsity.block_map().dim().end()};
+ auto block_map = flatbuffer_builder->CreateVector(block_map_vec);
+
+ // Create dimension metadata
+ std::vector<flatbuffers::Offset<tflite::DimensionMetadata>> dim_metadata_vec;
+ auto recipe_dim_metadata = sparsity.dim_metadata();
+ for (const auto &dm : recipe_dim_metadata)
+ {
+ // Create array segments
+ auto tflite_array_segments =
+ as_tflite_sparse_index_vec(*flatbuffer_builder, dm.array_segments());
+
+ // Create array indices
+ auto tflite_array_indices =
+ as_tflite_sparse_index_vec(*flatbuffer_builder, dm.array_indices());
+
+ auto tflite_dim_metadata_builder = tflite::DimensionMetadataBuilder{*flatbuffer_builder};
+ tflite_dim_metadata_builder.add_format(as_tflite_dimensiontype(dm.format()));
+ tflite_dim_metadata_builder.add_dense_size(dm.dense_size());
+ tflite_dim_metadata_builder.add_array_segments(tflite_array_segments);
+ tflite_dim_metadata_builder.add_array_segments_type(
+ as_tflite_sparse_idx_vec_type(dm.array_segments().type()));
+ tflite_dim_metadata_builder.add_array_indices(tflite_array_indices);
+ tflite_dim_metadata_builder.add_array_indices_type(
+ as_tflite_sparse_idx_vec_type(dm.array_indices().type()));
+ auto tflite_dim_metadata = tflite_dim_metadata_builder.Finish();
+ dim_metadata_vec.emplace_back(tflite_dim_metadata);
+ }
+ auto dim_metadata = flatbuffer_builder->CreateVector(dim_metadata_vec);
+
+ sparsity_index = tflite::CreateSparsityParameters(*flatbuffer_builder, traversal_order,
+ block_map, dim_metadata);
+ }
+
+ flatbuffers::Offset<flatbuffers::Vector<int32_t>> shape_signature;
+ if (operand.has_shape_signature())
+ {
+ auto signature = as_dims(operand.shape_signature());
+ shape_signature = flatbuffer_builder->CreateVector(signature);
+ }
+
// Create Tensor
tflite::TensorBuilder tensor_builder{*flatbuffer_builder};
@@ -352,8 +437,12 @@ GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe)
tensor_builder.add_type(as_tflite_tensortype(operand.type()));
tensor_builder.add_buffer(buffer_index);
tensor_builder.add_name(name);
+ tensor_builder.add_is_variable(operand.is_variable());
if (operand.has_quant())
tensor_builder.add_quantization(quant_index);
+ tensor_builder.add_sparsity(sparsity_index);
+ if (operand.has_shape_signature())
+ tensor_builder.add_shape_signature(shape_signature);
// Append!
tensor_vec.emplace_back(tensor_builder.Finish());
@@ -362,11 +451,13 @@ GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe)
int32_t tensor_index = symbol_table.size();
const auto &tensor_name = operand.name();
+ INFO(l) << "Symbol [" << tensor_name << "] = Tensor " << tensor_index << std::endl;
+
symbol_table[tensor_name] = tensor_index;
}
// Create Operator
- for (const auto &operation : model_recipe.operation())
+ for (const auto &operation : graph.operation())
{
assert(operation.has_type());
@@ -383,37 +474,39 @@ GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe)
// Create Option
auto options = op_chef->value(*flatbuffer_builder);
+ // Create Custom option
+ auto circle_custom_options = op_chef->custom_value(*flatbuffer_builder);
+
// Create Operator
tflite::OperatorBuilder op_builder{*flatbuffer_builder};
- // Get operator code index from opcode_set with assumption, order of
- // opcode_set is same as that of code_vec
- auto op_it = opcode_set.find(op_chef->code());
- assert(op_it != opcode_set.end());
- uint32_t opcode_index = std::distance(opcode_set.begin(), op_it);
+ // Get operator code index from builtin_code_set with assumption, order of
+ // builtin_code_set is same as that of code_vec
+ auto op_it = builtin_code_map.find(op_chef->code());
+ assert(op_it != builtin_code_map.end());
+ uint32_t opcode_index = std::distance(builtin_code_map.begin(), op_it);
op_builder.add_opcode_index(opcode_index);
op_builder.add_inputs(inputs);
op_builder.add_outputs(outputs);
op_builder.add_builtin_options_type(op_chef->type());
op_builder.add_builtin_options(options);
-
+ op_builder.add_custom_options(circle_custom_options);
+ op_builder.add_custom_options_format(tflite::CustomOptionsFormat_FLEXBUFFERS);
// Append Operator
operator_vec.emplace_back(op_builder.Finish());
}
// Create network input/output vector
- std::vector<int32_t> input_vec = as_dataset(model_recipe.input()).map(lookup).vectorize();
- std::vector<int32_t> output_vec = as_dataset(model_recipe.output()).map(lookup).vectorize();
+ std::vector<int32_t> input_vec = as_dataset(graph.input()).map(lookup).vectorize();
+ std::vector<int32_t> output_vec = as_dataset(graph.output()).map(lookup).vectorize();
// Create "SubGraph" arguments
auto tensors = flatbuffer_builder->CreateVector(tensor_vec);
auto inputs = flatbuffer_builder->CreateVector(input_vec);
auto outputs = flatbuffer_builder->CreateVector(output_vec);
auto operators = flatbuffer_builder->CreateVector(operator_vec);
-
- // Create "SubGraph"
- std::vector<flatbuffers::Offset<::tflite::SubGraph>> subgraph_vec;
+ auto name = flatbuffer_builder->CreateString(graph_name);
tflite::SubGraphBuilder subgraph_builder{*flatbuffer_builder};
@@ -421,8 +514,109 @@ GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe)
subgraph_builder.add_inputs(inputs);
subgraph_builder.add_outputs(outputs);
subgraph_builder.add_operators(operators);
+ subgraph_builder.add_name(name);
subgraph_vec.emplace_back(subgraph_builder.Finish());
+}
+
+} // namespace
+
+namespace tflchef
+{
+
+/**
+ * @brief Generate a (in-memory) TensorFlow Lite model from a given model recipe
+ */
+GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe)
+{
+// Initialize Op Chef Registry
+#define OP_CHEF(NAME, FACTORY_CLASS) \
+ op_chef_registry().add(#NAME, std::unique_ptr<FACTORY_CLASS>(new FACTORY_CLASS()));
+#include "OpChef.def"
+#undef OP_CHEF
+
+// Initialize Data Chef Registry
+#define DATA_CHEF(TYPE, NAME, FACTORY_CLASS) \
+ data_chef_registry(::tflchef::TYPE) \
+ .add(#NAME, std::unique_ptr<FACTORY_CLASS>(new FACTORY_CLASS()));
+#include <souschef/DataChef.def>
+#undef DATA_CHEF
+
+ //
+ // Create FlatBufferBuilder
+ //
+ auto flatbuffer_builder =
+ std::unique_ptr<flatbuffers::FlatBufferBuilder>(new flatbuffers::FlatBufferBuilder(1024));
+
+ // Operand-related
+ std::vector<flatbuffers::Offset<::tflite::Buffer>> buffer_vec;
+
+ // Operation-related
+ std::vector<flatbuffers::Offset<::tflite::OperatorCode>> code_vec;
+
+ // Graphs-related
+ std::vector<flatbuffers::Offset<::tflite::SubGraph>> subgraph_vec;
+
+ // Create OperatorCode with Builtin Operator
+ auto builtin_code_map = gather_builtincode_map(model_recipe);
+ for (auto const &opcode : builtin_code_map)
+ {
+ tflite::OperatorCodeBuilder code_builder{*flatbuffer_builder};
+ code_builder.add_builtin_code(opcode.first);
+ code_builder.add_version(opcode.second);
+ auto code = code_builder.Finish();
+ // Update OperatorCode vector
+ code_vec.emplace_back(code);
+ }
+
+ // Create OperatorCode with Custom Operator
+ std::set<std::string> custom_code_set = gather_customcode_set(model_recipe);
+ if (custom_code_set.size() &&
+ builtin_code_map.find(tflite::BuiltinOperator_CUSTOM) == builtin_code_map.end())
+ builtin_code_map[tflite::BuiltinOperator_CUSTOM] = 1;
+
+ for (auto opcode : custom_code_set)
+ {
+ auto custom_code = flatbuffer_builder->CreateString(opcode);
+ tflite::OperatorCodeBuilder code_builder{*flatbuffer_builder};
+ code_builder.add_builtin_code(tflite::BuiltinOperator_CUSTOM);
+ code_builder.add_custom_code(custom_code);
+ auto code = code_builder.Finish();
+ // Update OperatorCode vector
+ code_vec.emplace_back(code);
+ }
+
+ // Create an Empty Buffer
+ //
+ // Buffer 0 SHOULD be an empty buffer in TensorFlow Lite model file
+ // (Please refer to the comment for Tensor.buffer field in schema)
+ {
+ tflite::BufferBuilder buffer_builder{*flatbuffer_builder};
+ buffer_vec.emplace_back(buffer_builder.Finish());
+ }
+
+ //
+ // Create Main graph
+ //
+ CookParams cp{buffer_vec, code_vec, subgraph_vec, flatbuffer_builder, builtin_code_map, "main"};
+
+ cook_graph<::tflchef::ModelRecipe>(model_recipe, cp);
+
+ //
+ // Create subgraphs if exist
+ //
+ for (int g = 0; g < model_recipe.graph_size(); ++g)
+ {
+ const auto &graph = model_recipe.graph(g);
+
+ std::ostringstream stringStream;
+ stringStream << "sub_" << (g + 1);
+
+ CookParams cp{buffer_vec, code_vec, subgraph_vec,
+ flatbuffer_builder, builtin_code_map, stringStream.str()};
+
+ cook_graph<::tflchef::Graph>(graph, cp);
+ }
// Create "Model" arguments
auto buffers = flatbuffer_builder->CreateVector(buffer_vec);
diff --git a/compiler/tflchef/core/src/Op/Abs.cpp b/compiler/tflchef/core/src/Op/Abs.cpp
new file mode 100644
index 000000000..dcb27784c
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Abs.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 "Abs.h"
+#include "Convert.h"
+
+flatbuffers::Offset<void> AbsChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::AbsOptionsBuilder abs_options_builder{fbb};
+
+ return abs_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> AbsChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new AbsChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Abs.h b/compiler/tflchef/core/src/Op/Abs.h
new file mode 100644
index 000000000..5b694c6b6
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Abs.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_ABS_H__
+#define __OP_ABS_H__
+
+#include "OpChef.h"
+
+class AbsChef final : public OpChef
+{
+public:
+ explicit AbsChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_ABS; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_AbsOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct AbsChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_ABS_H__
diff --git a/compiler/tflchef/core/src/Op/Add.cpp b/compiler/tflchef/core/src/Op/Add.cpp
new file mode 100644
index 000000000..8679ba35e
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Add.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Add.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> AddChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_add_options());
+
+ auto tflite_activation = as_tflite_activation(operation.add_options().activation());
+
+ tflite::AddOptionsBuilder add_options_builder{fbb};
+ add_options_builder.add_fused_activation_function(tflite_activation);
+
+ return add_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> AddChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new AddChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Add.h b/compiler/tflchef/core/src/Op/Add.h
new file mode 100644
index 000000000..29ddb9470
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Add.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_ADD_H__
+#define __OP_ADD_H__
+
+#include "OpChef.h"
+
+class AddChef final : public OpChef
+{
+public:
+ explicit AddChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_ADD; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_AddOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct AddChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_ADD_H__
diff --git a/compiler/tflchef/core/src/Op/AddN.cpp b/compiler/tflchef/core/src/Op/AddN.cpp
new file mode 100644
index 000000000..2ac02d219
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/AddN.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AddN.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> AddNChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::AddNOptionsBuilder add_n_options_builder{fbb};
+
+ return add_n_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> AddNChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new AddNChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/AddN.h b/compiler/tflchef/core/src/Op/AddN.h
new file mode 100644
index 000000000..44dcc63ef
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/AddN.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_ADD_N_H__
+#define __OP_ADD_N_H__
+
+#include "OpChef.h"
+
+class AddNChef final : public OpChef
+{
+public:
+ explicit AddNChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_ADD_N; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_AddNOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct AddNChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_ADD_N_H__
diff --git a/compiler/tflchef/core/src/Op/ArgMax.cpp b/compiler/tflchef/core/src/Op/ArgMax.cpp
new file mode 100644
index 000000000..2c2995da5
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ArgMax.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ArgMax.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> ArgMaxChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_argmax_options());
+
+ auto tflite_output_type = as_tflite_tensortype(operation.argmax_options().output_type());
+
+ tflite::ArgMaxOptionsBuilder argmax_options_builder{fbb};
+ argmax_options_builder.add_output_type(tflite_output_type);
+
+ return argmax_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> ArgMaxChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new ArgMaxChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/ArgMax.h b/compiler/tflchef/core/src/Op/ArgMax.h
new file mode 100644
index 000000000..4033e0f6c
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ArgMax.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_ARGMAX_H__
+#define __OP_ARGMAX_H__
+
+#include "OpChef.h"
+
+class ArgMaxChef final : public OpChef
+{
+public:
+ explicit ArgMaxChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_ARG_MAX; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_ArgMaxOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct ArgMaxChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_ARGMAX_H__
diff --git a/compiler/tflchef/core/src/Op/ArgMin.cpp b/compiler/tflchef/core/src/Op/ArgMin.cpp
new file mode 100644
index 000000000..b599270b0
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ArgMin.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ArgMin.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> ArgMinChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_argmin_options());
+
+ auto tflite_output_type = as_tflite_tensortype(operation.argmin_options().output_type());
+
+ tflite::ArgMinOptionsBuilder argmin_options_builder{fbb};
+ argmin_options_builder.add_output_type(tflite_output_type);
+
+ return argmin_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> ArgMinChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new ArgMinChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/ArgMin.h b/compiler/tflchef/core/src/Op/ArgMin.h
new file mode 100644
index 000000000..222039f91
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ArgMin.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_ARGMIN_H__
+#define __OP_ARGMIN_H__
+
+#include "OpChef.h"
+
+class ArgMinChef final : public OpChef
+{
+public:
+ explicit ArgMinChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_ARG_MIN; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_ArgMinOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct ArgMinChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_ARGMIN_H__
diff --git a/compiler/tflchef/core/src/Op/BatchMatMul.cpp b/compiler/tflchef/core/src/Op/BatchMatMul.cpp
new file mode 100644
index 000000000..7722bcc5e
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/BatchMatMul.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "BatchMatMul.h"
+
+flatbuffers::Offset<void> BatchMatMulChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_batch_matmul_options());
+
+ tflite::BatchMatMulOptionsBuilder batch_matmul_options_options_builder{fbb};
+ batch_matmul_options_options_builder.add_adj_x(operation.batch_matmul_options().adj_x());
+ batch_matmul_options_options_builder.add_adj_y(operation.batch_matmul_options().adj_y());
+
+ return batch_matmul_options_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> BatchMatMulChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new BatchMatMulChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/BatchMatMul.h b/compiler/tflchef/core/src/Op/BatchMatMul.h
new file mode 100644
index 000000000..eaf943cb0
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/BatchMatMul.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_BATCH_MATMUL_H__
+#define __OP_BATCH_MATMUL_H__
+
+#include "OpChef.h"
+
+class BatchMatMulChef final : public OpChef
+{
+public:
+ explicit BatchMatMulChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_BATCH_MATMUL; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_BatchMatMulOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct BatchMatMulChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_BATCH_MATMUL_H__
diff --git a/compiler/tflchef/core/src/Op/BatchToSpaceND.cpp b/compiler/tflchef/core/src/Op/BatchToSpaceND.cpp
new file mode 100644
index 000000000..972f93256
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/BatchToSpaceND.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "BatchToSpaceND.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> BatchToSpaceNDChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::BatchToSpaceNDOptionsBuilder batch_to_space_nd_options_builder{fbb};
+
+ return batch_to_space_nd_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> BatchToSpaceNDChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new BatchToSpaceNDChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/BatchToSpaceND.h b/compiler/tflchef/core/src/Op/BatchToSpaceND.h
new file mode 100644
index 000000000..6ba1352ab
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/BatchToSpaceND.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_BATCHTOSPACEND_H__
+#define __OP_BATCHTOSPACEND_H__
+
+#include "OpChef.h"
+
+class BatchToSpaceNDChef final : public OpChef
+{
+public:
+ explicit BatchToSpaceNDChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_BATCH_TO_SPACE_ND;
+ }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_BatchToSpaceNDOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct BatchToSpaceNDChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_BATCHTOSPACEND_H__
diff --git a/compiler/tflchef/core/src/Op/Cast.cpp b/compiler/tflchef/core/src/Op/Cast.cpp
new file mode 100644
index 000000000..1a29f9ac4
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Cast.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "Cast.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> CastChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ if (!operation.has_cast_options())
+ return 0;
+
+ auto tflite_in_data_type = as_tflite_tensortype(operation.cast_options().in_data_type());
+ auto tflite_out_data_type = as_tflite_tensortype(operation.cast_options().out_data_type());
+
+ tflite::CastOptionsBuilder cast_options_builder{fbb};
+ cast_options_builder.add_in_data_type(tflite_in_data_type);
+ cast_options_builder.add_out_data_type(tflite_out_data_type);
+
+ return cast_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> CastChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new CastChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Cast.h b/compiler/tflchef/core/src/Op/Cast.h
new file mode 100644
index 000000000..84c8e29e4
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Cast.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_CAST_H__
+#define __OP_CAST_H__
+
+#include "OpChef.h"
+
+class CastChef final : public OpChef
+{
+public:
+ explicit CastChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_CAST; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_CastOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct CastChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_CAST_H__
diff --git a/compiler/tflchef/core/src/Op/Ceil.cpp b/compiler/tflchef/core/src/Op/Ceil.cpp
new file mode 100644
index 000000000..3da047727
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Ceil.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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 "Ceil.h"
+
+flatbuffers::Offset<void> CeilChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ // No tflite option for Ceil. Use void.
+ return flatbuffers::Offset<void>();
+}
+
+std::unique_ptr<OpChef> CeilChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new CeilChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Ceil.h b/compiler/tflchef/core/src/Op/Ceil.h
new file mode 100644
index 000000000..5a42b7f00
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Ceil.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_CEIL_H__
+#define __OP_CEIL_H__
+
+#include "OpChef.h"
+
+class CeilChef final : public OpChef
+{
+public:
+ explicit CeilChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_CEIL; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_NONE; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct CeilChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_CEIL_H__
diff --git a/compiler/tflchef/core/src/Op/Conv2D.cpp b/compiler/tflchef/core/src/Op/Conv2D.cpp
index d99c53351..0b3ef8a6d 100644
--- a/compiler/tflchef/core/src/Op/Conv2D.cpp
+++ b/compiler/tflchef/core/src/Op/Conv2D.cpp
@@ -25,14 +25,18 @@ flatbuffers::Offset<void> Conv2DChef::value(flatbuffers::FlatBufferBuilder &fbb)
assert(operation.has_conv2d_options());
- auto tflite_padding = as_tflite_padding(operation.conv2d_options().padding());
- auto tflite_activation = as_tflite_activation(operation.conv2d_options().activation());
+ const auto &conv2d_options = operation.conv2d_options();
+
+ auto tflite_padding = as_tflite_padding(conv2d_options.padding());
+ auto tflite_activation = as_tflite_activation(conv2d_options.activation());
tflite::Conv2DOptionsBuilder conv2d_options_builder{fbb};
conv2d_options_builder.add_padding(tflite_padding);
- conv2d_options_builder.add_stride_h(operation.conv2d_options().stride_h());
- conv2d_options_builder.add_stride_w(operation.conv2d_options().stride_w());
+ conv2d_options_builder.add_stride_h(conv2d_options.stride_h());
+ conv2d_options_builder.add_stride_w(conv2d_options.stride_w());
conv2d_options_builder.add_fused_activation_function(tflite_activation);
+ conv2d_options_builder.add_dilation_w_factor(conv2d_options.dilation_w_factor());
+ conv2d_options_builder.add_dilation_h_factor(conv2d_options.dilation_h_factor());
return conv2d_options_builder.Finish().Union();
}
diff --git a/compiler/tflchef/core/src/Op/Cos.cpp b/compiler/tflchef/core/src/Op/Cos.cpp
new file mode 100644
index 000000000..547bee1a9
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Cos.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "Cos.h"
+
+flatbuffers::Offset<void> CosChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::CosOptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> CosChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new CosChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Cos.h b/compiler/tflchef/core/src/Op/Cos.h
new file mode 100644
index 000000000..9bf8cbeab
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Cos.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_COS_H__
+#define __OP_COS_H__
+
+#include "OpChef.h"
+
+class CosChef final : public OpChef
+{
+public:
+ explicit CosChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_COS; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_CosOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct CosChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_COS_H__
diff --git a/compiler/tflchef/core/src/Op/DepthToSpace.cpp b/compiler/tflchef/core/src/Op/DepthToSpace.cpp
new file mode 100644
index 000000000..f0531d98d
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/DepthToSpace.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DepthToSpace.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> DepthToSpaceChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_depth_to_space_options());
+
+ auto options = operation.depth_to_space_options();
+
+ auto tflite_block_size = options.block_size();
+
+ tflite::DepthToSpaceOptionsBuilder options_builder{fbb};
+
+ options_builder.add_block_size(tflite_block_size);
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> DepthToSpaceChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new DepthToSpaceChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/DepthToSpace.h b/compiler/tflchef/core/src/Op/DepthToSpace.h
new file mode 100644
index 000000000..32cb24211
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/DepthToSpace.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_DEPTHTOSPACE_H__
+#define __OP_DEPTHTOSPACE_H__
+
+#include "OpChef.h"
+
+class DepthToSpaceChef final : public OpChef
+{
+public:
+ explicit DepthToSpaceChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_DEPTH_TO_SPACE;
+ }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_DepthToSpaceOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct DepthToSpaceChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_DEPTHTOSPACE_H__
diff --git a/compiler/tflchef/core/src/Op/DepthwiseConv2D.cpp b/compiler/tflchef/core/src/Op/DepthwiseConv2D.cpp
index e04cf50ff..5da5b63e4 100644
--- a/compiler/tflchef/core/src/Op/DepthwiseConv2D.cpp
+++ b/compiler/tflchef/core/src/Op/DepthwiseConv2D.cpp
@@ -36,6 +36,8 @@ flatbuffers::Offset<void> DepthwiseConv2DChef::value(flatbuffers::FlatBufferBuil
options_builder.add_stride_h(options.stride_h());
options_builder.add_depth_multiplier(options.depth_multiplier());
options_builder.add_fused_activation_function(tflite_activation);
+ options_builder.add_dilation_w_factor(options.dilation_w_factor());
+ options_builder.add_dilation_h_factor(options.dilation_h_factor());
return options_builder.Finish().Union();
}
diff --git a/compiler/tflchef/core/src/Op/Dequantize.cpp b/compiler/tflchef/core/src/Op/Dequantize.cpp
new file mode 100644
index 000000000..761d7f99e
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Dequantize.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dequantize.h"
+
+flatbuffers::Offset<void> DequantizeChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ return flatbuffers::Offset<void>();
+}
+
+std::unique_ptr<OpChef> DequantizeChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new DequantizeChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Dequantize.h b/compiler/tflchef/core/src/Op/Dequantize.h
new file mode 100644
index 000000000..82580560d
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Dequantize.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_DEQUANTIZE_H__
+#define __OP_DEQUANTIZE_H__
+
+#include "OpChef.h"
+
+class DequantizeChef final : public OpChef
+{
+public:
+ explicit DequantizeChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_DEQUANTIZE; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_NONE; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct DequantizeChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_DEQUANTIZE_H__
diff --git a/compiler/tflchef/core/src/Op/ELU.cpp b/compiler/tflchef/core/src/Op/ELU.cpp
new file mode 100644
index 000000000..d9dae16af
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ELU.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ELU.h"
+
+flatbuffers::Offset<void> ELUChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ return flatbuffers::Offset<void>();
+}
+
+std::unique_ptr<OpChef> ELUChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new ELUChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/ELU.h b/compiler/tflchef/core/src/Op/ELU.h
new file mode 100644
index 000000000..e164c0071
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ELU.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_ELU_H__
+#define __OP_ELU_H__
+
+#include "OpChef.h"
+
+class ELUChef final : public OpChef
+{
+public:
+ explicit ELUChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_ELU; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_NONE; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct ELUChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_ELU_H__
diff --git a/compiler/tflchef/core/src/Op/Equal.cpp b/compiler/tflchef/core/src/Op/Equal.cpp
new file mode 100644
index 000000000..f7a39f03d
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Equal.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "Equal.h"
+
+flatbuffers::Offset<void> EqualChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::EqualOptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> EqualChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new EqualChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Equal.h b/compiler/tflchef/core/src/Op/Equal.h
new file mode 100644
index 000000000..6e097991d
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Equal.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_EQUAL_H__
+#define __OP_EQUAL_H__
+
+#include "OpChef.h"
+
+class EqualChef final : public OpChef
+{
+public:
+ explicit EqualChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_EQUAL; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_EqualOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct EqualChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_EQUAL_H__
diff --git a/compiler/tflchef/core/src/Op/Exp.cpp b/compiler/tflchef/core/src/Op/Exp.cpp
new file mode 100644
index 000000000..b3c8d7e73
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Exp.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 "Exp.h"
+#include "Convert.h"
+
+flatbuffers::Offset<void> ExpChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::ExpOptionsBuilder exp_options_builder{fbb};
+
+ return exp_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> ExpChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new ExpChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Exp.h b/compiler/tflchef/core/src/Op/Exp.h
new file mode 100644
index 000000000..422a3310f
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Exp.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_EXP_H__
+#define __OP_EXP_H__
+
+#include "OpChef.h"
+
+class ExpChef final : public OpChef
+{
+public:
+ explicit ExpChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_EXP; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_ExpOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct ExpChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_ABS_H__
diff --git a/compiler/tflchef/core/src/Op/ExpandDims.cpp b/compiler/tflchef/core/src/Op/ExpandDims.cpp
new file mode 100644
index 000000000..c6082811f
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ExpandDims.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 "ExpandDims.h"
+#include "Convert.h"
+
+flatbuffers::Offset<void> ExpandDimsChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::ExpandDimsOptionsBuilder expand_dims_options_builder{fbb};
+
+ return expand_dims_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> ExpandDimsChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new ExpandDimsChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/ExpandDims.h b/compiler/tflchef/core/src/Op/ExpandDims.h
new file mode 100644
index 000000000..1f4c34a98
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ExpandDims.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_EXPAND_DIMS_H__
+#define __OP_EXPAND_DIMS_H__
+
+#include "OpChef.h"
+
+class ExpandDimsChef final : public OpChef
+{
+public:
+ explicit ExpandDimsChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_EXPAND_DIMS; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_ExpandDimsOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct ExpandDimsChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_EXPAND_DIMS_H__
diff --git a/compiler/tflchef/core/src/Op/Fill.cpp b/compiler/tflchef/core/src/Op/Fill.cpp
new file mode 100644
index 000000000..4a6829459
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Fill.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "Fill.h"
+
+#include <cassert>
+#include <vector>
+
+flatbuffers::Offset<void> FillChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::FillOptionsBuilder options_builder{fbb};
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> FillChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new FillChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Fill.h b/compiler/tflchef/core/src/Op/Fill.h
new file mode 100644
index 000000000..60f9084c8
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Fill.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_FILL_H__
+#define __OP_FILL_H__
+
+#include "OpChef.h"
+
+class FillChef final : public OpChef
+{
+public:
+ explicit FillChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_FILL; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_FillOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct FillChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_FILL_H__
diff --git a/compiler/tflchef/core/src/Op/Floor.cpp b/compiler/tflchef/core/src/Op/Floor.cpp
new file mode 100644
index 000000000..8f6820152
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Floor.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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 "Floor.h"
+
+flatbuffers::Offset<void> FloorChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ // No tflite option for Floor. Use void.
+ return flatbuffers::Offset<void>();
+}
+
+std::unique_ptr<OpChef> FloorChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new FloorChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Floor.h b/compiler/tflchef/core/src/Op/Floor.h
new file mode 100644
index 000000000..23385d737
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Floor.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_FLOOR_H__
+#define __OP_FLOOR_H__
+
+#include "OpChef.h"
+
+class FloorChef final : public OpChef
+{
+public:
+ explicit FloorChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_FLOOR; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_NONE; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct FloorChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_FLOOR_H__
diff --git a/compiler/tflchef/core/src/Op/FloorDiv.cpp b/compiler/tflchef/core/src/Op/FloorDiv.cpp
new file mode 100644
index 000000000..0d531bede
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/FloorDiv.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 "FloorDiv.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> FloorDivChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::FloorDivOptionsBuilder floor_div_options_builder{fbb};
+ return floor_div_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> FloorDivChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new FloorDivChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/FloorDiv.h b/compiler/tflchef/core/src/Op/FloorDiv.h
new file mode 100644
index 000000000..151f24314
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/FloorDiv.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_FLOORDIV_H__
+#define __OP_FLOORDIV_H__
+
+#include "OpChef.h"
+
+class FloorDivChef final : public OpChef
+{
+public:
+ explicit FloorDivChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_FLOOR_DIV; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_FloorDivOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct FloorDivChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_FLOORDIV_H__
diff --git a/compiler/tflchef/core/src/Op/FloorMod.cpp b/compiler/tflchef/core/src/Op/FloorMod.cpp
new file mode 100644
index 000000000..d17795a72
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/FloorMod.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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 "FloorMod.h"
+
+flatbuffers::Offset<void> FloorModChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::FloorModOptionsBuilder options_builder{fbb};
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> FloorModChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new FloorModChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/FloorMod.h b/compiler/tflchef/core/src/Op/FloorMod.h
new file mode 100644
index 000000000..b501f61e0
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/FloorMod.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_FLOOR_MOD_H__
+#define __OP_FLOOR_MOD_H__
+
+#include "OpChef.h"
+
+class FloorModChef final : public OpChef
+{
+public:
+ explicit FloorModChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_FLOOR_MOD; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_FloorModOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct FloorModChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_FLOOR_MOD_H__
diff --git a/compiler/tflchef/core/src/Op/Gather.cpp b/compiler/tflchef/core/src/Op/Gather.cpp
new file mode 100644
index 000000000..2b62c7be2
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Gather.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Gather.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> GatherChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_gather_options());
+
+ auto options = operation.gather_options();
+
+ tflite::GatherOptionsBuilder options_builder{fbb};
+
+ options_builder.add_axis(options.axis());
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> GatherChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new GatherChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Gather.h b/compiler/tflchef/core/src/Op/Gather.h
new file mode 100644
index 000000000..d937178c8
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Gather.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_GATHER_H__
+#define __OP_GATHER_H__
+
+#include "OpChef.h"
+
+class GatherChef final : public OpChef
+{
+public:
+ explicit GatherChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_GATHER; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_GatherOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct GatherChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_GATHER_H__
diff --git a/compiler/tflchef/core/src/Op/GatherNd.cpp b/compiler/tflchef/core/src/Op/GatherNd.cpp
new file mode 100644
index 000000000..c04db5350
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/GatherNd.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "GatherNd.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> GatherNdChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::GatherNdOptionsBuilder gather_nd_options_builder{fbb};
+ return gather_nd_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> GatherNdChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new GatherNdChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/GatherNd.h b/compiler/tflchef/core/src/Op/GatherNd.h
new file mode 100644
index 000000000..8865e7756
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/GatherNd.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_GATHER_ND_H__
+#define __OP_GATHER_ND_H__
+
+#include "OpChef.h"
+
+class GatherNdChef final : public OpChef
+{
+public:
+ explicit GatherNdChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_GATHER_ND; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_GatherNdOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct GatherNdChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_GATHER_ND_H__
diff --git a/compiler/tflchef/core/src/Op/Greater.cpp b/compiler/tflchef/core/src/Op/Greater.cpp
new file mode 100644
index 000000000..81765aee5
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Greater.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "Greater.h"
+
+flatbuffers::Offset<void> GreaterChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::GreaterOptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> GreaterChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new GreaterChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Greater.h b/compiler/tflchef/core/src/Op/Greater.h
new file mode 100644
index 000000000..c54eaa6cc
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Greater.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_GREATER_H__
+#define __OP_GREATER_H__
+
+#include "OpChef.h"
+
+class GreaterChef final : public OpChef
+{
+public:
+ explicit GreaterChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_GREATER; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_GreaterOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct GreaterChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_GREATER_H__
diff --git a/compiler/tflchef/core/src/Op/GreaterEqual.cpp b/compiler/tflchef/core/src/Op/GreaterEqual.cpp
new file mode 100644
index 000000000..80045f6aa
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/GreaterEqual.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "GreaterEqual.h"
+
+flatbuffers::Offset<void> GreaterEqualChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::GreaterEqualOptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> GreaterEqualChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new GreaterEqualChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/GreaterEqual.h b/compiler/tflchef/core/src/Op/GreaterEqual.h
new file mode 100644
index 000000000..105bac8a7
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/GreaterEqual.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_GREATEREQUAL_H__
+#define __OP_GREATEREQUAL_H__
+
+#include "OpChef.h"
+
+class GreaterEqualChef final : public OpChef
+{
+public:
+ explicit GreaterEqualChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_GREATER_EQUAL;
+ }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_GreaterEqualOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct GreaterEqualChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_GREATEREQUAL_H__
diff --git a/compiler/tflchef/core/src/Op/If.cpp b/compiler/tflchef/core/src/Op/If.cpp
new file mode 100644
index 000000000..b0e575e2b
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/If.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "If.h"
+
+flatbuffers::Offset<void> IfChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_if_options());
+
+ tflite::IfOptionsBuilder if_options_builder{fbb};
+ if_options_builder.add_then_subgraph_index(operation.if_options().then_subgraph_index());
+ if_options_builder.add_else_subgraph_index(operation.if_options().else_subgraph_index());
+
+ return if_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> IfChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new IfChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/If.h b/compiler/tflchef/core/src/Op/If.h
new file mode 100644
index 000000000..7e18c5609
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/If.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_IF_H__
+#define __OP_IF_H__
+
+#include "OpChef.h"
+
+class IfChef final : public OpChef
+{
+public:
+ explicit IfChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_IF; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_IfOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct IfChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_IF_H__
diff --git a/compiler/tflchef/core/src/Op/L2Normalize.cpp b/compiler/tflchef/core/src/Op/L2Normalize.cpp
new file mode 100644
index 000000000..62d15e56e
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/L2Normalize.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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 "L2Normalize.h"
+#include "Convert.h"
+
+flatbuffers::Offset<void> L2Normalize::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ tflite::L2NormOptionsBuilder l2norm_options_builder{fbb};
+ auto tflite_activation = as_tflite_activation(operation.l2norm_options().activation());
+ l2norm_options_builder.add_fused_activation_function(tflite_activation);
+ return l2norm_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> L2NormalizeChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new L2Normalize{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/L2Normalize.h b/compiler/tflchef/core/src/Op/L2Normalize.h
new file mode 100644
index 000000000..dd5f21cae
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/L2Normalize.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_L2NORMALIZE_H__
+#define __OP_L2NORMALIZE_H__
+
+#include "OpChef.h"
+
+class L2Normalize final : public OpChef
+{
+public:
+ explicit L2Normalize(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_L2_NORMALIZATION;
+ }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_L2NormOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct L2NormalizeChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_L2NORMALIZE_H__
diff --git a/compiler/tflchef/core/src/Op/L2Pool2D.cpp b/compiler/tflchef/core/src/Op/L2Pool2D.cpp
new file mode 100644
index 000000000..f22bb9642
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/L2Pool2D.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "L2Pool2D.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> L2Pool2DChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_l2pool2d_options());
+
+ auto options = operation.l2pool2d_options();
+
+ auto tflite_padding = as_tflite_padding(options.padding());
+ auto tflite_activation = as_tflite_activation(options.activation());
+
+ tflite::Pool2DOptionsBuilder options_builder{fbb};
+ options_builder.add_padding(tflite_padding);
+ options_builder.add_stride_h(options.stride_h());
+ options_builder.add_stride_w(options.stride_w());
+ options_builder.add_filter_width(options.filter_width());
+ options_builder.add_filter_height(options.filter_height());
+ options_builder.add_fused_activation_function(tflite_activation);
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> L2Pool2DChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new L2Pool2DChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/L2Pool2D.h b/compiler/tflchef/core/src/Op/L2Pool2D.h
new file mode 100644
index 000000000..6bd8bdb4d
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/L2Pool2D.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_L2_POOL_2D_H__
+#define __OP_L2_POOL_2D_H__
+
+#include "OpChef.h"
+
+class L2Pool2DChef final : public OpChef
+{
+public:
+ explicit L2Pool2DChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_L2_POOL_2D; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_Pool2DOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct L2Pool2DChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_L2_POOL_2D_H__
diff --git a/compiler/tflchef/core/src/Op/LeakyRelu.cpp b/compiler/tflchef/core/src/Op/LeakyRelu.cpp
new file mode 100644
index 000000000..247739ac0
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/LeakyRelu.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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 "LeakyRelu.h"
+
+flatbuffers::Offset<void> LeakyReluChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ assert(_operation->has_leaky_relu_options());
+
+ const auto &options = _operation->leaky_relu_options();
+
+ tflite::LeakyReluOptionsBuilder options_builder{fbb};
+ options_builder.add_alpha(options.alpha());
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> LeakyReluChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new LeakyReluChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/LeakyRelu.h b/compiler/tflchef/core/src/Op/LeakyRelu.h
new file mode 100644
index 000000000..5449e593b
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/LeakyRelu.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_LEAKY_RELU_H__
+#define __OP_LEAKY_RELU_H__
+
+#include "OpChef.h"
+
+class LeakyReluChef final : public OpChef
+{
+public:
+ explicit LeakyReluChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_LEAKY_RELU; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_LeakyReluOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct LeakyReluChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_LEAKY_RELU_H__
diff --git a/compiler/tflchef/core/src/Op/Less.cpp b/compiler/tflchef/core/src/Op/Less.cpp
new file mode 100644
index 000000000..c143d8332
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Less.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "Less.h"
+
+flatbuffers::Offset<void> LessChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::LessOptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> LessChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new LessChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Less.h b/compiler/tflchef/core/src/Op/Less.h
new file mode 100644
index 000000000..280036c3a
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Less.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_LESS_H__
+#define __OP_LESS_H__
+
+#include "OpChef.h"
+
+class LessChef final : public OpChef
+{
+public:
+ explicit LessChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_LESS; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_LessOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct LessChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_LESS_H__
diff --git a/compiler/tflchef/core/src/Op/LessEqual.cpp b/compiler/tflchef/core/src/Op/LessEqual.cpp
new file mode 100644
index 000000000..dc383e785
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/LessEqual.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "LessEqual.h"
+
+flatbuffers::Offset<void> LessEqualChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::LessEqualOptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> LessEqualChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new LessEqualChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/LessEqual.h b/compiler/tflchef/core/src/Op/LessEqual.h
new file mode 100644
index 000000000..1315b9c53
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/LessEqual.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_LESSEQUAL_H__
+#define __OP_LESSEQUAL_H__
+
+#include "OpChef.h"
+
+class LessEqualChef final : public OpChef
+{
+public:
+ explicit LessEqualChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_LESS_EQUAL; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_LessEqualOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct LessEqualChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_LESSEQUAL_H__
diff --git a/compiler/tflchef/core/src/Op/LocalResponseNormalization.cpp b/compiler/tflchef/core/src/Op/LocalResponseNormalization.cpp
new file mode 100644
index 000000000..f5430d4ca
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/LocalResponseNormalization.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LocalResponseNormalization.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void>
+LocalResponseNormalizationChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_local_response_normalization_options());
+
+ auto &lrn_options = operation.local_response_normalization_options();
+
+ auto tflite_radius = lrn_options.radius();
+ auto tflite_bias = lrn_options.bias();
+ auto tflite_alpha = lrn_options.alpha();
+ auto tflite_beta = lrn_options.beta();
+
+ tflite::LocalResponseNormalizationOptionsBuilder lrn_options_builder{fbb};
+
+ lrn_options_builder.add_radius(tflite_radius);
+ lrn_options_builder.add_bias(tflite_bias);
+ lrn_options_builder.add_alpha(tflite_alpha);
+ lrn_options_builder.add_beta(tflite_beta);
+
+ return lrn_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef>
+LocalResponseNormalizationChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new LocalResponseNormalizationChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/LocalResponseNormalization.h b/compiler/tflchef/core/src/Op/LocalResponseNormalization.h
new file mode 100644
index 000000000..62a2355f2
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/LocalResponseNormalization.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_LOCAL_RESPONSE_NORMALIZATION_H__
+#define __OP_LOCAL_RESPONSE_NORMALIZATION_H__
+
+#include "OpChef.h"
+
+class LocalResponseNormalizationChef final : public OpChef
+{
+public:
+ explicit LocalResponseNormalizationChef(const tflchef::Operation *operation)
+ : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_LOCAL_RESPONSE_NORMALIZATION;
+ }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_LocalResponseNormalizationOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct LocalResponseNormalizationChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_LOCAL_RESPONSE_NORMALIZATION_H__
diff --git a/compiler/tflchef/core/src/Op/Log.cpp b/compiler/tflchef/core/src/Op/Log.cpp
new file mode 100644
index 000000000..c4e65adec
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Log.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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 "Log.h"
+
+flatbuffers::Offset<void> LogChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ // No tflite option for Log. Use void.
+ return flatbuffers::Offset<void>();
+}
+
+std::unique_ptr<OpChef> LogChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new LogChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Log.h b/compiler/tflchef/core/src/Op/Log.h
new file mode 100644
index 000000000..2cc8a663b
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Log.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_LOG_H__
+#define __OP_LOG_H__
+
+#include "OpChef.h"
+
+class LogChef final : public OpChef
+{
+public:
+ explicit LogChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_LOG; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_NONE; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct LogChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_LOG_H__
diff --git a/compiler/tflchef/core/src/Op/LogSoftmax.cpp b/compiler/tflchef/core/src/Op/LogSoftmax.cpp
new file mode 100644
index 000000000..eb2f13243
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/LogSoftmax.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LogSoftmax.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> LogSoftmaxChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::LogSoftmaxOptionsBuilder soft_options_builder{fbb};
+
+ return soft_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> LogSoftmaxChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new LogSoftmaxChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/LogSoftmax.h b/compiler/tflchef/core/src/Op/LogSoftmax.h
new file mode 100644
index 000000000..3ce08b739
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/LogSoftmax.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_LOG_SOFTMAX_H__
+#define __OP_LOG_SOFTMAX_H__
+
+#include "OpChef.h"
+
+class LogSoftmaxChef final : public OpChef
+{
+public:
+ explicit LogSoftmaxChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_LOG_SOFTMAX; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_LogSoftmaxOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct LogSoftmaxChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_LOG_SOFTMAX_H__
diff --git a/compiler/tflchef/core/src/Op/LogicalAnd.cpp b/compiler/tflchef/core/src/Op/LogicalAnd.cpp
new file mode 100644
index 000000000..64a6113db
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/LogicalAnd.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "LogicalAnd.h"
+
+flatbuffers::Offset<void> LogicalAndChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::LogicalAndOptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> LogicalAndChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new LogicalAndChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/LogicalAnd.h b/compiler/tflchef/core/src/Op/LogicalAnd.h
new file mode 100644
index 000000000..1f272274f
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/LogicalAnd.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_LOGICALAND_H__
+#define __OP_LOGICALAND_H__
+
+#include "OpChef.h"
+
+class LogicalAndChef final : public OpChef
+{
+public:
+ explicit LogicalAndChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_LOGICAL_AND; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_LogicalAndOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct LogicalAndChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_LOGICALAND_H__
diff --git a/compiler/tflchef/core/src/Op/LogicalNot.cpp b/compiler/tflchef/core/src/Op/LogicalNot.cpp
new file mode 100644
index 000000000..26cdef308
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/LogicalNot.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "LogicalNot.h"
+
+flatbuffers::Offset<void> LogicalNotChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::LogicalNotOptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> LogicalNotChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new LogicalNotChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/LogicalNot.h b/compiler/tflchef/core/src/Op/LogicalNot.h
new file mode 100644
index 000000000..d2ca21b93
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/LogicalNot.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_LOGICALNOT_H__
+#define __OP_LOGICALNOT_H__
+
+#include "OpChef.h"
+
+class LogicalNotChef final : public OpChef
+{
+public:
+ explicit LogicalNotChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_LOGICAL_NOT; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_LogicalNotOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct LogicalNotChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_LOGICALNOT_H__
diff --git a/compiler/tflchef/core/src/Op/LogicalOr.cpp b/compiler/tflchef/core/src/Op/LogicalOr.cpp
new file mode 100644
index 000000000..483373a81
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/LogicalOr.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "LogicalOr.h"
+
+flatbuffers::Offset<void> LogicalOrChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::LogicalOrOptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> LogicalOrChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new LogicalOrChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/LogicalOr.h b/compiler/tflchef/core/src/Op/LogicalOr.h
new file mode 100644
index 000000000..b84c9a6ab
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/LogicalOr.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_LOGICALOR_H__
+#define __OP_LOGICALOR_H__
+
+#include "OpChef.h"
+
+class LogicalOrChef final : public OpChef
+{
+public:
+ explicit LogicalOrChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_LOGICAL_OR; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_LogicalOrOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct LogicalOrChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_LOGICALOR_H__
diff --git a/compiler/tflchef/core/src/Op/Logistic.cpp b/compiler/tflchef/core/src/Op/Logistic.cpp
new file mode 100644
index 000000000..4a5808235
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Logistic.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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 "Logistic.h"
+
+flatbuffers::Offset<void> LogisticChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ // No tflite option for Logistic. Use void.
+ return flatbuffers::Offset<void>();
+}
+
+std::unique_ptr<OpChef> LogisticChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new LogisticChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Logistic.h b/compiler/tflchef/core/src/Op/Logistic.h
new file mode 100644
index 000000000..c158af34d
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Logistic.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_LOGISTIC_H__
+#define __OP_LOGISTIC_H__
+
+#include "OpChef.h"
+
+class LogisticChef final : public OpChef
+{
+public:
+ explicit LogisticChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_LOGISTIC; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_NONE; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct LogisticChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_LOGISTIC_H__
diff --git a/compiler/tflchef/core/src/Op/MatrixDiag.cpp b/compiler/tflchef/core/src/Op/MatrixDiag.cpp
new file mode 100644
index 000000000..de505c056
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/MatrixDiag.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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 "MatrixDiag.h"
+
+flatbuffers::Offset<void> MatrixDiagChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::MatrixDiagOptionsBuilder options_builder{fbb};
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> MatrixDiagChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new MatrixDiagChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/MatrixDiag.h b/compiler/tflchef/core/src/Op/MatrixDiag.h
new file mode 100644
index 000000000..cbadf6b99
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/MatrixDiag.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_MATRIX_DIAG_H__
+#define __OP_MATRIX_DIAG_H__
+
+#include "OpChef.h"
+
+class MatrixDiagChef final : public OpChef
+{
+public:
+ explicit MatrixDiagChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_MATRIX_DIAG; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_MatrixDiagOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct MatrixDiagChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_MATRIX_DIAG_H__
diff --git a/compiler/tflchef/core/src/Op/MatrixSetDiag.cpp b/compiler/tflchef/core/src/Op/MatrixSetDiag.cpp
new file mode 100644
index 000000000..0a4ee71c9
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/MatrixSetDiag.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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 "MatrixSetDiag.h"
+
+flatbuffers::Offset<void> MatrixSetDiagChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::MatrixSetDiagOptionsBuilder options_builder{fbb};
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> MatrixSetDiagChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new MatrixSetDiagChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/MatrixSetDiag.h b/compiler/tflchef/core/src/Op/MatrixSetDiag.h
new file mode 100644
index 000000000..8114d32a8
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/MatrixSetDiag.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_MATRIX_SET_DIAG_H__
+#define __OP_MATRIX_SET_DIAG_H__
+
+#include "OpChef.h"
+
+class MatrixSetDiagChef final : public OpChef
+{
+public:
+ explicit MatrixSetDiagChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_MATRIX_SET_DIAG;
+ }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_MatrixSetDiagOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct MatrixSetDiagChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_MATRIX_SET_DIAG_H__
diff --git a/compiler/tflchef/core/src/Op/Maximum.cpp b/compiler/tflchef/core/src/Op/Maximum.cpp
new file mode 100644
index 000000000..8f415e2e4
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Maximum.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "Maximum.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> MaximumChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::MaximumMinimumOptionsBuilder maximum_options_builder{fbb};
+ return maximum_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> MaximumChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new MaximumChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Maximum.h b/compiler/tflchef/core/src/Op/Maximum.h
new file mode 100644
index 000000000..53e95240c
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Maximum.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_MAXIMUM_H__
+#define __OP_MAXIMUM_H__
+
+#include "OpChef.h"
+
+class MaximumChef final : public OpChef
+{
+public:
+ explicit MaximumChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_MAXIMUM; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_MaximumMinimumOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct MaximumChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_MAXIMUM_H__
diff --git a/compiler/tflchef/core/src/Op/Mean.cpp b/compiler/tflchef/core/src/Op/Mean.cpp
new file mode 100644
index 000000000..def8f7b3b
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Mean.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Mean.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> MeanChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_mean_options());
+
+ auto keep_dims = operation.mean_options().keep_dims();
+
+ tflite::ReducerOptionsBuilder mean_options_builder{fbb};
+ mean_options_builder.add_keep_dims(keep_dims);
+
+ return mean_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> MeanChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new MeanChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Mean.h b/compiler/tflchef/core/src/Op/Mean.h
new file mode 100644
index 000000000..9032aef3f
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Mean.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_MEAN_H__
+#define __OP_MEAN_H__
+
+#include "OpChef.h"
+
+class MeanChef final : public OpChef
+{
+public:
+ explicit MeanChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_MEAN; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_ReducerOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct MeanChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_MEAN_H__
diff --git a/compiler/tflchef/core/src/Op/Minimum.cpp b/compiler/tflchef/core/src/Op/Minimum.cpp
new file mode 100644
index 000000000..cc0c91901
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Minimum.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "Minimum.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> MinimumChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::MaximumMinimumOptionsBuilder minimum_options_builder{fbb};
+ return minimum_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> MinimumChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new MinimumChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Minimum.h b/compiler/tflchef/core/src/Op/Minimum.h
new file mode 100644
index 000000000..3990e1eca
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Minimum.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_MINIMUM_H__
+#define __OP_MINIMUM_H__
+
+#include "OpChef.h"
+
+class MinimumChef final : public OpChef
+{
+public:
+ explicit MinimumChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_MINIMUM; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_MaximumMinimumOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct MinimumChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_MINIMUM_H__
diff --git a/compiler/tflchef/core/src/Op/MirrorPad.cpp b/compiler/tflchef/core/src/Op/MirrorPad.cpp
new file mode 100644
index 000000000..2d68b6986
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/MirrorPad.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MirrorPad.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> MirrorPadChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_mirrorpad_options());
+
+ auto options = operation.mirrorpad_options();
+
+ auto tflite_mode = as_tflite_mirrorpadmode(options.mode());
+
+ tflite::MirrorPadOptionsBuilder options_builder{fbb};
+ options_builder.add_mode(tflite_mode);
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> MirrorPadChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new MirrorPadChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/MirrorPad.h b/compiler/tflchef/core/src/Op/MirrorPad.h
new file mode 100644
index 000000000..49461df35
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/MirrorPad.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_MIRRORPAD_H__
+#define __OP_MIRRORPAD_H__
+
+#include "OpChef.h"
+
+class MirrorPadChef final : public OpChef
+{
+public:
+ explicit MirrorPadChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_MIRROR_PAD; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_MirrorPadOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct MirrorPadChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_MIRRORPAD_H__
diff --git a/compiler/tflchef/core/src/Op/Mul.cpp b/compiler/tflchef/core/src/Op/Mul.cpp
new file mode 100644
index 000000000..10ec918c2
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Mul.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Mul.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> MulChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_mul_options());
+
+ auto tflite_activation = as_tflite_activation(operation.mul_options().activation());
+
+ tflite::MulOptionsBuilder mul_options_builder{fbb};
+ mul_options_builder.add_fused_activation_function(tflite_activation);
+
+ return mul_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> MulChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new MulChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Mul.h b/compiler/tflchef/core/src/Op/Mul.h
new file mode 100644
index 000000000..7f1d07ac9
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Mul.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_MUL_H__
+#define __OP_MUL_H__
+
+#include "OpChef.h"
+
+class MulChef final : public OpChef
+{
+public:
+ explicit MulChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_MUL; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_MulOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct MulChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_MUL_H__
diff --git a/compiler/tflchef/core/src/Op/Neg.cpp b/compiler/tflchef/core/src/Op/Neg.cpp
new file mode 100644
index 000000000..0e9fb9321
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Neg.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 "Neg.h"
+#include "Convert.h"
+
+flatbuffers::Offset<void> NegChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::NegOptionsBuilder neg_options_builder{fbb};
+
+ return neg_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> NegChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new NegChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Neg.h b/compiler/tflchef/core/src/Op/Neg.h
new file mode 100644
index 000000000..f7a2692c9
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Neg.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_NEG_H__
+#define __OP_NEG_H__
+
+#include "OpChef.h"
+
+class NegChef final : public OpChef
+{
+public:
+ explicit NegChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_NEG; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_NegOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct NegChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_NEG_H__
diff --git a/compiler/tflchef/core/src/Op/NonMaxSuppressionV4.cpp b/compiler/tflchef/core/src/Op/NonMaxSuppressionV4.cpp
new file mode 100644
index 000000000..eadd62cc6
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/NonMaxSuppressionV4.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 "NonMaxSuppressionV4.h"
+
+flatbuffers::Offset<void> NonMaxSuppressionV4Chef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::NonMaxSuppressionV4OptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef>
+NonMaxSuppressionV4ChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new NonMaxSuppressionV4Chef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/NonMaxSuppressionV4.h b/compiler/tflchef/core/src/Op/NonMaxSuppressionV4.h
new file mode 100644
index 000000000..a8e783d53
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/NonMaxSuppressionV4.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_NON_MAX_SUPPRESSION_V4_H__
+#define __OP_NON_MAX_SUPPRESSION_V4_H__
+
+#include "OpChef.h"
+
+class NonMaxSuppressionV4Chef final : public OpChef
+{
+public:
+ explicit NonMaxSuppressionV4Chef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_NON_MAX_SUPPRESSION_V4;
+ }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_NonMaxSuppressionV4Options;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct NonMaxSuppressionV4ChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_NON_MAX_SUPPRESSION_V4_H__
diff --git a/compiler/tflchef/core/src/Op/NonMaxSuppressionV5.cpp b/compiler/tflchef/core/src/Op/NonMaxSuppressionV5.cpp
new file mode 100644
index 000000000..500aa467f
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/NonMaxSuppressionV5.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 "NonMaxSuppressionV5.h"
+
+flatbuffers::Offset<void> NonMaxSuppressionV5Chef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::NonMaxSuppressionV5OptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef>
+NonMaxSuppressionV5ChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new NonMaxSuppressionV5Chef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/NonMaxSuppressionV5.h b/compiler/tflchef/core/src/Op/NonMaxSuppressionV5.h
new file mode 100644
index 000000000..a3c8b6009
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/NonMaxSuppressionV5.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_NON_MAX_SUPPRESSION_V5_H__
+#define __OP_NON_MAX_SUPPRESSION_V5_H__
+
+#include "OpChef.h"
+
+class NonMaxSuppressionV5Chef final : public OpChef
+{
+public:
+ explicit NonMaxSuppressionV5Chef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_NON_MAX_SUPPRESSION_V5;
+ }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_NonMaxSuppressionV5Options;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct NonMaxSuppressionV5ChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_NON_MAX_SUPPRESSION_V5_H__
diff --git a/compiler/tflchef/core/src/Op/NotEqual.cpp b/compiler/tflchef/core/src/Op/NotEqual.cpp
new file mode 100644
index 000000000..a408266f9
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/NotEqual.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "NotEqual.h"
+
+flatbuffers::Offset<void> NotEqualChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::NotEqualOptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> NotEqualChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new NotEqualChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/NotEqual.h b/compiler/tflchef/core/src/Op/NotEqual.h
new file mode 100644
index 000000000..3fd254773
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/NotEqual.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_NOT_EQUAL_H__
+#define __OP_NOT_EQUAL_H__
+
+#include "OpChef.h"
+
+class NotEqualChef final : public OpChef
+{
+public:
+ explicit NotEqualChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_NOT_EQUAL; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_NotEqualOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct NotEqualChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_NOT_EQUAL_H__
diff --git a/compiler/tflchef/core/src/Op/OneHot.cpp b/compiler/tflchef/core/src/Op/OneHot.cpp
new file mode 100644
index 000000000..421e50c9f
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/OneHot.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "OneHot.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> OneHotChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_onehot_options());
+
+ auto options = operation.onehot_options();
+
+ tflite::OneHotOptionsBuilder options_builder{fbb};
+
+ options_builder.add_axis(options.axis());
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> OneHotChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new OneHotChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/OneHot.h b/compiler/tflchef/core/src/Op/OneHot.h
new file mode 100644
index 000000000..b29cb7978
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/OneHot.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_ONEHOT_H__
+#define __OP_ONEHOT_H__
+
+#include "OpChef.h"
+
+class OneHotChef final : public OpChef
+{
+public:
+ explicit OneHotChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_ONE_HOT; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_OneHotOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct OneHotChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_ONEHOT_H__
diff --git a/compiler/tflchef/core/src/Op/PRelu.cpp b/compiler/tflchef/core/src/Op/PRelu.cpp
new file mode 100644
index 000000000..30e8b8ef4
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/PRelu.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "PRelu.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> PReluChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ // No option for PRelu
+ return flatbuffers::Offset<void>();
+}
+
+std::unique_ptr<OpChef> PReluChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new PReluChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/PRelu.h b/compiler/tflchef/core/src/Op/PRelu.h
new file mode 100644
index 000000000..4a5a935ed
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/PRelu.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_PRELU_H__
+#define __OP_PRELU_H__
+
+#include "OpChef.h"
+
+class PReluChef final : public OpChef
+{
+public:
+ explicit PReluChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_PRELU; }
+
+ // no builtin options for PRelu
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_NONE; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct PReluChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_PRELU_H__
diff --git a/compiler/tflchef/core/src/Op/Pack.cpp b/compiler/tflchef/core/src/Op/Pack.cpp
new file mode 100644
index 000000000..2532ac744
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Pack.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Pack.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> PackChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_pack_options());
+
+ tflite::PackOptionsBuilder pack_options_builder{fbb};
+ pack_options_builder.add_values_count(operation.pack_options().values_count());
+ pack_options_builder.add_axis(operation.pack_options().axis());
+
+ return pack_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> PackChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new PackChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Pack.h b/compiler/tflchef/core/src/Op/Pack.h
new file mode 100644
index 000000000..54bdc9338
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Pack.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_PACK_H__
+#define __OP_PACK_H__
+
+#include "OpChef.h"
+
+class PackChef final : public OpChef
+{
+public:
+ explicit PackChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_PACK; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_PackOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct PackChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_PACK_H__
diff --git a/compiler/tflchef/core/src/Op/Pad.cpp b/compiler/tflchef/core/src/Op/Pad.cpp
new file mode 100644
index 000000000..d0c471981
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Pad.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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 "Pad.h"
+
+flatbuffers::Offset<void> PadChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::PadOptionsBuilder pad_options_builder{fbb};
+ return pad_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> PadChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new PadChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Pad.h b/compiler/tflchef/core/src/Op/Pad.h
new file mode 100644
index 000000000..9da9c9b8a
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Pad.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_PAD_H__
+#define __OP_PAD_H__
+
+#include "OpChef.h"
+
+class PadChef final : public OpChef
+{
+public:
+ explicit PadChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_PAD; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_PadOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct PadChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_PAD_H__
diff --git a/compiler/tflchef/core/src/Op/PadV2.cpp b/compiler/tflchef/core/src/Op/PadV2.cpp
new file mode 100644
index 000000000..bfa2289e5
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/PadV2.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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 "PadV2.h"
+
+flatbuffers::Offset<void> PadV2Chef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::PadV2OptionsBuilder padv2_options_builder{fbb};
+ return padv2_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> PadV2ChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new PadV2Chef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/PadV2.h b/compiler/tflchef/core/src/Op/PadV2.h
new file mode 100644
index 000000000..d15532390
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/PadV2.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_PADV2_H__
+#define __OP_PADV2_H__
+
+#include "OpChef.h"
+
+class PadV2Chef final : public OpChef
+{
+public:
+ explicit PadV2Chef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_PADV2; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_PadV2Options; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct PadV2ChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_PADV2_H__
diff --git a/compiler/tflchef/core/src/Op/Pow.cpp b/compiler/tflchef/core/src/Op/Pow.cpp
new file mode 100644
index 000000000..25e180237
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Pow.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "Pow.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> PowChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::PowOptionsBuilder options_builder{fbb};
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> PowChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new PowChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Pow.h b/compiler/tflchef/core/src/Op/Pow.h
new file mode 100644
index 000000000..f2d809e92
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Pow.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_POW_H__
+#define __OP_POW_H__
+
+#include "OpChef.h"
+
+class PowChef final : public OpChef
+{
+public:
+ explicit PowChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_POW; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_PowOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct PowChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_POW_H__
diff --git a/compiler/tflchef/core/src/Op/Range.cpp b/compiler/tflchef/core/src/Op/Range.cpp
new file mode 100644
index 000000000..189c46526
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Range.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "Range.h"
+
+#include <cassert>
+#include <vector>
+
+flatbuffers::Offset<void> RangeChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::RangeOptionsBuilder options_builder{fbb};
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> RangeChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new RangeChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Range.h b/compiler/tflchef/core/src/Op/Range.h
new file mode 100644
index 000000000..f294d15a7
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Range.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_RANGE_H__
+#define __OP_RANGE_H__
+
+#include "OpChef.h"
+
+class RangeChef final : public OpChef
+{
+public:
+ explicit RangeChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_RANGE; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_RangeOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct RangeChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_RANGE_H__
diff --git a/compiler/tflchef/core/src/Op/Rank.cpp b/compiler/tflchef/core/src/Op/Rank.cpp
new file mode 100644
index 000000000..4eb2aa776
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Rank.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "Rank.h"
+
+flatbuffers::Offset<void> RankChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::RankOptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> RankChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new RankChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Rank.h b/compiler/tflchef/core/src/Op/Rank.h
new file mode 100644
index 000000000..0bce38095
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Rank.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_RANK_H__
+#define __OP_RANK_H__
+
+#include "OpChef.h"
+
+class RankChef final : public OpChef
+{
+public:
+ explicit RankChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_RANK; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_RankOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct RankChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_RANK_H__
diff --git a/compiler/tflchef/core/src/Op/ReLUN1To1.cpp b/compiler/tflchef/core/src/Op/ReLUN1To1.cpp
new file mode 100644
index 000000000..d57e82341
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ReLUN1To1.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReLUN1To1.h"
+
+flatbuffers::Offset<void> ReLUN1To1Chef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ return flatbuffers::Offset<void>();
+}
+
+std::unique_ptr<OpChef> ReLUN1To1ChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new ReLUN1To1Chef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/ReLUN1To1.h b/compiler/tflchef/core/src/Op/ReLUN1To1.h
new file mode 100644
index 000000000..e034c7999
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ReLUN1To1.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_RELU_N1_TO_1_H__
+#define __OP_RELU_N1_TO_1_H__
+
+#include "OpChef.h"
+
+class ReLUN1To1Chef final : public OpChef
+{
+public:
+ explicit ReLUN1To1Chef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_RELU_N1_TO_1; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_NONE; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct ReLUN1To1ChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_RELU_N1_TO_1_H__
diff --git a/compiler/tflchef/core/src/Op/ReduceAny.cpp b/compiler/tflchef/core/src/Op/ReduceAny.cpp
new file mode 100644
index 000000000..c94c8a3a4
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ReduceAny.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReduceAny.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> ReduceAnyChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_reduce_any_options());
+
+ auto keep_dims = operation.reduce_any_options().keep_dims();
+
+ tflite::ReducerOptionsBuilder reducer_options_builder{fbb};
+ reducer_options_builder.add_keep_dims(keep_dims);
+
+ return reducer_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> ReduceAnyChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new ReduceAnyChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/ReduceAny.h b/compiler/tflchef/core/src/Op/ReduceAny.h
new file mode 100644
index 000000000..cf6531732
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ReduceAny.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_REDUCE_ANY_H__
+#define __OP_REDUCE_ANY_H__
+
+#include "OpChef.h"
+
+class ReduceAnyChef final : public OpChef
+{
+public:
+ explicit ReduceAnyChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_REDUCE_ANY; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_ReducerOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct ReduceAnyChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_REDUCE_ANY_H__
diff --git a/compiler/tflchef/core/src/Op/ReduceMax.cpp b/compiler/tflchef/core/src/Op/ReduceMax.cpp
new file mode 100644
index 000000000..31543cdc0
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ReduceMax.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReduceMax.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> ReduceMaxChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_reduce_max_options());
+
+ auto keep_dims = operation.reduce_max_options().keep_dims();
+
+ tflite::ReducerOptionsBuilder reduce_max_options_builder{fbb};
+ reduce_max_options_builder.add_keep_dims(keep_dims);
+
+ return reduce_max_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> ReduceMaxChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new ReduceMaxChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/ReduceMax.h b/compiler/tflchef/core/src/Op/ReduceMax.h
new file mode 100644
index 000000000..854c5b87d
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ReduceMax.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_REDUCEMAX_H__
+#define __OP_REDUCEMAX_H__
+
+#include "OpChef.h"
+
+class ReduceMaxChef final : public OpChef
+{
+public:
+ explicit ReduceMaxChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_REDUCE_MAX; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_ReducerOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct ReduceMaxChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_REDUCEMAX_H__
diff --git a/compiler/tflchef/core/src/Op/ReduceMin.cpp b/compiler/tflchef/core/src/Op/ReduceMin.cpp
new file mode 100644
index 000000000..e194a1837
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ReduceMin.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReduceMin.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> ReduceMinChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_reduce_min_options());
+
+ auto keep_dims = operation.reduce_min_options().keep_dims();
+
+ tflite::ReducerOptionsBuilder reduce_min_options_builder{fbb};
+ reduce_min_options_builder.add_keep_dims(keep_dims);
+
+ return reduce_min_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> ReduceMinChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new ReduceMinChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/ReduceMin.h b/compiler/tflchef/core/src/Op/ReduceMin.h
new file mode 100644
index 000000000..f29d273b9
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ReduceMin.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_REDUCEMIN_H__
+#define __OP_REDUCEMIN_H__
+
+#include "OpChef.h"
+
+class ReduceMinChef final : public OpChef
+{
+public:
+ explicit ReduceMinChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_REDUCE_MIN; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_ReducerOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct ReduceMinChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_REDUCEMIN_H__
diff --git a/compiler/tflchef/core/src/Op/ReduceProd.cpp b/compiler/tflchef/core/src/Op/ReduceProd.cpp
new file mode 100644
index 000000000..c89aca27e
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ReduceProd.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReduceProd.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> ReduceProdChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_reduce_prod_options());
+
+ auto keep_dims = operation.reduce_prod_options().keep_dims();
+
+ tflite::ReducerOptionsBuilder reducer_options_builder{fbb};
+ reducer_options_builder.add_keep_dims(keep_dims);
+
+ return reducer_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> ReduceProdChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new ReduceProdChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/ReduceProd.h b/compiler/tflchef/core/src/Op/ReduceProd.h
new file mode 100644
index 000000000..d5a11fdbc
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ReduceProd.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_REDUCE_PROD_H__
+#define __OP_REDUCE_PROD_H__
+
+#include "OpChef.h"
+
+class ReduceProdChef final : public OpChef
+{
+public:
+ explicit ReduceProdChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_REDUCE_PROD; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_ReducerOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct ReduceProdChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_REDUCE_PROD_H__
diff --git a/compiler/tflchef/core/src/Op/Reshape.cpp b/compiler/tflchef/core/src/Op/Reshape.cpp
index 99555e898..01e521913 100644
--- a/compiler/tflchef/core/src/Op/Reshape.cpp
+++ b/compiler/tflchef/core/src/Op/Reshape.cpp
@@ -41,7 +41,8 @@ flatbuffers::Offset<void> ReshapeChef::value(flatbuffers::FlatBufferBuilder &fbb
{
auto &operation = (*_operation);
- assert(operation.has_reshape_options());
+ if (!operation.has_reshape_options())
+ return 0;
auto options = operation.reshape_options();
auto shapes = vector_new_shape(options);
diff --git a/compiler/tflchef/core/src/Op/ResizeBilinear.cpp b/compiler/tflchef/core/src/Op/ResizeBilinear.cpp
new file mode 100644
index 000000000..3d9299ce0
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ResizeBilinear.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "ResizeBilinear.h"
+#include "Convert.h"
+
+#include <cassert>
+#include <vector>
+
+flatbuffers::Offset<void> ResizeBilinearChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_resize_bilinear_options());
+
+ auto options = operation.resize_bilinear_options();
+
+ tflite::ResizeBilinearOptionsBuilder options_builder{fbb};
+
+ options_builder.add_align_corners(options.align_corners());
+ options_builder.add_half_pixel_centers(options.half_pixel_centers());
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> ResizeBilinearChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new ResizeBilinearChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/ResizeBilinear.h b/compiler/tflchef/core/src/Op/ResizeBilinear.h
new file mode 100644
index 000000000..9bd618538
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ResizeBilinear.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_RESIZE_BILINEAR_H__
+#define __OP_RESIZE_BILINEAR_H__
+
+#include "OpChef.h"
+
+class ResizeBilinearChef final : public OpChef
+{
+public:
+ explicit ResizeBilinearChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_RESIZE_BILINEAR;
+ }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_ResizeBilinearOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct ResizeBilinearChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_RESIZE_BILINEAR_H__
diff --git a/compiler/tflchef/core/src/Op/ResizeNearestNeighbor.cpp b/compiler/tflchef/core/src/Op/ResizeNearestNeighbor.cpp
new file mode 100644
index 000000000..1f930404f
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ResizeNearestNeighbor.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ResizeNearestNeighbor.h"
+#include "Convert.h"
+
+#include <cassert>
+#include <vector>
+
+flatbuffers::Offset<void>
+ResizeNearestNeighborChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_resize_nearest_neighbor_options());
+
+ auto options = operation.resize_nearest_neighbor_options();
+
+ tflite::ResizeNearestNeighborOptionsBuilder options_builder{fbb};
+
+ options_builder.add_align_corners(options.align_corners());
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef>
+ResizeNearestNeighborChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new ResizeNearestNeighborChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/ResizeNearestNeighbor.h b/compiler/tflchef/core/src/Op/ResizeNearestNeighbor.h
new file mode 100644
index 000000000..e6ee832a8
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ResizeNearestNeighbor.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_RESIZE_NEAREST_NEIGHBOR_H__
+#define __OP_RESIZE_NEAREST_NEIGHBOR_H__
+
+#include "OpChef.h"
+
+class ResizeNearestNeighborChef final : public OpChef
+{
+public:
+ explicit ResizeNearestNeighborChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR;
+ }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_ResizeNearestNeighborOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct ResizeNearestNeighborChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_RESIZE_NEAREST_NEIGHBOR_H__
diff --git a/compiler/tflchef/core/src/Op/ReverseSequence.cpp b/compiler/tflchef/core/src/Op/ReverseSequence.cpp
new file mode 100644
index 000000000..93541172b
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ReverseSequence.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "ReverseSequence.h"
+
+flatbuffers::Offset<void> ReverseSequenceChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_reverse_sequence_options());
+
+ auto options = operation.reverse_sequence_options();
+
+ auto tflite_seq_dim = options.seq_dim();
+ auto tflite_batch_dim = options.batch_dim();
+
+ tflite::ReverseSequenceOptionsBuilder options_builder{fbb};
+
+ options_builder.add_seq_dim(tflite_seq_dim);
+ options_builder.add_batch_dim(tflite_batch_dim);
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef>
+ReverseSequenceChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new ReverseSequenceChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/ReverseSequence.h b/compiler/tflchef/core/src/Op/ReverseSequence.h
new file mode 100644
index 000000000..329505cf0
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ReverseSequence.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_REVERSE_SEQUENCE_H__
+#define __OP_REVERSE_SEQUENCE_H__
+
+#include "OpChef.h"
+
+class ReverseSequenceChef final : public OpChef
+{
+public:
+ explicit ReverseSequenceChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_REVERSE_SEQUENCE;
+ }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_ReverseSequenceOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct ReverseSequenceChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_REVERSE_SEQUENCE_H__
diff --git a/compiler/tflchef/core/src/Op/ReverseV2.cpp b/compiler/tflchef/core/src/Op/ReverseV2.cpp
new file mode 100644
index 000000000..58ace1dd1
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ReverseV2.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "ReverseV2.h"
+
+flatbuffers::Offset<void> ReverseV2Chef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::ReverseV2OptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> ReverseV2ChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new ReverseV2Chef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/ReverseV2.h b/compiler/tflchef/core/src/Op/ReverseV2.h
new file mode 100644
index 000000000..a48a2d96a
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ReverseV2.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_REVERSEV2_H__
+#define __OP_REVERSEV2_H__
+
+#include "OpChef.h"
+
+class ReverseV2Chef final : public OpChef
+{
+public:
+ explicit ReverseV2Chef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_REVERSE_V2; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_ReverseV2Options;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct ReverseV2ChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_REVERSEV2_H__
diff --git a/compiler/tflchef/core/src/Op/Round.cpp b/compiler/tflchef/core/src/Op/Round.cpp
new file mode 100644
index 000000000..e16c86518
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Round.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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 "Round.h"
+
+flatbuffers::Offset<void> RoundChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ // No tflite option for Round. Use void.
+ return flatbuffers::Offset<void>();
+}
+
+std::unique_ptr<OpChef> RoundChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new RoundChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Round.h b/compiler/tflchef/core/src/Op/Round.h
new file mode 100644
index 000000000..7f0fbe370
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Round.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_ROUND_H__
+#define __OP_ROUND_H__
+
+#include "OpChef.h"
+
+class RoundChef final : public OpChef
+{
+public:
+ explicit RoundChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_ROUND; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_NONE; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct RoundChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_ROUND_H__
diff --git a/compiler/tflchef/core/src/Op/Rsqrt.cpp b/compiler/tflchef/core/src/Op/Rsqrt.cpp
new file mode 100644
index 000000000..fa837f6fa
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Rsqrt.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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 "Rsqrt.h"
+
+flatbuffers::Offset<void> RsqrtChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ // No tflite option for Rsqrt. Use void.
+ return flatbuffers::Offset<void>();
+}
+
+std::unique_ptr<OpChef> RsqrtChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new RsqrtChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Rsqrt.h b/compiler/tflchef/core/src/Op/Rsqrt.h
new file mode 100644
index 000000000..657f51ccb
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Rsqrt.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_RSQRT_H__
+#define __OP_RSQRT_H__
+
+#include "OpChef.h"
+
+class RsqrtChef final : public OpChef
+{
+public:
+ explicit RsqrtChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_RSQRT; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_NONE; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct RsqrtChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_RSQRT_H__
diff --git a/compiler/tflchef/core/src/Op/ScatterNd.cpp b/compiler/tflchef/core/src/Op/ScatterNd.cpp
new file mode 100644
index 000000000..7114dda6e
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ScatterNd.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ScatterNd.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> ScatterNdChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::ScatterNdOptionsBuilder scatter_nd_options_builder{fbb};
+
+ return scatter_nd_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> ScatterNdChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new ScatterNdChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/ScatterNd.h b/compiler/tflchef/core/src/Op/ScatterNd.h
new file mode 100644
index 000000000..2c89cf6a0
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ScatterNd.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_SCATTER_ND_H__
+#define __OP_SCATTER_ND_H__
+
+#include "OpChef.h"
+
+class ScatterNdChef final : public OpChef
+{
+public:
+ explicit ScatterNdChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_SCATTER_ND; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_ScatterNdOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct ScatterNdChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_SCATTER_ND_H__
diff --git a/compiler/tflchef/core/src/Op/SegmentSum.cpp b/compiler/tflchef/core/src/Op/SegmentSum.cpp
new file mode 100644
index 000000000..934bcb0ec
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/SegmentSum.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "SegmentSum.h"
+
+flatbuffers::Offset<void> SegmentSumChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::SegmentSumOptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> SegmentSumChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new SegmentSumChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/SegmentSum.h b/compiler/tflchef/core/src/Op/SegmentSum.h
new file mode 100644
index 000000000..c0ebfba52
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/SegmentSum.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_SEGMENT_SUM_H__
+#define __OP_SEGMENT_SUM_H__
+
+#include "OpChef.h"
+
+class SegmentSumChef final : public OpChef
+{
+public:
+ explicit SegmentSumChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_SEGMENT_SUM; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_SegmentSumOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct SegmentSumChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_SEGMENT_SUM_H__
diff --git a/compiler/tflchef/core/src/Op/Select.cpp b/compiler/tflchef/core/src/Op/Select.cpp
new file mode 100644
index 000000000..31be736c9
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Select.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "Select.h"
+
+flatbuffers::Offset<void> SelectChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::SelectOptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> SelectChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new SelectChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Select.h b/compiler/tflchef/core/src/Op/Select.h
new file mode 100644
index 000000000..91ace16e1
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Select.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_SELECT_H__
+#define __OP_SELECT_H__
+
+#include "OpChef.h"
+
+class SelectChef final : public OpChef
+{
+public:
+ explicit SelectChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_SELECT; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_SelectOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct SelectChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_SELECT_H__
diff --git a/compiler/tflchef/core/src/Op/SelectV2.cpp b/compiler/tflchef/core/src/Op/SelectV2.cpp
new file mode 100644
index 000000000..f6c0bfc49
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/SelectV2.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "SelectV2.h"
+
+flatbuffers::Offset<void> SelectV2Chef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::SelectV2OptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> SelectV2ChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new SelectV2Chef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/SelectV2.h b/compiler/tflchef/core/src/Op/SelectV2.h
new file mode 100644
index 000000000..36d74c344
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/SelectV2.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_SELECT_V2_H__
+#define __OP_SELECT_V2_H__
+
+#include "OpChef.h"
+
+class SelectV2Chef final : public OpChef
+{
+public:
+ explicit SelectV2Chef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_SELECT_V2; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_SelectV2Options;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct SelectV2ChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_SELECT_V2_H__
diff --git a/compiler/tflchef/core/src/Op/Shape.cpp b/compiler/tflchef/core/src/Op/Shape.cpp
new file mode 100644
index 000000000..74b1894da
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Shape.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Shape.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> ShapeChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_shape_options());
+
+ auto tflite_out_type = as_tflite_tensortype(operation.shape_options().out_type());
+
+ tflite::ShapeOptionsBuilder shape_options_builder{fbb};
+ shape_options_builder.add_out_type(tflite_out_type);
+
+ return shape_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> ShapeChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new ShapeChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Shape.h b/compiler/tflchef/core/src/Op/Shape.h
new file mode 100644
index 000000000..ddaeb1d95
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Shape.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_SHAPE_H__
+#define __OP_SHAPE_H__
+
+#include "OpChef.h"
+
+class ShapeChef final : public OpChef
+{
+public:
+ explicit ShapeChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_SHAPE; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_ShapeOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct ShapeChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_SHAPE_H__
diff --git a/compiler/tflchef/core/src/Op/Sin.cpp b/compiler/tflchef/core/src/Op/Sin.cpp
new file mode 100644
index 000000000..1752ce7a1
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Sin.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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 "Sin.h"
+
+flatbuffers::Offset<void> SinChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ // No tflite option for Sin. Use void.
+ return flatbuffers::Offset<void>();
+}
+
+std::unique_ptr<OpChef> SinChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new SinChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Sin.h b/compiler/tflchef/core/src/Op/Sin.h
new file mode 100644
index 000000000..121b73b68
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Sin.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_SIN_H__
+#define __OP_SIN_H__
+
+#include "OpChef.h"
+
+class SinChef final : public OpChef
+{
+public:
+ explicit SinChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_SIN; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_NONE; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct SinChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_SIN_H__
diff --git a/compiler/tflchef/core/src/Op/Slice.cpp b/compiler/tflchef/core/src/Op/Slice.cpp
new file mode 100644
index 000000000..27ae80a8a
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Slice.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "Slice.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> SliceChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::SliceOptionsBuilder slice_options_builder{fbb};
+ return slice_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> SliceChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new SliceChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Slice.h b/compiler/tflchef/core/src/Op/Slice.h
new file mode 100644
index 000000000..06fd6347b
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Slice.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_SLICE_H__
+#define __OP_SLICE_H__
+
+#include "OpChef.h"
+
+class SliceChef final : public OpChef
+{
+public:
+ explicit SliceChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_SLICE; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_SliceOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct SliceChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_SLICE_H__
diff --git a/compiler/tflchef/core/src/Op/Softmax.cpp b/compiler/tflchef/core/src/Op/Softmax.cpp
new file mode 100644
index 000000000..a554e0d81
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Softmax.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Softmax.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> SoftmaxChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_softmax_options());
+
+ auto tflite_beta = operation.softmax_options().beta();
+
+ tflite::SoftmaxOptionsBuilder soft_options_builder{fbb};
+ soft_options_builder.add_beta(tflite_beta);
+
+ return soft_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> SoftmaxChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new SoftmaxChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Softmax.h b/compiler/tflchef/core/src/Op/Softmax.h
new file mode 100644
index 000000000..8b3f0ebf6
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Softmax.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_SOFTMAX_H__
+#define __OP_SOFTMAX_H__
+
+#include "OpChef.h"
+
+class SoftmaxChef final : public OpChef
+{
+public:
+ explicit SoftmaxChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_SOFTMAX; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_SoftmaxOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct SoftmaxChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_SOFTMAX_H__
diff --git a/compiler/tflchef/core/src/Op/SpaceToBatchND.cpp b/compiler/tflchef/core/src/Op/SpaceToBatchND.cpp
new file mode 100644
index 000000000..74e052826
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/SpaceToBatchND.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "SpaceToBatchND.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> SpaceToBatchNDChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::SpaceToBatchNDOptionsBuilder space_to_batch_nd_options_builder{fbb};
+
+ return space_to_batch_nd_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> SpaceToBatchNDChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new SpaceToBatchNDChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/SpaceToBatchND.h b/compiler/tflchef/core/src/Op/SpaceToBatchND.h
new file mode 100644
index 000000000..e263bdc61
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/SpaceToBatchND.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_SPACETOBATCHND_H__
+#define __OP_SPACETOBATCHND_H__
+
+#include "OpChef.h"
+
+class SpaceToBatchNDChef final : public OpChef
+{
+public:
+ explicit SpaceToBatchNDChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_SPACE_TO_BATCH_ND;
+ }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_SpaceToBatchNDOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct SpaceToBatchNDChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_SPACETOBATCHND_H__
diff --git a/compiler/tflchef/core/src/Op/SpaceToDepth.cpp b/compiler/tflchef/core/src/Op/SpaceToDepth.cpp
new file mode 100644
index 000000000..98eed8c26
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/SpaceToDepth.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SpaceToDepth.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> SpaceToDepthChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_space_to_depth_options());
+
+ auto tflite_block_size = operation.space_to_depth_options().block_size();
+
+ tflite::SpaceToDepthOptionsBuilder std_options_builder{fbb};
+ std_options_builder.add_block_size(tflite_block_size);
+
+ return std_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> SpaceToDepthChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new SpaceToDepthChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/SpaceToDepth.h b/compiler/tflchef/core/src/Op/SpaceToDepth.h
new file mode 100644
index 000000000..db852feac
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/SpaceToDepth.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_SPACETODEPTH_H__
+#define __OP_SPACETODEPTH_H__
+
+#include "OpChef.h"
+
+class SpaceToDepthChef final : public OpChef
+{
+public:
+ explicit SpaceToDepthChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_SPACE_TO_DEPTH;
+ }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_SpaceToDepthOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct SpaceToDepthChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_SPACETODEPTH_H__
diff --git a/compiler/tflchef/core/src/Op/SparseToDense.cpp b/compiler/tflchef/core/src/Op/SparseToDense.cpp
new file mode 100644
index 000000000..f1f8a7150
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/SparseToDense.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SparseToDense.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> SparseToDenseChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_sparse_to_dense_options());
+
+ auto tflite_validate_indices = operation.sparse_to_dense_options().validate_indices();
+
+ tflite::SparseToDenseOptionsBuilder sparse_to_dense_options_builder(fbb);
+ sparse_to_dense_options_builder.add_validate_indices(tflite_validate_indices);
+
+ return sparse_to_dense_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> SparseToDenseChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new SparseToDenseChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/SparseToDense.h b/compiler/tflchef/core/src/Op/SparseToDense.h
new file mode 100644
index 000000000..02cbd6a6d
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/SparseToDense.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_SPARSETODENSE_H__
+#define __OP_SPARSETODENSE_H__
+
+#include "OpChef.h"
+
+class SparseToDenseChef final : public OpChef
+{
+public:
+ explicit SparseToDenseChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_SPARSE_TO_DENSE;
+ }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_SparseToDenseOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct SparseToDenseChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_SPARSETODENSE_H__
diff --git a/compiler/tflchef/core/src/Op/Split.cpp b/compiler/tflchef/core/src/Op/Split.cpp
new file mode 100644
index 000000000..f4704e537
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Split.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Split.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> SplitChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_split_options());
+
+ auto num_splits = operation.split_options().num_splits();
+
+ tflite::SplitOptionsBuilder split_options_builder{fbb};
+ split_options_builder.add_num_splits(num_splits);
+
+ return split_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> SplitChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new SplitChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Split.h b/compiler/tflchef/core/src/Op/Split.h
new file mode 100644
index 000000000..db6158069
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Split.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_SPLIT_H__
+#define __OP_SPLIT_H__
+
+#include "OpChef.h"
+
+class SplitChef final : public OpChef
+{
+public:
+ explicit SplitChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_SPLIT; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_SplitOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct SplitChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_SPLIT_H__
diff --git a/compiler/tflchef/core/src/Op/SplitV.cpp b/compiler/tflchef/core/src/Op/SplitV.cpp
new file mode 100644
index 000000000..fa93db6ba
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/SplitV.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SplitV.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> SplitVChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_split_v_options());
+
+ auto num_splits = operation.split_v_options().num_splits();
+
+ tflite::SplitVOptionsBuilder split_v_options_builder{fbb};
+ split_v_options_builder.add_num_splits(num_splits);
+
+ return split_v_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> SplitVChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new SplitVChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/SplitV.h b/compiler/tflchef/core/src/Op/SplitV.h
new file mode 100644
index 000000000..c37736e31
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/SplitV.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_SPLIT_V_H__
+#define __OP_SPLIT_V_H__
+
+#include "OpChef.h"
+
+class SplitVChef final : public OpChef
+{
+public:
+ explicit SplitVChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_SPLIT_V; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_SplitVOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct SplitVChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_SPLIT_V_H__
diff --git a/compiler/tflchef/core/src/Op/Square.cpp b/compiler/tflchef/core/src/Op/Square.cpp
new file mode 100644
index 000000000..fd3538072
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Square.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "Square.h"
+
+flatbuffers::Offset<void> SquareChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::SquareOptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> SquareChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new SquareChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Square.h b/compiler/tflchef/core/src/Op/Square.h
new file mode 100644
index 000000000..5b76e6302
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Square.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_SQUARE_H__
+#define __OP_SQUARE_H__
+
+#include "OpChef.h"
+
+class SquareChef final : public OpChef
+{
+public:
+ explicit SquareChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_SQUARE; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_SquareOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct SquareChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_SQUARE_H__
diff --git a/compiler/tflchef/core/src/Op/SquaredDifference.cpp b/compiler/tflchef/core/src/Op/SquaredDifference.cpp
new file mode 100644
index 000000000..757c148a9
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/SquaredDifference.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 "SquaredDifference.h"
+
+flatbuffers::Offset<void> SquaredDifferenceChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::SquaredDifferenceOptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef>
+SquaredDifferenceChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new SquaredDifferenceChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/SquaredDifference.h b/compiler/tflchef/core/src/Op/SquaredDifference.h
new file mode 100644
index 000000000..f919975f9
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/SquaredDifference.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_SQUAREDDIFFERENCE_H__
+#define __OP_SQUAREDDIFFERENCE_H__
+
+#include "OpChef.h"
+
+class SquaredDifferenceChef final : public OpChef
+{
+public:
+ explicit SquaredDifferenceChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_SQUARED_DIFFERENCE;
+ }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_SquaredDifferenceOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct SquaredDifferenceChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_SQUAREDDIFFERENCE_H__
diff --git a/compiler/tflchef/core/src/Op/Squeeze.cpp b/compiler/tflchef/core/src/Op/Squeeze.cpp
new file mode 100644
index 000000000..8d6ef42d6
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Squeeze.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Squeeze.h"
+#include "Convert.h"
+
+#include <cassert>
+#include <vector>
+
+flatbuffers::Offset<void> SqueezeChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_squeeze_options());
+
+ const auto &options = operation.squeeze_options();
+ // Note: 'CreateVector' should be placed before 'CreateOptions'
+ // Read flatbuffers.h 'void NotNested()' for more information
+ auto fb_squeeze_dims =
+ fbb.CreateVector(options.squeeze_dim().data(), options.squeeze_dim().size());
+
+ return tflite::CreateSqueezeOptions(fbb, fb_squeeze_dims).Union();
+}
+
+std::unique_ptr<OpChef> SqueezeChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new SqueezeChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Squeeze.h b/compiler/tflchef/core/src/Op/Squeeze.h
new file mode 100644
index 000000000..2787231f2
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Squeeze.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_SQUEEZE_H__
+#define __OP_SQUEEZE_H__
+
+#include "OpChef.h"
+
+class SqueezeChef final : public OpChef
+{
+public:
+ explicit SqueezeChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_SQUEEZE; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_SqueezeOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct SqueezeChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_SQUEEZE_H__
diff --git a/compiler/tflchef/core/src/Op/StridedSlice.cpp b/compiler/tflchef/core/src/Op/StridedSlice.cpp
new file mode 100644
index 000000000..587a95c66
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/StridedSlice.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "StridedSlice.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> StridedSliceChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_strided_slice_options());
+
+ tflite::StridedSliceOptionsBuilder strided_slice_options_builder{fbb};
+ strided_slice_options_builder.add_begin_mask(operation.strided_slice_options().begin_mask());
+ strided_slice_options_builder.add_end_mask(operation.strided_slice_options().end_mask());
+ strided_slice_options_builder.add_ellipsis_mask(
+ operation.strided_slice_options().ellipsis_mask());
+ strided_slice_options_builder.add_new_axis_mask(
+ operation.strided_slice_options().new_axis_mask());
+ strided_slice_options_builder.add_shrink_axis_mask(
+ operation.strided_slice_options().shrink_axis_mask());
+
+ return strided_slice_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> StridedSliceChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new StridedSliceChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/StridedSlice.h b/compiler/tflchef/core/src/Op/StridedSlice.h
new file mode 100644
index 000000000..49da44f12
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/StridedSlice.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_STRIDEDSLICE_H__
+#define __OP_STRIDEDSLICE_H__
+
+#include "OpChef.h"
+
+class StridedSliceChef final : public OpChef
+{
+public:
+ explicit StridedSliceChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_STRIDED_SLICE;
+ }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_StridedSliceOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct StridedSliceChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_STRIDEDSLICE_H__
diff --git a/compiler/tflchef/core/src/Op/Sum.cpp b/compiler/tflchef/core/src/Op/Sum.cpp
new file mode 100644
index 000000000..6b79d3ec5
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Sum.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Sum.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> SumChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_sum_options());
+
+ auto keep_dims = operation.sum_options().keep_dims();
+
+ tflite::ReducerOptionsBuilder sum_options_builder{fbb};
+ sum_options_builder.add_keep_dims(keep_dims);
+
+ return sum_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> SumChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new SumChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Sum.h b/compiler/tflchef/core/src/Op/Sum.h
new file mode 100644
index 000000000..d3cc8c173
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Sum.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_SUM_H__
+#define __OP_SUM_H__
+
+#include "OpChef.h"
+
+class SumChef final : public OpChef
+{
+public:
+ explicit SumChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_SUM; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_ReducerOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct SumChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_SUM_H__
diff --git a/compiler/tflchef/core/src/Op/Tanh.cpp b/compiler/tflchef/core/src/Op/Tanh.cpp
new file mode 100644
index 000000000..c25cad8f0
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Tanh.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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 "Tanh.h"
+
+flatbuffers::Offset<void> TanhChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ // No tflite option for Tanh. Use void.
+ return flatbuffers::Offset<void>();
+}
+
+std::unique_ptr<OpChef> TanhChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new TanhChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Tanh.h b/compiler/tflchef/core/src/Op/Tanh.h
new file mode 100644
index 000000000..f8f707a04
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Tanh.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_TANH_H__
+#define __OP_TANH_H__
+
+#include "OpChef.h"
+
+class TanhChef final : public OpChef
+{
+public:
+ explicit TanhChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_TANH; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_NONE; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct TanhChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_TANH_H__
diff --git a/compiler/tflchef/core/src/Op/Tile.cpp b/compiler/tflchef/core/src/Op/Tile.cpp
new file mode 100644
index 000000000..18710b4b0
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Tile.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "Tile.h"
+
+flatbuffers::Offset<void> TileChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::TileOptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> TileChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new TileChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Tile.h b/compiler/tflchef/core/src/Op/Tile.h
new file mode 100644
index 000000000..2870ff174
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Tile.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_TILE_H__
+#define __OP_TILE_H__
+
+#include "OpChef.h"
+
+class TileChef final : public OpChef
+{
+public:
+ explicit TileChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_TILE; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_TileOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct TileChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_TILE_H__
diff --git a/compiler/tflchef/core/src/Op/TopKV2.cpp b/compiler/tflchef/core/src/Op/TopKV2.cpp
new file mode 100644
index 000000000..08c4de66b
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/TopKV2.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "TopKV2.h"
+
+flatbuffers::Offset<void> TopKV2Chef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::TopKV2OptionsBuilder options_builder{fbb};
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> TopKV2ChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new TopKV2Chef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/TopKV2.h b/compiler/tflchef/core/src/Op/TopKV2.h
new file mode 100644
index 000000000..554822332
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/TopKV2.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_TOPK_V2_H__
+#define __OP_TOPK_V2_H__
+
+#include "OpChef.h"
+
+class TopKV2Chef final : public OpChef
+{
+public:
+ explicit TopKV2Chef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_TOPK_V2; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_TopKV2Options; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct TopKV2ChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_TOPK_V2_H__
diff --git a/compiler/tflchef/core/src/Op/Transpose.cpp b/compiler/tflchef/core/src/Op/Transpose.cpp
new file mode 100644
index 000000000..caae6cfa8
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Transpose.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Transpose.h"
+#include "Convert.h"
+
+#include <cassert>
+#include <vector>
+
+flatbuffers::Offset<void> TransposeChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::TransposeOptionsBuilder options_builder{fbb};
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> TransposeChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new TransposeChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Transpose.h b/compiler/tflchef/core/src/Op/Transpose.h
new file mode 100644
index 000000000..bb30f7bc9
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Transpose.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_TRANSPOSE_H__
+#define __OP_TRANSPOSE_H__
+
+#include "OpChef.h"
+
+class TransposeChef final : public OpChef
+{
+public:
+ explicit TransposeChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_TRANSPOSE; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_TransposeOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct TransposeChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_TRANSPOSE_H__
diff --git a/compiler/tflchef/core/src/Op/TransposeConv.cpp b/compiler/tflchef/core/src/Op/TransposeConv.cpp
new file mode 100644
index 000000000..c9e452714
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/TransposeConv.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TransposeConv.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> TransposeConvChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ tflite::TransposeConvOptionsBuilder options_builder{fbb};
+
+ assert(operation.has_transpose_conv_options());
+
+ auto tflite_padding = as_tflite_padding(operation.transpose_conv_options().padding());
+
+ options_builder.add_padding(tflite_padding);
+
+ options_builder.add_stride_h(operation.transpose_conv_options().stride_h());
+ options_builder.add_stride_w(operation.transpose_conv_options().stride_w());
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> TransposeConvChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new TransposeConvChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/TransposeConv.h b/compiler/tflchef/core/src/Op/TransposeConv.h
new file mode 100644
index 000000000..e664bfff2
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/TransposeConv.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_TRANSPOSE_CONV_H__
+#define __OP_TRANSPOSE_CONV_H__
+
+#include "OpChef.h"
+
+class TransposeConvChef final : public OpChef
+{
+public:
+ explicit TransposeConvChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_TRANSPOSE_CONV;
+ }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_TransposeConvOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct TransposeConvChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_TRANSPOSE_CONV_H__
diff --git a/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.cpp b/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.cpp
new file mode 100644
index 000000000..ceabfc13c
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "UnidirectionalSequenceLSTM.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void>
+UnidirectionalSequenceLSTMChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_unidirectional_sequence_lstm_options());
+
+ tflite::UnidirectionalSequenceLSTMOptionsBuilder options_builder(fbb);
+ options_builder.add_fused_activation_function(
+ as_tflite_activation(operation.unidirectional_sequence_lstm_options().activation()));
+ options_builder.add_cell_clip(operation.unidirectional_sequence_lstm_options().cell_clip());
+ options_builder.add_proj_clip(operation.unidirectional_sequence_lstm_options().proj_clip());
+ options_builder.add_time_major(operation.unidirectional_sequence_lstm_options().time_major());
+ options_builder.add_asymmetric_quantize_inputs(
+ operation.unidirectional_sequence_lstm_options().asymmetric_quantize_inputs());
+
+ return options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef>
+UnidirectionalSequenceLSTMChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new UnidirectionalSequenceLSTMChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.h b/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.h
new file mode 100644
index 000000000..6811ad378
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_UNIDIRECTIONALSEQUENCELSTM_H__
+#define __OP_UNIDIRECTIONALSEQUENCELSTM_H__
+
+#include "OpChef.h"
+
+class UnidirectionalSequenceLSTMChef final : public OpChef
+{
+public:
+ explicit UnidirectionalSequenceLSTMChef(const tflchef::Operation *operation)
+ : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override
+ {
+ return tflite::BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM;
+ }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_UnidirectionalSequenceLSTMOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct UnidirectionalSequenceLSTMChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_UNIDIRECTIONALSEQUENCELSTM_H__
diff --git a/compiler/tflchef/core/src/Op/Unique.cpp b/compiler/tflchef/core/src/Op/Unique.cpp
new file mode 100644
index 000000000..d9a7293c5
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Unique.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Unique.h"
+#include "Convert.h"
+
+#include <cassert>
+
+flatbuffers::Offset<void> UniqueChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_unique_options());
+
+ auto tflite_out_idx = as_tflite_tensortype(operation.unique_options().idx_out_type());
+
+ tflite::UniqueOptionsBuilder unique_options_builder{fbb};
+ unique_options_builder.add_idx_out_type(tflite_out_idx);
+
+ return unique_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> UniqueChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new UniqueChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Unique.h b/compiler/tflchef/core/src/Op/Unique.h
new file mode 100644
index 000000000..58aa7bfaa
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Unique.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_UNIQUE_H__
+#define __OP_UNIQUE_H__
+
+#include "OpChef.h"
+
+class UniqueChef final : public OpChef
+{
+public:
+ explicit UniqueChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_UNIQUE; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_UniqueOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct UniqueChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_UNIQUE_H__
diff --git a/compiler/tflchef/core/src/Op/Unpack.cpp b/compiler/tflchef/core/src/Op/Unpack.cpp
new file mode 100644
index 000000000..504da5a46
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Unpack.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "Unpack.h"
+
+flatbuffers::Offset<void> UnpackChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_unpack_options());
+
+ tflite::UnpackOptionsBuilder unpack_options_builder{fbb};
+ unpack_options_builder.add_num(operation.unpack_options().num());
+ unpack_options_builder.add_axis(operation.unpack_options().axis());
+
+ return unpack_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> UnpackChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new UnpackChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Unpack.h b/compiler/tflchef/core/src/Op/Unpack.h
new file mode 100644
index 000000000..3a425b1a3
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Unpack.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_UNPACK_H__
+#define __OP_UNPACK_H__
+
+#include "OpChef.h"
+
+class UnpackChef final : public OpChef
+{
+public:
+ explicit UnpackChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_UNPACK; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_UnpackOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct UnpackChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_ABS_H__
diff --git a/compiler/tflchef/core/src/Op/Where.cpp b/compiler/tflchef/core/src/Op/Where.cpp
new file mode 100644
index 000000000..0ce9102bc
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Where.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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 specwhileic language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Where.h"
+
+flatbuffers::Offset<void> WhereChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::WhereOptionsBuilder where_options_builder{fbb};
+ return where_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> WhereChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new WhereChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/Where.h b/compiler/tflchef/core/src/Op/Where.h
new file mode 100644
index 000000000..7991c64cd
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/Where.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_WHERE_H__
+#define __OP_WHERE_H__
+
+#include "OpChef.h"
+
+class WhereChef final : public OpChef
+{
+public:
+ explicit WhereChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_WHERE; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_WhereOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct WhereChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_WHERE_H__
diff --git a/compiler/tflchef/core/src/Op/While.cpp b/compiler/tflchef/core/src/Op/While.cpp
new file mode 100644
index 000000000..1253d0fcc
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/While.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 specwhileic language governing permissions and
+ * limitations under the License.
+ */
+
+#include "While.h"
+
+flatbuffers::Offset<void> WhileChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ auto &operation = (*_operation);
+
+ assert(operation.has_while_options());
+
+ tflite::WhileOptionsBuilder while_options_builder{fbb};
+ while_options_builder.add_cond_subgraph_index(operation.while_options().cond_subgraph_index());
+ while_options_builder.add_body_subgraph_index(operation.while_options().body_subgraph_index());
+
+ return while_options_builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> WhileChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new WhileChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/While.h b/compiler/tflchef/core/src/Op/While.h
new file mode 100644
index 000000000..150a14be3
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/While.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_WHILE_H__
+#define __OP_WHILE_H__
+
+#include "OpChef.h"
+
+class WhileChef final : public OpChef
+{
+public:
+ explicit WhileChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_WHILE; }
+
+ tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_WhileOptions; }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct WhileChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_WHILE_H__
diff --git a/compiler/tflchef/core/src/Op/ZerosLike.cpp b/compiler/tflchef/core/src/Op/ZerosLike.cpp
new file mode 100644
index 000000000..e47e2ab50
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ZerosLike.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ZerosLike.h"
+
+flatbuffers::Offset<void> ZerosLikeChef::value(flatbuffers::FlatBufferBuilder &fbb) const
+{
+ tflite::ZerosLikeOptionsBuilder builder{fbb};
+ return builder.Finish().Union();
+}
+
+std::unique_ptr<OpChef> ZerosLikeChefFactory::create(const tflchef::Operation *operation) const
+{
+ return std::unique_ptr<OpChef>{new ZerosLikeChef{operation}};
+}
diff --git a/compiler/tflchef/core/src/Op/ZerosLike.h b/compiler/tflchef/core/src/Op/ZerosLike.h
new file mode 100644
index 000000000..0af5b93e2
--- /dev/null
+++ b/compiler/tflchef/core/src/Op/ZerosLike.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OP_ZEROS_LIKE_H__
+#define __OP_ZEROS_LIKE_H__
+
+#include "OpChef.h"
+
+class ZerosLikeChef final : public OpChef
+{
+public:
+ explicit ZerosLikeChef(const tflchef::Operation *operation) : _operation{operation}
+ {
+ // DO NOTHING
+ }
+
+public:
+ tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_ZEROS_LIKE; }
+
+ tflite::BuiltinOptions type(void) const override
+ {
+ return tflite::BuiltinOptions_ZerosLikeOptions;
+ }
+
+ flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override;
+
+private:
+ const tflchef::Operation *_operation;
+};
+
+struct ZerosLikeChefFactory final : public OpChefFactory
+{
+ std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override;
+};
+
+#endif // __OP_ZEROS_LIKE_H__
diff --git a/compiler/tflchef/core/src/OpChef.def b/compiler/tflchef/core/src/OpChef.def
index cd34c7202..718fffc78 100644
--- a/compiler/tflchef/core/src/OpChef.def
+++ b/compiler/tflchef/core/src/OpChef.def
@@ -4,15 +4,119 @@
// Please keep the list in alphabetical order
// OP_CHEF(NAME, FACTORY_CLASS)
+OP_CHEF(Abs, AbsChefFactory)
+OP_CHEF(Add, AddChefFactory)
+OP_CHEF(AddN, AddNChefFactory)
+OP_CHEF(ArgMax, ArgMaxChefFactory)
+OP_CHEF(ArgMin, ArgMinChefFactory)
OP_CHEF(AveragePool2D, AveragePool2DChefFactory)
+OP_CHEF(BatchMatMul, BatchMatMulChefFactory)
+OP_CHEF(BatchToSpaceND, BatchToSpaceNDChefFactory)
+OP_CHEF(Cast, CastChefFactory)
+OP_CHEF(Ceil, CeilChefFactory)
OP_CHEF(Concatenation, ConcatenationChefFactory)
OP_CHEF(Conv2D, Conv2DChefFactory)
+OP_CHEF(Cos, CosChefFactory)
+OP_CHEF(DepthToSpace, DepthToSpaceChefFactory)
OP_CHEF(DepthwiseConv2D, DepthwiseConv2DChefFactory)
+OP_CHEF(Dequantize, DequantizeChefFactory)
OP_CHEF(Div, DivChefFactory)
+OP_CHEF(ELU, ELUChefFactory)
+OP_CHEF(Equal, EqualChefFactory)
+OP_CHEF(Exp, ExpChefFactory)
+OP_CHEF(ExpandDims, ExpandDimsChefFactory)
+OP_CHEF(Fill, FillChefFactory)
+OP_CHEF(Floor, FloorChefFactory)
+OP_CHEF(FloorDiv, FloorDivChefFactory)
+OP_CHEF(FloorMod, FloorModChefFactory)
OP_CHEF(FullyConnected, FullyConnectedChefFactory)
+OP_CHEF(Gather, GatherChefFactory)
+OP_CHEF(GatherNd, GatherNdChefFactory)
+OP_CHEF(Greater, GreaterChefFactory)
+OP_CHEF(GreaterEqual, GreaterEqualChefFactory)
+OP_CHEF(If, IfChefFactory)
+OP_CHEF(L2Normalize, L2NormalizeChefFactory)
+OP_CHEF(L2Pool2D, L2Pool2DChefFactory)
+OP_CHEF(LeakyRelu, LeakyReluChefFactory)
+OP_CHEF(Less, LessChefFactory)
+OP_CHEF(LessEqual, LessEqualChefFactory)
+OP_CHEF(LocalResponseNormalization, LocalResponseNormalizationChefFactory)
+OP_CHEF(Log, LogChefFactory)
+OP_CHEF(LogicalAnd, LogicalAndChefFactory)
+OP_CHEF(LogicalNot, LogicalNotChefFactory)
+OP_CHEF(LogicalOr, LogicalOrChefFactory)
+OP_CHEF(Logistic, LogisticChefFactory)
+OP_CHEF(LogSoftmax, LogSoftmaxChefFactory)
+OP_CHEF(MatrixDiag, MatrixDiagChefFactory)
+OP_CHEF(MatrixSetDiag, MatrixSetDiagChefFactory)
+OP_CHEF(Maximum, MaximumChefFactory)
OP_CHEF(MaxPool2D, MaxPool2DChefFactory)
+OP_CHEF(Mean, MeanChefFactory)
+OP_CHEF(Minimum, MinimumChefFactory)
+OP_CHEF(MirrorPad, MirrorPadChefFactory)
+OP_CHEF(Mul, MulChefFactory)
+OP_CHEF(Neg, NegChefFactory)
+OP_CHEF(NonMaxSuppressionV4, NonMaxSuppressionV4ChefFactory)
+OP_CHEF(NonMaxSuppressionV5, NonMaxSuppressionV5ChefFactory)
+OP_CHEF(NotEqual, NotEqualChefFactory)
+OP_CHEF(OneHot, OneHotChefFactory)
+OP_CHEF(Pack, PackChefFactory)
+OP_CHEF(Pad, PadChefFactory)
+OP_CHEF(PadV2, PadV2ChefFactory)
+OP_CHEF(Pow, PowChefFactory)
+OP_CHEF(PRelu, PReluChefFactory)
+OP_CHEF(Range, RangeChefFactory)
+OP_CHEF(Rank, RankChefFactory)
+OP_CHEF(ReduceAny, ReduceAnyChefFactory)
+OP_CHEF(ReduceMax, ReduceMaxChefFactory)
+OP_CHEF(ReduceMin, ReduceMinChefFactory)
+OP_CHEF(ReduceProd, ReduceProdChefFactory)
OP_CHEF(ReLU, ReLUChefFactory)
OP_CHEF(ReLU6, ReLU6ChefFactory)
+OP_CHEF(ReLUN1To1, ReLUN1To1ChefFactory)
OP_CHEF(Reshape, ReshapeChefFactory)
+OP_CHEF(ResizeBilinear, ResizeBilinearChefFactory)
+OP_CHEF(ResizeNearestNeighbor, ResizeNearestNeighborChefFactory)
+OP_CHEF(ReverseSequence, ReverseSequenceChefFactory)
+OP_CHEF(ReverseV2, ReverseV2ChefFactory)
+OP_CHEF(Round, RoundChefFactory)
+OP_CHEF(Rsqrt, RsqrtChefFactory)
+OP_CHEF(ScatterNd, ScatterNdChefFactory)
+OP_CHEF(SegmentSum,SegmentSumChefFactory)
+OP_CHEF(Select, SelectChefFactory)
+OP_CHEF(SelectV2, SelectV2ChefFactory)
+OP_CHEF(Shape, ShapeChefFactory)
+OP_CHEF(Sin, SinChefFactory)
+OP_CHEF(Slice, SliceChefFactory)
+OP_CHEF(Softmax, SoftmaxChefFactory)
+OP_CHEF(SpaceToBatchND, SpaceToBatchNDChefFactory)
+OP_CHEF(SpaceToDepth, SpaceToDepthChefFactory)
+OP_CHEF(SparseToDense, SparseToDenseChefFactory)
+OP_CHEF(Split, SplitChefFactory)
+OP_CHEF(SplitV, SplitVChefFactory)
OP_CHEF(Sqrt, SqrtChefFactory)
+OP_CHEF(Square, SquareChefFactory)
+OP_CHEF(SquaredDifference, SquaredDifferenceChefFactory)
+OP_CHEF(Squeeze, SqueezeChefFactory)
+OP_CHEF(StridedSlice, StridedSliceChefFactory)
OP_CHEF(Sub, SubChefFactory)
+OP_CHEF(Sum, SumChefFactory)
+OP_CHEF(Tanh, TanhChefFactory)
+OP_CHEF(Tile, TileChefFactory)
+OP_CHEF(TopKV2, TopKV2ChefFactory)
+OP_CHEF(Transpose, TransposeChefFactory)
+OP_CHEF(TransposeConv, TransposeConvChefFactory)
+OP_CHEF(UnidirectionalSequenceLSTM, UnidirectionalSequenceLSTMChefFactory)
+OP_CHEF(Unique, UniqueChefFactory)
+OP_CHEF(Unpack, UnpackChefFactory)
+OP_CHEF(Where, WhereChefFactory)
+OP_CHEF(While, WhileChefFactory)
+OP_CHEF(ZerosLike, ZerosLikeChefFactory)
+
+// Custom Op
+OP_CHEF(AddV2, AddV2ChefFactory)
+OP_CHEF(All, AllChefFactory)
+OP_CHEF(BatchMatMulV2, BatchMatMulV2ChefFactory)
+OP_CHEF(MatMul, MatMulChefFactory)
+OP_CHEF(MatrixBandPart, MatrixBandPartChefFactory)
+OP_CHEF(MaxPoolWithArgMax, MaxPoolWithArgMaxChefFactory)
diff --git a/compiler/tflchef/core/src/OpChef.h b/compiler/tflchef/core/src/OpChef.h
index 5290fec8b..7efa096cc 100644
--- a/compiler/tflchef/core/src/OpChef.h
+++ b/compiler/tflchef/core/src/OpChef.h
@@ -18,7 +18,7 @@
#define __OP_CHEF_H__
#include <tflchef.pb.h>
-#include <tflite_generated.h>
+#include <mio/tflite/schema_generated.h>
#include <memory>
@@ -29,6 +29,13 @@ struct OpChef
virtual tflite::BuiltinOperator code(void) const = 0;
virtual tflite::BuiltinOptions type(void) const = 0;
virtual flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const = 0;
+
+ // TODO Find a way to place this method in a better place
+ virtual flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
+ custom_value(flatbuffers::FlatBufferBuilder &fbb) const
+ {
+ return flatbuffers::Offset<flatbuffers::Vector<uint8_t>>();
+ }
};
struct OpChefFactory
diff --git a/compiler/tflchef/core/src/OpChefs.h b/compiler/tflchef/core/src/OpChefs.h
index 5e0f4aa71..3527937a0 100644
--- a/compiler/tflchef/core/src/OpChefs.h
+++ b/compiler/tflchef/core/src/OpChefs.h
@@ -17,17 +17,120 @@
#ifndef __OP_CHEFS_H__
#define __OP_CHEFS_H__
+#include "Op/Abs.h"
+#include "Op/Add.h"
+#include "Op/AddN.h"
+#include "Op/ArgMax.h"
+#include "Op/ArgMin.h"
#include "Op/AveragePool2D.h"
+#include "Op/BatchMatMul.h"
+#include "Op/BatchToSpaceND.h"
+#include "Op/Cast.h"
+#include "Op/Ceil.h"
#include "Op/Concatenation.h"
#include "Op/Conv2D.h"
+#include "Op/Cos.h"
+#include "Op/DepthToSpace.h"
#include "Op/DepthwiseConv2D.h"
+#include "Op/Dequantize.h"
#include "Op/Div.h"
+#include "Op/ELU.h"
+#include "Op/Equal.h"
+#include "Op/Exp.h"
+#include "Op/ExpandDims.h"
+#include "Op/Fill.h"
+#include "Op/Floor.h"
+#include "Op/FloorDiv.h"
+#include "Op/FloorMod.h"
#include "Op/FullyConnected.h"
+#include "Op/Gather.h"
+#include "Op/GatherNd.h"
+#include "Op/Greater.h"
+#include "Op/GreaterEqual.h"
+#include "Op/If.h"
+#include "Op/L2Normalize.h"
+#include "Op/L2Pool2D.h"
+#include "Op/LeakyRelu.h"
+#include "Op/Less.h"
+#include "Op/LessEqual.h"
+#include "Op/LocalResponseNormalization.h"
+#include "Op/Log.h"
+#include "Op/LogicalAnd.h"
+#include "Op/LogicalNot.h"
+#include "Op/LogicalOr.h"
+#include "Op/Logistic.h"
+#include "Op/LogSoftmax.h"
+#include "Op/MatrixDiag.h"
+#include "Op/MatrixSetDiag.h"
+#include "Op/Maximum.h"
#include "Op/MaxPool2D.h"
+#include "Op/Mean.h"
+#include "Op/Minimum.h"
+#include "Op/MirrorPad.h"
+#include "Op/Mul.h"
+#include "Op/Neg.h"
+#include "Op/NonMaxSuppressionV4.h"
+#include "Op/NonMaxSuppressionV5.h"
+#include "Op/NotEqual.h"
+#include "Op/OneHot.h"
+#include "Op/Pack.h"
+#include "Op/Pad.h"
+#include "Op/PadV2.h"
+#include "Op/Pow.h"
+#include "Op/PRelu.h"
+#include "Op/Range.h"
+#include "Op/Rank.h"
+#include "Op/ReduceAny.h"
+#include "Op/ReduceMax.h"
+#include "Op/ReduceMin.h"
+#include "Op/ReduceProd.h"
#include "Op/ReLU.h"
#include "Op/ReLU6.h"
+#include "Op/ReLUN1To1.h"
#include "Op/Reshape.h"
+#include "Op/ResizeBilinear.h"
+#include "Op/ResizeNearestNeighbor.h"
+#include "Op/ReverseSequence.h"
+#include "Op/ReverseV2.h"
+#include "Op/Round.h"
+#include "Op/Rsqrt.h"
+#include "Op/ScatterNd.h"
+#include "Op/SegmentSum.h"
+#include "Op/Select.h"
+#include "Op/SelectV2.h"
+#include "Op/Shape.h"
+#include "Op/Sin.h"
+#include "Op/Slice.h"
+#include "Op/Softmax.h"
+#include "Op/SpaceToBatchND.h"
+#include "Op/SpaceToDepth.h"
+#include "Op/SparseToDense.h"
+#include "Op/Split.h"
+#include "Op/SplitV.h"
#include "Op/Sqrt.h"
+#include "Op/Square.h"
+#include "Op/SquaredDifference.h"
+#include "Op/Squeeze.h"
+#include "Op/StridedSlice.h"
#include "Op/Sub.h"
+#include "Op/Sum.h"
+#include "Op/Tanh.h"
+#include "Op/Tile.h"
+#include "Op/TopKV2.h"
+#include "Op/Transpose.h"
+#include "Op/TransposeConv.h"
+#include "Op/UnidirectionalSequenceLSTM.h"
+#include "Op/Unique.h"
+#include "Op/Unpack.h"
+#include "Op/Where.h"
+#include "Op/While.h"
+#include "Op/ZerosLike.h"
+
+#include "CustomOp/AddV2.h"
+#include "CustomOp/All.h"
+#include "CustomOp/BatchMatMulV2.h"
+#include "CustomOp/MatMul.h"
+#include "CustomOp/MatrixBandPart.h"
+#include "CustomOp/MaxPoolWithArgMax.h"
#endif // __OP_CHEFS_H__
diff --git a/compiler/tflchef/log/CMakeLists.txt b/compiler/tflchef/log/CMakeLists.txt
new file mode 100644
index 000000000..330459ec1
--- /dev/null
+++ b/compiler/tflchef/log/CMakeLists.txt
@@ -0,0 +1,7 @@
+# TODO Find how to test logging framework
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+add_library(tflchef_log STATIC ${SOURCES})
+target_include_directories(tflchef_log PUBLIC include)
+target_link_libraries(tflchef_log PUBLIC hermes)
+target_link_libraries(tflchef_log PRIVATE hermes_std)
diff --git a/compiler/tflchef/log/include/Log.h b/compiler/tflchef/log/include/Log.h
new file mode 100644
index 000000000..178fe31c4
--- /dev/null
+++ b/compiler/tflchef/log/include/Log.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLCHEF_LOG_H__
+#define __TFLCHEF_LOG_H__
+
+#include <hermes.h>
+
+namespace tflchef
+{
+
+/**
+ * @brief Logger Implementation
+ */
+class Logger final : public hermes::Source
+{
+public:
+ Logger(hermes::Context *ctx);
+ ~Logger();
+};
+
+/**
+ * @brief Logger Configuration
+ *
+ * Users are able to turn logging on/off via TFLCHEF_LOG environment variable.
+ */
+class LoggerConfig final : public hermes::Config
+{
+public:
+ LoggerConfig();
+
+public:
+ void configure(const hermes::Source *, hermes::Source::Setting &) const final;
+ void configure(const Logger *, hermes::Source::Setting &) const;
+
+private:
+ bool _enabled;
+};
+
+} // namespace tflchef
+
+#include "LoggingContext.h"
+
+/**
+ * HOW TO USE:
+ *
+ * LOGGER(l);
+ *
+ * INFO(l) << "Hello, World" << std::endl;
+ *
+ */
+#define LOGGER(name) ::tflchef::Logger name{::tflchef::LoggingContext::get()};
+
+// TODO Support FATAL, ERROR, WARN, and VERBOSE
+#define INFO(name) HERMES_INFO(name)
+
+// WARNING!
+//
+// THE CURRENT IMPLEMENTATION IS NOT THREAD SAFE.
+//
+
+#endif // __TFLCHEF_LOG_H__
diff --git a/compiler/tflchef/log/include/LoggingContext.h b/compiler/tflchef/log/include/LoggingContext.h
new file mode 100644
index 000000000..860099482
--- /dev/null
+++ b/compiler/tflchef/log/include/LoggingContext.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLCHEF_LOGGING_CONTEXT_H__
+#define __TFLCHEF_LOGGING_CONTEXT_H__
+
+#include <hermes.h>
+
+namespace tflchef
+{
+
+/**
+ * @brief Global logging context
+ */
+struct LoggingContext
+{
+ static hermes::Context *get(void);
+};
+
+} // namespace tflchef
+
+#endif // __TFLCHEF_LOGGING_CONTEXT_H__
diff --git a/compiler/tflchef/log/src/Log.cpp b/compiler/tflchef/log/src/Log.cpp
new file mode 100644
index 000000000..62c377745
--- /dev/null
+++ b/compiler/tflchef/log/src/Log.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 "Log.h"
+
+#include <cassert>
+#include <cstdlib>
+#include <iostream>
+
+// TODO Extract these lexical conversion routines as a library
+namespace
+{
+
+/**
+ * @brief Convert C-string as a value of type T
+ *
+ * safecast(s, v) returns v if s is nullptr.
+ */
+template <typename T> T safecast(const char *, const T &);
+
+template <> bool safecast<bool>(const char *s, const bool &value)
+{
+ return (s == nullptr) ? value : (std::stoi(s) != 0);
+}
+
+} // namespace
+
+//
+// Logger
+//
+namespace tflchef
+{
+
+Logger::Logger(hermes::Context *ctx) { activate(ctx->sources(), ctx->bus()); }
+Logger::~Logger() { deactivate(); }
+
+} // namespace tflchef
+
+//
+// LoggerConfig
+//
+namespace tflchef
+{
+
+LoggerConfig::LoggerConfig()
+{
+ // Turn on logging if TFLCHEF_LOG is set as non-zero value
+ _enabled = safecast<bool>(std::getenv("TFLCHEF_LOG"), false);
+}
+
+void LoggerConfig::configure(const hermes::Source *source, hermes::Source::Setting &setting) const
+{
+ // Let's ignore hermes::Sources if that is not a moco logger
+ if (auto logger = dynamic_cast<const Logger *>(source))
+ {
+ configure(logger, setting);
+ }
+}
+
+void LoggerConfig::configure(const Logger *, hermes::Source::Setting &setting) const
+{
+ if (_enabled)
+ {
+ // Enable all catagories
+ setting.accept_all();
+ }
+ else
+ {
+ // Disable all catagories
+ setting.reject_all();
+ }
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/log/src/LoggingContext.cpp b/compiler/tflchef/log/src/LoggingContext.cpp
new file mode 100644
index 000000000..0514dc38c
--- /dev/null
+++ b/compiler/tflchef/log/src/LoggingContext.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LoggingContext.h"
+#include "Log.h"
+
+#include <hermes/ConsoleReporter.h>
+
+#include <memory>
+
+namespace tflchef
+{
+
+hermes::Context *LoggingContext::get(void)
+{
+ static hermes::Context *ctx = nullptr;
+
+ if (ctx == nullptr)
+ {
+ ctx = new hermes::Context;
+ ctx->sinks()->append(std::make_unique<hermes::ConsoleReporter>());
+ ctx->config(std::make_unique<LoggerConfig>());
+ }
+
+ return ctx;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/proto/tflchef.proto b/compiler/tflchef/proto/tflchef.proto
index dc63f0ca4..48a682d94 100644
--- a/compiler/tflchef/proto/tflchef.proto
+++ b/compiler/tflchef/proto/tflchef.proto
@@ -2,16 +2,45 @@ syntax = "proto2";
package tflchef;
+//
+// Initial version
+// - Our initial version
+//
+// Version 1
+// - Backward compatible with Initial version
+// - Added Graph to represent sub graphs
+// - Added name, version(default as 1), graph in ModelRecipe
+//
+
// This enum value corresponds to TensorType in TensorFlow Lite schema
enum TensorType {
FLOAT32 = 0;
INT32 = 2;
+ UINT8 = 3;
+ INT64 = 4;
+ BOOL = 6;
+}
+
+enum DimensionType {
+ DENSE = 0;
+ SPARSE_CSR = 1;
+}
+
+enum SparseIndexVecType {
+ SparseIdxVecType_NONE = 0;
+ INT32VEC = 1;
+ UINT16VEC = 2;
+ UINT8VEC = 3;
}
message TensorShape {
repeated uint32 dim = 3;
}
+message ShapeSignature {
+ repeated int32 dim = 1;
+}
+
message TensorFiller {
optional string tag = 1;
repeated string arg = 2;
@@ -22,6 +51,30 @@ message TensorQuantization {
repeated float max = 2;
repeated float scale = 3;
repeated int64 zero_point = 4;
+ optional int32 quantized_dimension = 5 [default = 0];
+}
+
+message TensorSparsity {
+ message TraversalOrder {
+ repeated int32 dim = 1;
+ }
+ message BlockMap {
+ repeated int32 dim = 1;
+ }
+ message IndexVec {
+ repeated int32 dim = 1;
+ optional SparseIndexVecType type = 2;
+ }
+ message DimMetaData {
+ optional DimensionType format = 1;
+ optional int32 dense_size = 2;
+ optional IndexVec array_segments = 3;
+ optional IndexVec array_indices = 4;
+ }
+
+ optional TraversalOrder traversal_order = 1;
+ optional BlockMap block_map = 2;
+ repeated DimMetaData dim_metadata = 3;
}
message Operand {
@@ -30,6 +83,9 @@ message Operand {
optional TensorShape shape = 3;
optional TensorFiller filler = 4;
optional TensorQuantization quant = 5;
+ optional TensorSparsity sparsity = 6;
+ optional bool is_variable = 7 [default = false];
+ optional ShapeSignature shape_signature = 8;
}
// This enum value corresponds to Padding in TensorFlow Lite schema
@@ -42,7 +98,16 @@ enum Padding {
enum Activation {
NONE = 0;
RELU = 1;
+ RELU_N1_TO_1 = 2;
RELU6 = 3;
+ TANH = 4;
+ SIGN_BIT = 5;
+}
+
+// This enum value corresponds to MirrorPadMode in TensorFlow Lite schema
+enum MirrorPadMode {
+ REFLECT = 0;
+ SYMMETRIC = 1;
}
message Conv2DOptions
@@ -51,6 +116,8 @@ message Conv2DOptions
optional int32 stride_w = 2 [default = 1];
optional int32 stride_h = 3 [default = 1];
optional Activation activation = 4 [default = NONE];
+ optional int32 dilation_w_factor = 5 [default = 1];
+ optional int32 dilation_h_factor = 6 [default = 1];
}
message Pool2DOptions {
@@ -78,6 +145,12 @@ message DepthwiseConv2DOptions
optional int32 stride_h = 3 [default = 1];
optional int32 depth_multiplier = 4 [default = 1];
optional Activation activation = 5 [default = NONE];
+ optional int32 dilation_w_factor = 6 [default = 1];
+ optional int32 dilation_h_factor = 7 [default = 1];
+}
+
+message ScatterNdOptions {
+ // None
}
message SubOptions {
@@ -88,14 +161,359 @@ message DivOptions {
optional Activation activation = 1 [default = NONE];
}
+message FloorDivOptions {
+ // None
+}
+
+message FloorModOptions {
+ // None
+}
+
message FullyConnectedOptions {
optional Activation activation = 1 [default = NONE];
}
+message AddOptions {
+ optional Activation activation = 1 [default = NONE];
+}
+
+message AddNOptions {
+ // None
+}
+
+message ArgMaxOptions {
+ optional TensorType output_type = 1 [default = INT64];
+}
+
+message ArgMinOptions {
+ optional TensorType output_type = 1 [default = INT64];
+}
+
+message PackOptions {
+ optional int32 values_count = 1;
+ optional int32 axis = 2 [default = 0];
+}
+
+message PadOptions {
+ // None
+}
+
+message PadV2Options {
+ // None
+}
+
+message MirrorPadOptions {
+ optional MirrorPadMode mode = 1 [default = REFLECT];
+}
+
+message SoftmaxOptions {
+ optional float beta = 1 [default = 0.0];
+}
+
+message MulOptions {
+ optional Activation activation = 1 [default = NONE];
+}
+
+message NegOptions {
+ // None
+}
+
+message RangeOptions {
+ // None
+}
+
+message ReducerOptions {
+ optional bool keep_dims = 1 [ default = false ];
+}
+
+message SpaceToDepthOptions {
+ optional int32 block_size = 1;
+}
+
+message LogicalOrOptions {
+ // None
+}
+
+message LogicalNotOptions {
+ // None
+}
+
+message LogicalAndOptions {
+ // None
+}
+
+message TransposeOptions {
+ // None
+}
+
+message AbsOptions {
+ // None
+}
+
+message CosOptions {
+ // None
+}
+
+message EqualOptions {
+ // None
+}
+
+message ShapeOptions {
+ optional TensorType out_type = 1 [default = INT32];
+}
+
+message BatchToSpaceNDOptions {
+ // None
+}
+
+message SpaceToBatchNDOptions {
+ // None
+}
+
+message StridedSliceOptions {
+ optional int32 begin_mask = 1;
+ optional int32 end_mask = 2;
+ optional int32 ellipsis_mask = 3;
+ optional int32 new_axis_mask = 4;
+ optional int32 shrink_axis_mask = 5;
+}
+
+message SliceOptions {
+ // None
+}
+
+message ExpOptions {
+ // None
+}
+
+message ExpandDimsOptions {
+ // None
+}
+
+message UnpackOptions {
+ optional int32 num = 1;
+ optional int32 axis = 2 [default = 0];
+}
+
+message GatherOptions {
+ optional int32 axis = 1 [default = 0];
+}
+
+message TileOptions {
+ // None
+}
+
+message BatchMatMulOptions {
+ optional bool adj_x = 1 [default = false];
+ optional bool adj_y = 2 [default = false];
+}
+
+message IfOptions {
+ optional int32 then_subgraph_index = 1;
+ optional int32 else_subgraph_index = 2;
+}
+
+message WhileOptions {
+ optional int32 cond_subgraph_index = 1;
+ optional int32 body_subgraph_index = 2;
+}
+
+message CastOptions {
+ optional TensorType in_data_type = 1 [default = FLOAT32];
+ optional TensorType out_data_type = 2 [default = FLOAT32];
+}
+
+message SquareOptions {
+ // None
+}
+
+message MaximumMinimumOptions {
+ //None
+}
+
+message GreaterEqualOptions {
+ // None
+}
+
+message SelectOptions {
+ // None
+}
+
+message SelectV2Options {
+ // None
+}
+
+message SplitOptions {
+ optional int32 num_splits = 1;
+}
+
+message SplitVOptions {
+ optional int32 num_splits = 1;
+}
+
+message SquaredDifferenceOptions {
+ // None
+}
+
+message FillOptions {
+ // None
+}
+
+message GreaterOptions {
+ // None
+}
+
+message L2NormOptions {
+ optional Activation activation = 1 [default = NONE];
+}
+
+message LessOptions {
+ // None
+}
+
+message LessEqualOptions {
+ // None
+}
+
+message LocalResponseNormalizationOptions {
+ optional int32 radius = 1 [default = 5];
+ optional float bias = 2 [default = 1.0];
+ optional float alpha = 3 [default = 1.0];
+ optional float beta = 4 [default = 0.5];
+}
+
+message MatMulOptions {
+ optional bool transpose_a = 1 [default = false];
+ optional bool transpose_b = 2 [default = false];
+}
+
+message SqueezeOptions {
+ repeated int32 squeeze_dim = 1;
+}
+
+message OneHotOptions {
+ optional int32 axis = 1 [default = -1];
+}
+
+message TopKV2Options {
+ // None
+}
+
+message LogSoftmaxOptions {
+ // None
+}
+
+message ZerosLikeOptions {
+ // None
+}
+
+message GatherNdOptions {
+ // None
+}
+
+message NonMaxSuppressionV4Options {
+ // None
+}
+
+message NonMaxSuppressionV5Options {
+ // None
+}
+
+message NotEqualOptions {
+ // None
+}
+
+message PowOptions {
+ // None
+}
+
+message LeakyReluOptions {
+ optional float alpha = 1 [default = 0.2];
+}
+
+message ResizeNearestNeighborOptions {
+ optional bool align_corners = 1 [default = false];
+}
+
+message ResizeBilinearOptions {
+ optional bool align_corners = 1 [default = false];
+ optional bool half_pixel_centers = 2 [default = false];
+}
+
+message DepthToSpaceOptions {
+ optional int32 block_size = 1;
+}
+
+message TransposeConvOptions {
+ optional Padding padding = 1 [default = VALID];
+ optional int32 stride_w = 2 [default = 1];
+ optional int32 stride_h = 3 [default = 1];
+}
+
+message ReverseSequenceOptions {
+ optional int32 seq_dim = 1 [default = 0];
+ optional int32 batch_dim = 2 [default = 0];
+}
+
+message RankOptions {
+ // NONE
+}
+
+message SegmentSumOptions {
+ // NONE
+}
+
+message UnidirectionalSequenceLSTMOptions {
+ optional Activation activation = 1 [default = NONE];
+ optional float cell_clip = 2 [default = 0.0];
+ optional float proj_clip = 3 [default = 0.0];
+ optional bool time_major = 4 [default = false];
+ optional bool asymmetric_quantize_inputs = 5 [default = false];
+}
+
+message UniqueOptions {
+ optional TensorType idx_out_type = 1 [default = INT32];
+}
+
+message WhereOptions {
+ // None
+}
+
+message SparseToDenseOptions {
+ optional bool validate_indices = 1 [default = true];
+}
+
+message ReverseV2Options {
+ // None
+}
+
+message MatrixDiagOptions {
+ // NONE
+}
+
+message MatrixSetDiagOptions {
+ // NONE
+}
+
+message DequantizeOptions {
+ // NONE
+}
+
+message MaxPoolWithArgMaxOptions {
+ optional Padding padding = 1 [default = VALID];
+ optional int32 stride_w = 2 [default = 1];
+ optional int32 stride_h = 3 [default = 1];
+ optional int32 filter_width = 4 [default = 1];
+ optional int32 filter_height = 5 [ default = 1];
+ optional TensorType output_type = 6 [default = INT64];
+ optional bool include_batch_in_index = 7 [default = false];
+}
+
message Operation {
optional string type = 1;
repeated string input = 2;
repeated string output = 3;
+ optional int32 version = 4 [default = 1];
optional Conv2DOptions conv2d_options = 100;
optional Pool2DOptions averagepool2d_options = 101;
@@ -106,6 +524,118 @@ message Operation {
optional SubOptions sub_options = 106;
optional DivOptions div_options = 107;
optional FullyConnectedOptions fullyconnected_options = 108;
+ optional AddOptions add_options = 109;
+ optional ArgMaxOptions argmax_options = 110;
+ optional PadOptions pad_options = 111;
+ optional SoftmaxOptions softmax_options = 112;
+ optional MulOptions mul_options = 113;
+ optional ReducerOptions mean_options = 114;
+ optional TransposeOptions transpose_options = 115;
+ optional PackOptions pack_options = 116;
+ optional LogicalOrOptions logical_or_options = 117;
+ optional LogicalNotOptions logical_not_options = 118;
+ optional LogicalAndOptions logical_and_options = 119;
+ optional AbsOptions abs_options = 120;
+ optional CosOptions cos_options = 121;
+ optional EqualOptions equal_options = 122;
+ optional ShapeOptions shape_options = 123;
+ optional FloorDivOptions floordiv_options = 124;
+ optional BatchToSpaceNDOptions batch_to_space_options = 125;
+ optional ExpOptions exp_options = 126;
+ optional UnpackOptions unpack_options = 127;
+ optional GatherOptions gather_options = 128;
+ optional BatchMatMulOptions batch_matmul_options = 129;
+ optional TileOptions tile_options = 130;
+ optional IfOptions if_options = 131;
+ optional WhileOptions while_options = 132;
+ optional SpaceToBatchNDOptions space_to_batch_nd_options = 133;
+ optional CastOptions cast_options = 134;
+ optional GreaterEqualOptions greaterequal_options = 135;
+ optional MaximumMinimumOptions maximum_options = 136;
+ optional StridedSliceOptions strided_slice_options = 137;
+ optional SquaredDifferenceOptions squared_difference_options = 138;
+ optional FillOptions fill_options = 139;
+ optional SelectOptions select_options = 140;
+ optional ReducerOptions reduce_prod_options = 141;
+ optional SplitOptions split_options = 142;
+ optional SplitVOptions split_v_options = 143;
+ optional ReducerOptions sum_options = 144;
+ optional GreaterOptions greater_options = 145;
+ optional SqueezeOptions squeeze_options = 146;
+ optional FloorModOptions floormod_options = 147;
+ optional OneHotOptions onehot_options = 148;
+ optional LessOptions less_options = 149;
+ optional ReducerOptions reduce_max_options = 150;
+ optional MaximumMinimumOptions minimum_options = 151;
+ optional ReducerOptions reduce_any_options = 152;
+ optional ZerosLikeOptions zeros_like_options = 153;
+ // ConcatEmbeddingsOptions 154
+ // LSHProjectionOptions 155
+ // SVDFOptions 156
+ // RNNOptions 157
+ optional L2NormOptions l2norm_options = 158;
+ optional LocalResponseNormalizationOptions local_response_normalization_options = 159;
+ // LSTMOptions 160
+ optional ResizeBilinearOptions resize_bilinear_options = 161;
+ // CallOptions 162
+ // SkipGramOptions 163
+ optional SpaceToDepthOptions space_to_depth_options = 164;
+ // EmbeddingLookupSparseOptions 165
+ // SequenceRNNOptions 166
+ optional TopKV2Options topk_v2_options = 167;
+ optional LogSoftmaxOptions log_softmax_options = 168;
+ optional DequantizeOptions dequantize_options = 169;
+ optional NegOptions neg_options = 170;
+ optional PadV2Options padv2_options = 171;
+ optional LessEqualOptions lessequal_options = 172;
+ optional SliceOptions slice_options = 173;
+ optional TransposeConvOptions transpose_conv_options = 174;
+ optional SparseToDenseOptions sparse_to_dense_options = 175;
+ optional PowOptions pow_options = 176;
+ optional ArgMinOptions argmin_options = 177;
+ // FakeQuantOptions 178
+ // BidirectionalSequenceLSTMOptions 179
+ // BidirectionalSequenceRNNOptions 180
+ optional UnidirectionalSequenceLSTMOptions unidirectional_sequence_lstm_options = 181;
+ optional RangeOptions range_options = 182;
+ optional ResizeNearestNeighborOptions resize_nearest_neighbor_options = 183;
+ optional LeakyReluOptions leaky_relu_options = 184;
+ optional MirrorPadOptions mirrorpad_options = 185;
+ optional UniqueOptions unique_options = 186;
+ optional ReverseV2Options reversev2_options = 187;
+ // AddNOptions 188
+ optional GatherNdOptions gather_nd_options = 189;
+ optional WhereOptions where_options = 190;
+ optional RankOptions rank_options = 191;
+ optional ReverseSequenceOptions reverse_sequence_options = 192;
+ optional MatrixDiagOptions matrix_diag_options = 193;
+ // QuantizeOptions 194
+ optional MatrixSetDiagOptions matrix_set_diag_options = 195;
+ // HardSwishOptions 196
+ optional DepthToSpaceOptions depth_to_space_options = 197;
+ optional NonMaxSuppressionV4Options non_max_suppression_v4_options = 198;
+ optional NonMaxSuppressionV5Options non_max_suppression_v5_options = 199;
+ optional ScatterNdOptions scatter_nd_options = 200;
+ optional NotEqualOptions notequal_options = 201;
+ optional ExpandDimsOptions expand_dims_options = 202;
+ optional Pool2DOptions l2pool2d_options = 203;
+ optional ReducerOptions all_options = 204;
+ optional ReducerOptions reduce_min_options = 205;
+ optional SegmentSumOptions segment_sum_options = 206;
+ optional AddNOptions add_n_options = 207;
+ optional MatMulOptions matmul_options = 208;
+ optional MaxPoolWithArgMaxOptions max_pool_with_argmax_options = 209;
+ // NOTE if there are more than two options with same type of Options
+ // use the number not listed in the above reserve list
+}
+
+// For additional subgraphs
+message Graph {
+ repeated Operand operand = 1;
+ repeated Operation operation = 2;
+ repeated string input = 3;
+ repeated string output = 4;
+ optional string name = 5;
}
message ModelRecipe {
@@ -113,4 +643,7 @@ message ModelRecipe {
repeated Operation operation = 2;
repeated string input = 3;
repeated string output = 4;
+ optional string name = 5;
+ optional uint32 version = 6 [default = 1];
+ repeated Graph graph = 7;
}
diff --git a/compiler/tflchef/requires.cmake b/compiler/tflchef/requires.cmake
index 5f055bbf8..4c02174b5 100644
--- a/compiler/tflchef/requires.cmake
+++ b/compiler/tflchef/requires.cmake
@@ -1,2 +1,9 @@
+require("arser")
require("nnkit")
require("cwrap")
+require("mio-tflite")
+require("safemain")
+require("hermes")
+require("hermes-std")
+require("foder")
+require("souschef")
diff --git a/compiler/tflchef/tests/CMakeLists.txt b/compiler/tflchef/tests/CMakeLists.txt
index e55f4c72c..5c4dff012 100644
--- a/compiler/tflchef/tests/CMakeLists.txt
+++ b/compiler/tflchef/tests/CMakeLists.txt
@@ -6,34 +6,62 @@ if(NOT TARGET nnkit_tflite_backend)
return()
endif(NOT TARGET nnkit_tflite_backend)
-file(GLOB RECIPES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*/test.recipe")
+nncc_find_resource(TensorFlowLiteRecipes)
+set(TENSORFLOWLITERECIPES_DIR "${TensorFlowLiteRecipes_DIR}")
+
+file(GLOB RECIPES RELATIVE ${TENSORFLOWLITERECIPES_DIR} "${TENSORFLOWLITERECIPES_DIR}/*/test.recipe")
foreach(RECIPE IN ITEMS ${RECIPES})
get_filename_component(RECIPE_PREFIX ${RECIPE} DIRECTORY)
set(RECIPE_SOURCE_FILE "${RECIPE_PREFIX}.recipe")
- set(RECIPE_SOURCE_TARGET tflchef_${RECIPE_PREFIX}_recipe)
+ set(RECIPE_OUTPUT_FILE "${RECIPE_PREFIX}.tflite")
+
+ # Copy .recipe
+ add_custom_command(OUTPUT ${RECIPE_SOURCE_FILE}
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
+ "${TENSORFLOWLITERECIPES_DIR}/${RECIPE}" ${RECIPE_SOURCE_FILE}
+ DEPENDS "${TENSORFLOWLITERECIPES_DIR}/${RECIPE}"
+ COMMENT "Generating ${RECIPE_SOURCE_FILE}")
+ # Generate .tflite
+ add_custom_command(OUTPUT ${RECIPE_OUTPUT_FILE}
+ COMMAND tflchef-file ${RECIPE_SOURCE_FILE} ${RECIPE_OUTPUT_FILE}
+ DEPENDS tflchef-file ${RECIPE_SOURCE_FILE}
+ COMMENT "Generating ${RECIPE_OUTPUT_FILE}")
+
+ list(APPEND TESTS ${RECIPE_PREFIX})
+ list(APPEND TESTFILES ${RECIPE_OUTPUT_FILE})
+endforeach(RECIPE)
+
+# Add local files
+file(GLOB RECIPES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*/test.recipe")
+
+foreach(RECIPE IN ITEMS ${RECIPES})
+ get_filename_component(RECIPE_PREFIX ${RECIPE} DIRECTORY)
+
+ set(RECIPE_SOURCE_FILE "${RECIPE_PREFIX}.recipe")
set(RECIPE_OUTPUT_FILE "${RECIPE_PREFIX}.tflite")
- set(RECIPE_OUTPUT_TARGET tflchef_${RECIPE_PREFIX}_tflite)
# Copy .recipe
- add_custom_target(${RECIPE_SOURCE_TARGET}
- ALL ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/${RECIPE}"
- "${CMAKE_CURRENT_BINARY_DIR}/${RECIPE_SOURCE_FILE}"
- COMMENT "Generate ${RECIPE_PREFIX}.recipe")
+ add_custom_command(OUTPUT ${RECIPE_SOURCE_FILE}
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
+ "${CMAKE_CURRENT_SOURCE_DIR}/${RECIPE}" ${RECIPE_SOURCE_FILE}
+ DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${RECIPE}"
+ COMMENT "Generating ${RECIPE_SOURCE_FILE}")
# Generate .tflite
- add_custom_target(${RECIPE_OUTPUT_TARGET}
- ALL $<TARGET_FILE:tflchef-file> ${RECIPE_SOURCE_FILE} ${RECIPE_OUTPUT_FILE}
- DEPENDS ${RECIPE_SOURCE_TARGET}
- COMMENT "Generate ${RECIPE_PREFIX}.tflite")
+ add_custom_command(OUTPUT ${RECIPE_OUTPUT_FILE}
+ COMMAND tflchef-file ${RECIPE_SOURCE_FILE} ${RECIPE_OUTPUT_FILE}
+ DEPENDS tflchef-file ${RECIPE_SOURCE_FILE}
+ COMMENT "Generating ${RECIPE_OUTPUT_FILE}")
list(APPEND TESTS ${RECIPE_PREFIX})
+ list(APPEND TESTFILES ${RECIPE_OUTPUT_FILE})
endforeach(RECIPE)
# Test tflchef-reverse
-file(GLOB GEN_TFLITEFILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*/test.reverse")
+file(GLOB GEN_TFLITEFILES RELATIVE ${TENSORFLOWLITERECIPES_DIR} "${TENSORFLOWLITERECIPES_DIR}/*/test.reverse")
# Note: While in development, tflchef-reverse may not handle the operator.
# To separate this linkage scan empty test.reverse for test targets for tflchef-reverse.
@@ -43,34 +71,59 @@ foreach(TFLITEFILE IN ITEMS ${GEN_TFLITEFILES})
# file from above tflchef-file block
# use tflite file as input of tflchef-reverse generated from tflchef-file
set(RECIPE_OUTPUT_FILE "${TFLITE_PREFIX}.tflite")
- set(RECIPE_OUTPUT_TARGET tflchef_${TFLITE_PREFIX}_tflite)
-
set(RECIPE_GEN_OUTPUT_FILE "${TFLITE_PREFIX}.gen.recipe")
- set(RECIPE_GEN_OUTPUT_TARGET tflchef_${TFLITE_PREFIX}_gen_recipe)
+ set(RECIPE_GEN_OUTPUT_FILE2 "${TFLITE_PREFIX}.gen.tflite")
# Generate .gen.recipe from generated .tflite
- add_custom_target(${RECIPE_GEN_OUTPUT_TARGET}
- ALL $<TARGET_FILE:tflchef-reverse> ${RECIPE_OUTPUT_FILE} ${RECIPE_GEN_OUTPUT_FILE}
- DEPENDS ${RECIPE_OUTPUT_TARGET}
- COMMENT "Generate ${TFLITE_PREFIX}.gen.recipe")
+ add_custom_command(OUTPUT ${RECIPE_GEN_OUTPUT_FILE}
+ COMMAND tflchef-reverse ${RECIPE_OUTPUT_FILE} ${RECIPE_GEN_OUTPUT_FILE}
+ DEPENDS tflchef-reverse ${RECIPE_OUTPUT_FILE}
+ COMMENT "Generating ${RECIPE_GEN_OUTPUT_FILE}")
# now we are going to generate .gen.tflite from .gen.recipe
# to check generated .gen.recipe file is correct by using it.
# as weight values may be different, binary comparision is not acceptable.
+ add_custom_command(OUTPUT ${RECIPE_GEN_OUTPUT_FILE2}
+ COMMAND tflchef-file ${RECIPE_GEN_OUTPUT_FILE} ${RECIPE_GEN_OUTPUT_FILE2}
+ DEPENDS tflchef-file ${RECIPE_GEN_OUTPUT_FILE}
+ COMMENT "Generating ${RECIPE_GEN_OUTPUT_FILE2}")
+
+ list(APPEND TESTS ${TFLITE_PREFIX}.gen)
+ list(APPEND TESTFILES ${RECIPE_GEN_OUTPUT_FILE2})
+endforeach(TFLITEFILE)
+
+# Test local tflchef-reverse
+file(GLOB GEN_TFLITEFILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*/test.reverse")
+
+foreach(TFLITEFILE IN ITEMS ${GEN_TFLITEFILES})
+ get_filename_component(TFLITE_PREFIX ${TFLITEFILE} DIRECTORY)
+
+ set(RECIPE_OUTPUT_FILE "${TFLITE_PREFIX}.tflite")
+ set(RECIPE_GEN_OUTPUT_FILE "${TFLITE_PREFIX}.gen.recipe")
set(RECIPE_GEN_OUTPUT_FILE2 "${TFLITE_PREFIX}.gen.tflite")
- set(RECIPE_GEN_OUTPUT_TARGET2 tflchef_${TFLITE_PREFIX}_gen_tflite)
- add_custom_target(${RECIPE_GEN_OUTPUT_TARGET2}
- ALL $<TARGET_FILE:tflchef-file> ${RECIPE_GEN_OUTPUT_FILE} ${RECIPE_GEN_OUTPUT_FILE2}
- DEPENDS ${RECIPE_GEN_OUTPUT_TARGET}
- COMMENT "Generate ${TFLITE_PREFIX}.gen.tflite")
+ # Generate .gen.recipe from generated .tflite
+ add_custom_command(OUTPUT ${RECIPE_GEN_OUTPUT_FILE}
+ COMMAND tflchef-reverse ${RECIPE_OUTPUT_FILE} ${RECIPE_GEN_OUTPUT_FILE}
+ DEPENDS tflchef-reverse ${RECIPE_OUTPUT_FILE}
+ COMMENT "Generating ${RECIPE_GEN_OUTPUT_FILE}")
+
+ add_custom_command(OUTPUT ${RECIPE_GEN_OUTPUT_FILE2}
+ COMMAND tflchef-file ${RECIPE_GEN_OUTPUT_FILE} ${RECIPE_GEN_OUTPUT_FILE2}
+ DEPENDS tflchef-file ${RECIPE_GEN_OUTPUT_FILE}
+ COMMENT "Generating ${RECIPE_GEN_OUTPUT_FILE2}")
list(APPEND TESTS ${TFLITE_PREFIX}.gen)
+ list(APPEND TESTFILES ${RECIPE_GEN_OUTPUT_FILE2})
endforeach(TFLITEFILE)
+# Add a dummy target to create a target-level dependency.
+# TODO Find a way to create a dependency between tflchef_test and generated testfiles.
+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
add_test(NAME tflchef_test
- COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/runall.sh"
- $<TARGET_FILE:nnkit-run>
- $<TARGET_FILE:nnkit_tflite_backend>
- "${CMAKE_CURRENT_BINARY_DIR}"
+ COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/runvalidate.sh"
+ $<TARGET_FILE:mio_tflite_validate>
${TESTS})
diff --git a/compiler/tflchef/tests/averagepool2d/test.recipe b/compiler/tflchef/tests/averagepool2d/test.recipe
deleted file mode 100644
index 746c34334..000000000
--- a/compiler/tflchef/tests/averagepool2d/test.recipe
+++ /dev/null
@@ -1,24 +0,0 @@
-operand {
- name: "ifm"
- type: FLOAT32
- shape { dim: 1 dim: 8 dim: 8 dim: 1 }
-}
-operand {
- name: "ofm"
- type: FLOAT32
- shape { dim: 1 dim: 7 dim: 7 dim: 1 }
-}
-operation {
- type: "AveragePool2D"
- averagepool2d_options {
- padding: VALID
- stride_w: 1
- stride_h: 1
- filter_width: 2
- filter_height: 2
- }
- input: "ifm"
- output: "ofm"
-}
-input: "ifm"
-output: "ofm"
diff --git a/compiler/tflchef/tests/concatenation/test.recipe b/compiler/tflchef/tests/concatenation/test.recipe
deleted file mode 100644
index 35641bd07..000000000
--- a/compiler/tflchef/tests/concatenation/test.recipe
+++ /dev/null
@@ -1,28 +0,0 @@
-operand {
- name: "ifm1"
- type: FLOAT32
- shape { dim: 1 dim: 4 dim: 4 dim: 1 }
-}
-operand {
- name: "ifm2"
- type: FLOAT32
- shape { dim: 1 dim: 4 dim: 4 dim: 2 }
-}
-operand {
- name: "ofm"
- type: FLOAT32
- shape { dim: 1 dim: 4 dim: 4 dim: 3 }
-}
-operation {
- type: "Concatenation"
- concatenation_options {
- axis: 3
- activation: NONE
- }
- input: "ifm1"
- input: "ifm2"
- output: "ofm"
-}
-input: "ifm1"
-input: "ifm2"
-output: "ofm"
diff --git a/compiler/tflchef/tests/conv2d/test.recipe b/compiler/tflchef/tests/conv2d/test.recipe
deleted file mode 100644
index 9cf8a0f69..000000000
--- a/compiler/tflchef/tests/conv2d/test.recipe
+++ /dev/null
@@ -1,44 +0,0 @@
-operand {
- name: "ifm"
- type: FLOAT32
- shape { dim: 1 dim: 3 dim: 3 dim: 2 }
-}
-operand {
- name: "ker"
- type: FLOAT32
- shape { dim: 1 dim: 1 dim: 1 dim: 2 }
- filler {
- tag: "gaussian"
- arg: "0.0"
- arg: "1.0"
- }
-}
-operand {
- name: "bias"
- type: FLOAT32
- shape { dim: 1 }
- filler {
- tag: "gaussian"
- arg: "0.0"
- arg: "1.0"
- }
-}
-operand {
- name: "ofm"
- type: FLOAT32
- shape { dim: 1 dim: 3 dim: 3 dim: 1 }
-}
-operation {
- type: "Conv2D"
- conv2d_options {
- padding: VALID
- stride_w: 1
- stride_h: 1
- }
- input: "ifm"
- input: "ker"
- input: "bias"
- output: "ofm"
-}
-input: "ifm"
-output: "ofm"
diff --git a/compiler/tflchef/tests/conv2d/test.reverse b/compiler/tflchef/tests/conv2d/test.reverse
deleted file mode 100644
index e69de29bb..000000000
--- a/compiler/tflchef/tests/conv2d/test.reverse
+++ /dev/null
diff --git a/compiler/tflchef/tests/depthwiseconv2d/test.recipe b/compiler/tflchef/tests/depthwiseconv2d/test.recipe
deleted file mode 100644
index 17a3b06c7..000000000
--- a/compiler/tflchef/tests/depthwiseconv2d/test.recipe
+++ /dev/null
@@ -1,41 +0,0 @@
-operand {
- name: "ifm"
- type: FLOAT32
- shape { dim: 1 dim: 64 dim: 64 dim: 8 }
-}
-operand {
- name: "ker"
- type: FLOAT32
- shape { dim: 1 dim: 3 dim: 3 dim: 8 }
-}
-operand {
- name: "bias"
- type: FLOAT32
- shape { dim: 8 }
- filler {
- tag: "constant"
- arg: "1.1"
- }
-}
-operand {
- name: "ofm"
- type: FLOAT32
- shape { dim: 1 dim: 64 dim: 64 dim: 8 }
-}
-operation {
- type: "DepthwiseConv2D"
- depthwiseconv2d_options {
- padding: SAME
- stride_w: 1
- stride_h: 1
- depth_multiplier: 1
- activation : RELU6
- }
- input: "ifm"
- input: "ker"
- input: "bias"
- output: "ofm"
-}
-input: "ifm"
-input: "ker"
-output: "ofm"
diff --git a/compiler/tflchef/tests/depthwiseconv2d/test.reverse b/compiler/tflchef/tests/depthwiseconv2d/test.reverse
deleted file mode 100644
index e69de29bb..000000000
--- a/compiler/tflchef/tests/depthwiseconv2d/test.reverse
+++ /dev/null
diff --git a/compiler/tflchef/tests/div/test.recipe b/compiler/tflchef/tests/div/test.recipe
deleted file mode 100644
index 4fb76d467..000000000
--- a/compiler/tflchef/tests/div/test.recipe
+++ /dev/null
@@ -1,27 +0,0 @@
-operand {
- name: "ifm1"
- type: FLOAT32
- shape { dim: 1 dim: 4 dim: 4 dim: 3 }
-}
-operand {
- name: "ifm2"
- type: FLOAT32
- shape { dim: 1 dim: 4 dim: 4 dim: 3 }
-}
-operand {
- name: "ofm"
- type: FLOAT32
- shape { dim: 1 dim: 4 dim: 4 dim: 3 }
-}
-operation {
- type: "Div"
- input: "ifm1"
- input: "ifm2"
- output: "ofm"
- div_options {
- activation: NONE
- }
-}
-input: "ifm1"
-input: "ifm2"
-output: "ofm"
diff --git a/compiler/tflchef/tests/div/test.reverse b/compiler/tflchef/tests/div/test.reverse
deleted file mode 100644
index e69de29bb..000000000
--- a/compiler/tflchef/tests/div/test.reverse
+++ /dev/null
diff --git a/compiler/tflchef/tests/fullyconnected/test.recipe b/compiler/tflchef/tests/fullyconnected/test.recipe
deleted file mode 100644
index dca4c09f0..000000000
--- a/compiler/tflchef/tests/fullyconnected/test.recipe
+++ /dev/null
@@ -1,34 +0,0 @@
-operand {
- name: "in"
- type: FLOAT32
- shape { dim: 1 dim: 64 }
-}
-operand {
- name: "weight"
- type: FLOAT32
- shape { dim: 8 dim: 64 }
-}
-operand {
- name: "bias"
- type: FLOAT32
- shape { dim: 8 }
-}
-operand {
- name: "out"
- type: FLOAT32
- shape { dim: 1 dim: 8 }
-}
-operation {
- type: "FullyConnected"
- fullyconnected_options {
- activation: NONE
- }
- input: "in"
- input: "weight"
- input: "bias"
- output: "out"
-}
-input: "in"
-input: "weight"
-input: "bias"
-output: "out"
diff --git a/compiler/tflchef/tests/fullyconnected/test.reverse b/compiler/tflchef/tests/fullyconnected/test.reverse
deleted file mode 100644
index e69de29bb..000000000
--- a/compiler/tflchef/tests/fullyconnected/test.reverse
+++ /dev/null
diff --git a/compiler/tflchef/tests/fullyconnected2/test.recipe b/compiler/tflchef/tests/fullyconnected2/test.recipe
deleted file mode 100644
index e404f759f..000000000
--- a/compiler/tflchef/tests/fullyconnected2/test.recipe
+++ /dev/null
@@ -1,34 +0,0 @@
-operand {
- name: "in"
- type: FLOAT32
- shape { dim: 1 dim: 1 dim: 1 dim: 4 }
-}
-operand {
- name: "weight"
- type: FLOAT32
- shape { dim: 2 dim: 4 }
-}
-operand {
- name: "bias"
- type: FLOAT32
- shape { dim: 2 }
-}
-operand {
- name: "out"
- type: FLOAT32
- shape { dim: 1 dim: 2 }
-}
-operation {
- type: "FullyConnected"
- fullyconnected_options {
- activation: NONE
- }
- input: "in"
- input: "weight"
- input: "bias"
- output: "out"
-}
-input: "in"
-input: "weight"
-input: "bias"
-output: "out"
diff --git a/compiler/tflchef/tests/fullyconnected2/test.reverse b/compiler/tflchef/tests/fullyconnected2/test.reverse
deleted file mode 100644
index e69de29bb..000000000
--- a/compiler/tflchef/tests/fullyconnected2/test.reverse
+++ /dev/null
diff --git a/compiler/tflchef/tests/maxpool2d/test.recipe b/compiler/tflchef/tests/maxpool2d/test.recipe
deleted file mode 100644
index 718630f08..000000000
--- a/compiler/tflchef/tests/maxpool2d/test.recipe
+++ /dev/null
@@ -1,24 +0,0 @@
-operand {
- name: "ifm"
- type: FLOAT32
- shape { dim: 1 dim: 8 dim: 8 dim: 1 }
-}
-operand {
- name: "ofm"
- type: FLOAT32
- shape { dim: 1 dim: 7 dim: 7 dim: 1 }
-}
-operation {
- type: "MaxPool2D"
- maxpool2d_options {
- padding: VALID
- stride_w: 1
- stride_h: 1
- filter_width: 2
- filter_height: 2
- }
- input: "ifm"
- output: "ofm"
-}
-input: "ifm"
-output: "ofm"
diff --git a/compiler/tflchef/tests/maxpool2d/test.reverse b/compiler/tflchef/tests/maxpool2d/test.reverse
deleted file mode 100644
index e69de29bb..000000000
--- a/compiler/tflchef/tests/maxpool2d/test.reverse
+++ /dev/null
diff --git a/compiler/tflchef/tests/multisubgraph/test.recipe b/compiler/tflchef/tests/multisubgraph/test.recipe
new file mode 100644
index 000000000..b55af1337
--- /dev/null
+++ b/compiler/tflchef/tests/multisubgraph/test.recipe
@@ -0,0 +1,72 @@
+version: 1
+
+graph {
+ 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: "ReLU"
+ input: "ifm"
+ output: "ofm"
+ }
+ input: "ifm"
+ output: "ofm"
+ name: "Sub_01"
+}
+
+graph {
+ 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: "ReLU6"
+ input: "ifm"
+ output: "ofm"
+ }
+ input: "ifm"
+ output: "ofm"
+ name: "Sub_01"
+}
+
+operand {
+ name: "ifm1"
+ type: FLOAT32
+ shape { dim: 1 dim: 3 dim: 3 dim: 2 }
+}
+operand {
+ name: "ifm2"
+ 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: "Add"
+ input: "ifm1"
+ input: "ifm2"
+ output: "ofm"
+ add_options {
+ activation: NONE
+ }
+}
+input: "ifm1"
+input: "ifm2"
+output: "ofm"
+name: "Main"
diff --git a/compiler/tflchef/tests/no_shape/test.recipe b/compiler/tflchef/tests/no_shape/test.recipe
new file mode 100644
index 000000000..38efef96a
--- /dev/null
+++ b/compiler/tflchef/tests/no_shape/test.recipe
@@ -0,0 +1,43 @@
+operand {
+ name: "indices"
+ type: INT32
+ shape { dim: 4 }
+}
+operand {
+ name: "depth"
+ type: INT32
+ # shape is intentionally omitted here
+ filler { tag: "explicit" arg: "1" }
+}
+operand {
+ name: "on_value"
+ type: INT32
+ # shape is intentionally omitted here
+ filler { tag: "explicit" arg: "1" }
+}
+operand {
+ name: "off_value"
+ type: INT32
+ # shape is intentionally omitted here
+ filler { tag: "explicit" arg: "0" }
+}
+operand {
+ name: "ofm"
+ type: INT32
+ shape { dim: 4 dim: 1 }
+}
+operation {
+ type: "OneHot"
+ onehot_options {
+ axis: -1
+ }
+ input: "indices"
+ input: "depth"
+ input: "on_value"
+ input: "off_value"
+ output: "ofm"
+}
+input: "indices"
+input: "on_value"
+input: "off_value"
+output: "ofm"
diff --git a/compiler/tflchef/tests/averagepool2d/test.reverse b/compiler/tflchef/tests/no_shape/test.reverse
index e69de29bb..e69de29bb 100644
--- a/compiler/tflchef/tests/averagepool2d/test.reverse
+++ b/compiler/tflchef/tests/no_shape/test.reverse
diff --git a/compiler/tflchef/tests/quantization/test.recipe b/compiler/tflchef/tests/quantization/test.recipe
deleted file mode 100644
index be5d222a2..000000000
--- a/compiler/tflchef/tests/quantization/test.recipe
+++ /dev/null
@@ -1,46 +0,0 @@
-operand {
- name: "ifm"
- type: FLOAT32
- shape { dim: 1 dim: 3 dim: 3 dim: 2 }
- quant { min: 0 max: 128 scale: 2 zero_point: 2 }
-}
-operand {
- name: "ker"
- type: FLOAT32
- shape { dim: 1 dim: 1 dim: 1 dim: 2 }
- filler {
- tag: "gaussian"
- arg: "0.0"
- arg: "1.0"
- }
-}
-operand {
- name: "bias"
- type: FLOAT32
- shape { dim: 1 }
- filler {
- tag: "gaussian"
- arg: "0.0"
- arg: "1.0"
- }
-}
-operand {
- name: "ofm"
- type: FLOAT32
- shape { dim: 1 dim: 3 dim: 3 dim: 1 }
- quant { min: 0 max: 80 scale: 1.5 zero_point: 3 }
-}
-operation {
- type: "Conv2D"
- conv2d_options {
- padding: VALID
- stride_w: 1
- stride_h: 1
- }
- input: "ifm"
- input: "ker"
- input: "bias"
- output: "ofm"
-}
-input: "ifm"
-output: "ofm"
diff --git a/compiler/tflchef/tests/quantization/test.reverse b/compiler/tflchef/tests/quantization/test.reverse
deleted file mode 100644
index e69de29bb..000000000
--- a/compiler/tflchef/tests/quantization/test.reverse
+++ /dev/null
diff --git a/compiler/tflchef/tests/relu/test.recipe b/compiler/tflchef/tests/relu/test.recipe
deleted file mode 100644
index 8eaa3602f..000000000
--- a/compiler/tflchef/tests/relu/test.recipe
+++ /dev/null
@@ -1,17 +0,0 @@
-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: "ReLU"
- input: "ifm"
- output: "ofm"
-}
-input: "ifm"
-output: "ofm"
diff --git a/compiler/tflchef/tests/relu/test.reverse b/compiler/tflchef/tests/relu/test.reverse
deleted file mode 100644
index e69de29bb..000000000
--- a/compiler/tflchef/tests/relu/test.reverse
+++ /dev/null
diff --git a/compiler/tflchef/tests/relu6/test.recipe b/compiler/tflchef/tests/relu6/test.recipe
deleted file mode 100644
index 226593593..000000000
--- a/compiler/tflchef/tests/relu6/test.recipe
+++ /dev/null
@@ -1,17 +0,0 @@
-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: "ReLU6"
- input: "ifm"
- output: "ofm"
-}
-input: "ifm"
-output: "ofm"
diff --git a/compiler/tflchef/tests/relu6/test.reverse b/compiler/tflchef/tests/relu6/test.reverse
deleted file mode 100644
index e69de29bb..000000000
--- a/compiler/tflchef/tests/relu6/test.reverse
+++ /dev/null
diff --git a/compiler/tflchef/tests/reshape/test.recipe b/compiler/tflchef/tests/reshape/test.recipe
deleted file mode 100644
index cdca58980..000000000
--- a/compiler/tflchef/tests/reshape/test.recipe
+++ /dev/null
@@ -1,20 +0,0 @@
-operand {
- name: "ifm"
- type: FLOAT32
- shape { dim: 1 dim: 1 dim: 1 dim: 10 }
-}
-operand {
- name: "ofm"
- type: FLOAT32
- shape { dim: 10 }
-}
-operation {
- type: "Reshape"
- reshape_options {
- new_shape: 10
- }
- input: "ifm"
- output: "ofm"
-}
-input: "ifm"
-output: "ofm"
diff --git a/compiler/tflchef/tests/reshape/test.reverse b/compiler/tflchef/tests/reshape/test.reverse
deleted file mode 100644
index e69de29bb..000000000
--- a/compiler/tflchef/tests/reshape/test.reverse
+++ /dev/null
diff --git a/compiler/tflchef/tests/runvalidate.sh b/compiler/tflchef/tests/runvalidate.sh
new file mode 100755
index 000000000..0dd9d16e0
--- /dev/null
+++ b/compiler/tflchef/tests/runvalidate.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+
+if [[ $# -le 2 ]]; then
+ echo "USAGE: $0 [mio_tflite_validate path] [prefix 0] "
+ exit 255
+fi
+
+MIO_TFLITE_VALIDATE_PATH="$1"; shift
+
+echo "-- Found mio_tflite_validate: ${MIO_TFLITE_VALIDATE_PATH}"
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+pushd "${WORKDIR}"
+while [[ $# -ne 0 ]]; do
+ PREFIX="$1"; shift
+
+ TESTED+=("${PREFIX}")
+
+ PASSED_TAG="${PREFIX}.passed"
+
+ rm -f "${PASSED_TAG}"
+
+ cat > "${PREFIX}.log" <(
+ exec 2>&1
+
+ echo "'${MIO_TFLITE_VALIDATE_PATH}' '${PREFIX}.tflite'"
+ "${MIO_TFLITE_VALIDATE_PATH}" "${PREFIX}.tflite"
+
+ if [[ $? -eq 0 ]]; then
+ touch "${PASSED_TAG}"
+ fi
+ )
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ PASSED+=("$PREFIX")
+ else
+ FAILED+=("$PREFIX")
+ fi
+done
+popd
+
+echo "SUMMARY: ${#PASSED[@]} PASS AND ${#FAILED[@]} FAIL AMONG ${#TESTED[@]} TESTS"
+
+if [[ ${#TESTED[@]} -ne ${#PASSED[@]} ]]; then
+ echo "FAILED"
+ for TEST in "${FAILED[@]}"
+ do
+ echo "- ${TEST}"
+ done
+ exit 255
+fi
+
+exit 0
diff --git a/compiler/tflchef/tests/shape_signature/test.recipe b/compiler/tflchef/tests/shape_signature/test.recipe
new file mode 100644
index 000000000..fa4293e35
--- /dev/null
+++ b/compiler/tflchef/tests/shape_signature/test.recipe
@@ -0,0 +1,19 @@
+operand {
+ name: "ifm"
+ type: FLOAT32
+ shape { dim: 1 dim: 3 dim: 3 dim: 2 }
+ shape_signature { dim: -1 dim: 3 dim: 3 dim: 2 }
+}
+operand {
+ name: "ofm"
+ type: FLOAT32
+ shape { dim: 1 dim: 3 dim: 3 dim: 2 }
+ shape_signature { dim: -1 dim: 3 dim: 3 dim: 2 }
+}
+operation {
+ type: "ReLU"
+ input: "ifm"
+ output: "ofm"
+}
+input: "ifm"
+output: "ofm"
diff --git a/compiler/tflchef/tests/concatenation/test.reverse b/compiler/tflchef/tests/shape_signature/test.reverse
index e69de29bb..e69de29bb 100644
--- a/compiler/tflchef/tests/concatenation/test.reverse
+++ b/compiler/tflchef/tests/shape_signature/test.reverse
diff --git a/compiler/tflchef/tests/sqrt/test.recipe b/compiler/tflchef/tests/sqrt/test.recipe
deleted file mode 100644
index 1754f9a58..000000000
--- a/compiler/tflchef/tests/sqrt/test.recipe
+++ /dev/null
@@ -1,18 +0,0 @@
-operand {
- name: "ifm"
- type: FLOAT32
- shape { dim: 1 dim: 3 dim: 3 dim: 2 }
- filler { tag: "constant" arg: "3.5" }
-}
-operand {
- name: "ofm"
- type: FLOAT32
- shape { dim: 1 dim: 3 dim: 3 dim: 2 }
-}
-operation {
- type: "Sqrt"
- input: "ifm"
- output: "ofm"
-}
-input: "ifm"
-output: "ofm"
diff --git a/compiler/tflchef/tests/sub/test.recipe b/compiler/tflchef/tests/sub/test.recipe
deleted file mode 100644
index c934bb8aa..000000000
--- a/compiler/tflchef/tests/sub/test.recipe
+++ /dev/null
@@ -1,27 +0,0 @@
-operand {
- name: "ifm1"
- type: FLOAT32
- shape { dim: 1 dim: 5 dim:2 dim:3 }
-}
-operand {
- name: "ifm2"
- type: FLOAT32
- shape { dim: 1 dim: 5 dim:2 dim:3 }
-}
-operand {
- name: "ofm"
- type: FLOAT32
- shape { dim: 1 dim: 5 dim:2 dim:3 }
-}
-operation {
- type: "Sub"
- sub_options {
- activation: 0
- }
- input: "ifm1"
- input: "ifm2"
- output: "ofm"
-}
-input: "ifm1"
-input: "ifm2"
-output: "ofm"
diff --git a/compiler/tflchef/tests/sub/test.reverse b/compiler/tflchef/tests/sub/test.reverse
deleted file mode 100644
index e69de29bb..000000000
--- a/compiler/tflchef/tests/sub/test.reverse
+++ /dev/null
diff --git a/compiler/tflchef/tflite/CMakeLists.txt b/compiler/tflchef/tflite/CMakeLists.txt
index 477c70c4c..83127cb3e 100644
--- a/compiler/tflchef/tflite/CMakeLists.txt
+++ b/compiler/tflchef/tflite/CMakeLists.txt
@@ -4,6 +4,7 @@ add_library(tflchef_tflite STATIC ${SOURCES})
target_include_directories(tflchef_tflite PUBLIC include)
target_include_directories(tflchef_tflite PRIVATE src)
target_link_libraries(tflchef_tflite tflchef_proto)
-target_link_libraries(tflchef_tflite tflchef_flatbuffer)
+target_link_libraries(tflchef_tflite mio_tflite)
target_link_libraries(tflchef_tflite stdex)
target_link_libraries(tflchef_tflite cwrap)
+target_link_libraries(tflchef_tflite souschef)
diff --git a/compiler/tflchef/tflite/include/tflchef/RawModel.h b/compiler/tflchef/tflite/include/tflchef/RawModel.h
deleted file mode 100644
index 6979f65fd..000000000
--- a/compiler/tflchef/tflite/include/tflchef/RawModel.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __RAW_MODEL_H__
-#define __RAW_MODEL_H__
-
-#include "tflite_generated.h"
-
-namespace tflchef
-{
-
-struct RawModel
-{
- virtual ~RawModel() = default;
-
- virtual const ::tflite::Model *model(void) const = 0;
-};
-
-/**
- * @brief Load TensorFlow Lite model (as a RawModel) from a given path
- *
- * @note May return a nullptr
- */
-std::unique_ptr<RawModel> load_tflite(const std::string &path);
-
-} // namespace tflchef
-
-#endif // __RAW_MODEL_H__
diff --git a/compiler/tflchef/tflite/include/tflchef/RecipeChef.h b/compiler/tflchef/tflite/include/tflchef/RecipeChef.h
index 98f9f1f82..2d292c3d5 100644
--- a/compiler/tflchef/tflite/include/tflchef/RecipeChef.h
+++ b/compiler/tflchef/tflite/include/tflchef/RecipeChef.h
@@ -17,7 +17,7 @@
#ifndef __RECIPE_CHEF_H__
#define __RECIPE_CHEF_H__
-#include <tflite_generated.h>
+#include <mio/tflite/schema_generated.h>
#include <tflchef.pb.h>
#include <memory>
diff --git a/compiler/tflchef/tflite/src/Convert.cpp b/compiler/tflchef/tflite/src/Convert.cpp
index b7a93cb57..29276ff94 100644
--- a/compiler/tflchef/tflite/src/Convert.cpp
+++ b/compiler/tflchef/tflite/src/Convert.cpp
@@ -27,12 +27,15 @@ tflchef::TensorType as_tflchef_type(const tflite::TensorType type)
return tflchef::FLOAT32;
case tflite::TensorType_INT32:
return tflchef::INT32;
+ case tflite::TensorType_INT64:
+ return tflchef::INT64;
+ case tflite::TensorType_UINT8:
+ return tflchef::UINT8;
+ case tflite::TensorType_BOOL:
+ return tflchef::BOOL;
// TODO handle other types
// TensorType_FLOAT16
- // TensorType_UINT8
- // TensorType_INT64
// TensorType_STRING
- // TensorType_BOOL
// TensorType_INT16
// TensorType_COMPLEX64
default:
@@ -48,12 +51,14 @@ tflchef::Activation as_tflchef_activation(const tflite::ActivationFunctionType t
return tflchef::NONE;
case tflite::ActivationFunctionType_RELU:
return tflchef::RELU;
+ case tflite::ActivationFunctionType_RELU_N1_TO_1:
+ return tflchef::RELU_N1_TO_1;
case tflite::ActivationFunctionType_RELU6:
return tflchef::RELU6;
- // TODO handle other types
- // ActivationFunctionType_RELU_N1_TO_1
- // ActivationFunctionType_TANH
- // ActivationFunctionType_SIGN_BIT
+ case tflite::ActivationFunctionType_TANH:
+ return tflchef::TANH;
+ case tflite::ActivationFunctionType_SIGN_BIT:
+ return tflchef::SIGN_BIT;
default:
throw std::runtime_error{"unsupported activation type"};
}
@@ -72,4 +77,47 @@ tflchef::Padding as_tflchef_padding(const tflite::Padding padding)
}
}
+tflchef::MirrorPadMode as_tflchef_mirrorpadmode(const tflite::MirrorPadMode mode)
+{
+ switch (mode)
+ {
+ case tflite::MirrorPadMode_REFLECT:
+ return tflchef::REFLECT;
+ case tflite::MirrorPadMode_SYMMETRIC:
+ return tflchef::SYMMETRIC;
+ default:
+ throw std::runtime_error{"Unknown mirrorpad mode"};
+ }
+}
+
+tflchef::DimensionType as_tflchef_sparse_dim_type(const tflite::DimensionType type)
+{
+ switch (type)
+ {
+ case tflite::DimensionType_DENSE:
+ return tflchef::DimensionType::DENSE;
+ case tflite::DimensionType_SPARSE_CSR:
+ return tflchef::DimensionType::SPARSE_CSR;
+ default:
+ throw std::runtime_error("unsupported sparse dimension type");
+ }
+}
+
+tflchef::SparseIndexVecType as_tflchef_sparse_idx_vec_type(const tflite::SparseIndexVector type)
+{
+ switch (type)
+ {
+ case tflite::SparseIndexVector_NONE:
+ return tflchef::SparseIndexVecType::SparseIdxVecType_NONE;
+ case tflite::SparseIndexVector_Int32Vector:
+ return tflchef::SparseIndexVecType::INT32VEC;
+ case tflite::SparseIndexVector_Uint16Vector:
+ return tflchef::SparseIndexVecType::UINT16VEC;
+ case tflite::SparseIndexVector_Uint8Vector:
+ return tflchef::SparseIndexVecType::UINT8VEC;
+ default:
+ throw std::runtime_error("unsupported sparse index vector type");
+ }
+}
+
} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Convert.h b/compiler/tflchef/tflite/src/Convert.h
index 4973da111..cf0c61550 100644
--- a/compiler/tflchef/tflite/src/Convert.h
+++ b/compiler/tflchef/tflite/src/Convert.h
@@ -17,7 +17,7 @@
#ifndef __CONVERT_H__
#define __CONVERT_H__
-#include <tflite_generated.h>
+#include <mio/tflite/schema_generated.h>
#include <tflchef.pb.h>
@@ -27,6 +27,22 @@ namespace tflchef
tflchef::TensorType as_tflchef_type(const tflite::TensorType type);
tflchef::Activation as_tflchef_activation(const tflite::ActivationFunctionType type);
tflchef::Padding as_tflchef_padding(const tflite::Padding padding);
+tflchef::MirrorPadMode as_tflchef_mirrorpadmode(const tflite::MirrorPadMode mode);
+tflchef::DimensionType as_tflchef_sparse_dim_type(const tflite::DimensionType type);
+tflchef::SparseIndexVecType as_tflchef_sparse_idx_vec_type(const tflite::SparseIndexVector type);
+
+/**
+ * @brief extract buffer data to std::vector<DT>
+ */
+template <typename DT> std::vector<DT> extract_buffer(const tflite::Buffer *buffer)
+{
+ assert(buffer->data() != nullptr);
+ auto buffer_length = buffer->data()->size();
+ auto num_elements = buffer_length / sizeof(DT);
+ std::vector<DT> result(num_elements);
+ std::memcpy(result.data(), buffer->data()->data(), buffer_length);
+ return result;
+}
template <typename T> std::vector<T> as_index_vector(const flatbuffers::Vector<T> *flat_array)
{
diff --git a/compiler/tflchef/tflite/src/FillerHelper.cpp b/compiler/tflchef/tflite/src/FillerHelper.cpp
new file mode 100644
index 000000000..cf96d2e8c
--- /dev/null
+++ b/compiler/tflchef/tflite/src/FillerHelper.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FillerHelper.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void fill_tensor_to_import(int32_t idx, TFliteImport *import)
+{
+ const tflite::Tensor *tensor = import->tensors()->Get(idx);
+ if (tensor != nullptr)
+ {
+ if (tensor->type() == tflite::TensorType::TensorType_INT32)
+ {
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ if (buffer && buffer->data())
+ {
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(idx, vec);
+ }
+ }
+ else if (tensor->type() == tflite::TensorType::TensorType_FLOAT32)
+ {
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ if (buffer && buffer->data())
+ {
+ auto vec = extract_buffer<float>(buffer);
+ import->set_tensor_filler(idx, vec);
+ }
+ }
+ }
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/FillerHelper.h b/compiler/tflchef/tflite/src/FillerHelper.h
new file mode 100644
index 000000000..053a5c18a
--- /dev/null
+++ b/compiler/tflchef/tflite/src/FillerHelper.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __FILLER_HELPER_H__
+#define __FILLER_HELPER_H__
+
+#include "TFliteImport.h"
+
+#include <mio/tflite/schema_generated.h>
+
+namespace tflchef
+{
+
+void fill_tensor_to_import(int32_t idx, TFliteImport *import);
+
+} // namespace tflchef
+
+#endif // __FILLER_HELPER_H__
diff --git a/compiler/tflchef/tflite/src/Op/Abs.cpp b/compiler/tflchef/tflite/src/Op/Abs.cpp
new file mode 100644
index 000000000..7d769e344
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Abs.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Abs.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpAbs::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpAbs::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Abs");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Abs.h b/compiler/tflchef/tflite/src/Op/Abs.h
new file mode 100644
index 000000000..d99b0d593
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Abs.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_ABS_H__
+#define __TFLITE_OP_ABS_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for abs
+ */
+class TFliteOpAbs : 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_ABS_H__
diff --git a/compiler/tflchef/tflite/src/Op/Add.cpp b/compiler/tflchef/tflite/src/Op/Add.cpp
new file mode 100644
index 000000000..3e880a63b
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Add.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Add.h"
+
+#include "Convert.h"
+#include "FillerHelper.h"
+
+namespace tflchef
+{
+
+void TFliteOpAdd::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Add may have constant input
+
+ const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
+ assert(inputs.size() == 2);
+
+ fill_tensor_to_import(inputs[0], import);
+ fill_tensor_to_import(inputs[1], import);
+}
+
+tflchef::Operation *TFliteOpAdd::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_AddOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Add");
+
+ auto op_options = operation->mutable_add_options();
+
+ op_options->set_activation(as_tflchef_activation(op_params->fused_activation_function()));
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Add.h b/compiler/tflchef/tflite/src/Op/Add.h
new file mode 100644
index 000000000..49d945f8b
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Add.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_ADD_H__
+#define __TFLITE_OP_ADD_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for add
+ */
+class TFliteOpAdd : 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_ADD_H__
diff --git a/compiler/tflchef/tflite/src/Op/AddN.cpp b/compiler/tflchef/tflite/src/Op/AddN.cpp
new file mode 100644
index 000000000..aeb3803ab
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/AddN.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AddN.h"
+
+#include "Convert.h"
+#include "FillerHelper.h"
+
+namespace tflchef
+{
+
+void TFliteOpAddN::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // AddN may have constant input
+
+ const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
+
+ for (uint32_t idx = 0; idx < inputs.size(); ++idx)
+ fill_tensor_to_import(inputs[idx], import);
+}
+
+tflchef::Operation *TFliteOpAddN::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("AddN");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/AddN.h b/compiler/tflchef/tflite/src/Op/AddN.h
new file mode 100644
index 000000000..4387aa06a
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/AddN.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_ADD_N_H__
+#define __TFLITE_OP_ADD_N_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for AddN
+ */
+class TFliteOpAddN : 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_ADD_N_H__
diff --git a/compiler/tflchef/tflite/src/Op/ArgMax.cpp b/compiler/tflchef/tflite/src/Op/ArgMax.cpp
new file mode 100644
index 000000000..f4d1c5e66
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ArgMax.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ArgMax.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpArgMax::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // filler for second input, argmax/dim
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *dim_tensor = import->tensors()->Get(inputs[1]);
+ assert(dim_tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(dim_tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+}
+
+tflchef::Operation *TFliteOpArgMax::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_ArgMaxOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("ArgMax");
+
+ auto op_options = operation->mutable_argmax_options();
+
+ op_options->set_output_type(as_tflchef_type(op_params->output_type()));
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/ArgMax.h b/compiler/tflchef/tflite/src/Op/ArgMax.h
new file mode 100644
index 000000000..30068ecf2
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ArgMax.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_ARGMAX_H__
+#define __TFLITE_OP_ARGMAX_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for ArgMax
+ */
+class TFliteOpArgMax : 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_ARGMAX_H__
diff --git a/compiler/tflchef/tflite/src/Op/ArgMin.cpp b/compiler/tflchef/tflite/src/Op/ArgMin.cpp
new file mode 100644
index 000000000..faab0b830
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ArgMin.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ArgMin.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpArgMin::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // filler for second input, argmin/dim
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *dim_tensor = import->tensors()->Get(inputs[1]);
+ assert(dim_tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(dim_tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+}
+
+tflchef::Operation *TFliteOpArgMin::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_ArgMinOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("ArgMin");
+
+ auto op_options = operation->mutable_argmin_options();
+
+ op_options->set_output_type(as_tflchef_type(op_params->output_type()));
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/ArgMin.h b/compiler/tflchef/tflite/src/Op/ArgMin.h
new file mode 100644
index 000000000..83c643c1a
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ArgMin.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_ARGMIN_H__
+#define __TFLITE_OP_ARGMIN_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for ArgMin
+ */
+class TFliteOpArgMin : 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_ARGMIN_H__
diff --git a/compiler/tflchef/tflite/src/Op/BatchMatMul.cpp b/compiler/tflchef/tflite/src/Op/BatchMatMul.cpp
new file mode 100644
index 000000000..598e58c94
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/BatchMatMul.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BatchMatMul.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpBatchMatMul::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpBatchMatMul::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("BatchMatMul");
+
+ auto op_options = operation->mutable_batch_matmul_options();
+
+ auto op_params = op->builtin_options_as_BatchMatMulOptions();
+ assert(op_params != nullptr);
+
+ op_options->set_adj_x(op_params->adj_x());
+ op_options->set_adj_y(op_params->adj_y());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/BatchMatMul.h b/compiler/tflchef/tflite/src/Op/BatchMatMul.h
new file mode 100644
index 000000000..6eb4c6e68
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/BatchMatMul.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_BATCHMATMUL_H__
+#define __TFLITE_OP_BATCHMATMUL_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for BATCH_MATMUL
+ */
+class TFliteOpBatchMatMul : 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_BATCHMATMUL_H__
diff --git a/compiler/tflchef/tflite/src/Op/BatchToSpaceND.cpp b/compiler/tflchef/tflite/src/Op/BatchToSpaceND.cpp
new file mode 100644
index 000000000..d5d9606d1
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/BatchToSpaceND.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BatchToSpaceND.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpBatchToSpaceND::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // filler for second, third input
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[1]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+
+ tensor = import->tensors()->Get(inputs[2]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ buffer = import->buffers()->Get(tensor->buffer());
+ vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[2], vec);
+}
+
+tflchef::Operation *TFliteOpBatchToSpaceND::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("BatchToSpaceND");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/BatchToSpaceND.h b/compiler/tflchef/tflite/src/Op/BatchToSpaceND.h
new file mode 100644
index 000000000..ae2114c97
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/BatchToSpaceND.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_BATCHTOSPACEND_H__
+#define __TFLITE_OP_BATCHTOSPACEND_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for BatchToSpaceND
+ */
+class TFliteOpBatchToSpaceND : 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_BATCHTOSPACEND_H__
diff --git a/compiler/tflchef/tflite/src/Op/Cast.cpp b/compiler/tflchef/tflite/src/Op/Cast.cpp
new file mode 100644
index 000000000..393bb4b35
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Cast.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Cast.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpCast::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpCast::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_CastOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Cast");
+
+ auto op_options = operation->mutable_cast_options();
+
+ op_options->set_in_data_type(as_tflchef_type(op_params->in_data_type()));
+ op_options->set_out_data_type(as_tflchef_type(op_params->out_data_type()));
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Cast.h b/compiler/tflchef/tflite/src/Op/Cast.h
new file mode 100644
index 000000000..29c126c93
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Cast.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_CAST_H__
+#define __TFLITE_OP_CAST_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for CAST
+ */
+class TFliteOpCast : 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_CAST_H__
diff --git a/compiler/tflchef/tflite/src/Op/Ceil.cpp b/compiler/tflchef/tflite/src/Op/Ceil.cpp
new file mode 100644
index 000000000..d3ef3adae
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Ceil.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Ceil.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpCeil::filler(const tflite::Operator *, TFliteImport *, tflchef::ModelRecipe *) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpCeil::build(const tflite::Operator *, TFliteImport *,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Ceil");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Ceil.h b/compiler/tflchef/tflite/src/Op/Ceil.h
new file mode 100644
index 000000000..44df20778
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Ceil.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_CEIL_H__
+#define __TFLITE_OP_CEIL_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for CEIL
+ */
+class TFliteOpCeil : 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_CEIL_H__
diff --git a/compiler/tflchef/tflite/src/Op/Conv2D.cpp b/compiler/tflchef/tflite/src/Op/Conv2D.cpp
index 5d48ee24f..57abd4649 100644
--- a/compiler/tflchef/tflite/src/Op/Conv2D.cpp
+++ b/compiler/tflchef/tflite/src/Op/Conv2D.cpp
@@ -50,7 +50,8 @@ tflchef::Operation *TFliteOpConv2D::build(const tflite::Operator *op, TFliteImpo
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()));
- // TODO support dilation
+ op_options->set_dilation_w_factor(op_params->dilation_w_factor());
+ op_options->set_dilation_h_factor(op_params->dilation_h_factor());
return operation;
}
diff --git a/compiler/tflchef/tflite/src/Op/Cos.cpp b/compiler/tflchef/tflite/src/Op/Cos.cpp
new file mode 100644
index 000000000..9f2c49d49
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Cos.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Cos.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpCos::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpCos::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Cos");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Cos.h b/compiler/tflchef/tflite/src/Op/Cos.h
new file mode 100644
index 000000000..8f3dbe3a6
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Cos.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_COS_H__
+#define __TFLITE_OP_COS_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Cos
+ */
+class TFliteOpCos : 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_COS_H__
diff --git a/compiler/tflchef/tflite/src/Op/DepthToSpace.cpp b/compiler/tflchef/tflite/src/Op/DepthToSpace.cpp
new file mode 100644
index 000000000..1a0917e8e
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/DepthToSpace.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DepthToSpace.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpDepthToSpace::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpDepthToSpace::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("DepthToSpace");
+
+ auto op_params = op->builtin_options_as_DepthToSpaceOptions();
+ assert(op_params != nullptr);
+
+ auto op_options = operation->mutable_depth_to_space_options();
+
+ op_options->set_block_size(op_params->block_size());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/DepthToSpace.h b/compiler/tflchef/tflite/src/Op/DepthToSpace.h
new file mode 100644
index 000000000..b5852ac89
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/DepthToSpace.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_DEPTHTOSPACE_H__
+#define __TFLITE_OP_DEPTHTOSPACE_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for DepthToSpace
+ */
+class TFliteOpDepthToSpace : 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_DEPTHTOSPACE_H__
diff --git a/compiler/tflchef/tflite/src/Op/DepthwiseConv2D.cpp b/compiler/tflchef/tflite/src/Op/DepthwiseConv2D.cpp
index b19f9330f..5fed3353a 100644
--- a/compiler/tflchef/tflite/src/Op/DepthwiseConv2D.cpp
+++ b/compiler/tflchef/tflite/src/Op/DepthwiseConv2D.cpp
@@ -50,9 +50,8 @@ tflchef::Operation *TFliteOpDepthwiseConv2D::build(const tflite::Operator *op, T
op_options->set_stride_h(op_params->stride_h());
op_options->set_stride_w(op_params->stride_w());
op_options->set_depth_multiplier(op_params->depth_multiplier());
- // TODO support dilation
- // op_params->dilation_w_factor()
- // op_params->dilation_h_factor()
+ op_options->set_dilation_w_factor(op_params->dilation_w_factor());
+ op_options->set_dilation_h_factor(op_params->dilation_h_factor());
op_options->set_padding(as_tflchef_padding(op_params->padding()));
return operation;
diff --git a/compiler/tflchef/tflite/src/Op/Dequantize.cpp b/compiler/tflchef/tflite/src/Op/Dequantize.cpp
new file mode 100644
index 000000000..436a0db19
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Dequantize.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dequantize.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpDequantize::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpDequantize::build(const tflite::Operator *, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Dequantize");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Dequantize.h b/compiler/tflchef/tflite/src/Op/Dequantize.h
new file mode 100644
index 000000000..df1c7bbdb
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Dequantize.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_DEQUANTIZE_H__
+#define __TFLITE_OP_DEQUANTIZE_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Dequantize
+ */
+class TFliteOpDequantize : public TFliteOpChef
+{
+public:
+ void filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const override;
+ tflchef::Operation *build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const override;
+};
+
+} // namespace tflchef
+
+#endif // __TFLITE_OP_DEQUANTIZE_H__
diff --git a/compiler/tflchef/tflite/src/Op/ELU.cpp b/compiler/tflchef/tflite/src/Op/ELU.cpp
new file mode 100644
index 000000000..cb4b61d66
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ELU.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ELU.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpELU::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpELU::build(const tflite::Operator *, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("ELU");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/ELU.h b/compiler/tflchef/tflite/src/Op/ELU.h
new file mode 100644
index 000000000..490c9fde4
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ELU.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_ELU_H__
+#define __TFLITE_OP_ELU_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for ELU
+ */
+class TFliteOpELU : 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_ELU_H__
diff --git a/compiler/tflchef/tflite/src/Op/Equal.cpp b/compiler/tflchef/tflite/src/Op/Equal.cpp
new file mode 100644
index 000000000..a51586228
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Equal.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Equal.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpEqual::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpEqual::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Equal");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Equal.h b/compiler/tflchef/tflite/src/Op/Equal.h
new file mode 100644
index 000000000..fd4b40001
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Equal.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_EQUAL_H__
+#define __TFLITE_OP_EQUAL_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Equal
+ */
+class TFliteOpEqual : 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_EQUAL_H__
diff --git a/compiler/tflchef/tflite/src/Op/Exp.cpp b/compiler/tflchef/tflite/src/Op/Exp.cpp
new file mode 100644
index 000000000..f715da6ef
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Exp.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Exp.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpExp::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpExp::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Exp");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Exp.h b/compiler/tflchef/tflite/src/Op/Exp.h
new file mode 100644
index 000000000..5ff3ddc8b
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Exp.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_EXP_H__
+#define __TFLITE_OP_EXP_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Exp
+ */
+class TFliteOpExp : 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_EXP_H__
diff --git a/compiler/tflchef/tflite/src/Op/ExpandDims.cpp b/compiler/tflchef/tflite/src/Op/ExpandDims.cpp
new file mode 100644
index 000000000..e30e8dbcc
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ExpandDims.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ExpandDims.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpExpandDims::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Fill for axis input
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[1]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+}
+
+tflchef::Operation *TFliteOpExpandDims::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("ExpandDims");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/ExpandDims.h b/compiler/tflchef/tflite/src/Op/ExpandDims.h
new file mode 100644
index 000000000..e2f3e4e50
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ExpandDims.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_EXPAND_DIMS_H__
+#define __TFLITE_OP_EXPAND_DIMS_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for ExpandDims
+ */
+class TFliteOpExpandDims : 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_EXPAND_DIMS_H__
diff --git a/compiler/tflchef/tflite/src/Op/Fill.cpp b/compiler/tflchef/tflite/src/Op/Fill.cpp
new file mode 100644
index 000000000..08b695fd7
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Fill.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Fill.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpFill::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *dims_tensor = import->tensors()->Get(inputs[0]);
+ assert(dims_tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(dims_tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[0], vec);
+}
+
+tflchef::Operation *TFliteOpFill::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+ operation->set_type("Fill");
+
+ // FillOptions are empty
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Fill.h b/compiler/tflchef/tflite/src/Op/Fill.h
new file mode 100644
index 000000000..4f46f628a
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Fill.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_FILL_H__
+#define __TFLITE_OP_FILL_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Fill
+ */
+class TFliteOpFill : 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_FILL_H__
diff --git a/compiler/tflchef/tflite/src/Op/Floor.cpp b/compiler/tflchef/tflite/src/Op/Floor.cpp
new file mode 100644
index 000000000..373c69f71
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Floor.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Floor.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpFloor::filler(const tflite::Operator *, TFliteImport *, tflchef::ModelRecipe *) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpFloor::build(const tflite::Operator *, TFliteImport *,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Floor");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Floor.h b/compiler/tflchef/tflite/src/Op/Floor.h
new file mode 100644
index 000000000..f0f8ef38a
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Floor.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_FLOOR_H__
+#define __TFLITE_OP_FLOOR_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for FLOOR
+ */
+class TFliteOpFloor : 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_FLOOR_H__
diff --git a/compiler/tflchef/tflite/src/Op/FloorDiv.cpp b/compiler/tflchef/tflite/src/Op/FloorDiv.cpp
new file mode 100644
index 000000000..492c6941f
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/FloorDiv.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FloorDiv.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpFloorDiv::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpFloorDiv::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("FloorDiv");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/FloorDiv.h b/compiler/tflchef/tflite/src/Op/FloorDiv.h
new file mode 100644
index 000000000..5d049a668
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/FloorDiv.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_FLOORDIV_H__
+#define __TFLITE_OP_FLOORDIV_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for floor division
+ */
+class TFliteOpFloorDiv : 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_FLOORDIV_H__
diff --git a/compiler/tflchef/tflite/src/Op/FloorMod.cpp b/compiler/tflchef/tflite/src/Op/FloorMod.cpp
new file mode 100644
index 000000000..997d82664
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/FloorMod.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FloorMod.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpFloorMod::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpFloorMod::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("FloorMod");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/FloorMod.h b/compiler/tflchef/tflite/src/Op/FloorMod.h
new file mode 100644
index 000000000..f36dfe813
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/FloorMod.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_FLOOR_MOD_H__
+#define __TFLITE_OP_FLOOR_MOD_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for FLOOR_MOD
+ */
+class TFliteOpFloorMod : 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_FLOOR_MOD_H__
diff --git a/compiler/tflchef/tflite/src/Op/FullyConnected.cpp b/compiler/tflchef/tflite/src/Op/FullyConnected.cpp
index 4291c844b..1f6e73aa6 100644
--- a/compiler/tflchef/tflite/src/Op/FullyConnected.cpp
+++ b/compiler/tflchef/tflite/src/Op/FullyConnected.cpp
@@ -17,6 +17,7 @@
#include "FullyConnected.h"
#include "Convert.h"
+#include "FillerHelper.h"
namespace tflchef
{
@@ -24,7 +25,14 @@ namespace tflchef
void TFliteOpFullyConnected::filler(const tflite::Operator *op, TFliteImport *import,
tflchef::ModelRecipe *model_recipe) const
{
- // Nothing to do with filler
+ const auto &inputs = *op->inputs();
+
+ for (uint32_t idx = 1; idx < inputs.size(); idx++)
+ {
+ // optional input tensor idx has minus value.
+ if (inputs[idx] >= 0)
+ fill_tensor_to_import(inputs[idx], import);
+ }
}
tflchef::Operation *TFliteOpFullyConnected::build(const tflite::Operator *op, TFliteImport *import,
diff --git a/compiler/tflchef/tflite/src/Op/Gather.cpp b/compiler/tflchef/tflite/src/Op/Gather.cpp
new file mode 100644
index 000000000..98da3ec43
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Gather.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Gather.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpGather::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+ // But second input has filler for constant inputs
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[1]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+
+ if (buffer && buffer->data())
+ {
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+ }
+}
+
+tflchef::Operation *TFliteOpGather::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_GatherOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Gather");
+
+ auto op_options = operation->mutable_gather_options();
+
+ op_options->set_axis(op_params->axis());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Gather.h b/compiler/tflchef/tflite/src/Op/Gather.h
new file mode 100644
index 000000000..e01276b76
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Gather.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_GATHER_H__
+#define __TFLITE_OP_GATHER_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Gather
+ */
+class TFliteOpGather : 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_GATHER_H__
diff --git a/compiler/tflchef/tflite/src/Op/GatherNd.cpp b/compiler/tflchef/tflite/src/Op/GatherNd.cpp
new file mode 100644
index 000000000..0ff5a0b7f
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/GatherNd.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GatherNd.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpGatherNd::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // indices buffer has filler
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[1]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+
+ if (buffer && buffer->data())
+ {
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+ }
+}
+
+tflchef::Operation *TFliteOpGatherNd::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+ operation->set_type("GatherNd");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/GatherNd.h b/compiler/tflchef/tflite/src/Op/GatherNd.h
new file mode 100644
index 000000000..112f23d33
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/GatherNd.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_GATHER_ND_H__
+#define __TFLITE_OP_GATHER_ND_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for GatherNd
+ */
+class TFliteOpGatherNd : 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_GATHER_ND_H__
diff --git a/compiler/tflchef/tflite/src/Op/Greater.cpp b/compiler/tflchef/tflite/src/Op/Greater.cpp
new file mode 100644
index 000000000..4e41efb2d
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Greater.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Greater.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpGreater::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpGreater::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Greater");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Greater.h b/compiler/tflchef/tflite/src/Op/Greater.h
new file mode 100644
index 000000000..3ab2d1a4e
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Greater.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_GREATER_H__
+#define __TFLITE_OP_GREATER_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Greater
+ */
+class TFliteOpGreater : 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_GREATER_H__
diff --git a/compiler/tflchef/tflite/src/Op/GreaterEqual.cpp b/compiler/tflchef/tflite/src/Op/GreaterEqual.cpp
new file mode 100644
index 000000000..aead30e57
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/GreaterEqual.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GreaterEqual.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpGreaterEqual::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpGreaterEqual::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("GreaterEqual");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/GreaterEqual.h b/compiler/tflchef/tflite/src/Op/GreaterEqual.h
new file mode 100644
index 000000000..96b0af78a
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/GreaterEqual.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_GREATEREQUAL_H__
+#define __TFLITE_OP_GREATEREQUAL_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Greater Equal
+ */
+class TFliteOpGreaterEqual : 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_GREATEREQUAL_H__
diff --git a/compiler/tflchef/tflite/src/Op/L2Normalize.cpp b/compiler/tflchef/tflite/src/Op/L2Normalize.cpp
new file mode 100644
index 000000000..0a8908472
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/L2Normalize.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "L2Normalize.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpL2Normalize::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpL2Normalize::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_L2NormOptions();
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("L2Normalize");
+
+ auto op_options = operation->mutable_l2norm_options();
+
+ op_options->set_activation(as_tflchef_activation(op_params->fused_activation_function()));
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/L2Normalize.h b/compiler/tflchef/tflite/src/Op/L2Normalize.h
new file mode 100644
index 000000000..a73eae6c8
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/L2Normalize.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_L2NORMALIZE_H__
+#define __TFLITE_OP_L2NORMALIZE_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for L2Normalize
+ */
+class TFliteOpL2Normalize : 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_L2NORMALIZE_H__
diff --git a/compiler/tflchef/tflite/src/Op/L2Pool2D.cpp b/compiler/tflchef/tflite/src/Op/L2Pool2D.cpp
new file mode 100644
index 000000000..8db4b02b6
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/L2Pool2D.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "L2Pool2D.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpL2Pool2D::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpL2Pool2D::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_Pool2DOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("L2Pool2D");
+
+ auto op_options = operation->mutable_l2pool2d_options();
+
+ op_options->set_padding(as_tflchef_padding(op_params->padding()));
+ op_options->set_stride_h(op_params->stride_h());
+ op_options->set_stride_w(op_params->stride_w());
+ op_options->set_filter_height(op_params->filter_height());
+ op_options->set_filter_width(op_params->filter_width());
+ op_options->set_activation(as_tflchef_activation(op_params->fused_activation_function()));
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/L2Pool2D.h b/compiler/tflchef/tflite/src/Op/L2Pool2D.h
new file mode 100644
index 000000000..046353440
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/L2Pool2D.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_L2_POOL2D_H__
+#define __TFLITE_OP_L2_POOL2D_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for L2_POOL_2D
+ */
+class TFliteOpL2Pool2D : 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_L2_POOL2D_H__
diff --git a/compiler/tflchef/tflite/src/Op/LeakyRelu.cpp b/compiler/tflchef/tflite/src/Op/LeakyRelu.cpp
new file mode 100644
index 000000000..bf9cb2fb3
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/LeakyRelu.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LeakyRelu.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpLeakyRelu::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpLeakyRelu::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_LeakyReluOptions();
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("LeakyRelu");
+
+ auto *op_options = operation->mutable_leaky_relu_options();
+
+ op_options->set_alpha(op_params->alpha());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/LeakyRelu.h b/compiler/tflchef/tflite/src/Op/LeakyRelu.h
new file mode 100644
index 000000000..28e63e0ca
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/LeakyRelu.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_LEAKY_RELU_H__
+#define __TFLITE_OP_LEAKY_RELU_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for LeakyReLU
+ */
+class TFliteOpLeakyRelu : 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_LEAKY_RELU_H__
diff --git a/compiler/tflchef/tflite/src/Op/Less.cpp b/compiler/tflchef/tflite/src/Op/Less.cpp
new file mode 100644
index 000000000..0360317c7
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Less.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Less.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpLess::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpLess::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Less");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Less.h b/compiler/tflchef/tflite/src/Op/Less.h
new file mode 100644
index 000000000..1316cb613
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Less.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_LESS_H__
+#define __TFLITE_OP_LESS_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Less
+ */
+class TFliteOpLess : 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_LESS_H__
diff --git a/compiler/tflchef/tflite/src/Op/LessEqual.cpp b/compiler/tflchef/tflite/src/Op/LessEqual.cpp
new file mode 100644
index 000000000..b8c42e80d
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/LessEqual.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LessEqual.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpLessEqual::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpLessEqual::build(const tflite::Operator *, TFliteImport *,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("LessEqual");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/LessEqual.h b/compiler/tflchef/tflite/src/Op/LessEqual.h
new file mode 100644
index 000000000..81c710fbc
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/LessEqual.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_LESSEQUAL_H__
+#define __TFLITE_OP_LESSEQUAL_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for LessEqual
+ */
+class TFliteOpLessEqual : 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_LESSEQUAL_H__
diff --git a/compiler/tflchef/tflite/src/Op/LocalResponseNormalization.cpp b/compiler/tflchef/tflite/src/Op/LocalResponseNormalization.cpp
new file mode 100644
index 000000000..8bebd9e90
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/LocalResponseNormalization.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LocalResponseNormalization.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpLocalResponseNormalization::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *
+TFliteOpLocalResponseNormalization::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_LocalResponseNormalizationOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("LocalResponseNormalization");
+
+ auto op_options = operation->mutable_local_response_normalization_options();
+
+ op_options->set_radius(op_params->radius());
+ op_options->set_bias(op_params->bias());
+ op_options->set_alpha(op_params->alpha());
+ op_options->set_beta(op_params->beta());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/LocalResponseNormalization.h b/compiler/tflchef/tflite/src/Op/LocalResponseNormalization.h
new file mode 100644
index 000000000..c0eb3f2b1
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/LocalResponseNormalization.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_LOCAL_RESPONSE_NORMALIZATION_H__
+#define __TFLITE_OP_LOCAL_RESPONSE_NORMALIZATION_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for LocalResponseNormalization
+ */
+class TFliteOpLocalResponseNormalization : 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_LOCAL_RESPONSE_NORMALIZATION_H__
diff --git a/compiler/tflchef/tflite/src/Op/Log.cpp b/compiler/tflchef/tflite/src/Op/Log.cpp
new file mode 100644
index 000000000..a68dc9a31
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Log.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Log.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpLog::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpLog::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Log");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Log.h b/compiler/tflchef/tflite/src/Op/Log.h
new file mode 100644
index 000000000..9d17e2f81
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Log.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_LOG_H__
+#define __TFLITE_OP_LOG_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Log
+ */
+class TFliteOpLog : 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_LOG_H__
diff --git a/compiler/tflchef/tflite/src/Op/LogSoftmax.cpp b/compiler/tflchef/tflite/src/Op/LogSoftmax.cpp
new file mode 100644
index 000000000..8f0e1a9f9
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/LogSoftmax.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LogSoftmax.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpLogSoftmax::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpLogSoftmax::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("LogSoftmax");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/LogSoftmax.h b/compiler/tflchef/tflite/src/Op/LogSoftmax.h
new file mode 100644
index 000000000..efd81f3e9
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/LogSoftmax.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_LOG_SOFTMAX_H__
+#define __TFLITE_OP_LOG_SOFTMAX_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for LogSoftmax
+ */
+class TFliteOpLogSoftmax : 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_LOG_SOFTMAX_H__
diff --git a/compiler/tflchef/tflite/src/Op/LogicalAnd.cpp b/compiler/tflchef/tflite/src/Op/LogicalAnd.cpp
new file mode 100644
index 000000000..2cc486426
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/LogicalAnd.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LogicalAnd.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpLogicalAnd::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpLogicalAnd::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("LogicalAnd");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/LogicalAnd.h b/compiler/tflchef/tflite/src/Op/LogicalAnd.h
new file mode 100644
index 000000000..1f7a964b9
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/LogicalAnd.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_LOGICALAND_H__
+#define __TFLITE_OP_LOGICALAND_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for LogicalAnd
+ */
+class TFliteOpLogicalAnd : 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_LOGICALAND_H__
diff --git a/compiler/tflchef/tflite/src/Op/LogicalNot.cpp b/compiler/tflchef/tflite/src/Op/LogicalNot.cpp
new file mode 100644
index 000000000..ecd5b903c
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/LogicalNot.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LogicalNot.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpLogicalNot::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpLogicalNot::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("LogicalNot");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/LogicalNot.h b/compiler/tflchef/tflite/src/Op/LogicalNot.h
new file mode 100644
index 000000000..b75d33554
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/LogicalNot.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_LOGICALNOT_H__
+#define __TFLITE_OP_LOGICALNOT_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for LogicalNot
+ */
+class TFliteOpLogicalNot : 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_LOGICALNOT_H__
diff --git a/compiler/tflchef/tflite/src/Op/LogicalOr.cpp b/compiler/tflchef/tflite/src/Op/LogicalOr.cpp
new file mode 100644
index 000000000..b91f4cfca
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/LogicalOr.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LogicalOr.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpLogicalOr::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpLogicalOr::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("LogicalOr");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/LogicalOr.h b/compiler/tflchef/tflite/src/Op/LogicalOr.h
new file mode 100644
index 000000000..5331a0d65
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/LogicalOr.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_LOGICALOR_H__
+#define __TFLITE_OP_LOGICALOR_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for LogicalOr
+ */
+class TFliteOpLogicalOr : 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_LOGICALOR_H__
diff --git a/compiler/tflchef/tflite/src/Op/Logistic.cpp b/compiler/tflchef/tflite/src/Op/Logistic.cpp
new file mode 100644
index 000000000..18b3b5c00
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Logistic.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Logistic.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpLogistic::filler(const tflite::Operator *, TFliteImport *,
+ tflchef::ModelRecipe *) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpLogistic::build(const tflite::Operator *, TFliteImport *,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Logistic");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Logistic.h b/compiler/tflchef/tflite/src/Op/Logistic.h
new file mode 100644
index 000000000..a75bf490e
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Logistic.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_LOGISTIC_H__
+#define __TFLITE_OP_LOGISTIC_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for LOGISTIC
+ */
+class TFliteOpLogistic : 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_LOGISTIC_H__
diff --git a/compiler/tflchef/tflite/src/Op/MatrixDiag.cpp b/compiler/tflchef/tflite/src/Op/MatrixDiag.cpp
new file mode 100644
index 000000000..ca84c4949
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/MatrixDiag.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MatrixDiag.h"
+
+namespace tflchef
+{
+
+void TFliteOpMatrixDiag::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpMatrixDiag::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("MatrixDiag");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/MatrixDiag.h b/compiler/tflchef/tflite/src/Op/MatrixDiag.h
new file mode 100644
index 000000000..4074f2c36
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/MatrixDiag.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_MATRIX_DIAG_H__
+#define __TFLITE_OP_MATRIX_DIAG_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for MatrixDiag
+ */
+class TFliteOpMatrixDiag : 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_MATRIX_DIAG_H__
diff --git a/compiler/tflchef/tflite/src/Op/MatrixSetDiag.cpp b/compiler/tflchef/tflite/src/Op/MatrixSetDiag.cpp
new file mode 100644
index 000000000..97c7de41f
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/MatrixSetDiag.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MatrixSetDiag.h"
+
+namespace tflchef
+{
+
+void TFliteOpMatrixSetDiag::filler(const tflite::Operator *, TFliteImport *,
+ tflchef::ModelRecipe *) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpMatrixSetDiag::build(const tflite::Operator *, TFliteImport *,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("MatrixSetDiag");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/MatrixSetDiag.h b/compiler/tflchef/tflite/src/Op/MatrixSetDiag.h
new file mode 100644
index 000000000..0e7ec7f32
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/MatrixSetDiag.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_MATRIX_SET_DIAG_H__
+#define __TFLITE_OP_MATRIX_SET_DIAG_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for MatrixSetDiag
+ */
+class TFliteOpMatrixSetDiag : 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_MATRIX_SET_DIAG_H__
diff --git a/compiler/tflchef/tflite/src/Op/Maximum.cpp b/compiler/tflchef/tflite/src/Op/Maximum.cpp
new file mode 100644
index 000000000..fb977b6ed
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Maximum.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Maximum.h"
+
+namespace tflchef
+{
+
+void TFliteOpMaximum::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpMaximum::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Maximum");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Maximum.h b/compiler/tflchef/tflite/src/Op/Maximum.h
new file mode 100644
index 000000000..acafec343
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Maximum.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_MAXIMUM_H__
+#define __TFLITE_OP_MAXIMUM_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for maximum
+ */
+class TFliteOpMaximum : 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_MAXIMUM_H__
diff --git a/compiler/tflchef/tflite/src/Op/Mean.cpp b/compiler/tflchef/tflite/src/Op/Mean.cpp
new file mode 100644
index 000000000..1c2975781
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Mean.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Mean.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpMean::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // filler for second input
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[1]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+}
+
+tflchef::Operation *TFliteOpMean::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_ReducerOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Mean");
+
+ auto op_options = operation->mutable_mean_options();
+
+ op_options->set_keep_dims(op_params->keep_dims());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Mean.h b/compiler/tflchef/tflite/src/Op/Mean.h
new file mode 100644
index 000000000..532c40c66
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Mean.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_MEAN_H__
+#define __TFLITE_OP_MEAN_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for mean
+ */
+class TFliteOpMean : 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_MEAN_H__
diff --git a/compiler/tflchef/tflite/src/Op/Minimum.cpp b/compiler/tflchef/tflite/src/Op/Minimum.cpp
new file mode 100644
index 000000000..2bb50cb89
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Minimum.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Minimum.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpMinimum::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpMinimum::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Minimum");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Minimum.h b/compiler/tflchef/tflite/src/Op/Minimum.h
new file mode 100644
index 000000000..5db5b7940
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Minimum.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_MINIMUM_H__
+#define __TFLITE_OP_MINIMUM_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for minimum
+ */
+class TFliteOpMinimum : 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_MINIMUM_H__
diff --git a/compiler/tflchef/tflite/src/Op/MirrorPad.cpp b/compiler/tflchef/tflite/src/Op/MirrorPad.cpp
new file mode 100644
index 000000000..c688552ee
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/MirrorPad.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MirrorPad.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpMirrorPad::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // filler for second input
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[1]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+}
+
+tflchef::Operation *TFliteOpMirrorPad::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+ operation->set_type("MirrorPad");
+
+ auto op_options = operation->mutable_mirrorpad_options();
+
+ auto op_params = op->builtin_options_as_MirrorPadOptions();
+ assert(op_params != nullptr);
+
+ op_options->set_mode(as_tflchef_mirrorpadmode(op_params->mode()));
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/MirrorPad.h b/compiler/tflchef/tflite/src/Op/MirrorPad.h
new file mode 100644
index 000000000..c9acdd498
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/MirrorPad.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_MIRROR_PAD_H__
+#define __TFLITE_OP_MIRROR_PAD_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for MIRROR_PAD
+ */
+class TFliteOpMirrorPad : 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_MIRROR_PAD_H__
diff --git a/compiler/tflchef/tflite/src/Op/Mul.cpp b/compiler/tflchef/tflite/src/Op/Mul.cpp
new file mode 100644
index 000000000..9faa4acaf
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Mul.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Mul.h"
+
+#include "Convert.h"
+#include "FillerHelper.h"
+
+namespace tflchef
+{
+
+void TFliteOpMul::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Mul may have constant input
+
+ const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
+ assert(inputs.size() == 2);
+
+ fill_tensor_to_import(inputs[0], import);
+ fill_tensor_to_import(inputs[1], import);
+}
+
+tflchef::Operation *TFliteOpMul::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Mul");
+
+ auto op_params = op->builtin_options_as_MulOptions();
+ assert(op_params != nullptr);
+
+ auto op_options = operation->mutable_mul_options();
+ op_options->set_activation(as_tflchef_activation(op_params->fused_activation_function()));
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Mul.h b/compiler/tflchef/tflite/src/Op/Mul.h
new file mode 100644
index 000000000..fd009d2fd
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Mul.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_MUL_H__
+#define __TFLITE_OP_MUL_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for MUL
+ */
+class TFliteOpMul : 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_MUL_H__
diff --git a/compiler/tflchef/tflite/src/Op/Neg.cpp b/compiler/tflchef/tflite/src/Op/Neg.cpp
new file mode 100644
index 000000000..c691390a3
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Neg.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Neg.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpNeg::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpNeg::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Neg");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Neg.h b/compiler/tflchef/tflite/src/Op/Neg.h
new file mode 100644
index 000000000..c77ab7e84
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Neg.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_NEG_H__
+#define __TFLITE_OP_NEG_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for abs
+ */
+class TFliteOpNeg : 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_NEG_H__
diff --git a/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV4.cpp b/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV4.cpp
new file mode 100644
index 000000000..ad9921970
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV4.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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 "NonMaxSuppressionV4.h"
+
+#include "Convert.h"
+#include "FillerHelper.h"
+
+namespace tflchef
+{
+
+void TFliteOpNonMaxSuppressionV4::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *max_output_size_tensor = import->tensors()->Get(inputs[2]);
+ assert(max_output_size_tensor->type() == tflite::TensorType::TensorType_INT32);
+
+ const tflite::Tensor *iou_threshold_tensor = import->tensors()->Get(inputs[3]);
+ assert(iou_threshold_tensor->type() == tflite::TensorType::TensorType_FLOAT32);
+
+ const tflite::Tensor *score_threshold_tensor = import->tensors()->Get(inputs[4]);
+ assert(score_threshold_tensor->type() == tflite::TensorType::TensorType_FLOAT32);
+
+ for (int32_t index = 2; index < 5; ++index)
+ {
+ fill_tensor_to_import(index, import);
+ }
+}
+
+tflchef::Operation *TFliteOpNonMaxSuppressionV4::build(const tflite::Operator *op,
+ TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("NonMaxSuppressionV4");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV4.h b/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV4.h
new file mode 100644
index 000000000..114a2ad2f
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV4.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_NON_MAX_SUPPRESSION_V4_H__
+#define __TFLITE_OP_NON_MAX_SUPPRESSION_V4_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for NON_MAX_SUPPRESSION_V4
+ */
+class TFliteOpNonMaxSuppressionV4 : 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_NON_MAX_SUPPRESSION_V4_H__
diff --git a/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV5.cpp b/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV5.cpp
new file mode 100644
index 000000000..db7f4c932
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV5.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NonMaxSuppressionV5.h"
+
+#include "Convert.h"
+#include "FillerHelper.h"
+
+namespace tflchef
+{
+
+void TFliteOpNonMaxSuppressionV5::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *max_output_size_tensor = import->tensors()->Get(inputs[2]);
+ assert(max_output_size_tensor->type() == tflite::TensorType::TensorType_INT32);
+
+ const tflite::Tensor *iou_threshold_tensor = import->tensors()->Get(inputs[3]);
+ assert(iou_threshold_tensor->type() == tflite::TensorType::TensorType_FLOAT32);
+
+ const tflite::Tensor *score_threshold_tensor = import->tensors()->Get(inputs[4]);
+ assert(score_threshold_tensor->type() == tflite::TensorType::TensorType_FLOAT32);
+
+ const tflite::Tensor *soft_nms_sigma_tensor = import->tensors()->Get(inputs[5]);
+ assert(soft_nms_sigma_tensor->type() == tflite::TensorType::TensorType_FLOAT32);
+
+ for (int32_t index = 2; index < 6; ++index)
+ {
+ fill_tensor_to_import(index, import);
+ }
+}
+
+tflchef::Operation *TFliteOpNonMaxSuppressionV5::build(const tflite::Operator *op,
+ TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("NonMaxSuppressionV5");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV5.h b/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV5.h
new file mode 100644
index 000000000..c948043f4
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/NonMaxSuppressionV5.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_NON_MAX_SUPPRESSION_V5_H__
+#define __TFLITE_OP_NON_MAX_SUPPRESSION_V5_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for NON_MAX_SUPPRESSION_V5
+ */
+class TFliteOpNonMaxSuppressionV5 : 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_NON_MAX_SUPPRESSION_V5_H__
diff --git a/compiler/tflchef/tflite/src/Op/NotEqual.cpp b/compiler/tflchef/tflite/src/Op/NotEqual.cpp
new file mode 100644
index 000000000..c2275db06
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/NotEqual.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NotEqual.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpNotEqual::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpNotEqual::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("NotEqual");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/NotEqual.h b/compiler/tflchef/tflite/src/Op/NotEqual.h
new file mode 100644
index 000000000..b1febdcc5
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/NotEqual.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_NOTEQUAL_H__
+#define __TFLITE_OP_NOTEQUAL_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Not Equal
+ */
+class TFliteOpNotEqual : 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_NOTEQUAL_H__
diff --git a/compiler/tflchef/tflite/src/Op/OneHot.cpp b/compiler/tflchef/tflite/src/Op/OneHot.cpp
new file mode 100644
index 000000000..f26ed3e7f
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/OneHot.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 "OneHot.h"
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpOneHot::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // only depth(second input) has constant on recipe cause depth value is used in shape inference.
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[1]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+
+ if (buffer && buffer->data())
+ {
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+ }
+
+ // on/off can be dtype of input/output. let's support INT32/FLOAT32 for now
+ for (int32_t index = 2; index <= 3; ++index)
+ {
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[index]);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ if (buffer && buffer->data())
+ {
+ switch (tensor->type())
+ {
+ case tflite::TensorType::TensorType_INT32:
+ {
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[index], vec);
+ break;
+ }
+
+ case tflite::TensorType::TensorType_FLOAT32:
+ {
+ auto vec = extract_buffer<float>(buffer);
+ import->set_tensor_filler(inputs[index], vec);
+ break;
+ }
+
+ default:
+ assert(false);
+ break;
+ }
+ }
+ }
+}
+
+tflchef::Operation *TFliteOpOneHot::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_OneHotOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("OneHot");
+
+ auto op_options = operation->mutable_onehot_options();
+
+ op_options->set_axis(op_params->axis());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/OneHot.h b/compiler/tflchef/tflite/src/Op/OneHot.h
new file mode 100644
index 000000000..50bbed095
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/OneHot.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_ONEHOT_H__
+#define __TFLITE_OP_ONEHOT_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for OneHot
+ */
+class TFliteOpOneHot : 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_ONEHOT_H__
diff --git a/compiler/tflchef/tflite/src/Op/PRelu.cpp b/compiler/tflchef/tflite/src/Op/PRelu.cpp
new file mode 100644
index 000000000..8a5e83a84
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/PRelu.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PRelu.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpPRelu::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+}
+
+tflchef::Operation *TFliteOpPRelu::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+ operation->set_type("PRelu");
+
+ // PReluOptions are empty
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/PRelu.h b/compiler/tflchef/tflite/src/Op/PRelu.h
new file mode 100644
index 000000000..b35c6e7ce
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/PRelu.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_PRELU_H__
+#define __TFLITE_OP_PRELU_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for PRelu
+ */
+class TFliteOpPRelu : 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_PRELU_H__
diff --git a/compiler/tflchef/tflite/src/Op/Pack.cpp b/compiler/tflchef/tflite/src/Op/Pack.cpp
new file mode 100644
index 000000000..ddf8c7d5d
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Pack.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Pack.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpPack::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpPack::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_PackOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Pack");
+
+ auto op_options = operation->mutable_pack_options();
+
+ op_options->set_axis(op_params->axis());
+ op_options->set_values_count(op_params->values_count());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Pack.h b/compiler/tflchef/tflite/src/Op/Pack.h
new file mode 100644
index 000000000..7779f64ed
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Pack.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_PACK_H__
+#define __TFLITE_OP_PACK_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for pack
+ */
+class TFliteOpPack : 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_PACK_H__
diff --git a/compiler/tflchef/tflite/src/Op/Pad.cpp b/compiler/tflchef/tflite/src/Op/Pad.cpp
new file mode 100644
index 000000000..2978e4422
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Pad.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Pad.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpPad::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // filler for second input
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[1]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+}
+
+tflchef::Operation *TFliteOpPad::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Pad");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Pad.h b/compiler/tflchef/tflite/src/Op/Pad.h
new file mode 100644
index 000000000..99998d418
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Pad.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_PAD_H__
+#define __TFLITE_OP_PAD_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for PAD
+ */
+class TFliteOpPad : 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_PAD_H__
diff --git a/compiler/tflchef/tflite/src/Op/PadV2.cpp b/compiler/tflchef/tflite/src/Op/PadV2.cpp
new file mode 100644
index 000000000..0b1c9f3b2
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/PadV2.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "PadV2.h"
+
+#include "FillerHelper.h"
+
+namespace tflchef
+{
+
+void TFliteOpPadV2::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Filler for paddings and constant_values
+ fill_tensor_to_import(1, import);
+ fill_tensor_to_import(2, import);
+}
+
+tflchef::Operation *TFliteOpPadV2::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("PadV2");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/PadV2.h b/compiler/tflchef/tflite/src/Op/PadV2.h
new file mode 100644
index 000000000..3aa474b92
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/PadV2.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_PADV2_H__
+#define __TFLITE_OP_PADV2_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for PADV2
+ */
+class TFliteOpPadV2 : 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_PADV2_H__
diff --git a/compiler/tflchef/tflite/src/Op/Pow.cpp b/compiler/tflchef/tflite/src/Op/Pow.cpp
new file mode 100644
index 000000000..fe8e8ac0f
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Pow.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Pow.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpPow::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+}
+
+tflchef::Operation *TFliteOpPow::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+ operation->set_type("Pow");
+
+ // PowOptions are empty
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Pow.h b/compiler/tflchef/tflite/src/Op/Pow.h
new file mode 100644
index 000000000..20e847377
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Pow.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_POW_H__
+#define __TFLITE_OP_POW_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Pow
+ */
+class TFliteOpPow : 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_POW_H__
diff --git a/compiler/tflchef/tflite/src/Op/Range.cpp b/compiler/tflchef/tflite/src/Op/Range.cpp
new file mode 100644
index 000000000..2958b9c41
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Range.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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 "Range.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpRange::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // filler for all inputs
+ const auto &inputs = *op->inputs();
+
+ for (int index = 0; index < 3; ++index)
+ {
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[index]);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ if (tensor->type() == tflite::TensorType::TensorType_INT32)
+ {
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[index], vec);
+ }
+ else if (tensor->type() == tflite::TensorType::TensorType_FLOAT32)
+ {
+ auto vec = extract_buffer<float>(buffer);
+ import->set_tensor_filler(inputs[index], vec);
+ }
+ else
+ {
+ assert(false && "Invalid tensor type");
+ }
+ }
+}
+
+tflchef::Operation *TFliteOpRange::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Range");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Range.h b/compiler/tflchef/tflite/src/Op/Range.h
new file mode 100644
index 000000000..ad10dc58b
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Range.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_RANGE_H__
+#define __TFLITE_OP_RANGE_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for abs
+ */
+class TFliteOpRange : 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_RANGE_H__
diff --git a/compiler/tflchef/tflite/src/Op/Rank.cpp b/compiler/tflchef/tflite/src/Op/Rank.cpp
new file mode 100644
index 000000000..184c8e482
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Rank.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Rank.h"
+
+namespace tflchef
+{
+
+void TFliteOpRank::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpRank::build(const tflite::Operator *, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Rank");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Rank.h b/compiler/tflchef/tflite/src/Op/Rank.h
new file mode 100644
index 000000000..003d9d310
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Rank.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_RANK_H__
+#define __TFLITE_OP_RANK_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for rank
+ */
+class TFliteOpRank : 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_RANK_H__
diff --git a/compiler/tflchef/tflite/src/Op/ReLUN1To1.cpp b/compiler/tflchef/tflite/src/Op/ReLUN1To1.cpp
new file mode 100644
index 000000000..4cc8dbd2b
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ReLUN1To1.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReLUN1To1.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpReLUN1To1::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpReLUN1To1::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("ReLUN1To1");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/ReLUN1To1.h b/compiler/tflchef/tflite/src/Op/ReLUN1To1.h
new file mode 100644
index 000000000..0767006af
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ReLUN1To1.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_RELU_N1_TO_1_H__
+#define __TFLITE_OP_RELU_N1_TO_1_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for RELU_N1_TO_1
+ */
+class TFliteOpReLUN1To1 : 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_RELU_N1_TO_1_H__
diff --git a/compiler/tflchef/tflite/src/Op/ReduceAny.cpp b/compiler/tflchef/tflite/src/Op/ReduceAny.cpp
new file mode 100644
index 000000000..e0cc503c4
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ReduceAny.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReduceAny.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpReduceAny::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // filler for second input
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[1]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+}
+
+tflchef::Operation *TFliteOpReduceAny::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+ operation->set_type("ReduceAny");
+
+ auto op_params = op->builtin_options_as_ReducerOptions();
+ assert(op_params != nullptr);
+
+ auto op_options = operation->mutable_reduce_any_options();
+ op_options->set_keep_dims(op_params->keep_dims());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/ReduceAny.h b/compiler/tflchef/tflite/src/Op/ReduceAny.h
new file mode 100644
index 000000000..dd5e361d5
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ReduceAny.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_REDUCE_ANY_H__
+#define __TFLITE_OP_REDUCE_ANY_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for REDUCE_ANY
+ */
+class TFliteOpReduceAny : 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_REDUCE_ANY_H__
diff --git a/compiler/tflchef/tflite/src/Op/ReduceMax.cpp b/compiler/tflchef/tflite/src/Op/ReduceMax.cpp
new file mode 100644
index 000000000..499f58566
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ReduceMax.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReduceMax.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpReduceMax::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // filler for second input
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[1]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+}
+
+tflchef::Operation *TFliteOpReduceMax::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_ReducerOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("ReduceMax");
+
+ auto op_options = operation->mutable_reduce_max_options();
+
+ op_options->set_keep_dims(op_params->keep_dims());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/ReduceMax.h b/compiler/tflchef/tflite/src/Op/ReduceMax.h
new file mode 100644
index 000000000..8e65cf47c
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ReduceMax.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_REDUCEMAX_H__
+#define __TFLITE_OP_REDUCEMAX_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Reduce Max
+ */
+class TFliteOpReduceMax : 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_REDUCEMAX_H__
diff --git a/compiler/tflchef/tflite/src/Op/ReduceMin.cpp b/compiler/tflchef/tflite/src/Op/ReduceMin.cpp
new file mode 100644
index 000000000..09e2e134c
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ReduceMin.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReduceMin.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpReduceMin::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // filler for second input
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[1]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+}
+
+tflchef::Operation *TFliteOpReduceMin::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_ReducerOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("ReduceMin");
+
+ auto op_options = operation->mutable_reduce_min_options();
+
+ op_options->set_keep_dims(op_params->keep_dims());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/ReduceMin.h b/compiler/tflchef/tflite/src/Op/ReduceMin.h
new file mode 100644
index 000000000..88cba6fe7
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ReduceMin.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_REDUCEMIN_H__
+#define __TFLITE_OP_REDUCEMIN_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Reduce Min
+ */
+class TFliteOpReduceMin : 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_REDUCEMIN_H__
diff --git a/compiler/tflchef/tflite/src/Op/ReduceProd.cpp b/compiler/tflchef/tflite/src/Op/ReduceProd.cpp
new file mode 100644
index 000000000..e2d98970d
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ReduceProd.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReduceProd.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpReduceProd::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // filler for second input
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[1]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+}
+
+tflchef::Operation *TFliteOpReduceProd::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+ operation->set_type("ReduceProd");
+
+ auto op_params = op->builtin_options_as_ReducerOptions();
+ assert(op_params != nullptr);
+
+ auto op_options = operation->mutable_reduce_prod_options();
+ op_options->set_keep_dims(op_params->keep_dims());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/ReduceProd.h b/compiler/tflchef/tflite/src/Op/ReduceProd.h
new file mode 100644
index 000000000..e7766840a
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ReduceProd.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_REDUCE_PROD_H__
+#define __TFLITE_OP_REDUCE_PROD_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for REDUCE_PROD
+ */
+class TFliteOpReduceProd : 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_REDUCE_PROD_H__
diff --git a/compiler/tflchef/tflite/src/Op/Reshape.cpp b/compiler/tflchef/tflite/src/Op/Reshape.cpp
index 663ab3ec3..0094d5df5 100644
--- a/compiler/tflchef/tflite/src/Op/Reshape.cpp
+++ b/compiler/tflchef/tflite/src/Op/Reshape.cpp
@@ -17,6 +17,7 @@
#include "Reshape.h"
#include "Convert.h"
+#include "FillerHelper.h"
namespace tflchef
{
@@ -27,33 +28,29 @@ void TFliteOpReshape::filler(const tflite::Operator *op, TFliteImport *import,
const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
bool hasShape = (inputs.size() == 2);
- assert(inputs.size() == 1 || hasShape);
-
if (hasShape)
{
- auto op_params = op->builtin_options_as_ReshapeOptions();
- std::vector<int32_t> new_shape = as_index_vector(op_params->new_shape());
- import->set_tensor_filler(inputs.at(1), new_shape);
+ fill_tensor_to_import(inputs[1], import);
}
}
tflchef::Operation *TFliteOpReshape::build(const tflite::Operator *op, TFliteImport *import,
tflchef::ModelRecipe *model_recipe) const
{
- auto op_params = op->builtin_options_as_ReshapeOptions();
- assert(op_params != nullptr);
-
auto operation = model_recipe->add_operation();
operation->set_type("Reshape");
- auto op_options = operation->mutable_reshape_options();
-
- std::vector<int32_t> new_shape = as_index_vector(op_params->new_shape());
-
- for (auto shape : new_shape)
+ auto op_params = op->builtin_options_as_ReshapeOptions();
+ if (op_params != nullptr)
{
- op_options->add_new_shape(shape);
+ auto op_options = operation->mutable_reshape_options();
+
+ std::vector<int32_t> new_shape = as_index_vector(op_params->new_shape());
+ for (auto shape : new_shape)
+ {
+ op_options->add_new_shape(shape);
+ }
}
return operation;
diff --git a/compiler/tflchef/tflite/src/Op/ResizeBilinear.cpp b/compiler/tflchef/tflite/src/Op/ResizeBilinear.cpp
new file mode 100644
index 000000000..0f6db1fcb
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ResizeBilinear.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ResizeBilinear.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpResizeBilinear::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // size buffer has filler
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[1]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+
+ if (buffer && buffer->data())
+ {
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+ }
+}
+
+tflchef::Operation *TFliteOpResizeBilinear::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_ResizeBilinearOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("ResizeBilinear");
+
+ auto op_options = operation->mutable_resize_bilinear_options();
+
+ op_options->set_align_corners(op_params->align_corners());
+ op_options->set_half_pixel_centers(op_params->half_pixel_centers());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/ResizeBilinear.h b/compiler/tflchef/tflite/src/Op/ResizeBilinear.h
new file mode 100644
index 000000000..98c49c534
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ResizeBilinear.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_RESIZE_BILINEAR_H__
+#define __TFLITE_OP_RESIZE_BILINEAR_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for ResizeBilinear
+ */
+class TFliteOpResizeBilinear : 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_RESIZE_BILINEAR_H__
diff --git a/compiler/tflchef/tflite/src/Op/ResizeNearestNeighbor.cpp b/compiler/tflchef/tflite/src/Op/ResizeNearestNeighbor.cpp
new file mode 100644
index 000000000..f3dd8fed0
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ResizeNearestNeighbor.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ResizeNearestNeighbor.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpResizeNearestNeighbor::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // size buffer has filler
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[1]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+
+ if (buffer && buffer->data())
+ {
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+ }
+}
+
+tflchef::Operation *TFliteOpResizeNearestNeighbor::build(const tflite::Operator *op,
+ TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_ResizeNearestNeighborOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("ResizeNearestNeighbor");
+
+ auto op_options = operation->mutable_resize_nearest_neighbor_options();
+
+ op_options->set_align_corners(op_params->align_corners());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/ResizeNearestNeighbor.h b/compiler/tflchef/tflite/src/Op/ResizeNearestNeighbor.h
new file mode 100644
index 000000000..5090bb938
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ResizeNearestNeighbor.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_RESIZE_NEAREST_NEIGHBOR_H__
+#define __TFLITE_OP_RESIZE_NEAREST_NEIGHBOR_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for ResizeNearestNeighbor
+ */
+class TFliteOpResizeNearestNeighbor : 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_RESIZE_NEAREST_NEIGHBOR_H__
diff --git a/compiler/tflchef/tflite/src/Op/ReverseSequence.cpp b/compiler/tflchef/tflite/src/Op/ReverseSequence.cpp
new file mode 100644
index 000000000..6ef6c2326
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ReverseSequence.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReverseSequence.h"
+
+#include "Convert.h"
+#include "FillerHelper.h"
+
+namespace tflchef
+{
+
+void TFliteOpReverseSequence::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *) const
+{
+ const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
+ assert(inputs.size() == 2);
+
+ fill_tensor_to_import(inputs[1], import);
+}
+
+tflchef::Operation *TFliteOpReverseSequence::build(const tflite::Operator *op, TFliteImport *,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("ReverseSequence");
+
+ auto op_params = op->builtin_options_as_ReverseSequenceOptions();
+
+ assert(op_params != nullptr);
+
+ auto op_options = operation->mutable_reverse_sequence_options();
+
+ op_options->set_seq_dim(op_params->seq_dim());
+ op_options->set_batch_dim(op_params->batch_dim());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/ReverseSequence.h b/compiler/tflchef/tflite/src/Op/ReverseSequence.h
new file mode 100644
index 000000000..8c8c811e4
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ReverseSequence.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_REVERSE_SEQUENCE_H__
+#define __TFLITE_OP_REVERSE_SEQUENCE_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for ReverseSequence
+ */
+class TFliteOpReverseSequence : 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_REVERSE_SEQUENCE_H__
diff --git a/compiler/tflchef/tflite/src/Op/ReverseV2.cpp b/compiler/tflchef/tflite/src/Op/ReverseV2.cpp
new file mode 100644
index 000000000..c59d97574
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ReverseV2.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReverseV2.h"
+
+#include "Convert.h"
+#include "FillerHelper.h"
+
+namespace tflchef
+{
+
+void TFliteOpReverseV2::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *) const
+{
+ const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
+ assert(inputs.size() == 2);
+
+ fill_tensor_to_import(inputs[1], import);
+}
+
+tflchef::Operation *TFliteOpReverseV2::build(const tflite::Operator *, TFliteImport *,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("ReverseV2");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/ReverseV2.h b/compiler/tflchef/tflite/src/Op/ReverseV2.h
new file mode 100644
index 000000000..6a8a75e6b
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ReverseV2.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_REVERSEV2_H__
+#define __TFLITE_OP_REVERSEV2_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for ReverseV2
+ */
+class TFliteOpReverseV2 : 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_REVERSEV2_H__
diff --git a/compiler/tflchef/tflite/src/Op/Round.cpp b/compiler/tflchef/tflite/src/Op/Round.cpp
new file mode 100644
index 000000000..c3f6bf6c4
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Round.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Round.h"
+
+namespace tflchef
+{
+
+void TFliteOpRound::filler(const tflite::Operator *, TFliteImport *, tflchef::ModelRecipe *) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpRound::build(const tflite::Operator *, TFliteImport *,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Round");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Round.h b/compiler/tflchef/tflite/src/Op/Round.h
new file mode 100644
index 000000000..df0da3fa1
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Round.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_ROUND_H__
+#define __TFLITE_OP_ROUND_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Round
+ */
+class TFliteOpRound : 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_ROUND_H__
diff --git a/compiler/tflchef/tflite/src/Op/Rsqrt.cpp b/compiler/tflchef/tflite/src/Op/Rsqrt.cpp
new file mode 100644
index 000000000..1639214e4
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Rsqrt.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Rsqrt.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpRsqrt::filler(const tflite::Operator *, TFliteImport *, tflchef::ModelRecipe *) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpRsqrt::build(const tflite::Operator *, TFliteImport *,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Rsqrt");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Rsqrt.h b/compiler/tflchef/tflite/src/Op/Rsqrt.h
new file mode 100644
index 000000000..5d68344c2
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Rsqrt.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_RSQRT_H__
+#define __TFLITE_OP_RSQRT_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Rsqrt
+ */
+class TFliteOpRsqrt : 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_RSQRT_H__
diff --git a/compiler/tflchef/tflite/src/Op/ScatterNd.cpp b/compiler/tflchef/tflite/src/Op/ScatterNd.cpp
new file mode 100644
index 000000000..548a09a67
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ScatterNd.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ScatterNd.h"
+
+#include "Convert.h"
+#include "FillerHelper.h"
+
+namespace tflchef
+{
+
+void TFliteOpScatterNd::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Filler for indices and shape
+ fill_tensor_to_import(0, import);
+ fill_tensor_to_import(2, import);
+}
+
+tflchef::Operation *TFliteOpScatterNd::build(const tflite::Operator *, TFliteImport *,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("ScatterNd");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/ScatterNd.h b/compiler/tflchef/tflite/src/Op/ScatterNd.h
new file mode 100644
index 000000000..76362d775
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ScatterNd.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_SCATTER_ND_H__
+#define __TFLITE_OP_SCATTER_ND_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for ScatterNd
+ */
+class TFliteOpScatterNd : 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_SCATTER_ND_H__
diff --git a/compiler/tflchef/tflite/src/Op/SegmentSum.cpp b/compiler/tflchef/tflite/src/Op/SegmentSum.cpp
new file mode 100644
index 000000000..a975ca4b3
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/SegmentSum.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SegmentSum.h"
+
+#include "FillerHelper.h"
+
+namespace tflchef
+{
+
+void TFliteOpSegmentSum::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Filler for indices and shape
+ fill_tensor_to_import(1, import);
+}
+
+tflchef::Operation *TFliteOpSegmentSum::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("SegmentSum");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/SegmentSum.h b/compiler/tflchef/tflite/src/Op/SegmentSum.h
new file mode 100644
index 000000000..d20e63bd7
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/SegmentSum.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_SEGMENT_SUM_H__
+#define __TFLITE_OP_SEGMENT_SUM_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for SEGMENT_SUM
+ */
+class TFliteOpSegmentSum : 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_SEGMENT_SUM_H__
diff --git a/compiler/tflchef/tflite/src/Op/Select.cpp b/compiler/tflchef/tflite/src/Op/Select.cpp
new file mode 100644
index 000000000..741ffb8f6
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Select.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Select.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpSelect::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpSelect::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Select");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Select.h b/compiler/tflchef/tflite/src/Op/Select.h
new file mode 100644
index 000000000..bf8e57d78
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Select.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_SELECT_H__
+#define __TFLITE_OP_SELECT_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for SELECT
+ */
+class TFliteOpSelect : 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_SELECT_H__
diff --git a/compiler/tflchef/tflite/src/Op/SelectV2.cpp b/compiler/tflchef/tflite/src/Op/SelectV2.cpp
new file mode 100644
index 000000000..0ddabb4be
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/SelectV2.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SelectV2.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpSelectV2::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpSelectV2::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("SelectV2");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/SelectV2.h b/compiler/tflchef/tflite/src/Op/SelectV2.h
new file mode 100644
index 000000000..ff03341d7
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/SelectV2.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_SELECT_V2_H__
+#define __TFLITE_OP_SELECT_V2_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for SELECT
+ */
+class TFliteOpSelectV2 : 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_SELECT_V2_H__
diff --git a/compiler/tflchef/tflite/src/Op/Shape.cpp b/compiler/tflchef/tflite/src/Op/Shape.cpp
new file mode 100644
index 000000000..d6e490d63
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Shape.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Shape.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpShape::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpShape::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+ operation->set_type("Shape");
+
+ auto op_params = op->builtin_options_as_ShapeOptions();
+ assert(op_params != nullptr);
+
+ auto op_options = operation->mutable_shape_options();
+ op_options->set_out_type(as_tflchef_type(op_params->out_type()));
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Shape.h b/compiler/tflchef/tflite/src/Op/Shape.h
new file mode 100644
index 000000000..ebe1befb3
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Shape.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_SHAPE_H__
+#define __TFLITE_OP_SHAPE_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for SHAPE
+ */
+class TFliteOpShape : 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_SHAPE_H__
diff --git a/compiler/tflchef/tflite/src/Op/Sin.cpp b/compiler/tflchef/tflite/src/Op/Sin.cpp
new file mode 100644
index 000000000..8c063f424
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Sin.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Sin.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpSin::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpSin::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Sin");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Sin.h b/compiler/tflchef/tflite/src/Op/Sin.h
new file mode 100644
index 000000000..51eabceb5
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Sin.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_SIN_H__
+#define __TFLITE_OP_SIN_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Sin
+ */
+class TFliteOpSin : 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_SIN_H__
diff --git a/compiler/tflchef/tflite/src/Op/Slice.cpp b/compiler/tflchef/tflite/src/Op/Slice.cpp
new file mode 100644
index 000000000..f0c44da2d
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Slice.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Slice.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpSlice::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
+
+ // for begin and size
+ for (int32_t index = 1; index <= 2; ++index)
+ {
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[index]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[index], vec);
+ }
+}
+
+tflchef::Operation *TFliteOpSlice::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Slice");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Slice.h b/compiler/tflchef/tflite/src/Op/Slice.h
new file mode 100644
index 000000000..6ca6724d3
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Slice.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_SLICE_H__
+#define __TFLITE_OP_SLICE_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for SLICE
+ */
+class TFliteOpSlice : 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_SLICE_H__
diff --git a/compiler/tflchef/tflite/src/Op/Softmax.cpp b/compiler/tflchef/tflite/src/Op/Softmax.cpp
new file mode 100644
index 000000000..5b5c94f7e
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Softmax.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Softmax.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpSoftmax::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpSoftmax::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_SoftmaxOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Softmax");
+
+ auto op_options = operation->mutable_softmax_options();
+
+ op_options->set_beta(op_params->beta());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Softmax.h b/compiler/tflchef/tflite/src/Op/Softmax.h
new file mode 100644
index 000000000..cf168bdd9
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Softmax.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_SOFTMAX_H__
+#define __TFLITE_OP_SOFTMAX_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Softmax
+ */
+class TFliteOpSoftmax : 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_SOFTMAX_H__
diff --git a/compiler/tflchef/tflite/src/Op/SpaceToBatchND.cpp b/compiler/tflchef/tflite/src/Op/SpaceToBatchND.cpp
new file mode 100644
index 000000000..9de0775a9
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/SpaceToBatchND.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SpaceToBatchND.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpSpaceToBatchND::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // filler for second, third input
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[1]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+
+ tensor = import->tensors()->Get(inputs[2]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ buffer = import->buffers()->Get(tensor->buffer());
+ vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[2], vec);
+}
+
+tflchef::Operation *TFliteOpSpaceToBatchND::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("SpaceToBatchND");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/SpaceToBatchND.h b/compiler/tflchef/tflite/src/Op/SpaceToBatchND.h
new file mode 100644
index 000000000..9d7bc44e8
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/SpaceToBatchND.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_SPACETOBATCHND_H__
+#define __TFLITE_OP_SPACETOBATCHND_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for SpaceToBatchND
+ */
+class TFliteOpSpaceToBatchND : 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_SPACETOBATCHND_H__
diff --git a/compiler/tflchef/tflite/src/Op/SpaceToDepth.cpp b/compiler/tflchef/tflite/src/Op/SpaceToDepth.cpp
new file mode 100644
index 000000000..e5718b515
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/SpaceToDepth.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SpaceToDepth.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpSpaceToDepth::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpSpaceToDepth::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("SpaceToDepth");
+
+ auto op_params = op->builtin_options_as_SpaceToDepthOptions();
+ assert(op_params != nullptr);
+
+ auto op_options = operation->mutable_space_to_depth_options();
+
+ op_options->set_block_size(op_params->block_size());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/SpaceToDepth.h b/compiler/tflchef/tflite/src/Op/SpaceToDepth.h
new file mode 100644
index 000000000..784ad940a
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/SpaceToDepth.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_SPACETODEPTH_H__
+#define __TFLITE_OP_SPACETODEPTH_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for SpaceToDepth
+ */
+class TFliteOpSpaceToDepth : 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_SPACETODEPTH_H__
diff --git a/compiler/tflchef/tflite/src/Op/SparseToDense.cpp b/compiler/tflchef/tflite/src/Op/SparseToDense.cpp
new file mode 100644
index 000000000..9e4f0a067
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/SparseToDense.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SparseToDense.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpSparseToDense::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // filler for Shape
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *output_shape_tensor = import->tensors()->Get(inputs[1]);
+ assert(output_shape_tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(output_shape_tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+}
+
+tflchef::Operation *TFliteOpSparseToDense::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_SparseToDenseOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("SparseToDense");
+
+ auto op_options = operation->mutable_sparse_to_dense_options();
+
+ op_options->set_validate_indices(op_params->validate_indices());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/SparseToDense.h b/compiler/tflchef/tflite/src/Op/SparseToDense.h
new file mode 100644
index 000000000..5ffe4789d
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/SparseToDense.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_SPARSETODENSE_H__
+#define __TFLITE_OP_SPARSETODENSE_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for SparseToDense
+ */
+class TFliteOpSparseToDense : 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_SPARSETODENSE_H__
diff --git a/compiler/tflchef/tflite/src/Op/Split.cpp b/compiler/tflchef/tflite/src/Op/Split.cpp
new file mode 100644
index 000000000..49f9aa2c6
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Split.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Split.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpSplit::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ const auto &inputs = *op->inputs();
+
+ // for input split_dim
+ // NOTE unlike other Ops, Split input 0 is split_dim
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[0]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[0], vec);
+}
+
+tflchef::Operation *TFliteOpSplit::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Split");
+
+ auto op_options = operation->mutable_split_options();
+
+ auto op_params = op->builtin_options_as_SplitOptions();
+ assert(op_params != nullptr);
+
+ op_options->set_num_splits(op_params->num_splits());
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Split.h b/compiler/tflchef/tflite/src/Op/Split.h
new file mode 100644
index 000000000..af247a1b9
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Split.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_SPLIT_H__
+#define __TFLITE_OP_SPLIT_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for SPLIT
+ */
+class TFliteOpSplit : 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_SPLIT_H__
diff --git a/compiler/tflchef/tflite/src/Op/SplitV.cpp b/compiler/tflchef/tflite/src/Op/SplitV.cpp
new file mode 100644
index 000000000..18035e6f4
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/SplitV.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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 "SplitV.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpSplitV::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ const auto &inputs = *op->inputs();
+
+ // for input "size_splits" and "split_dim"
+ for (int32_t idx = 1; idx <= 2; idx++)
+ {
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[idx]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[idx], vec);
+ }
+}
+
+tflchef::Operation *TFliteOpSplitV::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("SplitV");
+
+ auto op_options = operation->mutable_split_v_options();
+
+ auto op_params = op->builtin_options_as_SplitVOptions();
+ assert(op_params != nullptr);
+
+ op_options->set_num_splits(op_params->num_splits());
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/SplitV.h b/compiler/tflchef/tflite/src/Op/SplitV.h
new file mode 100644
index 000000000..3f715b5f9
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/SplitV.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_SPLIT_V_H__
+#define __TFLITE_OP_SPLIT_V_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for SPLIT_V
+ */
+class TFliteOpSplitV : 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_SPLIT_V_H__
diff --git a/compiler/tflchef/tflite/src/Op/Sqrt.cpp b/compiler/tflchef/tflite/src/Op/Sqrt.cpp
index 5d967d66f..dd6bfcab0 100644
--- a/compiler/tflchef/tflite/src/Op/Sqrt.cpp
+++ b/compiler/tflchef/tflite/src/Op/Sqrt.cpp
@@ -25,6 +25,19 @@ void TFliteOpSqrt::filler(const tflite::Operator *op, TFliteImport *import,
tflchef::ModelRecipe *model_recipe) const
{
// Nothing to do with filler
+ // But input has filler for constant inputs
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[0]);
+ if (tensor->type() == tflite::TensorType::TensorType_FLOAT32)
+ {
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ if (buffer && buffer->data())
+ {
+ auto vec = extract_buffer<float>(buffer);
+ import->set_tensor_filler(inputs[0], vec);
+ }
+ }
}
tflchef::Operation *TFliteOpSqrt::build(const tflite::Operator *op, TFliteImport *import,
diff --git a/compiler/tflchef/tflite/src/Op/Square.cpp b/compiler/tflchef/tflite/src/Op/Square.cpp
new file mode 100644
index 000000000..d3803284a
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Square.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Square.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpSquare::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpSquare::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Square");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Square.h b/compiler/tflchef/tflite/src/Op/Square.h
new file mode 100644
index 000000000..9c008fe52
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Square.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_SQUARE_H__
+#define __TFLITE_OP_SQUARE_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Square
+ */
+class TFliteOpSquare : 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_SQUARE_H__
diff --git a/compiler/tflchef/tflite/src/Op/SquaredDifference.cpp b/compiler/tflchef/tflite/src/Op/SquaredDifference.cpp
new file mode 100644
index 000000000..1ee536e76
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/SquaredDifference.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SquaredDifference.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpSquaredDifference::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpSquaredDifference::build(const tflite::Operator *op,
+ TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("SquaredDifference");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/SquaredDifference.h b/compiler/tflchef/tflite/src/Op/SquaredDifference.h
new file mode 100644
index 000000000..58c2ed460
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/SquaredDifference.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_SQUAREDDIFFERENCE_H__
+#define __TFLITE_OP_SQUAREDDIFFERENCE_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for SquaredDifference
+ */
+class TFliteOpSquaredDifference : 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_SQUAREDDIFFERENCE_H__
diff --git a/compiler/tflchef/tflite/src/Op/Squeeze.cpp b/compiler/tflchef/tflite/src/Op/Squeeze.cpp
new file mode 100644
index 000000000..7983fc62a
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Squeeze.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Squeeze.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpSqueeze::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpSqueeze::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_SqueezeOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Squeeze");
+
+ auto op_options = operation->mutable_squeeze_options();
+
+ std::vector<int32_t> squeeze_dims = as_index_vector(op_params->squeeze_dims());
+
+ for (auto dim : squeeze_dims)
+ {
+ op_options->add_squeeze_dim(dim);
+ }
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Squeeze.h b/compiler/tflchef/tflite/src/Op/Squeeze.h
new file mode 100644
index 000000000..b6c89f73d
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Squeeze.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_SQUEEZE_H__
+#define __TFLITE_OP_SQUEEZE_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Squeeze
+ */
+class TFliteOpSqueeze : 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_SQUEEZE_H__
diff --git a/compiler/tflchef/tflite/src/Op/StridedSlice.cpp b/compiler/tflchef/tflite/src/Op/StridedSlice.cpp
new file mode 100644
index 000000000..c770236c7
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/StridedSlice.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "StridedSlice.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpStridedSlice::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
+
+ // for begin, end and strides
+ for (int32_t index = 1; index <= 3; ++index)
+ {
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[index]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[index], vec);
+ }
+}
+
+tflchef::Operation *TFliteOpStridedSlice::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_StridedSliceOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("StridedSlice");
+
+ auto op_options = operation->mutable_strided_slice_options();
+
+ op_options->set_begin_mask(op_params->begin_mask());
+ op_options->set_end_mask(op_params->end_mask());
+ op_options->set_ellipsis_mask(op_params->ellipsis_mask());
+ op_options->set_new_axis_mask(op_params->new_axis_mask());
+ op_options->set_shrink_axis_mask(op_params->shrink_axis_mask());
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/StridedSlice.h b/compiler/tflchef/tflite/src/Op/StridedSlice.h
new file mode 100644
index 000000000..98054b9b9
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/StridedSlice.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_STRIDEDSLICE_H__
+#define __TFLITE_OP_STRIDEDSLICE_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for StridedSlice
+ */
+class TFliteOpStridedSlice : 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_STRIDEDSLICE_H__
diff --git a/compiler/tflchef/tflite/src/Op/Sub.cpp b/compiler/tflchef/tflite/src/Op/Sub.cpp
index db77fddf7..0a08bbfdf 100644
--- a/compiler/tflchef/tflite/src/Op/Sub.cpp
+++ b/compiler/tflchef/tflite/src/Op/Sub.cpp
@@ -17,6 +17,7 @@
#include "Sub.h"
#include "Convert.h"
+#include "FillerHelper.h"
namespace tflchef
{
@@ -24,7 +25,13 @@ namespace tflchef
void TFliteOpSub::filler(const tflite::Operator *op, TFliteImport *import,
tflchef::ModelRecipe *model_recipe) const
{
- // Nothing to do with filler
+ // Sub may have constant input
+
+ const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
+ assert(inputs.size() == 2);
+
+ fill_tensor_to_import(inputs[0], import);
+ fill_tensor_to_import(inputs[1], import);
}
tflchef::Operation *TFliteOpSub::build(const tflite::Operator *op, TFliteImport *import,
diff --git a/compiler/tflchef/tflite/src/Op/Sum.cpp b/compiler/tflchef/tflite/src/Op/Sum.cpp
new file mode 100644
index 000000000..9f3133e85
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Sum.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Sum.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpSum::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // filler for second input
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[1]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+}
+
+tflchef::Operation *TFliteOpSum::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_ReducerOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Sum");
+
+ auto op_options = operation->mutable_sum_options();
+
+ op_options->set_keep_dims(op_params->keep_dims());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Sum.h b/compiler/tflchef/tflite/src/Op/Sum.h
new file mode 100644
index 000000000..38eeb080d
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Sum.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_SUM_H__
+#define __TFLITE_OP_SUM_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for sum
+ */
+class TFliteOpSum : 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_SUM_H__
diff --git a/compiler/tflchef/tflite/src/Op/Tanh.cpp b/compiler/tflchef/tflite/src/Op/Tanh.cpp
new file mode 100644
index 000000000..cab8ca460
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Tanh.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Tanh.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpTanh::filler(const tflite::Operator *, TFliteImport *, tflchef::ModelRecipe *) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpTanh::build(const tflite::Operator *, TFliteImport *,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Tanh");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Tanh.h b/compiler/tflchef/tflite/src/Op/Tanh.h
new file mode 100644
index 000000000..7339e4103
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Tanh.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_TANH_H__
+#define __TFLITE_OP_TANH_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Tanh
+ */
+class TFliteOpTanh : 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_TANH_H__
diff --git a/compiler/tflchef/tflite/src/Op/Tile.cpp b/compiler/tflchef/tflite/src/Op/Tile.cpp
new file mode 100644
index 000000000..14e65131c
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Tile.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Tile.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpTile::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // filler for second input
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[1]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32 ||
+ tensor->type() == tflite::TensorType::TensorType_INT64);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+}
+
+tflchef::Operation *TFliteOpTile::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Tile");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Tile.h b/compiler/tflchef/tflite/src/Op/Tile.h
new file mode 100644
index 000000000..640f52a1f
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Tile.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_TILE_H__
+#define __TFLITE_OP_TILE_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Tile
+ */
+class TFliteOpTile : 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_TILE_H__
diff --git a/compiler/tflchef/tflite/src/Op/TopKV2.cpp b/compiler/tflchef/tflite/src/Op/TopKV2.cpp
new file mode 100644
index 000000000..461456ae2
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/TopKV2.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TopKV2.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpTopKV2::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // filler for second input
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[1]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32 ||
+ tensor->type() == tflite::TensorType::TensorType_INT64);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+}
+
+tflchef::Operation *TFliteOpTopKV2::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("TopKV2");
+
+ // NOTE there is `sorted` attribute in TF but it's always true for TFlite
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/TopKV2.h b/compiler/tflchef/tflite/src/Op/TopKV2.h
new file mode 100644
index 000000000..b2b74cc75
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/TopKV2.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_TOPK_V2_H__
+#define __TFLITE_OP_TOPK_V2_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for TOPK_V2
+ */
+class TFliteOpTopKV2 : 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_TOPK_V2_H__
diff --git a/compiler/tflchef/tflite/src/Op/Transpose.cpp b/compiler/tflchef/tflite/src/Op/Transpose.cpp
new file mode 100644
index 000000000..a997bb08e
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Transpose.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Transpose.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpTranspose::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *perm_tensor = import->tensors()->Get(inputs[1]);
+ assert(perm_tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(perm_tensor->buffer());
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[1], vec);
+}
+
+tflchef::Operation *TFliteOpTranspose::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Transpose");
+
+ // No options for Transpose
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Transpose.h b/compiler/tflchef/tflite/src/Op/Transpose.h
new file mode 100644
index 000000000..f0d944b6b
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Transpose.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_TRANSPOSE_H__
+#define __TFLITE_OP_TRANSPOSE_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Transpose
+ */
+class TFliteOpTranspose : 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_TRANSPOSE_H__
diff --git a/compiler/tflchef/tflite/src/Op/TransposeConv.cpp b/compiler/tflchef/tflite/src/Op/TransposeConv.cpp
new file mode 100644
index 000000000..4e7adf6c6
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/TransposeConv.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 "TransposeConv.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpTransposeConv::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ const auto &inputs = *op->inputs();
+
+ const tflite::Tensor *tensor = import->tensors()->Get(inputs[0]);
+ assert(tensor->type() == tflite::TensorType::TensorType_INT32);
+ const tflite::Buffer *buffer = import->buffers()->Get(tensor->buffer());
+
+ if (buffer && buffer->data())
+ {
+ auto vec = extract_buffer<int32_t>(buffer);
+ import->set_tensor_filler(inputs[0], vec);
+ }
+
+ // filter
+ const tflite::Tensor *filter_tensor = import->tensors()->Get(inputs[1]);
+ import->set_tensor_filler(inputs[1]);
+}
+
+tflchef::Operation *TFliteOpTransposeConv::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_TransposeConvOptions();
+
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("TransposeConv");
+
+ auto op_options = operation->mutable_transpose_conv_options();
+
+ 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()));
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/TransposeConv.h b/compiler/tflchef/tflite/src/Op/TransposeConv.h
new file mode 100644
index 000000000..c79cdabd2
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/TransposeConv.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_TRANSPOSE_CONV_H__
+#define __TFLITE_OP_TRANSPOSE_CONV_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for TransposeConv
+ */
+class TFliteOpTransposeConv : 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_TRANSPOSE_CONV_H__
diff --git a/compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.cpp b/compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.cpp
new file mode 100644
index 000000000..c2c79285b
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "UnidirectionalSequenceLSTM.h"
+
+#include "Convert.h"
+#include "FillerHelper.h"
+
+namespace tflchef
+{
+
+void TFliteOpUnidirectionalSequenceLSTM::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
+ assert(inputs.size() == 24);
+
+ for (int32_t i = 0; i < inputs.size(); i++)
+ {
+ // Except for Input 0, 17 and 18.
+ // Each Input mean Input[0](=Input Tensor), Input[17](=OutputState Tensor) and
+ // Input[18](=CellState Tensor).
+ // This could be updated from previous input or User Given data, so This could not be Const
+ if (i == 0 || i == 17 || i == 18)
+ continue;
+ if (inputs[i] != -1)
+ fill_tensor_to_import(inputs[i], import);
+ }
+}
+
+tflchef::Operation *
+TFliteOpUnidirectionalSequenceLSTM::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_UnidirectionalSequenceLSTMOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("UnidirectionalSequenceLSTM");
+
+ auto op_options = operation->mutable_unidirectional_sequence_lstm_options();
+
+ op_options->set_activation(as_tflchef_activation(op_params->fused_activation_function()));
+ op_options->set_cell_clip(op_params->cell_clip());
+ op_options->set_proj_clip(op_params->proj_clip());
+ op_options->set_time_major(op_params->time_major());
+ op_options->set_asymmetric_quantize_inputs(op_params->asymmetric_quantize_inputs());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.h b/compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.h
new file mode 100644
index 000000000..cc4e5fb0f
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_UNIDIRECTIONALSEQUENCELSTM_H__
+#define __TFLITE_OP_UNIDIRECTIONALSEQUENCELSTM_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for UnidirectionalSequenceLSTM
+ */
+class TFliteOpUnidirectionalSequenceLSTM : public TFliteOpChef
+{
+public:
+ void filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const override;
+ tflchef::Operation *build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const override;
+};
+
+} // namespace tflchef
+
+#endif // __TFLITE_OP_UNIDIRECTIONALSEQUENCELSTM_H__
diff --git a/compiler/tflchef/tflite/src/Op/Unique.cpp b/compiler/tflchef/tflite/src/Op/Unique.cpp
new file mode 100644
index 000000000..e3f77f40e
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Unique.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Unique.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpUnique::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpUnique::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto op_params = op->builtin_options_as_UniqueOptions();
+ assert(op_params != nullptr);
+
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Unique");
+
+ auto op_options = operation->mutable_unique_options();
+
+ op_options->set_idx_out_type(as_tflchef_type(op_params->idx_out_type()));
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Unique.h b/compiler/tflchef/tflite/src/Op/Unique.h
new file mode 100644
index 000000000..fae037c9f
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Unique.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_UNIQUE_H__
+#define __TFLITE_OP_UNIQUE_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Unique
+ */
+class TFliteOpUnique : 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_UNIQUE_H__
diff --git a/compiler/tflchef/tflite/src/Op/Unpack.cpp b/compiler/tflchef/tflite/src/Op/Unpack.cpp
new file mode 100644
index 000000000..a51ef84ef
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Unpack.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Unpack.h"
+
+namespace tflchef
+{
+
+void TFliteOpUnpack::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with filler
+}
+
+tflchef::Operation *TFliteOpUnpack::build(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+
+ operation->set_type("Unpack");
+
+ auto op_params = op->builtin_options_as_UnpackOptions();
+ assert(op_params != nullptr);
+
+ auto op_options = operation->mutable_unpack_options();
+ op_options->set_num(op_params->num());
+ op_options->set_axis(op_params->axis());
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Unpack.h b/compiler/tflchef/tflite/src/Op/Unpack.h
new file mode 100644
index 000000000..1036bdc14
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Unpack.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_UNPACK_H__
+#define __TFLITE_OP_UNPACK_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Unpack
+ */
+class TFliteOpUnpack : 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_UNPACK_H__
diff --git a/compiler/tflchef/tflite/src/Op/Where.cpp b/compiler/tflchef/tflite/src/Op/Where.cpp
new file mode 100644
index 000000000..e42de3737
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Where.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Where.h"
+
+namespace tflchef
+{
+
+void TFliteOpWhere::filler(const tflite::Operator *, TFliteImport *, tflchef::ModelRecipe *) const
+{
+ // Nothing to do with fillers here
+}
+
+tflchef::Operation *TFliteOpWhere::build(const tflite::Operator *, TFliteImport *,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+ operation->set_type("Where");
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/Where.h b/compiler/tflchef/tflite/src/Op/Where.h
new file mode 100644
index 000000000..00cdc4b00
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/Where.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_WHERE_H__
+#define __TFLITE_OP_WHERE_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for Where
+ */
+class TFliteOpWhere : 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_WHERE_H__
diff --git a/compiler/tflchef/tflite/src/Op/ZerosLike.cpp b/compiler/tflchef/tflite/src/Op/ZerosLike.cpp
new file mode 100644
index 000000000..a56b6bdfb
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ZerosLike.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "ZerosLike.h"
+
+#include "Convert.h"
+
+namespace tflchef
+{
+
+void TFliteOpZerosLike::filler(const tflite::Operator *op, TFliteImport *import,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ // Nothing to do with fillers here
+}
+
+tflchef::Operation *TFliteOpZerosLike::build(const tflite::Operator *op, TFliteImport *,
+ tflchef::ModelRecipe *model_recipe) const
+{
+ auto operation = model_recipe->add_operation();
+ operation->set_type("ZerosLike");
+
+ auto op_options = operation->mutable_zeros_like_options();
+ (void)op_options;
+
+ return operation;
+}
+
+} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/Op/ZerosLike.h b/compiler/tflchef/tflite/src/Op/ZerosLike.h
new file mode 100644
index 000000000..163c1fa21
--- /dev/null
+++ b/compiler/tflchef/tflite/src/Op/ZerosLike.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFLITE_OP_ZEROS_LIKE_H__
+#define __TFLITE_OP_ZEROS_LIKE_H__
+
+#include "TFliteOpChef.h"
+
+namespace tflchef
+{
+
+/**
+ * @brief tflchef operator builder for ZerosLike
+ */
+class TFliteOpZerosLike : 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_ZEROS_LIKE_H__
diff --git a/compiler/tflchef/tflite/src/RawModelLoader.cpp b/compiler/tflchef/tflite/src/RawModelLoader.cpp
deleted file mode 100644
index e9ef8ec8b..000000000
--- a/compiler/tflchef/tflite/src/RawModelLoader.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <tflchef/RawModel.h>
-
-#include <cwrap/Fildes.h>
-
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
-namespace
-{
-
-class MemoryMappedRawModel final : public tflchef::RawModel
-{
-public:
- /**
- * @require fd and data SHOULD be valid
- */
- explicit MemoryMappedRawModel(int fd, void *data, size_t size) : _fd{fd}, _data{data}, _size{size}
- {
- // DO NOTHING
- }
-
-public:
- ~MemoryMappedRawModel()
- {
- munmap(_data, _size);
- close(_fd);
- }
-
-public:
- MemoryMappedRawModel(const MemoryMappedRawModel &) = delete;
- MemoryMappedRawModel(MemoryMappedRawModel &&) = delete;
-
-public:
- const ::tflite::Model *model(void) const override { return ::tflite::GetModel(_data); }
-
-private:
- int _fd = -1;
- void *_data = nullptr;
- size_t _size = 0;
-};
-
-} // namespace
-
-namespace tflchef
-{
-
-std::unique_ptr<RawModel> load_tflite(const std::string &path)
-{
- cwrap::Fildes fildes{open(path.c_str(), O_RDONLY)};
-
- if (fildes.get() == -1)
- {
- // Return nullptr on open failure
- return nullptr;
- }
-
- struct stat st;
- if (fstat(fildes.get(), &st) == -1)
- {
- // Return nullptr on fstat failure
- return nullptr;
- }
-
- auto size = st.st_size;
- auto data = mmap(nullptr, size, PROT_READ, MAP_SHARED, fildes.get(), 0);
-
- if (data == MAP_FAILED)
- {
- // Return nullptr on mmap failure
- return nullptr;
- }
-
- return std::unique_ptr<tflchef::RawModel>{new MemoryMappedRawModel(fildes.release(), data, size)};
-}
-
-} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/RecipeChef.cpp b/compiler/tflchef/tflite/src/RecipeChef.cpp
index 0a26ae066..d9215a4c4 100644
--- a/compiler/tflchef/tflite/src/RecipeChef.cpp
+++ b/compiler/tflchef/tflite/src/RecipeChef.cpp
@@ -35,9 +35,16 @@ void set_inputs(TFliteImport *import, tflchef::Operation *operation, const tflit
for (auto input : inputs)
{
- auto tensor = tensors->Get(input);
- std::string name = tensor_name(tensor);
- operation->add_input(name);
+ if (input == -1)
+ {
+ operation->add_input("");
+ }
+ else
+ {
+ auto tensor = tensors->Get(input);
+ std::string name = tensor_name(tensor);
+ operation->add_input(name);
+ }
}
}
@@ -103,16 +110,21 @@ std::unique_ptr<ModelRecipe> generate_recipe(const tflite::Model *model)
operand->set_name(tensor_name(tensor));
operand->set_type(as_tflchef_type(tensor->type()));
+ operand->set_is_variable(tensor->is_variable());
- std::vector<int32_t> dims = as_index_vector(tensor->shape());
- ::tflchef::TensorShape *shape = operand->mutable_shape();
- for (auto dim : dims)
+ if (tensor->shape())
{
- shape->add_dim(dim);
+ std::vector<int32_t> dims = as_index_vector(tensor->shape());
+ ::tflchef::TensorShape *shape = operand->mutable_shape();
+ for (auto dim : dims)
+ {
+ shape->add_dim(dim);
+ }
}
// filler for weights, bias and so on
std::vector<int32_t> expvalues;
+ std::vector<float> expfvalues;
if (tflite_import.get_tensor_filler(i))
{
tflchef::TensorFiller *filler = operand->mutable_filler();
@@ -132,6 +144,17 @@ std::unique_ptr<ModelRecipe> generate_recipe(const tflite::Model *model)
filler->add_arg(ss.str());
}
}
+ else if (tflite_import.get_tensor_filler(i, expfvalues))
+ {
+ tflchef::TensorFiller *filler = operand->mutable_filler();
+ filler->set_tag("explicit");
+ for (auto value : expfvalues)
+ {
+ std::ostringstream ss;
+ ss << value;
+ filler->add_arg(ss.str());
+ }
+ }
auto quant = tensor->quantization();
if (quant != nullptr)
@@ -162,6 +185,101 @@ std::unique_ptr<ModelRecipe> generate_recipe(const tflite::Model *model)
for (uint32_t idx = 0; idx < quant->zero_point()->size(); ++idx)
chef_quant->add_zero_point(quant->zero_point()->Get(idx));
}
+ tflchef::TensorQuantization *chef_quant = operand->mutable_quant();
+ chef_quant->set_quantized_dimension(quant->quantized_dimension());
+ }
+
+ auto sparsity = tensor->sparsity();
+ if (sparsity != nullptr)
+ {
+ tflchef::TensorSparsity *chef_sparsity = operand->mutable_sparsity();
+ // traversal_order
+ auto chef_traversal_order = chef_sparsity->mutable_traversal_order();
+ for (const auto &to : *(sparsity->traversal_order()))
+ {
+ chef_traversal_order->add_dim(to);
+ }
+ // block_map
+ auto chef_block_map = chef_sparsity->mutable_block_map();
+ for (const auto &bm : *(sparsity->block_map()))
+ {
+ chef_block_map->add_dim(bm);
+ }
+ // dim_metadata
+ for (const auto &dm : *(sparsity->dim_metadata()))
+ {
+ auto chef_dm = chef_sparsity->add_dim_metadata();
+ // format
+ chef_dm->set_format(as_tflchef_sparse_dim_type(dm->format()));
+ // dense_size
+ chef_dm->set_dense_size(dm->dense_size());
+ // array_segments
+ auto chef_array_segments = chef_dm->mutable_array_segments();
+ switch (dm->array_segments_type())
+ {
+ case tflite::SparseIndexVector_NONE:
+ // DO NOTHING
+ break;
+ case tflite::SparseIndexVector_Int32Vector:
+ for (const auto &as : *(dm->array_segments_as_Int32Vector()->values()))
+ {
+ chef_array_segments->add_dim(as);
+ }
+ break;
+ case tflite::SparseIndexVector_Uint16Vector:
+ for (const auto &as : *(dm->array_segments_as_Uint16Vector()->values()))
+ {
+ chef_array_segments->add_dim(as);
+ }
+ break;
+ case tflite::SparseIndexVector_Uint8Vector:
+ for (const auto &as : *(dm->array_segments_as_Uint8Vector()->values()))
+ {
+ chef_array_segments->add_dim(as);
+ }
+ break;
+ default:
+ throw std::runtime_error("unsupported sparse index vector type");
+ }
+ // array_indices
+ auto chef_array_indices = chef_dm->mutable_array_indices();
+ switch (dm->array_indices_type())
+ {
+ case tflite::SparseIndexVector_NONE:
+ // DO NOTHING
+ break;
+ case tflite::SparseIndexVector_Int32Vector:
+ for (const auto &as : *(dm->array_indices_as_Int32Vector()->values()))
+ {
+ chef_array_indices->add_dim(as);
+ }
+ break;
+ case tflite::SparseIndexVector_Uint16Vector:
+ for (const auto &as : *(dm->array_indices_as_Uint16Vector()->values()))
+ {
+ chef_array_indices->add_dim(as);
+ }
+ break;
+ case tflite::SparseIndexVector_Uint8Vector:
+ for (const auto &as : *(dm->array_indices_as_Uint8Vector()->values()))
+ {
+ chef_array_indices->add_dim(as);
+ }
+ break;
+ default:
+ throw std::runtime_error("unsupported sparse index vector type");
+ }
+ }
+ }
+
+ auto shape_signature = tensor->shape_signature();
+ if (shape_signature != nullptr)
+ {
+ tflchef::ShapeSignature *chef_shape_signature = operand->mutable_shape_signature();
+ for (uint32_t i = 0; i < shape_signature->size(); ++i)
+ {
+ chef_shape_signature->add_dim(shape_signature->Get(i));
+ }
}
}
diff --git a/compiler/tflchef/tflite/src/TFliteImport.h b/compiler/tflchef/tflite/src/TFliteImport.h
index ade8fc810..9d0a642ab 100644
--- a/compiler/tflchef/tflite/src/TFliteImport.h
+++ b/compiler/tflchef/tflite/src/TFliteImport.h
@@ -17,7 +17,9 @@
#ifndef __TFLITE_IMPORT_H__
#define __TFLITE_IMPORT_H__
-#include <tflite_generated.h>
+#include <mio/tflite/schema_generated.h>
+
+#include <souschef/TensorFiller.h>
#include <tflchef.pb.h>
@@ -40,7 +42,7 @@ bool is_custom(const tflite::OperatorCode *opcode);
/**
* @brief Loads TF lite file and provides helpers to access attributes
*/
-class TFliteImport
+class TFliteImport : public souschef::TensorFiller
{
public:
TFliteImport(const tflite::Model *model);
@@ -63,59 +65,15 @@ public:
std::string opcode_name(const tflite::Operator *op) const;
size_t buffer_info(const tflite::Tensor *tensor, const uint8_t **buff_data);
- /**
- * @brief This will record the tensor by index, if it needs filler option,
- * such as kernel, bias.
- */
- void set_tensor_filler(uint32_t tensor_index) { _tensor_filler[tensor_index] = true; }
-
- /**
- * @brief This will store int32 filler values such as reshape information for the tensor
- */
- void set_tensor_filler(uint32_t tensor_index, std::vector<int32_t> &expvalues)
- {
- _tensor_filler_vint32[tensor_index] = expvalues;
- }
-
- /**
- * @brief This will return true if the tensor by index, needs a filler option.
- */
- bool get_tensor_filler(uint32_t tensor_index)
- {
- auto it = _tensor_filler.find(tensor_index);
- if (it != _tensor_filler.end())
- {
- return it->second;
- }
- return false;
- }
-
- /**
- * @brief This will return true if the tensor by index, needs a int array filler option.
- */
- bool get_tensor_filler(uint32_t tensor_index, std::vector<int32_t> &expvalues)
- {
- auto it = _tensor_filler_vint32.find(tensor_index);
- if (it != _tensor_filler_vint32.end())
- {
- expvalues = it->second;
- return true;
- }
- return false;
- }
-
private:
- const TFliteSubGraphs_t *_subgraphs;
- const TFliteBuffers_t *_buffers;
- const TFliteTensors_t *_tensors;
- const TFliteOperators_t *_operators;
-
- std::vector<const tflite::OperatorCode *> _op_codes;
- std::vector<int32_t> _inputs;
- std::vector<int32_t> _outputs;
-
- std::map<uint32_t, bool> _tensor_filler;
- std::map<uint32_t, std::vector<int32_t>> _tensor_filler_vint32;
+ const TFliteSubGraphs_t *_subgraphs{nullptr};
+ const TFliteBuffers_t *_buffers{nullptr};
+ const TFliteTensors_t *_tensors{nullptr};
+ const TFliteOperators_t *_operators{nullptr};
+
+ std::vector<const tflite::OperatorCode *> _op_codes{};
+ std::vector<int32_t> _inputs{};
+ std::vector<int32_t> _outputs{};
};
} // namespace tflchef
diff --git a/compiler/tflchef/tflite/src/TFliteOpChef.h b/compiler/tflchef/tflite/src/TFliteOpChef.h
index fc9047ce7..98564293b 100644
--- a/compiler/tflchef/tflite/src/TFliteOpChef.h
+++ b/compiler/tflchef/tflite/src/TFliteOpChef.h
@@ -17,7 +17,7 @@
#ifndef __TFLITE_OP_CHEF_H__
#define __TFLITE_OP_CHEF_H__
-#include <tflite_generated.h>
+#include <mio/tflite/schema_generated.h>
#include <tflchef.pb.h>
diff --git a/compiler/tflchef/tflite/src/TFliteOpChefs.h b/compiler/tflchef/tflite/src/TFliteOpChefs.h
index 9f3446840..2e4d28051 100644
--- a/compiler/tflchef/tflite/src/TFliteOpChefs.h
+++ b/compiler/tflchef/tflite/src/TFliteOpChefs.h
@@ -18,17 +18,111 @@
#define __TFLITE_OP_CHEFS_H__
// In alphabet order
+#include "Op/Abs.h"
+#include "Op/Add.h"
+#include "Op/AddN.h"
+#include "Op/ArgMax.h"
+#include "Op/ArgMin.h"
#include "Op/AveragePool2D.h"
+#include "Op/BatchMatMul.h"
+#include "Op/BatchToSpaceND.h"
+#include "Op/Cast.h"
+#include "Op/Ceil.h"
#include "Op/Concatenation.h"
#include "Op/Conv2D.h"
+#include "Op/Cos.h"
+#include "Op/DepthToSpace.h"
#include "Op/DepthwiseConv2D.h"
+#include "Op/Dequantize.h"
#include "Op/Div.h"
+#include "Op/ELU.h"
+#include "Op/Equal.h"
+#include "Op/Exp.h"
+#include "Op/ExpandDims.h"
+#include "Op/Fill.h"
+#include "Op/Floor.h"
+#include "Op/FloorDiv.h"
+#include "Op/FloorMod.h"
#include "Op/FullyConnected.h"
+#include "Op/Gather.h"
+#include "Op/GatherNd.h"
+#include "Op/Greater.h"
+#include "Op/GreaterEqual.h"
+#include "Op/L2Normalize.h"
+#include "Op/L2Pool2D.h"
+#include "Op/LeakyRelu.h"
+#include "Op/Less.h"
+#include "Op/LessEqual.h"
+#include "Op/LocalResponseNormalization.h"
+#include "Op/Log.h"
+#include "Op/LogicalAnd.h"
+#include "Op/LogicalNot.h"
+#include "Op/LogicalOr.h"
+#include "Op/Logistic.h"
+#include "Op/LogSoftmax.h"
+#include "Op/MatrixDiag.h"
+#include "Op/MatrixSetDiag.h"
+#include "Op/Maximum.h"
#include "Op/MaxPool2D.h"
+#include "Op/Mean.h"
+#include "Op/Minimum.h"
+#include "Op/MirrorPad.h"
+#include "Op/Mul.h"
+#include "Op/Neg.h"
+#include "Op/NonMaxSuppressionV4.h"
+#include "Op/NonMaxSuppressionV5.h"
+#include "Op/NotEqual.h"
+#include "Op/OneHot.h"
+#include "Op/Pack.h"
+#include "Op/Pad.h"
+#include "Op/PadV2.h"
+#include "Op/Pow.h"
+#include "Op/PRelu.h"
+#include "Op/Range.h"
+#include "Op/Rank.h"
+#include "Op/ReduceAny.h"
+#include "Op/ReduceMax.h"
+#include "Op/ReduceMin.h"
+#include "Op/ReduceProd.h"
#include "Op/ReLU.h"
#include "Op/ReLU6.h"
+#include "Op/ReLUN1To1.h"
#include "Op/Reshape.h"
+#include "Op/ResizeBilinear.h"
+#include "Op/ResizeNearestNeighbor.h"
+#include "Op/ReverseSequence.h"
+#include "Op/ReverseV2.h"
+#include "Op/Round.h"
+#include "Op/Rsqrt.h"
+#include "Op/ScatterNd.h"
+#include "Op/SegmentSum.h"
+#include "Op/Select.h"
+#include "Op/SelectV2.h"
+#include "Op/Shape.h"
+#include "Op/Sin.h"
+#include "Op/Slice.h"
+#include "Op/Softmax.h"
+#include "Op/SpaceToBatchND.h"
+#include "Op/SpaceToDepth.h"
+#include "Op/SparseToDense.h"
+#include "Op/Split.h"
+#include "Op/SplitV.h"
#include "Op/Sqrt.h"
+#include "Op/Square.h"
+#include "Op/SquaredDifference.h"
+#include "Op/Squeeze.h"
+#include "Op/StridedSlice.h"
#include "Op/Sub.h"
+#include "Op/Sum.h"
+#include "Op/Tanh.h"
+#include "Op/Tile.h"
+#include "Op/TopKV2.h"
+#include "Op/Transpose.h"
+#include "Op/TransposeConv.h"
+#include "Op/UnidirectionalSequenceLSTM.h"
+#include "Op/Unique.h"
+#include "Op/Unpack.h"
+#include "Op/Where.h"
+#include "Op/ZerosLike.h"
#endif // __TFLITE_OP_CHEFS_H__
diff --git a/compiler/tflchef/tflite/src/TFliteOpRegistry.h b/compiler/tflchef/tflite/src/TFliteOpRegistry.h
index 9e085028c..9cc630a97 100644
--- a/compiler/tflchef/tflite/src/TFliteOpRegistry.h
+++ b/compiler/tflchef/tflite/src/TFliteOpRegistry.h
@@ -20,9 +20,7 @@
#include "TFliteOpChef.h"
#include "TFliteOpChefs.h"
-#include <stdex/Memory.h>
-
-using stdex::make_unique;
+#include <memory>
namespace tflchef
{
@@ -54,19 +52,117 @@ public:
private:
TFliteOpRegistry()
{
- _tfliteop_map[tflite::BuiltinOperator_AVERAGE_POOL_2D] = make_unique<TFliteOpAveragePool2D>();
- _tfliteop_map[tflite::BuiltinOperator_CONCATENATION] = make_unique<TFliteOpConcatenation>();
- _tfliteop_map[tflite::BuiltinOperator_CONV_2D] = make_unique<TFliteOpConv2D>();
- _tfliteop_map[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] =
- make_unique<TFliteOpDepthwiseConv2D>();
- _tfliteop_map[tflite::BuiltinOperator_DIV] = make_unique<TFliteOpDiv>();
- _tfliteop_map[tflite::BuiltinOperator_FULLY_CONNECTED] = make_unique<TFliteOpFullyConnected>();
- _tfliteop_map[tflite::BuiltinOperator_MAX_POOL_2D] = make_unique<TFliteOpMaxPool2D>();
- _tfliteop_map[tflite::BuiltinOperator_RELU] = make_unique<TFliteOpReLU>();
- _tfliteop_map[tflite::BuiltinOperator_RELU6] = make_unique<TFliteOpReLU6>();
- _tfliteop_map[tflite::BuiltinOperator_RESHAPE] = make_unique<TFliteOpReshape>();
- _tfliteop_map[tflite::BuiltinOperator_SQRT] = make_unique<TFliteOpSqrt>();
- _tfliteop_map[tflite::BuiltinOperator_SUB] = make_unique<TFliteOpSub>();
+#define REG_TFL_OP(OPCODE, CLASS) \
+ _tfliteop_map[tflite::BuiltinOperator_##OPCODE] = std::make_unique<CLASS>()
+
+ REG_TFL_OP(ABS, TFliteOpAbs);
+ REG_TFL_OP(ADD, TFliteOpAdd);
+ REG_TFL_OP(ADD_N, TFliteOpAddN);
+ REG_TFL_OP(ARG_MAX, TFliteOpArgMax);
+ REG_TFL_OP(ARG_MIN, TFliteOpArgMin);
+ REG_TFL_OP(AVERAGE_POOL_2D, TFliteOpAveragePool2D);
+ REG_TFL_OP(BATCH_MATMUL, TFliteOpBatchMatMul);
+ REG_TFL_OP(BATCH_TO_SPACE_ND, TFliteOpBatchToSpaceND);
+ REG_TFL_OP(CAST, TFliteOpCast);
+ REG_TFL_OP(CEIL, TFliteOpCeil);
+ REG_TFL_OP(CONCATENATION, TFliteOpConcatenation);
+ REG_TFL_OP(CONV_2D, TFliteOpConv2D);
+ REG_TFL_OP(COS, TFliteOpCos);
+ REG_TFL_OP(DEPTH_TO_SPACE, TFliteOpDepthToSpace);
+ REG_TFL_OP(DEPTHWISE_CONV_2D, TFliteOpDepthwiseConv2D);
+ REG_TFL_OP(DEQUANTIZE, TFliteOpDequantize);
+ REG_TFL_OP(DIV, TFliteOpDiv);
+ REG_TFL_OP(ELU, TFliteOpELU);
+ REG_TFL_OP(EQUAL, TFliteOpEqual);
+ REG_TFL_OP(EXP, TFliteOpExp);
+ REG_TFL_OP(EXPAND_DIMS, TFliteOpExpandDims);
+ REG_TFL_OP(FILL, TFliteOpFill);
+ REG_TFL_OP(FLOOR, TFliteOpFloor);
+ REG_TFL_OP(FLOOR_DIV, TFliteOpFloorDiv);
+ REG_TFL_OP(FLOOR_MOD, TFliteOpFloorMod);
+ REG_TFL_OP(FULLY_CONNECTED, TFliteOpFullyConnected);
+ REG_TFL_OP(GATHER, TFliteOpGather);
+ REG_TFL_OP(GATHER_ND, TFliteOpGatherNd);
+ REG_TFL_OP(GREATER, TFliteOpGreater);
+ REG_TFL_OP(GREATER_EQUAL, TFliteOpGreaterEqual);
+ REG_TFL_OP(L2_NORMALIZATION, TFliteOpL2Normalize);
+ REG_TFL_OP(L2_POOL_2D, TFliteOpL2Pool2D);
+ REG_TFL_OP(LEAKY_RELU, TFliteOpLeakyRelu);
+ REG_TFL_OP(LESS, TFliteOpLess);
+ REG_TFL_OP(LESS_EQUAL, TFliteOpLessEqual);
+ REG_TFL_OP(LOCAL_RESPONSE_NORMALIZATION, TFliteOpLocalResponseNormalization);
+ REG_TFL_OP(LOG, TFliteOpLog);
+ REG_TFL_OP(LOGICAL_AND, TFliteOpLogicalAnd);
+ REG_TFL_OP(LOGICAL_NOT, TFliteOpLogicalNot);
+ REG_TFL_OP(LOGICAL_OR, TFliteOpLogicalOr);
+ REG_TFL_OP(LOGISTIC, TFliteOpLogistic);
+ REG_TFL_OP(LOG_SOFTMAX, TFliteOpLogSoftmax);
+ REG_TFL_OP(MATRIX_DIAG, TFliteOpMatrixDiag);
+ REG_TFL_OP(MAX_POOL_2D, TFliteOpMaxPool2D);
+ REG_TFL_OP(MATRIX_SET_DIAG, TFliteOpMatrixSetDiag);
+ REG_TFL_OP(MAXIMUM, TFliteOpMaximum);
+ REG_TFL_OP(MEAN, TFliteOpMean);
+ REG_TFL_OP(MINIMUM, TFliteOpMinimum);
+ REG_TFL_OP(MIRROR_PAD, TFliteOpMirrorPad);
+ REG_TFL_OP(MUL, TFliteOpMul);
+ REG_TFL_OP(NEG, TFliteOpNeg);
+ REG_TFL_OP(NON_MAX_SUPPRESSION_V4, TFliteOpNonMaxSuppressionV4);
+ REG_TFL_OP(NON_MAX_SUPPRESSION_V5, TFliteOpNonMaxSuppressionV5);
+ REG_TFL_OP(NOT_EQUAL, TFliteOpNotEqual);
+ REG_TFL_OP(ONE_HOT, TFliteOpOneHot);
+ REG_TFL_OP(PACK, TFliteOpPack);
+ REG_TFL_OP(PAD, TFliteOpPad);
+ REG_TFL_OP(PADV2, TFliteOpPadV2);
+ REG_TFL_OP(POW, TFliteOpPow);
+ REG_TFL_OP(PRELU, TFliteOpPRelu);
+ REG_TFL_OP(RANGE, TFliteOpRange);
+ REG_TFL_OP(RANK, TFliteOpRank);
+ REG_TFL_OP(REDUCE_ANY, TFliteOpReduceAny);
+ REG_TFL_OP(REDUCE_MAX, TFliteOpReduceMax);
+ REG_TFL_OP(REDUCE_MIN, TFliteOpReduceMin);
+ REG_TFL_OP(REDUCE_PROD, TFliteOpReduceProd);
+ REG_TFL_OP(RELU, TFliteOpReLU);
+ REG_TFL_OP(RELU6, TFliteOpReLU6);
+ REG_TFL_OP(RELU_N1_TO_1, TFliteOpReLUN1To1);
+ REG_TFL_OP(RESHAPE, TFliteOpReshape);
+ REG_TFL_OP(RESIZE_BILINEAR, TFliteOpResizeBilinear);
+ REG_TFL_OP(RESIZE_NEAREST_NEIGHBOR, TFliteOpResizeNearestNeighbor);
+ REG_TFL_OP(REVERSE_SEQUENCE, TFliteOpReverseSequence);
+ REG_TFL_OP(REVERSE_V2, TFliteOpReverseV2);
+ REG_TFL_OP(ROUND, TFliteOpRound);
+ REG_TFL_OP(RSQRT, TFliteOpRsqrt);
+ REG_TFL_OP(SCATTER_ND, TFliteOpScatterNd);
+ REG_TFL_OP(SEGMENT_SUM, TFliteOpSegmentSum);
+ REG_TFL_OP(SELECT, TFliteOpSelect);
+ REG_TFL_OP(SELECT_V2, TFliteOpSelectV2);
+ REG_TFL_OP(SHAPE, TFliteOpShape);
+ REG_TFL_OP(SIN, TFliteOpSin);
+ REG_TFL_OP(SLICE, TFliteOpSlice);
+ REG_TFL_OP(SOFTMAX, TFliteOpSoftmax);
+ REG_TFL_OP(SPACE_TO_BATCH_ND, TFliteOpSpaceToBatchND);
+ REG_TFL_OP(SPACE_TO_DEPTH, TFliteOpSpaceToDepth);
+ REG_TFL_OP(SPARSE_TO_DENSE, TFliteOpSparseToDense);
+ REG_TFL_OP(SPLIT, TFliteOpSplit);
+ REG_TFL_OP(SPLIT_V, TFliteOpSplitV);
+ REG_TFL_OP(SQRT, TFliteOpSqrt);
+ REG_TFL_OP(SQUARE, TFliteOpSquare);
+ REG_TFL_OP(SQUARED_DIFFERENCE, TFliteOpSquaredDifference);
+ REG_TFL_OP(SQUEEZE, TFliteOpSqueeze);
+ REG_TFL_OP(STRIDED_SLICE, TFliteOpStridedSlice);
+ REG_TFL_OP(SUB, TFliteOpSub);
+ REG_TFL_OP(SUM, TFliteOpSum);
+ REG_TFL_OP(TANH, TFliteOpTanh);
+ REG_TFL_OP(TILE, TFliteOpTile);
+ REG_TFL_OP(TOPK_V2, TFliteOpTopKV2);
+ REG_TFL_OP(TRANSPOSE, TFliteOpTranspose);
+ REG_TFL_OP(TRANSPOSE_CONV, TFliteOpTransposeConv);
+ REG_TFL_OP(UNIDIRECTIONAL_SEQUENCE_LSTM, TFliteOpUnidirectionalSequenceLSTM);
+ REG_TFL_OP(UNIQUE, TFliteOpUnique);
+ REG_TFL_OP(UNPACK, TFliteOpUnpack);
+ REG_TFL_OP(WHERE, TFliteOpWhere);
+ REG_TFL_OP(ZEROS_LIKE, TFliteOpZerosLike);
+
+#undef REG_TFL_OP
}
private:
diff --git a/compiler/tflchef/tools/console/Driver.cpp b/compiler/tflchef/tools/console/Driver.cpp
index 0f319c0cd..d6f7ba1ae 100644
--- a/compiler/tflchef/tools/console/Driver.cpp
+++ b/compiler/tflchef/tools/console/Driver.cpp
@@ -24,6 +24,8 @@
int entry(int argc, char **argv)
{
+ int32_t model_version = 1;
+
::tflchef::ModelRecipe model_recipe;
// Read a model recipe from standard input
@@ -31,9 +33,20 @@ int entry(int argc, char **argv)
google::protobuf::io::IstreamInputStream iis{&std::cin};
if (!google::protobuf::TextFormat::Parse(&iis, &model_recipe))
{
- std::cerr << "ERROR: Failed to parse recipe '" << argv[1] << "'" << std::endl;
+ std::cerr << "ERROR: Failed to parse recipe" << std::endl;
return 255;
}
+
+ if (model_recipe.has_version())
+ {
+ model_version = model_recipe.version();
+ }
+ }
+
+ if (model_version > 1)
+ {
+ std::cerr << "ERROR: Unsupported recipe version: " << model_version << std::endl;
+ return 255;
}
auto generated_model = tflchef::cook(model_recipe);
diff --git a/compiler/tflchef/tools/file/CMakeLists.txt b/compiler/tflchef/tools/file/CMakeLists.txt
index 477b7d974..f411d60f1 100644
--- a/compiler/tflchef/tools/file/CMakeLists.txt
+++ b/compiler/tflchef/tools/file/CMakeLists.txt
@@ -1,3 +1,4 @@
add_executable(tflchef-file Driver.cpp)
+target_link_libraries(tflchef-file arser)
target_link_libraries(tflchef-file tflchef_core)
target_link_libraries(tflchef-file safemain)
diff --git a/compiler/tflchef/tools/file/Driver.cpp b/compiler/tflchef/tools/file/Driver.cpp
index 1287b0104..46e5b5583 100644
--- a/compiler/tflchef/tools/file/Driver.cpp
+++ b/compiler/tflchef/tools/file/Driver.cpp
@@ -20,37 +20,64 @@
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/text_format.h>
+#include <arser/arser.h>
+
#include <fstream>
#include <iostream>
int entry(int argc, char **argv)
{
- if (argc != 3)
+ arser::Arser arser;
+ arser.add_argument("recipe")
+ .type(arser::DataType::STR)
+ .help("Source recipe file path to convert");
+ arser.add_argument("tflite").type(arser::DataType::STR).help("Target tflite file path");
+
+ try
+ {
+ arser.parse(argc, argv);
+ }
+ catch (const std::runtime_error &err)
{
- std::cerr << "ERROR: Failed to parse arguments" << std::endl;
- std::cerr << std::endl;
- std::cerr << "USAGE: " << argv[0] << " [recipe] [output]" << std::endl;
+ std::cout << err.what() << std::endl;
+ std::cout << arser;
return 255;
}
+ int32_t model_version = 1;
+
::tflchef::ModelRecipe model_recipe;
+ std::string recipe_path = arser.get<std::string>("recipe");
// Load model recipe from a file
{
- std::ifstream is{argv[1]};
+ std::ifstream is{recipe_path};
google::protobuf::io::IstreamInputStream iis{&is};
if (!google::protobuf::TextFormat::Parse(&iis, &model_recipe))
{
- std::cerr << "ERROR: Failed to parse recipe '" << argv[1] << "'" << std::endl;
+ std::cerr << "ERROR: Failed to parse recipe '" << recipe_path << "'" << std::endl;
return 255;
}
+
+ if (model_recipe.has_version())
+ {
+ model_version = model_recipe.version();
+ }
+ }
+
+ if (model_version > 1)
+ {
+ std::cerr << "ERROR: Unsupported recipe version: " << model_version << ", '" << argv[1] << "'"
+ << std::endl;
+ return 255;
}
auto generated_model = tflchef::cook(model_recipe);
+ std::string tflite_path = arser.get<std::string>("tflite");
// Dump generated model into a file
{
- std::ofstream os{argv[2], std::ios::binary};
+ std::ofstream os{tflite_path, std::ios::binary};
os.write(generated_model.base(), generated_model.size());
}
diff --git a/compiler/tflchef/tools/reverse/CMakeLists.txt b/compiler/tflchef/tools/reverse/CMakeLists.txt
index 63cb36c06..a5c0f5bca 100644
--- a/compiler/tflchef/tools/reverse/CMakeLists.txt
+++ b/compiler/tflchef/tools/reverse/CMakeLists.txt
@@ -1,3 +1,5 @@
add_executable(tflchef-reverse Driver.cpp)
+target_link_libraries(tflchef-reverse arser)
target_link_libraries(tflchef-reverse tflchef_tflite)
target_link_libraries(tflchef-reverse safemain)
+target_link_libraries(tflchef-reverse foder)
diff --git a/compiler/tflchef/tools/reverse/Driver.cpp b/compiler/tflchef/tools/reverse/Driver.cpp
index 549756463..4d795a3d0 100644
--- a/compiler/tflchef/tools/reverse/Driver.cpp
+++ b/compiler/tflchef/tools/reverse/Driver.cpp
@@ -14,34 +14,41 @@
* limitations under the License.
*/
-#include <tflchef/RawModel.h>
#include <tflchef/RecipeChef.h>
+#include <arser/arser.h>
+#include <foder/FileLoader.h>
+
#include <memory>
#include <iostream>
int entry(int argc, char **argv)
{
- if (argc != 3)
+ arser::Arser arser;
+ arser.add_argument("tflite")
+ .type(arser::DataType::STR)
+ .help("Source tflite file path to convert");
+ arser.add_argument("recipe").type(arser::DataType::STR).help("Target recipe file path");
+
+ try
{
- std::cerr << "ERROR: Failed to parse arguments" << std::endl;
- std::cerr << std::endl;
- std::cerr << "USAGE: " << argv[0] << " [tflite] [output]" << std::endl;
- return 255;
+ arser.parse(argc, argv);
}
-
- // Load TF lite model from a tflite file
- std::unique_ptr<tflchef::RawModel> rawmodel = tflchef::load_tflite(argv[1]);
- if (rawmodel == nullptr)
+ catch (const std::runtime_error &err)
{
- std::cerr << "ERROR: Failed to load tflite '" << argv[1] << "'" << std::endl;
+ std::cout << err.what() << std::endl;
+ std::cout << arser;
return 255;
}
- const tflite::Model *tflmodel = rawmodel->model();
+ std::string tflite_path = arser.get<std::string>("tflite");
+ // Load TF lite model from a tflite file
+ const foder::FileLoader fileLoader{tflite_path};
+ std::vector<char> modelData = fileLoader.load();
+ const tflite::Model *tflmodel = tflite::GetModel(modelData.data());
if (tflmodel == nullptr)
{
- std::cerr << "ERROR: Failed to load tflite '" << argv[1] << "'" << std::endl;
+ std::cerr << "ERROR: Failed to load tflite '" << tflite_path << "'" << std::endl;
return 255;
}
@@ -53,11 +60,12 @@ int entry(int argc, char **argv)
return 255;
}
+ std::string recipe_path = arser.get<std::string>("recipe");
// Save to a file
- bool result = tflchef::write_recipe(argv[2], recipe);
+ bool result = tflchef::write_recipe(recipe_path, recipe);
if (!result)
{
- std::cerr << "ERROR: Failed to write to recipe '" << argv[2] << "'" << std::endl;
+ std::cerr << "ERROR: Failed to write to recipe '" << recipe_path << "'" << std::endl;
return 255;
}
return 0;
diff --git a/compiler/tfldump/CMakeLists.txt b/compiler/tfldump/CMakeLists.txt
index a526ae83b..e6afcb6d2 100644
--- a/compiler/tfldump/CMakeLists.txt
+++ b/compiler/tfldump/CMakeLists.txt
@@ -1,13 +1,7 @@
-nncc_find_package(FlatBuffers QUIET)
-
-if(NOT FlatBuffers_FOUND)
+if(NOT TARGET mio_tflite)
+ message(STATUS "Build tfldump: FAILED (missing mio_tflite)")
return()
-endif(NOT FlatBuffers_FOUND)
-
-FlatBuffers_Target(tfldump_flatbuffer
- OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated/schema"
- SCHEMA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/schema"
- SCHEMA_FILES schema.fbs)
+endif(NOT TARGET mio_tflite)
set(DRIVER "driver/Driver.cpp")
@@ -15,7 +9,7 @@ file(GLOB_RECURSE SOURCES "src/*.cpp")
add_executable(tfldump ${DRIVER} ${SOURCES})
target_include_directories(tfldump PRIVATE include)
-target_link_libraries(tfldump tfldump_flatbuffer)
+target_link_libraries(tfldump arser)
+target_link_libraries(tfldump mio_tflite)
target_link_libraries(tfldump safemain)
-target_link_libraries(tfldump stdex)
target_link_libraries(tfldump flatbuffers)
diff --git a/compiler/tfldump/driver/Driver.cpp b/compiler/tfldump/driver/Driver.cpp
index 2ede0fdd9..38c9c062f 100644
--- a/compiler/tfldump/driver/Driver.cpp
+++ b/compiler/tfldump/driver/Driver.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <arser/arser.h>
#include <tflread/Model.h>
#include <tfldump/Dump.h>
@@ -21,30 +22,37 @@
int entry(int argc, char **argv)
{
- if (argc != 2)
+ arser::Arser arser;
+ arser.add_argument("tflite").type(arser::DataType::STR).help("TFLite file to dump");
+
+ try
+ {
+ arser.parse(argc, argv);
+ }
+ catch (const std::runtime_error &err)
{
- std::cerr << "ERROR: Failed to parse arguments" << std::endl;
- std::cerr << std::endl;
- std::cerr << "USAGE: " << argv[0] << " [tflite]" << std::endl;
+ std::cout << err.what() << '\n';
+ std::cout << arser;
return 255;
}
+ std::string tflite_path = arser.get<std::string>("tflite");
// Load TF lite model from a tflite file
- std::unique_ptr<tflread::Model> model = tflread::load_tflite(argv[1]);
+ std::unique_ptr<tflread::Model> model = tflread::load_tflite(tflite_path);
if (model == nullptr)
{
- std::cerr << "ERROR: Failed to load tflite '" << argv[1] << "'" << std::endl;
+ std::cerr << "ERROR: Failed to load tflite '" << tflite_path << "'" << std::endl;
return 255;
}
const tflite::Model *tflmodel = model->model();
if (tflmodel == nullptr)
{
- std::cerr << "ERROR: Failed to load tflite '" << argv[1] << "'" << std::endl;
+ std::cerr << "ERROR: Failed to load tflite '" << tflite_path << "'" << std::endl;
return 255;
}
- std::cout << "Dump: " << argv[1] << std::endl << std::endl;
+ std::cout << "Dump: " << tflite_path << std::endl << std::endl;
std::cout << tflmodel << std::endl;
diff --git a/compiler/tfldump/include/tfldump/Dump.h b/compiler/tfldump/include/tfldump/Dump.h
index 979ef58a0..af04bb132 100644
--- a/compiler/tfldump/include/tfldump/Dump.h
+++ b/compiler/tfldump/include/tfldump/Dump.h
@@ -17,7 +17,7 @@
#ifndef __TFLDUMP_DUMP_H__
#define __TFLDUMP_DUMP_H__
-#include <schema_generated.h>
+#include <mio/tflite/schema_generated.h>
#include <ostream>
diff --git a/compiler/tfldump/include/tflread/Model.h b/compiler/tfldump/include/tflread/Model.h
index 267e72415..c6e4a94ac 100644
--- a/compiler/tfldump/include/tflread/Model.h
+++ b/compiler/tfldump/include/tflread/Model.h
@@ -17,7 +17,7 @@
#ifndef __TFLREAD_MODEL_H__
#define __TFLREAD_MODEL_H__
-#include <schema_generated.h>
+#include <mio/tflite/schema_generated.h>
#include <memory>
diff --git a/compiler/tfldump/requires.cmake b/compiler/tfldump/requires.cmake
new file mode 100644
index 000000000..2cdd3a391
--- /dev/null
+++ b/compiler/tfldump/requires.cmake
@@ -0,0 +1,3 @@
+require("arser")
+require("mio-tflite")
+require("safemain")
diff --git a/compiler/tfldump/schema/schema.fbs b/compiler/tfldump/schema/schema.fbs
deleted file mode 100644
index 3da3188c3..000000000
--- a/compiler/tfldump/schema/schema.fbs
+++ /dev/null
@@ -1,698 +0,0 @@
-// 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.
-
-// Revision History
-// Version 0: Initial version.
-// Version 1: Add subgraphs to schema.
-// Version 2: Rename operators to conform to NN API.
-// Version 3: Move buffer data from Model.Subgraph.Tensors to Model.Buffers.
-
-namespace tflite;
-
-// This corresponds to the version.
-file_identifier "TFL3";
-// File extension of any written files.
-file_extension "tflite";
-
-// The type of data stored in a tensor.
-enum TensorType : byte {
- FLOAT32 = 0,
- FLOAT16 = 1,
- INT32 = 2,
- UINT8 = 3,
- INT64 = 4,
- STRING = 5,
- BOOL = 6,
- INT16 = 7,
- COMPLEX64 = 8,
-}
-
-// Parameters for converting a quantized tensor back to float. Given a
-// quantized value q, the corresponding float value f should be:
-// f = scale * (q - zero_point)
-table QuantizationParameters {
- min:[float]; // For importing back into tensorflow.
- max:[float]; // For importing back into tensorflow.
- scale:[float]; // For dequantizing the tensor's values.
- zero_point:[long];
-}
-
-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
- // Tensorflow's NHWC).
- shape:[int];
- type:TensorType;
- // An index that refers to the buffers table at the root of the model. Or,
- // if there is no data buffer associated (i.e. intermediate results), then
- // this is 0 (which refers to an always existent empty buffer).
- //
- // The data_buffer itself is an opaque container, with the assumption that the
- // target device is little-endian. In addition, all builtin operators assume
- // the memory is ordered such that if `shape` is [4, 3, 2], then index
- // [i, j, k] maps to data_buffer[i*3*2 + j*2 + k].
- buffer:uint;
- name:string; // For debugging and importing back into tensorflow.
- quantization:QuantizationParameters; // Optional.
-
- is_variable:bool = false;
-}
-
-// A list of builtin operators. Builtin operators are slightly faster than custom
-// ones, but not by much. Moreover, while custom operators accept an opaque
-// object containing configuration parameters, builtins have a predetermined
-// set of acceptable options.
-enum BuiltinOperator : byte {
- ADD = 0,
- AVERAGE_POOL_2D = 1,
- CONCATENATION = 2,
- CONV_2D = 3,
- DEPTHWISE_CONV_2D = 4,
- // DEPTH_TO_SPACE = 5,
- DEQUANTIZE = 6,
- EMBEDDING_LOOKUP = 7,
- FLOOR = 8,
- FULLY_CONNECTED = 9,
- HASHTABLE_LOOKUP = 10,
- L2_NORMALIZATION = 11,
- L2_POOL_2D = 12,
- LOCAL_RESPONSE_NORMALIZATION = 13,
- LOGISTIC = 14,
- LSH_PROJECTION = 15,
- LSTM = 16,
- MAX_POOL_2D = 17,
- MUL = 18,
- RELU = 19,
- // NOTE(aselle): RELU_N1_TO_1 used to be called RELU1, but it was renamed
- // since different model developers use RELU1 in different ways. Never
- // create another op called RELU1.
- RELU_N1_TO_1 = 20,
- RELU6 = 21,
- RESHAPE = 22,
- RESIZE_BILINEAR = 23,
- RNN = 24,
- SOFTMAX = 25,
- SPACE_TO_DEPTH = 26,
- SVDF = 27,
- TANH = 28,
- // TODO(aselle): Consider rename to CONCATENATE_EMBEDDINGS
- CONCAT_EMBEDDINGS = 29,
- SKIP_GRAM = 30,
- CALL = 31,
- CUSTOM = 32,
- EMBEDDING_LOOKUP_SPARSE = 33,
- PAD = 34,
- UNIDIRECTIONAL_SEQUENCE_RNN = 35,
- GATHER = 36,
- BATCH_TO_SPACE_ND = 37,
- SPACE_TO_BATCH_ND = 38,
- TRANSPOSE = 39,
- MEAN = 40,
- SUB = 41,
- DIV = 42,
- SQUEEZE = 43,
- UNIDIRECTIONAL_SEQUENCE_LSTM = 44,
- STRIDED_SLICE = 45,
- BIDIRECTIONAL_SEQUENCE_RNN = 46,
- EXP = 47,
- TOPK_V2 = 48,
- SPLIT = 49,
- LOG_SOFTMAX = 50,
- // DELEGATE is a special op type for the operations which are delegated to
- // other backends.
- // WARNING: Experimental interface, subject to change
- DELEGATE = 51,
- BIDIRECTIONAL_SEQUENCE_LSTM = 52,
- CAST = 53,
- PRELU = 54,
- MAXIMUM = 55,
- ARG_MAX = 56,
- MINIMUM = 57,
- LESS = 58,
- NEG = 59,
- PADV2 = 60,
- GREATER = 61,
- GREATER_EQUAL = 62,
- LESS_EQUAL = 63,
- SELECT = 64,
- SLICE = 65,
- SIN = 66,
- TRANSPOSE_CONV = 67,
- SPARSE_TO_DENSE = 68,
- TILE = 69,
- EXPAND_DIMS = 70,
- EQUAL = 71,
- NOT_EQUAL = 72,
- LOG = 73,
- SUM = 74,
- SQRT = 75,
- RSQRT = 76,
- SHAPE = 77,
- POW = 78,
- ARG_MIN = 79,
- FAKE_QUANT = 80,
- REDUCE_PROD = 81,
- REDUCE_MAX = 82,
- PACK = 83,
- LOGICAL_OR = 84,
- ONE_HOT = 85,
- LOGICAL_AND = 86,
- LOGICAL_NOT = 87,
- UNPACK = 88,
- REDUCE_MIN = 89,
- FLOOR_DIV = 90,
- REDUCE_ANY = 91,
- SQUARE = 92,
- ZEROS_LIKE = 93,
- FILL = 94,
-}
-
-// Options for the builtin operators.
-union BuiltinOptions {
- 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,
-}
-
-enum Padding : byte { SAME, VALID }
-
-enum ActivationFunctionType : byte {
- NONE = 0,
- RELU = 1,
- RELU_N1_TO_1 = 2,
- RELU6 = 3,
- TANH = 4,
- SIGN_BIT = 5,
-}
-
-table Conv2DOptions {
- padding:Padding;
- stride_w:int;
- stride_h:int;
- fused_activation_function:ActivationFunctionType;
- dilation_w_factor:int = 1;
- dilation_h_factor:int = 1;
-}
-
-table Pool2DOptions {
- padding:Padding;
- stride_w:int;
- stride_h:int;
- filter_width:int;
- filter_height:int;
- fused_activation_function:ActivationFunctionType;
-}
-
-table DepthwiseConv2DOptions {
- // Parameters for DepthwiseConv version 1 or above.
- padding:Padding;
- stride_w:int;
- stride_h:int;
- depth_multiplier:int;
- fused_activation_function:ActivationFunctionType;
- // Parameters for DepthwiseConv version 2 or above.
- dilation_w_factor:int = 1;
- dilation_h_factor:int = 1;
-}
-
-table ConcatEmbeddingsOptions {
- num_channels:int;
- num_columns_per_channel:[int];
- embedding_dim_per_channel:[int]; // This could be inferred from parameters.
-}
-
-enum LSHProjectionType: byte {
- UNKNOWN = 0,
- SPARSE = 1,
- DENSE = 2,
-}
-
-table LSHProjectionOptions {
- type: LSHProjectionType;
-}
-
-table SVDFOptions {
- rank:int;
- fused_activation_function:ActivationFunctionType;
-}
-
-// An implementation of TensorFlow RNNCell.
-table RNNOptions {
- fused_activation_function:ActivationFunctionType;
-}
-
-// An implementation of TensorFlow dynamic_rnn with RNNCell.
-table SequenceRNNOptions {
- time_major:bool;
- fused_activation_function:ActivationFunctionType;
-}
-
-// An implementation of TensorFlow bidrectional_dynamic_rnn with RNNCell.
-table BidirectionalSequenceRNNOptions {
- time_major:bool;
- fused_activation_function:ActivationFunctionType;
-}
-
-enum FullyConnectedOptionsWeightsFormat: byte {
- DEFAULT = 0,
- SHUFFLED4x16INT8 = 1,
-}
-
-// An implementation of TensorFlow fully_connected (a.k.a Dense) layer.
-table FullyConnectedOptions {
- // Parameters for FullyConnected version 1 or above.
- fused_activation_function:ActivationFunctionType;
-
- // Parameters for FullyConnected version 2 or above.
- weights_format:FullyConnectedOptionsWeightsFormat = DEFAULT;
-}
-
-table SoftmaxOptions {
- beta: float;
-}
-
-// An implementation of TensorFlow concat.
-table ConcatenationOptions {
- axis:int;
- fused_activation_function:ActivationFunctionType;
-}
-
-table AddOptions {
- fused_activation_function:ActivationFunctionType;
-}
-
-table MulOptions {
- fused_activation_function:ActivationFunctionType;
-}
-
-table L2NormOptions {
- fused_activation_function:ActivationFunctionType;
-}
-
-table LocalResponseNormalizationOptions {
- radius:int;
- bias:float;
- alpha:float;
- beta:float;
-}
-
-enum LSTMKernelType : byte {
- // Full LSTM kernel which supports peephole and projection.
- FULL = 0,
- // Basic LSTM kernels. Equivalent to TensorFlow BasicLSTMCell.
- BASIC = 1,
-}
-
-// An implementation of TensorFlow LSTMCell and CoupledInputForgetGateLSTMCell
-table LSTMOptions {
- // Parameters for LSTM version 1 or above.
- fused_activation_function:ActivationFunctionType;
- cell_clip: float; // Optional, 0.0 means no clipping
- proj_clip: float; // Optional, 0.0 means no clipping
-
- // Parameters for LSTM version 2 or above.
- // Basic kernel is only supported in version 2 or above.
- kernel_type: LSTMKernelType = FULL;
-}
-
-table ResizeBilinearOptions {
- new_height: int (deprecated);
- new_width: int (deprecated);
- align_corners: bool;
-}
-
-// A call operation options
-table CallOptions {
- // The subgraph index that needs to be called.
- subgraph:uint;
-}
-
-table PadOptions {
-}
-
-table PadV2Options {
-}
-
-table ReshapeOptions {
- new_shape:[int];
-}
-
-table SpaceToBatchNDOptions {
-}
-
-table BatchToSpaceNDOptions {
-}
-
-table SkipGramOptions {
- ngram_size: int;
- max_skip_size: int;
- include_all_ngrams: bool;
-}
-
-table SpaceToDepthOptions {
- block_size: int;
-}
-
-table SubOptions {
- fused_activation_function:ActivationFunctionType;
-}
-
-table DivOptions {
- fused_activation_function:ActivationFunctionType;
-}
-
-table TopKV2Options {
-}
-
-enum CombinerType : byte {
- SUM = 0,
- MEAN = 1,
- SQRTN = 2,
-}
-
-table EmbeddingLookupSparseOptions {
- combiner:CombinerType;
-}
-
-table GatherOptions {
- axis: int;
-}
-
-table TransposeOptions {
-}
-
-table ExpOptions {
-}
-
-table ReducerOptions {
- keep_dims: bool;
-}
-
-table SqueezeOptions {
- squeeze_dims:[int];
-}
-
-table SplitOptions {
- num_splits: int;
-}
-
-table StridedSliceOptions {
- begin_mask: int;
- end_mask: int;
- ellipsis_mask: int;
- new_axis_mask: int;
- shrink_axis_mask: int;
-}
-
-table LogSoftmaxOptions {
-}
-
-table CastOptions {
- in_data_type: TensorType;
- out_data_type: TensorType;
-}
-
-table DequantizeOptions {
-}
-
-table MaximumMinimumOptions {
-}
-
-table TileOptions {
-}
-
-table ArgMaxOptions {
- output_type : TensorType;
-}
-
-table ArgMinOptions {
- output_type : TensorType;
-}
-
-table GreaterOptions {
-}
-
-table GreaterEqualOptions {
-}
-
-table LessOptions {
-}
-
-table LessEqualOptions {
-}
-
-table NegOptions {
-}
-
-table SelectOptions {
-}
-
-table SliceOptions {
-}
-
-table TransposeConvOptions {
- padding:Padding;
- stride_w:int;
- stride_h:int;
-}
-
-table ExpandDimsOptions {
-}
-
-table SparseToDenseOptions {
- validate_indices:bool;
-}
-
-table EqualOptions {
-}
-
-table NotEqualOptions {
-}
-
-table ShapeOptions {
- // Optional output type of the operation (int32 or int64). Defaults to int32.
- out_type : TensorType;
-}
-
-table PowOptions {
-}
-
-table FakeQuantOptions {
- // Parameters supported by version 1:
- min:float;
- max:float;
- num_bits:int;
-
- // Parameters supported by version 2:
- narrow_range:bool;
-}
-
-table PackOptions {
- values_count:int;
- axis:int;
-}
-
-table LogicalOrOptions {
-}
-
-table OneHotOptions {
- axis:int;
-}
-
-table LogicalAndOptions {
-}
-
-table LogicalNotOptions {
-}
-
-table UnpackOptions {
- num:int;
- axis:int;
-}
-
-table FloorDivOptions {
-}
-
-table SquareOptions {
-}
-
-table ZerosLikeOptions {
-}
-
-table FillOptions {
-}
-
-// An OperatorCode can be an enum value (BuiltinOperator) if the operator is a
-// builtin, or a string if the operator is custom.
-table OperatorCode {
- builtin_code:BuiltinOperator;
- custom_code:string;
-
- // The version of the operator. The version need to be bumped whenever new
- // parameters are introduced into an op.
- version:int = 1;
-}
-
-enum CustomOptionsFormat : byte {
- FLEXBUFFERS = 0,
-}
-
-// An operator takes tensors as inputs and outputs. The type of operation being
-// performed is determined by an index into the list of valid OperatorCodes,
-// while the specifics of each operations is configured using builtin_options
-// or custom_options.
-table Operator {
- // Index into the operator_codes array. Using an integer here avoids
- // complicate map lookups.
- opcode_index:uint;
-
- // Optional input and output tensors are indicated by -1.
- inputs:[int];
- outputs:[int];
-
- builtin_options:BuiltinOptions;
- custom_options:[ubyte];
- custom_options_format:CustomOptionsFormat;
-
- // A list of booleans indicating the input tensors which are being mutated by
- // this operator.(e.g. used by RNN and LSTM).
- // For example, if the "inputs" array refers to 5 tensors and the second and
- // fifth are mutable variables, then this list will contain
- // [false, true, false, false, true].
- //
- // If the list is empty, no variable is mutated in this operator.
- // The list either has the same length as `inputs`, or is empty.
- mutating_variable_inputs:[bool];
-}
-
-// The root type, defining a subgraph, which typically represents an entire
-// model.
-table SubGraph {
- // A list of all tensors used in this subgraph.
- tensors:[Tensor];
-
- // Indices of the tensors that are inputs into this subgraph. Note this is
- // the list of non-static tensors that feed into the subgraph for inference.
- inputs:[int];
-
- // Indices of the tensors that are outputs out of this subgraph. Note this is
- // the list of output tensors that are considered the product of the
- // subgraph's inference.
- outputs:[int];
-
- // All operators, in execution order.
- operators:[Operator];
-
- // Name of this subgraph (used for debugging).
- name:string;
-}
-
-// Table of raw data buffers (used for constant tensors). Referenced by tensors
-// by index. The generous alignment accommodates mmap-friendly data structures.
-table Buffer {
- data:[ubyte] (force_align: 16);
-}
-
-table Model {
- // Version of the schema.
- version:uint;
-
- // A list of all operator codes used in this model. This is
- // kept in order because operators carry an index into this
- // vector.
- operator_codes:[OperatorCode];
-
- // All the subgraphs of the model. The 0th is assumed to be the main
- // model.
- subgraphs:[SubGraph];
-
- // A description of the model.
- description:string;
-
- // Buffers of the model.
- // Note the 0th entry of this array must be an empty buffer (sentinel).
- // This is a convention so that tensors without a buffer can provide 0 as
- // their buffer.
- buffers:[Buffer];
-
- // Metadata about the model. Indirects into the existings buffers list.
- metadata_buffer:[int];
-}
-
-root_type Model;
diff --git a/compiler/tfldump/schema/schema.meta b/compiler/tfldump/schema/schema.meta
deleted file mode 100644
index cd90a3d63..000000000
--- a/compiler/tfldump/schema/schema.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-URL="https://raw.githubusercontent.com/tensorflow/tensorflow/a6d8ffae097/tensorflow/contrib/lite/schema/schema.fbs"
-NOTE="TensorFlow Lite Schema from TensorFlow v1.12.0 release"
diff --git a/compiler/tfldump/src/Dump.cpp b/compiler/tfldump/src/Dump.cpp
index 82d8d7b12..8c8178f93 100644
--- a/compiler/tfldump/src/Dump.cpp
+++ b/compiler/tfldump/src/Dump.cpp
@@ -73,12 +73,10 @@ std::ostream &operator<<(std::ostream &os, const std::vector<int32_t> &vect)
return os;
}
-template <typename T> void dump_fbvect(std::ostream &os, const flatbuffers::Vector<T> *fbvect)
+template <typename T>
+void dump_fbvect(std::ostream &os, const flatbuffers::Vector<T> *fbvect, uint32_t size)
{
- if (fbvect == nullptr)
- return;
-
- for (uint32_t q = 0; q < fbvect->size(); q++)
+ for (uint32_t q = 0; q < size; q++)
{
if (q)
os << ", ";
@@ -86,64 +84,67 @@ template <typename T> void dump_fbvect(std::ostream &os, const flatbuffers::Vect
}
}
-template <typename T>
-std::ostream &operator<<(std::ostream &os, const flatbuffers::Vector<T> *fbvect)
+template <>
+void dump_fbvect(std::ostream &os, const flatbuffers::Vector<uint8_t> *fbvect, uint32_t size)
{
- dump_fbvect(os, fbvect);
- return os;
+ assert(fbvect);
+ for (uint32_t q = 0; q < size; q++)
+ {
+ if (q)
+ os << ", ";
+ os << static_cast<uint32_t>(fbvect->Get(q));
+ }
}
-void dump_model(std::ostream &os, const tflite::Model *model)
+template <typename T>
+std::ostream &operator<<(std::ostream &os, const flatbuffers::Vector<T> *fbvect)
{
- tflread::Reader reader(model);
-
- assert(reader.num_subgraph() == 1);
- reader.select_subgraph(0);
+ if (fbvect == nullptr)
+ return os;
- auto opcodes = reader.opcodes();
- auto buffers = reader.buffers();
- auto tensors = reader.tensors();
- auto operators = reader.operators();
+ bool ellipsis = (fbvect->size() > 4);
+ auto limit_size = ellipsis ? 4 : fbvect->size();
- // dump operator_codes
- os << "Operator Codes: [order] OpCodeName (OpCode Enum)" << std::endl;
- int32_t opcode_index = 0;
- for (auto opcode : opcodes)
+ if (ellipsis)
{
- tflite::BuiltinOperator op_code = opcode->builtin_code();
- auto op_name = tflread::opcode_name(opcode);
+ os << "(" << fbvect->size() << ") ";
+ }
- os << "[" << opcode_index << "] " << op_name << " (code: " << op_code << ")" << std::endl;
+ dump_fbvect(os, fbvect, limit_size);
- opcode_index++;
+ if (ellipsis)
+ {
+ os << " ... ";
}
- os << std::endl;
- // dump buffer
- os << "Buffers: B(index) (length) values, if any" << std::endl;
- for (uint32_t i = 0; i < buffers->Length(); ++i)
- {
- const uint8_t *buff_data;
- size_t size = reader.buffer_info(i, &buff_data);
+ return os;
+}
- os << "B(" << i << ") (" << size << ") ";
- if (buff_data != nullptr)
- {
- dump_buffer(os, buff_data, size, 16);
- }
- os << std::endl;
- }
- os << std::endl;
+void dump_sub_graph(std::ostream &os, tflread::Reader &reader)
+{
+ auto tensors = reader.tensors();
+ auto operators = reader.operators();
// dump operands(tensors)
- os << "Operands: T(tensor index) TYPE (shape) B(buffer index) OperandName" << std::endl;
+ os << "Operands: T(subgraph index : tensor index) TYPE (shape) (shape_signature) "
+ << "B(buffer index) OperandName" << std::endl;
for (uint32_t i = 0; i < tensors->Length(); ++i)
{
+ // TODO refactor to some better structure
auto tensor = tensors->Get(i);
- auto dims = tflread::as_index_vector(tensor->shape());
+ std::vector<int32_t> dims = {-1};
- os << "T(" << i << ") " << tflread::tensor_type(tensor) << " ";
+ if (tensor->shape())
+ dims = tflread::as_index_vector(tensor->shape());
+
+ os << "T(" << reader.subgraph_index() << ":" << i << ") " << tflread::tensor_type(tensor)
+ << " ";
os << "(" << dims << ") ";
+ if (tensor->shape_signature())
+ {
+ std::vector<int32_t> dims_sig = tflread::as_index_vector(tensor->shape_signature());
+ os << "(" << dims_sig << ") ";
+ }
os << "B(" << tensor->buffer() << ") ";
os << tflread::tensor_name(tensor) << std::endl;
@@ -151,25 +152,126 @@ void dump_model(std::ostream &os, const tflite::Model *model)
{
if ((q_params->min() && q_params->max()) || (q_params->scale() && q_params->zero_point()))
{
- os << " Quantization: ";
+ std::string strquantiz = " Quantization: ";
+ std::string strqindent(strquantiz.size(), ' ');
+ os << strquantiz;
if (q_params->min())
+ {
os << "min(" << q_params->min() << ") ";
+ if (q_params->min()->size() > 1)
+ os << std::endl << strqindent;
+ }
if (q_params->max())
+ {
os << "max(" << q_params->max() << ") ";
+ if (q_params->max()->size() > 1)
+ os << std::endl << strqindent;
+ }
if (q_params->scale())
+ {
os << "scale(" << q_params->scale() << ") ";
+ if (q_params->scale()->size() > 1)
+ os << std::endl << strqindent;
+ }
if (q_params->zero_point())
+ {
os << "zeropt(" << q_params->zero_point() << ") ";
+ if (q_params->zero_point()->size() > 1)
+ os << std::endl << strqindent;
+ }
+ os << "quantized_dimension(" << q_params->quantized_dimension() << ")";
os << std::endl;
}
}
+
+ if (const auto &s_params = tensor->sparsity())
+ {
+ std::string strsparsity = " Sparsity: ";
+ std::string strsindent(strsparsity.size(), ' ');
+ os << strsparsity;
+
+ if (s_params->traversal_order())
+ {
+ os << "traversal_order(" << s_params->traversal_order() << ") ";
+ os << std::endl << strsindent;
+ }
+ if (s_params->block_map())
+ {
+ os << "block_map(" << s_params->block_map() << ") ";
+ os << std::endl << strsindent;
+ }
+ if (const auto &dim_metadata = s_params->dim_metadata())
+ {
+ uint32_t idx = 0;
+ for (const auto &dm : *dim_metadata)
+ {
+ std::string strdm = "dim_metadata[" + std::to_string(idx++) + "]: ";
+ std::string strdm_indent = strsindent + std::string(strdm.size(), ' ');
+ os << strdm;
+
+ os << "format(" << tflite::EnumNameDimensionType(dm->format()) << ") ";
+ os << std::endl << strdm_indent;
+
+ os << "dense_size(" << dm->dense_size() << ") ";
+ os << std::endl << strdm_indent;
+
+ os << "array_segments_type("
+ << tflite::EnumNameSparseIndexVector(dm->array_segments_type()) << ") ";
+ os << std::endl << strdm_indent;
+
+ os << "array_segments(";
+ switch (dm->array_segments_type())
+ {
+ case tflite::SparseIndexVector_NONE:
+ // DO NOTHING
+ break;
+ case tflite::SparseIndexVector_Int32Vector:
+ os << dm->array_segments_as_Int32Vector()->values();
+ break;
+ case tflite::SparseIndexVector_Uint16Vector:
+ os << dm->array_segments_as_Uint16Vector()->values();
+ break;
+ case tflite::SparseIndexVector_Uint8Vector:
+ os << dm->array_segments_as_Uint8Vector()->values();
+ break;
+ default:
+ throw std::runtime_error("Invalid SparseIndexVector type of array_segments");
+ }
+ os << ")" << std::endl << strdm_indent;
+
+ os << "array_indices_type(" << tflite::EnumNameSparseIndexVector(dm->array_indices_type())
+ << ") ";
+ os << std::endl << strdm_indent;
+
+ os << "array_indices(";
+ switch (dm->array_indices_type())
+ {
+ case tflite::SparseIndexVector_NONE:
+ // DO NOTHING
+ break;
+ case tflite::SparseIndexVector_Int32Vector:
+ os << dm->array_indices_as_Int32Vector()->values();
+ break;
+ case tflite::SparseIndexVector_Uint16Vector:
+ os << dm->array_indices_as_Uint16Vector()->values();
+ break;
+ case tflite::SparseIndexVector_Uint8Vector:
+ os << dm->array_indices_as_Uint8Vector()->values();
+ break;
+ default:
+ throw std::runtime_error("Invalid SparseIndexVector type of array_indices");
+ }
+ os << ")" << std::endl << strsindent;
+ }
+ }
+ }
+ os << std::endl;
}
- os << std::endl;
// dump operators
- os << "Operators: O(operator index) OpCodeName " << std::endl;
+ os << "Operators: O(subgraph index : operator index) OpCodeName " << std::endl;
os << " Option(values) ... <-- depending on OpCode" << std::endl;
os << " I T(tensor index) OperandName <-- as input" << std::endl;
os << " O T(tensor index) OperandName <-- as output" << std::endl;
@@ -182,7 +284,7 @@ void dump_model(std::ostream &os, const tflite::Model *model)
const std::vector<int32_t> &outputs = tflread::as_index_vector(op->outputs());
auto op_name = reader.opcode_name(op);
- os << "O(" << i << ") " << op_name << " ";
+ os << "O(" << reader.subgraph_index() << ":" << i << ") " << op_name << " ";
os << std::endl;
if (auto op_prn = OpPrinterRegistry::get().lookup(builtincode))
@@ -192,13 +294,23 @@ void dump_model(std::ostream &os, const tflite::Model *model)
for (auto input : inputs)
{
- auto tensor = tensors->Get(input);
- os << " I T(" << input << ") " << tflread::tensor_name(tensor) << std::endl;
+ os << " I T(" << reader.subgraph_index() << ":" << input << ") ";
+ if (input >= 0)
+ {
+ auto tensor = tensors->Get(input);
+ os << tflread::tensor_name(tensor);
+ }
+ os << std::endl;
}
for (auto output : outputs)
{
- auto tensor = tensors->Get(output);
- os << " O T(" << output << ") " << tflread::tensor_name(tensor) << std::endl;
+ os << " O T(" << reader.subgraph_index() << ":" << output << ") ";
+ if (output >= 0)
+ {
+ auto tensor = tensors->Get(output);
+ os << tflread::tensor_name(tensor);
+ }
+ os << std::endl;
}
}
os << std::endl;
@@ -210,15 +322,78 @@ void dump_model(std::ostream &os, const tflite::Model *model)
{
auto tensor = tensors->Get(input);
std::string name = tflread::tensor_name(tensor);
- os << "I T(" << input << ") " << name << std::endl;
+ os << "I T(" << reader.subgraph_index() << ":" << input << ") " << name << std::endl;
}
for (const auto output : reader.outputs())
{
auto tensor = tensors->Get(output);
std::string name = tflread::tensor_name(tensor);
- os << "O T(" << output << ") " << name << std::endl;
+ os << "O T(" << reader.subgraph_index() << ":" << output << ") " << name << std::endl;
}
+
+ os << std::endl;
+}
+
+void dump_model(std::ostream &os, const tflite::Model *model)
+{
+ tflread::Reader reader(model);
+
+ uint32_t num_subgraph = reader.num_subgraph();
+
+ // dump model version
+ os << "===================================================================" << std::endl;
+ os << "Model version: " << reader.version() << std::endl;
+ os << " # sub graphs: " << num_subgraph << std::endl;
+ os << std::endl;
+
+ auto opcodes = reader.opcodes();
+ auto buffers = reader.buffers();
+
+ // dump operator_codes
+ os << "Operator Codes: [order] OpCodeName (OpCode Enum)" << std::endl;
+ int32_t opcode_index = 0;
+ for (auto opcode : opcodes)
+ {
+ tflite::BuiltinOperator op_code = opcode->builtin_code();
+ auto op_name = tflread::opcode_name(opcode);
+ auto op_version = opcode->version();
+
+ os << "[" << opcode_index << "] " << op_name << " (code: " << op_code
+ << ", version: " << op_version << ")" << std::endl;
+
+ opcode_index++;
+ }
+ os << std::endl;
+
+ // dump buffer
+ os << "Buffers: B(index) (length) values, if any" << std::endl;
+ for (uint32_t i = 0; i < buffers->Length(); ++i)
+ {
+ const uint8_t *buff_data;
+ size_t size = reader.buffer_info(i, &buff_data);
+
+ os << "B(" << i << ") (" << size << ") ";
+ if (buff_data != nullptr)
+ {
+ dump_buffer(os, buff_data, size, 16);
+ }
+ os << std::endl;
+ }
+ os << std::endl;
+
+ for (uint32_t sg = 0; sg < num_subgraph; ++sg)
+ {
+ reader.select_subgraph(sg);
+
+ os << "-------------------------------------------------------------------" << std::endl;
+ os << "Sub-Graph: #" << sg << " " << reader.subgraph_name() << std::endl;
+ os << std::endl;
+
+ dump_sub_graph(os, reader);
+ }
+
+ os << "===================================================================" << std::endl;
}
} // namespace tfldump
diff --git a/compiler/tfldump/src/OpPrinter.cpp b/compiler/tfldump/src/OpPrinter.cpp
index 9ecc4ff11..5d279632c 100644
--- a/compiler/tfldump/src/OpPrinter.cpp
+++ b/compiler/tfldump/src/OpPrinter.cpp
@@ -17,11 +17,11 @@
#include "OpPrinter.h"
#include "Read.h"
-#include <stdex/Memory.h>
+#include <memory>
#include <flatbuffers/flexbuffers.h>
-using stdex::make_unique;
+using std::make_unique;
namespace tfldump
{
@@ -29,6 +29,66 @@ namespace tfldump
// TODO move to some header
std::ostream &operator<<(std::ostream &os, const std::vector<int32_t> &vect);
+// TODO Re-arrange in alphabetical order
+
+class AddPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_AddOptions())
+ {
+ os << " ";
+ os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function())
+ << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class ArgMaxPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_ArgMaxOptions())
+ {
+ os << " ";
+ os << "OutputType(" << EnumNameTensorType(params->output_type()) << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class ArgMinPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_ArgMinOptions())
+ {
+ os << " ";
+ os << "OutputType(" << EnumNameTensorType(params->output_type()) << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class CastPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto cast_params = op->builtin_options_as_CastOptions())
+ {
+ os << " ";
+ os << "in_data_type(" << tflite::EnumNameTensorType(cast_params->in_data_type()) << ") ";
+ os << "out_data_type(" << tflite::EnumNameTensorType(cast_params->out_data_type()) << ") ";
+ os << std::endl;
+ }
+ }
+};
+
class Conv2DPrinter : public OpPrinter
{
public:
@@ -40,7 +100,25 @@ public:
os << "Padding(" << conv_params->padding() << ") ";
os << "Stride.W(" << conv_params->stride_w() << ") ";
os << "Stride.H(" << conv_params->stride_h() << ") ";
- os << "Activation(" << conv_params->fused_activation_function() << ")";
+ os << "Dilation.W(" << conv_params->dilation_w_factor() << ") ";
+ os << "Dilation.H(" << conv_params->dilation_h_factor() << ") ";
+ os << "Activation("
+ << EnumNameActivationFunctionType(conv_params->fused_activation_function()) << ")";
+ os << std::endl;
+ }
+ }
+};
+
+class DivPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_DivOptions())
+ {
+ os << " ";
+ os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function())
+ << ") ";
os << std::endl;
}
}
@@ -59,7 +137,8 @@ public:
os << "Stride.H(" << pool_params->stride_h() << ") ";
os << "Filter.W(" << pool_params->filter_width() << ") ";
os << "Filter.H(" << pool_params->filter_height() << ") ";
- os << "Activation(" << pool_params->fused_activation_function() << ")";
+ os << "Activation("
+ << EnumNameActivationFunctionType(pool_params->fused_activation_function()) << ")";
os << std::endl;
}
}
@@ -73,13 +152,29 @@ public:
if (auto *concatenation_params = op->builtin_options_as_ConcatenationOptions())
{
os << " ";
- os << "Activation(" << concatenation_params->fused_activation_function() << ") ";
+ os << "Activation("
+ << EnumNameActivationFunctionType(concatenation_params->fused_activation_function())
+ << ") ";
os << "Axis(" << concatenation_params->axis() << ")";
os << std::endl;
}
}
};
+class ReducerPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto reducer_params = op->builtin_options_as_ReducerOptions())
+ {
+ os << " ";
+ os << "keep_dims(" << reducer_params->keep_dims() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
class ReshapePrinter : public OpPrinter
{
public:
@@ -95,6 +190,80 @@ public:
}
};
+class ResizeBilinearPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *resize_params = op->builtin_options_as_ResizeBilinearOptions())
+ {
+ os << " ";
+ os << std::boolalpha;
+ os << "align_corners(" << resize_params->align_corners() << ")";
+ os << "half_pixel_centers(" << resize_params->half_pixel_centers() << ")";
+ os << std::endl;
+ }
+ }
+};
+
+class ResizeNearestNeighborPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *resize_params = op->builtin_options_as_ResizeNearestNeighborOptions())
+ {
+ os << " ";
+ os << std::boolalpha;
+ os << "align_corners(" << resize_params->align_corners() << ")";
+ os << std::endl;
+ }
+ }
+};
+
+class ReverseSequencePrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *std_params = op->builtin_options_as_ReverseSequenceOptions())
+ {
+ os << " ";
+ os << "seq_dim(" << std_params->seq_dim() << ") ";
+ os << "batch_dim(" << std_params->batch_dim() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class DepthToSpacePrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *std_params = op->builtin_options_as_DepthToSpaceOptions())
+ {
+ os << " ";
+ os << "BlockSize(" << std_params->block_size() << ")";
+ os << std::endl;
+ }
+ }
+};
+
+class SparseToDensePrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *std_params = op->builtin_options_as_SparseToDenseOptions())
+ {
+ os << " ";
+ os << "ValidateIndices(" << std_params->validate_indices() << ")";
+ os << std::endl;
+ }
+ }
+};
+
class DepthwiseConv2DPrinter : public OpPrinter
{
public:
@@ -107,9 +276,176 @@ public:
os << "Stride.W(" << conv_params->stride_w() << ") ";
os << "Stride.H(" << conv_params->stride_h() << ") ";
os << "DepthMultiplier(" << conv_params->depth_multiplier() << ") ";
- os << "Activation(" << conv_params->fused_activation_function() << ") ";
os << "Dilation.W(" << conv_params->dilation_w_factor() << ") ";
os << "Dilation.H(" << conv_params->dilation_h_factor() << ")";
+ os << "Activation("
+ << EnumNameActivationFunctionType(conv_params->fused_activation_function()) << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class FullyConnectedPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_FullyConnectedOptions())
+ {
+ os << " ";
+ os << "WeightFormat(" << EnumNameFullyConnectedOptionsWeightsFormat(params->weights_format())
+ << ") ";
+ os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function())
+ << ") ";
+
+ os << std::endl;
+ }
+ }
+};
+
+class GatherPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_GatherOptions())
+ {
+ os << " ";
+ os << "Axis(" << params->axis() << ") ";
+
+ os << std::endl;
+ }
+ }
+};
+
+class IfPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_IfOptions())
+ {
+ os << " ";
+ os << "then_subgraph_index(" << params->then_subgraph_index() << ") ";
+ os << "else_subgraph_index(" << params->else_subgraph_index() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class L2NormPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_L2NormOptions())
+ {
+ os << " ";
+ os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function())
+ << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class LeakyReluPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_LeakyReluOptions())
+ {
+ os << " ";
+ os << "alpha(" << params->alpha() << ") ";
+ }
+ }
+};
+
+class LocalResponseNormalizationPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_LocalResponseNormalizationOptions())
+ {
+ os << " ";
+ os << "radius(" << params->radius() << ") ";
+ os << "bias(" << params->bias() << ") ";
+ os << "alpha(" << params->alpha() << ") ";
+ os << "beta(" << params->beta() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class MirrorPadPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_MirrorPadOptions())
+ {
+ os << " ";
+ os << "mode(" << EnumNameMirrorPadMode(params->mode()) << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class MulPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_MulOptions())
+ {
+ os << " ";
+ os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function())
+ << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class PackPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_PackOptions())
+ {
+ os << " ";
+ os << "ValuesCount(" << params->values_count() << ") ";
+ os << "Axis(" << params->axis() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class OneHotPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_OneHotOptions())
+ {
+ os << " ";
+ os << "Axis(" << params->axis() << ") ";
+
+ os << std::endl;
+ }
+ }
+};
+
+class ShapePrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_ShapeOptions())
+ {
+ os << " ";
+ os << "out_type(" << EnumNameTensorType(params->out_type()) << ") ";
os << std::endl;
}
}
@@ -129,6 +465,166 @@ public:
}
};
+class SpaceToDepthPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *std_params = op->builtin_options_as_SpaceToDepthOptions())
+ {
+ os << " ";
+ os << "BlockSize(" << std_params->block_size() << ")";
+ os << std::endl;
+ }
+ }
+};
+
+class SqueezePrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_SqueezeOptions())
+ {
+ os << " ";
+ os << "SqueezeDims(";
+ for (int i = 0; i < params->squeeze_dims()->size(); ++i)
+ {
+ if (i != 0)
+ os << ", ";
+ os << params->squeeze_dims()->Get(i);
+ }
+ os << ")";
+ os << std::endl;
+ }
+ }
+};
+
+class StridedSlicePrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *strided_slice_params = op->builtin_options_as_StridedSliceOptions())
+ {
+ os << " ";
+ os << "begin_mask(" << strided_slice_params->begin_mask() << ") ";
+ os << "end_mask(" << strided_slice_params->end_mask() << ") ";
+ os << "ellipsis_mask(" << strided_slice_params->ellipsis_mask() << ") ";
+ os << "new_axis_mask(" << strided_slice_params->new_axis_mask() << ") ";
+ os << "shrink_axis_mask(" << strided_slice_params->shrink_axis_mask() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class SplitPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_SplitOptions())
+ {
+ os << " ";
+ os << "num_splits(" << params->num_splits() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class SplitVPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_SplitVOptions())
+ {
+ os << " ";
+ os << "num_splits(" << params->num_splits() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class SubPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_SubOptions())
+ {
+ os << " ";
+ os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function())
+ << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class TransposeConvPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_TransposeConvOptions())
+ {
+ os << " ";
+ os << "Padding(" << params->padding() << ") ";
+ os << "Stride.W(" << params->stride_w() << ") ";
+ os << "Stride.H(" << params->stride_h() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class WhilePrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_WhileOptions())
+ {
+ os << " ";
+ os << "cond_subgraph_index(" << params->cond_subgraph_index() << ") ";
+ os << "body_subgraph_index(" << params->body_subgraph_index() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class UnidirectionalSequenceLSTMPrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_UnidirectionalSequenceLSTMOptions())
+ {
+ os << " ";
+ os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function())
+ << ") ";
+ os << "cell_clip(" << params->cell_clip() << ") ";
+ os << "proj_clip(" << params->proj_clip() << ") ";
+ os << "time_major(" << params->time_major() << ") ";
+ os << "asymmetric_quantize_inputs(" << params->asymmetric_quantize_inputs() << ") ";
+ os << std::endl;
+ }
+ }
+};
+
+class UniquePrinter : public OpPrinter
+{
+public:
+ void options(const tflite::Operator *op, std::ostream &os) const override
+ {
+ if (auto *params = op->builtin_options_as_UniqueOptions())
+ {
+ os << " ";
+ os << "idx_out_type(" << EnumNameTensorType(params->idx_out_type()) << ") ";
+ os << std::endl;
+ }
+ }
+};
+
class CustomOpPrinter : public OpPrinter
{
public:
@@ -171,14 +667,76 @@ public:
OpPrinterRegistry::OpPrinterRegistry()
{
+ _op_map[tflite::BuiltinOperator_ADD] = make_unique<AddPrinter>();
+ // There is no Option for ADD_N
+ _op_map[tflite::BuiltinOperator_ARG_MAX] = make_unique<ArgMaxPrinter>();
+ _op_map[tflite::BuiltinOperator_ARG_MIN] = make_unique<ArgMinPrinter>();
_op_map[tflite::BuiltinOperator_AVERAGE_POOL_2D] = make_unique<Pool2DPrinter>();
+ _op_map[tflite::BuiltinOperator_CAST] = make_unique<CastPrinter>();
+ // There is no Option for CEIL
_op_map[tflite::BuiltinOperator_CONCATENATION] = make_unique<ConcatenationPrinter>();
_op_map[tflite::BuiltinOperator_CONV_2D] = make_unique<Conv2DPrinter>();
+ _op_map[tflite::BuiltinOperator_DEPTH_TO_SPACE] = make_unique<DepthToSpacePrinter>();
_op_map[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = make_unique<DepthwiseConv2DPrinter>();
+ // There is no Option for DEQUANTIZE
+ _op_map[tflite::BuiltinOperator_DIV] = make_unique<DivPrinter>();
+ // There is no Option for FLOOR
+ // There is no Option for FLOOR_MOD
+ _op_map[tflite::BuiltinOperator_FULLY_CONNECTED] = make_unique<FullyConnectedPrinter>();
+ _op_map[tflite::BuiltinOperator_GATHER] = make_unique<GatherPrinter>();
+ _op_map[tflite::BuiltinOperator_IF] = make_unique<IfPrinter>();
+ _op_map[tflite::BuiltinOperator_L2_POOL_2D] = make_unique<Pool2DPrinter>();
+ _op_map[tflite::BuiltinOperator_L2_NORMALIZATION] = make_unique<L2NormPrinter>();
+ _op_map[tflite::BuiltinOperator_LEAKY_RELU] = make_unique<LeakyReluPrinter>();
+ _op_map[tflite::BuiltinOperator_LOCAL_RESPONSE_NORMALIZATION] =
+ make_unique<LocalResponseNormalizationPrinter>();
+ // There is no Option for LOG
+ // There is no Option for LOGISTIC
+ // There is no Option for LOG_SOFTMAX
_op_map[tflite::BuiltinOperator_MAX_POOL_2D] = make_unique<Pool2DPrinter>();
- // There is no Option for ReLU and ReLU6
+ _op_map[tflite::BuiltinOperator_MIRROR_PAD] = make_unique<MirrorPadPrinter>();
+ _op_map[tflite::BuiltinOperator_MUL] = make_unique<MulPrinter>();
+ // There is no Option for NON_MAX_SUPPRESSION_V4
+ // There is no Option for NON_MAX_SUPPRESSION_V5
+ _op_map[tflite::BuiltinOperator_ONE_HOT] = make_unique<OneHotPrinter>();
+ _op_map[tflite::BuiltinOperator_PACK] = make_unique<PackPrinter>();
+ // There is no Option for PAD
+ // There is no Option for PADV2
+ // There is no Option for PRELU
+ // There is no Option for RELU
+ // There is no Option for RELU6
+ // There is no Option for RELU_N1_TO_1
+ _op_map[tflite::BuiltinOperator_REDUCE_ANY] = make_unique<ReducerPrinter>();
+ _op_map[tflite::BuiltinOperator_REDUCE_MAX] = make_unique<ReducerPrinter>();
+ _op_map[tflite::BuiltinOperator_REDUCE_MIN] = make_unique<ReducerPrinter>();
+ _op_map[tflite::BuiltinOperator_REDUCE_PROD] = make_unique<ReducerPrinter>();
_op_map[tflite::BuiltinOperator_RESHAPE] = make_unique<ReshapePrinter>();
+ _op_map[tflite::BuiltinOperator_RESIZE_BILINEAR] = make_unique<ResizeBilinearPrinter>();
+ _op_map[tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR] =
+ make_unique<ResizeNearestNeighborPrinter>();
+ _op_map[tflite::BuiltinOperator_REVERSE_SEQUENCE] = make_unique<ReverseSequencePrinter>();
+ // There is no Option for ROUND
+ // There is no Option for SELECT
+ // There is no Option for SELECT_V2
+ _op_map[tflite::BuiltinOperator_SHAPE] = make_unique<ShapePrinter>();
+ // There is no Option for SIN
+ // There is no Option for SLICE
_op_map[tflite::BuiltinOperator_SOFTMAX] = make_unique<SoftmaxPrinter>();
+ _op_map[tflite::BuiltinOperator_SPACE_TO_DEPTH] = make_unique<SpaceToDepthPrinter>();
+ // There is no Option for SPACE_TO_BATCH_ND
+ _op_map[tflite::BuiltinOperator_SPARSE_TO_DENSE] = make_unique<SparseToDensePrinter>();
+ _op_map[tflite::BuiltinOperator_SPLIT] = make_unique<SplitPrinter>();
+ _op_map[tflite::BuiltinOperator_SPLIT_V] = make_unique<SplitVPrinter>();
+ _op_map[tflite::BuiltinOperator_SQUEEZE] = make_unique<SqueezePrinter>();
+ _op_map[tflite::BuiltinOperator_STRIDED_SLICE] = make_unique<StridedSlicePrinter>();
+ _op_map[tflite::BuiltinOperator_SUB] = make_unique<SubPrinter>();
+ _op_map[tflite::BuiltinOperator_SUM] = make_unique<ReducerPrinter>();
+ _op_map[tflite::BuiltinOperator_TRANSPOSE_CONV] = make_unique<TransposeConvPrinter>();
+ // There is no Option for TOPK_V2
+ _op_map[tflite::BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM] =
+ make_unique<UnidirectionalSequenceLSTMPrinter>();
+ _op_map[tflite::BuiltinOperator_UNIQUE] = make_unique<UniquePrinter>();
+ _op_map[tflite::BuiltinOperator_WHILE] = make_unique<WhilePrinter>();
_op_map[tflite::BuiltinOperator_CUSTOM] = make_unique<CustomOpPrinter>();
}
diff --git a/compiler/tfldump/src/OpPrinter.h b/compiler/tfldump/src/OpPrinter.h
index dd6df8875..f46b22fdc 100644
--- a/compiler/tfldump/src/OpPrinter.h
+++ b/compiler/tfldump/src/OpPrinter.h
@@ -17,7 +17,7 @@
#ifndef __TFLDUMP_OPPRINTER_H__
#define __TFLDUMP_OPPRINTER_H__
-#include <schema_generated.h>
+#include <mio/tflite/schema_generated.h>
#include <ostream>
#include <map>
diff --git a/compiler/tfldump/src/Read.cpp b/compiler/tfldump/src/Read.cpp
index 962d7d13c..f9782d9ef 100644
--- a/compiler/tfldump/src/Read.cpp
+++ b/compiler/tfldump/src/Read.cpp
@@ -50,7 +50,10 @@ std::string opcode_name(const tflite::OperatorCode *opcode)
if (!opcode->custom_code())
return "(invalid custom)";
- return opcode->custom_code()->c_str();
+ std::string custom_op = "CUSTOM(";
+ custom_op += opcode->custom_code()->c_str();
+ custom_op += ")";
+ return custom_op;
}
tflite::BuiltinOperator code = opcode->builtin_code();
@@ -75,6 +78,7 @@ const char *tensor_name(const tflite::Tensor *tensor)
Reader::Reader(const tflite::Model *model)
{
+ _version = model->version();
_subgraphs = model->subgraphs();
_buffers = model->buffers();
@@ -134,6 +138,7 @@ std::string Reader::opcode_name(const tflite::Operator *op) const
bool Reader::select_subgraph(uint32_t sgindex)
{
+ _subgraph_index = sgindex;
_tensors = nullptr;
_operators = nullptr;
@@ -148,6 +153,9 @@ bool Reader::select_subgraph(uint32_t sgindex)
const tflite::SubGraph *subgraph = (*_subgraphs)[sgindex];
+ auto name = subgraph->name();
+ _subgraph_name = name ? name->c_str() : "(noname)";
+
_tensors = subgraph->tensors();
_operators = subgraph->operators();
diff --git a/compiler/tfldump/src/Read.h b/compiler/tfldump/src/Read.h
index 3a4521118..7af2fa59b 100644
--- a/compiler/tfldump/src/Read.h
+++ b/compiler/tfldump/src/Read.h
@@ -17,7 +17,7 @@
#ifndef __TFLREAD_READ_H__
#define __TFLREAD_READ_H__
-#include <schema_generated.h>
+#include <mio/tflite/schema_generated.h>
#include <map>
#include <string>
@@ -59,6 +59,8 @@ public:
Reader() = delete;
public:
+ uint32_t version() const { return _version; }
+
const std::vector<const tflite::OperatorCode *> &opcodes() { return _op_codes; }
const TFliteBuffers_t *buffers() { return _buffers; }
const TFliteTensors_t *tensors() { return _tensors; }
@@ -74,13 +76,19 @@ public:
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 TFliteSubGraphs_t *_subgraphs{nullptr};
const TFliteBuffers_t *_buffers{nullptr};
const TFliteTensors_t *_tensors{nullptr};
const TFliteOperators_t *_operators{nullptr};
+ uint32_t _subgraph_index;
+ std::string _subgraph_name;
std::vector<const tflite::OperatorCode *> _op_codes;
std::vector<int32_t> _inputs;
std::vector<int32_t> _outputs;
diff --git a/compiler/tflite2circle-conversion-test/CMakeLists.txt b/compiler/tflite2circle-conversion-test/CMakeLists.txt
new file mode 100644
index 000000000..83fe23a8f
--- /dev/null
+++ b/compiler/tflite2circle-conversion-test/CMakeLists.txt
@@ -0,0 +1,68 @@
+nnas_include(TargetRequire)
+
+unset(REQUIRED_TARGETS)
+list(APPEND REQUIRED_TARGETS tflite2circle)
+TargetRequire_Return(${REQUIRED_TARGETS})
+
+nncc_find_resource(TensorFlowLiteRecipes)
+
+set(TEST_REPO "${TensorFlowLiteRecipes_DIR}")
+set(TEST_RECIPE_FILENAME "test.recipe")
+
+file(GLOB RECIPES RELATIVE ${TEST_REPO} "${TEST_REPO}/*/${TEST_RECIPE_FILENAME}")
+
+unset(TEST_DEPS)
+unset(TEST_NAMES)
+
+foreach(RECIPE IN ITEMS ${RECIPES})
+ get_filename_component(PREFIX ${RECIPE} DIRECTORY)
+ if(NOT IS_DIRECTORY "${TEST_REPO}/${RECIPE_PREFIX}")
+ message(FATAL_ERROR "Missing '${RECIPE_PREFIX}' test")
+ endif()
+
+ list(APPEND TEST_NAMES ${PREFIX})
+endforeach(RECIPE IN ITEMS ${RECIPES})
+
+##
+## Copy testall
+##
+set(TEST_RUNNER "${CMAKE_CURRENT_BINARY_DIR}/testall.sh")
+set(TEST_RUNNER_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/testall.sh")
+
+add_custom_command(
+ OUTPUT ${TEST_RUNNER}
+ COMMAND ${CMAKE_COMMAND} -E copy "${TEST_RUNNER_SOURCE}" "${TEST_RUNNER}"
+ DEPENDS ${TEST_RUNNER_SOURCE}
+ COMMENT "Generate test runner"
+)
+
+list(APPEND TEST_DEPS "${TEST_RUNNER}")
+
+get_target_property(ARTIFACTS_BIN_PATH testDataGenerator BINARY_DIR)
+
+###
+### Generate test.config
+###
+set(TEST_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/test.config")
+
+add_custom_command(
+ OUTPUT ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_CONFIG}
+ COMMAND ${CMAKE_COMMAND} -E echo 'TFLITE2CIRCLE_PATH=\"$<TARGET_FILE:tflite2circle>\"' >> ${TEST_CONFIG}
+ DEPENDS tflite2circle
+ COMMENT "Generate test configuration"
+)
+
+list(APPEND TEST_DEPS "${TEST_CONFIG}")
+
+# This "tflite2circle_conversion_test_deps" target enforces CMake to generate all the dependencies during "build" phase
+add_custom_target(tflite2circle_conversion_test_deps ALL DEPENDS ${TEST_DEPS})
+
+# Run tests
+add_test(
+ NAME tflite2circle_conversion_test
+ COMMAND "${TEST_RUNNER}"
+ "${TEST_CONFIG}"
+ "${ARTIFACTS_BIN_PATH}"
+ ${TEST_NAMES}
+)
diff --git a/compiler/tflite2circle-conversion-test/README.md b/compiler/tflite2circle-conversion-test/README.md
new file mode 100644
index 000000000..e6b4cd223
--- /dev/null
+++ b/compiler/tflite2circle-conversion-test/README.md
@@ -0,0 +1,3 @@
+# tflite2circle-conversion-test
+
+Run `tflite2circle` to check whether _tflite_ model is able to be converted into _circle_ model.
diff --git a/compiler/tflite2circle-conversion-test/requires.cmake b/compiler/tflite2circle-conversion-test/requires.cmake
new file mode 100644
index 000000000..87b00993f
--- /dev/null
+++ b/compiler/tflite2circle-conversion-test/requires.cmake
@@ -0,0 +1,2 @@
+require("common-artifacts")
+require("tflite2circle")
diff --git a/compiler/tflite2circle-conversion-test/testall.sh b/compiler/tflite2circle-conversion-test/testall.sh
new file mode 100755
index 000000000..2290ee6db
--- /dev/null
+++ b/compiler/tflite2circle-conversion-test/testall.sh
@@ -0,0 +1,77 @@
+#!/bin/bash
+
+# Need at least 2 arguments
+if [[ $# -lt 2 ]]; then
+ echo "USAGE: $0 ..."
+ echo
+ echo "ARGUMENTS:"
+ echo " [test.config path]"
+ echo " [WORKDIR]"
+ echo " [Prefix1]"
+ echo " [Prefix2]"
+ echo " ..."
+ exit 255
+fi
+
+BINDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+CONFIG_PATH="$1"; shift
+WORKDIR="$1"; shift
+
+source "${CONFIG_PATH}"
+
+echo "-- Found TFLITE2CIRCLE: ${TFLITE2CIRCLE_PATH}"
+echo "-- Found workdir: ${WORKDIR}"
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+pushd "${WORKDIR}"
+while [[ $# -ne 0 ]]; do
+ PREFIX="$1"; shift
+
+ TESTED+=("${PREFIX}")
+
+ PASSED_TAG="${BINDIR}/${PREFIX}.passed"
+
+ rm -f "${PASSED_TAG}"
+
+ cat > "${BINDIR}/${PREFIX}.log" <(
+ exec 2>&1
+
+ echo "-- Found tflite: ${PREFIX}.tflite"
+
+ # Exit immediately if any command fails
+ set -e
+ # Show commands
+ set -x
+
+ # Generate circle
+ "${TFLITE2CIRCLE_PATH}" \
+ "${WORKDIR}/${PREFIX}.tflite" \
+ "${BINDIR}/${PREFIX}.circle"
+
+ if [[ $? -eq 0 ]]; then
+ touch "${PASSED_TAG}"
+ fi
+ )
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ PASSED+=("$PREFIX")
+ else
+ FAILED+=("$PREFIX")
+ 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/tflite2circle/CMakeLists.txt b/compiler/tflite2circle/CMakeLists.txt
new file mode 100644
index 000000000..b1d1f6149
--- /dev/null
+++ b/compiler/tflite2circle/CMakeLists.txt
@@ -0,0 +1,19 @@
+nnas_include(TargetRequire)
+
+unset(REQUIRED_TARGETS)
+list(APPEND REQUIRED_TARGETS mio_tflite)
+list(APPEND REQUIRED_TARGETS mio_circle)
+TargetRequire_Return(${REQUIRED_TARGETS})
+
+set(DRIVER "driver/Driver.cpp")
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+add_executable(tflite2circle ${DRIVER} ${SOURCES})
+target_include_directories(tflite2circle PRIVATE include)
+target_include_directories(tflite2circle PRIVATE src)
+target_link_libraries(tflite2circle arser)
+target_link_libraries(tflite2circle safemain)
+target_link_libraries(tflite2circle mio_tflite)
+target_link_libraries(tflite2circle mio_circle)
+target_link_libraries(tflite2circle vconone)
+
+install(TARGETS tflite2circle DESTINATION bin)
diff --git a/compiler/tflite2circle/README.md b/compiler/tflite2circle/README.md
new file mode 100644
index 000000000..f6eba73d3
--- /dev/null
+++ b/compiler/tflite2circle/README.md
@@ -0,0 +1,11 @@
+# tflite2circle
+
+_tflite2circle_ is a Tensorflow Lite to Circle model converter.
+
+## Usage
+
+Provide _tflite_ file input path and _circle_ file output path as a parameter to convert.
+
+```
+$ tflite2circle in.tflite out.circle
+```
diff --git a/compiler/tflite2circle/driver/Driver.cpp b/compiler/tflite2circle/driver/Driver.cpp
new file mode 100644
index 000000000..2f11e0a13
--- /dev/null
+++ b/compiler/tflite2circle/driver/Driver.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <arser/arser.h>
+
+#include "CircleModel.h"
+#include "TFLModel.h"
+
+#include <vconone/vconone.h>
+
+void print_version(void)
+{
+ std::cout << "tflite2circle version " << vconone::get_string() << std::endl;
+ std::cout << vconone::get_copyright() << std::endl;
+}
+
+int entry(int argc, char **argv)
+{
+ arser::Arser arser{"tflite2circle is a Tensorflow lite to circle model converter"};
+
+ arser.add_argument("--version")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("Show version information and exit")
+ .exit_with(print_version);
+
+ arser.add_argument("tflite")
+ .nargs(1)
+ .type(arser::DataType::STR)
+ .help("Source tflite file path to convert");
+ arser.add_argument("circle").nargs(1).type(arser::DataType::STR).help("Target circle file path");
+
+ try
+ {
+ arser.parse(argc, argv);
+ }
+ catch (const std::runtime_error &err)
+ {
+ std::cout << err.what() << std::endl;
+ std::cout << arser;
+ return 255;
+ }
+
+ std::string tfl_path = arser.get<std::string>("tflite");
+ std::string circle_path = arser.get<std::string>("circle");
+ // read tflite file
+ tflite2circle::TFLModel tfl_model(tfl_path);
+ if (!tfl_model.is_valid())
+ {
+ std::cerr << "ERROR: Failed to load tflite '" << tfl_path << "'" << std::endl;
+ return 255;
+ }
+
+ // create flatbuffer builder
+ auto flatbuffer_builder = std::make_unique<flatbuffers::FlatBufferBuilder>(1024);
+
+ // convert tflite to circle
+ tflite2circle::CircleModel circle_model{flatbuffer_builder, tfl_model};
+
+ std::ofstream outfile{circle_path, std::ios::binary};
+
+ outfile.write(circle_model.base(), circle_model.size());
+ outfile.close();
+ // TODO find a better way of error handling
+ if (outfile.fail())
+ {
+ std::cerr << "ERROR: Failed to write circle '" << circle_path << "'" << std::endl;
+ return 255;
+ }
+
+ return 0;
+}
diff --git a/compiler/tflite2circle/include/CircleModel.h b/compiler/tflite2circle/include/CircleModel.h
new file mode 100644
index 000000000..e1e35d8ff
--- /dev/null
+++ b/compiler/tflite2circle/include/CircleModel.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CIRCLE_MODEL_H__
+#define __CIRCLE_MODEL_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "TFLModel.h"
+
+namespace tflite2circle
+{
+
+using FlatBufBuilder = std::unique_ptr<flatbuffers::FlatBufferBuilder>;
+
+struct OperatorCodeLink
+{
+ using TFL = flatbuffers::Offset<::tflite::OperatorCode>;
+ using CIR = flatbuffers::Offset<::circle::OperatorCode>;
+};
+
+struct SubGraphLink
+{
+ using TFL = flatbuffers::Offset<::tflite::SubGraph>;
+ using CIR = flatbuffers::Offset<::circle::SubGraph>;
+};
+
+struct BufferLink
+{
+ using TFL = flatbuffers::Offset<::tflite::Buffer>;
+ using CIR = flatbuffers::Offset<::circle::Buffer>;
+};
+
+struct MetaDataBufferLink
+{
+ using TFL = int32_t;
+ using CIR = int32_t;
+};
+
+template <typename T> class Offset
+{
+private:
+ using TFLFlatBufVec = flatbuffers::Vector<typename T::TFL>;
+ using CIRFlatBufVecOffset = flatbuffers::Offset<flatbuffers::Vector<typename T::CIR>>;
+
+public:
+ Offset(void) = delete;
+ Offset(FlatBufBuilder &fb, const TFLFlatBufVec *tflite_flatbuffer_vec);
+
+public:
+ CIRFlatBufVecOffset offset(void) const { return _circle_flatbuffer_vec_offset; }
+
+private:
+ CIRFlatBufVecOffset _circle_flatbuffer_vec_offset;
+};
+
+class CircleModel
+{
+private:
+ using Description = flatbuffers::Offset<flatbuffers::String>;
+
+public:
+ CircleModel(void) = delete;
+ CircleModel(FlatBufBuilder &fb, TFLModel &tfl_model);
+
+public:
+ void model_build(void) const;
+ const char *base(void) const;
+ size_t size(void) const;
+
+private:
+ uint32_t _version;
+ Description _description;
+ FlatBufBuilder &_fb;
+ std::unique_ptr<Offset<OperatorCodeLink>> _operator_codes_offset;
+ std::unique_ptr<Offset<SubGraphLink>> _subGraphs_offset;
+ std::unique_ptr<Offset<BufferLink>> _buffers_offset;
+ std::unique_ptr<Offset<MetaDataBufferLink>> _metadata_buffer_offset;
+};
+
+} // namespace tflite2circle
+
+#endif // __CIRCLE_MODEL_H__
diff --git a/compiler/tflite2circle/include/TFLModel.h b/compiler/tflite2circle/include/TFLModel.h
new file mode 100644
index 000000000..e53d62749
--- /dev/null
+++ b/compiler/tflite2circle/include/TFLModel.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFL_MODEL_H__
+#define __TFL_MODEL_H__
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include <mio/tflite/schema_generated.h>
+
+namespace tflite2circle
+{
+
+class TFLModel
+{
+private:
+ using DataBuffer = std::vector<char>;
+
+public:
+ TFLModel(void) = delete;
+ TFLModel(const std::string &path);
+
+public:
+ bool is_valid(void) { return _valid; }
+
+private:
+ const tflite::Model *load_model(void);
+
+private:
+ std::ifstream _infile;
+ DataBuffer _data;
+ bool _valid;
+
+ friend class CircleModel;
+};
+
+} // namespace tflite2circle
+
+#endif // __TFL_MODEL_H__
diff --git a/compiler/tflite2circle/requires.cmake b/compiler/tflite2circle/requires.cmake
new file mode 100644
index 000000000..837c287b6
--- /dev/null
+++ b/compiler/tflite2circle/requires.cmake
@@ -0,0 +1,5 @@
+require("arser")
+require("mio-tflite")
+require("mio-circle")
+require("safemain")
+require("vconone")
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions.h
new file mode 100644
index 000000000..56a16d4e0
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BUILD_BUITIN_OPTIONS_H__
+#define __BUILD_BUITIN_OPTIONS_H__
+
+// NOTE please add new option headers in alphabetical order
+
+#include "BuildBuiltinOptions/AbsOptions.h"
+#include "BuildBuiltinOptions/AddOptions.h"
+#include "BuildBuiltinOptions/AddNOptions.h"
+#include "BuildBuiltinOptions/ArgMaxOptions.h"
+#include "BuildBuiltinOptions/ArgMinOptions.h"
+#include "BuildBuiltinOptions/BatchMatMulOptions.h"
+#include "BuildBuiltinOptions/BatchToSpaceNDOptions.h"
+#include "BuildBuiltinOptions/CastOptions.h"
+#include "BuildBuiltinOptions/ConcatenationOptions.h"
+#include "BuildBuiltinOptions/Conv2DOptions.h"
+#include "BuildBuiltinOptions/CosOptions.h"
+#include "BuildBuiltinOptions/DepthToSpaceOptions.h"
+#include "BuildBuiltinOptions/DepthwiseConv2DOptions.h"
+#include "BuildBuiltinOptions/DivOptions.h"
+#include "BuildBuiltinOptions/EqualOptions.h"
+#include "BuildBuiltinOptions/ExpandDimsOptions.h"
+#include "BuildBuiltinOptions/ExpOptions.h"
+#include "BuildBuiltinOptions/FillOptions.h"
+#include "BuildBuiltinOptions/FloorDivOptions.h"
+#include "BuildBuiltinOptions/FloorModOptions.h"
+#include "BuildBuiltinOptions/FullyConnectedOptions.h"
+#include "BuildBuiltinOptions/GatherOptions.h"
+#include "BuildBuiltinOptions/GatherNdOptions.h"
+#include "BuildBuiltinOptions/GreaterOptions.h"
+#include "BuildBuiltinOptions/GreaterEqualOptions.h"
+#include "BuildBuiltinOptions/IfOptions.h"
+#include "BuildBuiltinOptions/L2NormalizeOptions.h"
+// L2Pool2D uses Pool2DOptions
+#include "BuildBuiltinOptions/LeakyReluOptions.h"
+#include "BuildBuiltinOptions/LessOptions.h"
+#include "BuildBuiltinOptions/LessEqualOptions.h"
+#include "BuildBuiltinOptions/LocalResponseNormalizationOptions.h"
+#include "BuildBuiltinOptions/LogicalAndOptions.h"
+#include "BuildBuiltinOptions/LogicalNotOptions.h"
+#include "BuildBuiltinOptions/LogicalOrOptions.h"
+// There is no LogisticOptions
+#include "BuildBuiltinOptions/LogSoftmaxOptions.h"
+#include "BuildBuiltinOptions/MatrixDiagOptions.h"
+#include "BuildBuiltinOptions/MatrixSetDiagOptions.h"
+#include "BuildBuiltinOptions/MaximumMinimumOptions.h"
+#include "BuildBuiltinOptions/MirrorPadOptions.h"
+#include "BuildBuiltinOptions/MulOptions.h"
+#include "BuildBuiltinOptions/NegOptions.h"
+#include "BuildBuiltinOptions/NonMaxSuppressionV4Options.h"
+#include "BuildBuiltinOptions/NonMaxSuppressionV5Options.h"
+#include "BuildBuiltinOptions/NotEqualOptions.h"
+#include "BuildBuiltinOptions/OneHotOptions.h"
+#include "BuildBuiltinOptions/PackOptions.h"
+#include "BuildBuiltinOptions/PadOptions.h"
+#include "BuildBuiltinOptions/PadV2Options.h"
+#include "BuildBuiltinOptions/RangeOptions.h"
+#include "BuildBuiltinOptions/Pool2DOptions.h"
+#include "BuildBuiltinOptions/PowOptions.h"
+#include "BuildBuiltinOptions/RankOptions.h"
+// There is no PReluOptions
+#include "BuildBuiltinOptions/ReducerOptions.h"
+#include "BuildBuiltinOptions/ReshapeOptions.h"
+#include "BuildBuiltinOptions/ResizeBilinearOptions.h"
+#include "BuildBuiltinOptions/ResizeNearestNeighborOptions.h"
+#include "BuildBuiltinOptions/ReverseSequenceOptions.h"
+#include "BuildBuiltinOptions/ReverseV2Options.h"
+// There is no RoundOptions
+// There is no RsqrtOptions
+#include "BuildBuiltinOptions/ScatterNdOptions.h"
+#include "BuildBuiltinOptions/SegmentSumOptions.h"
+#include "BuildBuiltinOptions/SelectOptions.h"
+#include "BuildBuiltinOptions/SelectV2Options.h"
+#include "BuildBuiltinOptions/ShapeOptions.h"
+// There is no SinOptions
+#include "BuildBuiltinOptions/SliceOptions.h"
+#include "BuildBuiltinOptions/SoftmaxOptions.h"
+#include "BuildBuiltinOptions/SpaceToBatchNDOptions.h"
+#include "BuildBuiltinOptions/SpaceToDepthOptions.h"
+#include "BuildBuiltinOptions/SparseToDenseOptions.h"
+#include "BuildBuiltinOptions/SplitOptions.h"
+#include "BuildBuiltinOptions/SplitVOptions.h"
+#include "BuildBuiltinOptions/SquaredDifferenceOptions.h"
+#include "BuildBuiltinOptions/SquareOptions.h"
+#include "BuildBuiltinOptions/SqueezeOptions.h"
+#include "BuildBuiltinOptions/StridedSliceOptions.h"
+#include "BuildBuiltinOptions/SubOptions.h"
+#include "BuildBuiltinOptions/TileOptions.h"
+#include "BuildBuiltinOptions/TopKV2Options.h"
+#include "BuildBuiltinOptions/TransposeOptions.h"
+#include "BuildBuiltinOptions/TransposeConvOptions.h"
+#include "BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.h"
+#include "BuildBuiltinOptions/UniqueOptions.h"
+#include "BuildBuiltinOptions/UnpackOptions.h"
+#include "BuildBuiltinOptions/WhereOptions.h"
+#include "BuildBuiltinOptions/WhileOptions.h"
+#include "BuildBuiltinOptions/ZerosLikeOptions.h"
+
+#endif // __BUILD_BUITIN_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/AbsOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/AbsOptions.cpp
new file mode 100644
index 000000000..70aa06d3e
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/AbsOptions.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AbsOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::AbsOptions> build_circle_AbsOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *)
+{
+ circle::AbsOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/AbsOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/AbsOptions.h
new file mode 100644
index 000000000..a118eb52b
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/AbsOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_ABS_OPTIONS_H__
+#define __BBO_ABS_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::AbsOptions> build_circle_AbsOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_ABS_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/AddNOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/AddNOptions.cpp
new file mode 100644
index 000000000..df8b083d6
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/AddNOptions.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AddNOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::AddNOptions>
+build_circle_AddNOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::AddNOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/AddNOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/AddNOptions.h
new file mode 100644
index 000000000..9e18e8aaf
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/AddNOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_ADD_N_OPTIONS_H__
+#define __BBO_ADD_N_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::AddNOptions>
+build_circle_AddNOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_ADD_N_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/AddOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/AddOptions.cpp
new file mode 100644
index 000000000..f93a0f21f
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/AddOptions.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AddOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::AddOptions> build_circle_AddOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_AddOptions();
+ assert(tflite_builtin_options);
+ circle::AddOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_fused_activation_function(
+ get_circle_activation_function_type(tflite_builtin_options->fused_activation_function()));
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/AddOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/AddOptions.h
new file mode 100644
index 000000000..bbfa03f0d
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/AddOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_ADD_OPTIONS_H__
+#define __BBO_ADD_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::AddOptions> build_circle_AddOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_ADD_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMaxOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMaxOptions.cpp
new file mode 100644
index 000000000..0ccdde4cb
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMaxOptions.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ArgMaxOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ArgMaxOptions>
+build_circle_ArgMaxOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_ArgMaxOptions();
+ assert(tflite_builtin_options);
+ circle::ArgMaxOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_output_type(
+ get_circle_tensortype(tflite_builtin_options->output_type()));
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMaxOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMaxOptions.h
new file mode 100644
index 000000000..2c8476252
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMaxOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_ARGMAX_OPTIONS_H__
+#define __BBO_ARGMAX_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ArgMaxOptions>
+build_circle_ArgMaxOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_ARGMAX_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMinOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMinOptions.cpp
new file mode 100644
index 000000000..204558df8
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMinOptions.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ArgMinOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ArgMinOptions>
+build_circle_ArgMinOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_ArgMinOptions();
+ assert(tflite_builtin_options);
+ circle::ArgMinOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_output_type(
+ get_circle_tensortype(tflite_builtin_options->output_type()));
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMinOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMinOptions.h
new file mode 100644
index 000000000..76cbc39a4
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMinOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_ARGMIN_OPTIONS_H__
+#define __BBO_ARGMIN_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ArgMinOptions>
+build_circle_ArgMinOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_ARGMIN_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/BatchMatMulOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/BatchMatMulOptions.cpp
new file mode 100644
index 000000000..ac0a094d2
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/BatchMatMulOptions.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BatchMatMulOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::BatchMatMulOptions>
+build_circle_BatchMatMulOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_BatchMatMulOptions();
+ assert(tflite_builtin_options);
+ circle::BatchMatMulOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_adjoint_lhs(tflite_builtin_options->adj_x());
+ builtin_options_builder.add_adjoint_rhs(tflite_builtin_options->adj_y());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/BatchMatMulOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/BatchMatMulOptions.h
new file mode 100644
index 000000000..b1e90aa5a
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/BatchMatMulOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_BATCH_MATMUL_OPTIONS_H__
+#define __BBO_BATCH_MATMUL_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::BatchMatMulOptions>
+build_circle_BatchMatMulOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_BATCH_MATMUL_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/BatchToSpaceNDOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/BatchToSpaceNDOptions.cpp
new file mode 100644
index 000000000..29799de70
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/BatchToSpaceNDOptions.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BatchToSpaceNDOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::BatchToSpaceNDOptions>
+build_circle_BatchToSpaceNDOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *)
+{
+ circle::BatchToSpaceNDOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/BatchToSpaceNDOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/BatchToSpaceNDOptions.h
new file mode 100644
index 000000000..ca720f03c
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/BatchToSpaceNDOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_BATCH_TO_SPACE_ND_OPTIONS_H__
+#define __BBO_BATCH_TO_SPACE_ND_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::BatchToSpaceNDOptions>
+build_circle_BatchToSpaceNDOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_BATCH_TO_SPACE_ND_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/CastOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/CastOptions.cpp
new file mode 100644
index 000000000..bc1445248
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/CastOptions.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CastOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::CastOptions>
+build_circle_CastOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_CastOptions();
+ if (tflite_builtin_options == nullptr)
+ return 0;
+
+ circle::CastOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_in_data_type(
+ get_circle_tensortype(tflite_builtin_options->in_data_type()));
+ builtin_options_builder.add_out_data_type(
+ get_circle_tensortype(tflite_builtin_options->out_data_type()));
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/CastOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/CastOptions.h
new file mode 100644
index 000000000..a8cc5441b
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/CastOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_CAST_OPTIONS_H__
+#define __BBO_CAST_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::CastOptions>
+build_circle_CastOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_CAST_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ConcatenationOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/ConcatenationOptions.cpp
new file mode 100644
index 000000000..933e7cf66
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ConcatenationOptions.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ConcatenationOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ConcatenationOptions>
+build_circle_ConcatenationOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_ConcatenationOptions();
+ assert(tflite_builtin_options);
+ circle::ConcatenationOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_axis(tflite_builtin_options->axis());
+ builtin_options_builder.add_fused_activation_function(
+ get_circle_activation_function_type(tflite_builtin_options->fused_activation_function()));
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ConcatenationOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/ConcatenationOptions.h
new file mode 100644
index 000000000..004e6433c
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ConcatenationOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_CONCATENATION_OPTIONS_H__
+#define __BBO_CONCATENATION_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ConcatenationOptions>
+build_circle_ConcatenationOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_CONCATENATION_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/Conv2DOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/Conv2DOptions.cpp
new file mode 100644
index 000000000..ace63dd26
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/Conv2DOptions.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Conv2DOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::Conv2DOptions>
+build_circle_Conv2DOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_Conv2DOptions();
+ assert(tflite_builtin_options);
+ circle::Conv2DOptionsBuilder builtin_options_builder{fb};
+ 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()));
+ builtin_options_builder.add_dilation_w_factor(tflite_builtin_options->dilation_w_factor());
+ builtin_options_builder.add_dilation_h_factor(tflite_builtin_options->dilation_h_factor());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/Conv2DOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/Conv2DOptions.h
new file mode 100644
index 000000000..1f2bdc169
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/Conv2DOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_CONV2D_OPTIONS_H__
+#define __BBO_CONV2D_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::Conv2DOptions>
+build_circle_Conv2DOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_CONV2D_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/CosOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/CosOptions.cpp
new file mode 100644
index 000000000..9f6ebf16b
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/CosOptions.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CosOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::CosOptions> build_circle_CosOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op)
+{
+ circle::CosOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/CosOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/CosOptions.h
new file mode 100644
index 000000000..bc15573b9
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/CosOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_COS_OPTIONS_H__
+#define __BBO_COS_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::CosOptions> build_circle_CosOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_COS_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/DepthToSpaceOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/DepthToSpaceOptions.cpp
new file mode 100644
index 000000000..669aadb57
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/DepthToSpaceOptions.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "DepthToSpaceOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::DepthToSpaceOptions>
+build_circle_DepthToSpaceOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_DepthToSpaceOptions();
+ assert(tflite_builtin_options);
+ circle::DepthToSpaceOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_block_size(tflite_builtin_options->block_size());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/DepthToSpaceOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/DepthToSpaceOptions.h
new file mode 100644
index 000000000..47c09a8aa
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/DepthToSpaceOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_DEPTH_TO_SPACE_OPTIONS_H__
+#define __BBO_DEPTH_TO_SPACE_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::DepthToSpaceOptions>
+build_circle_DepthToSpaceOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_DEPTH_TO_SPACE_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/DepthwiseConv2DOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/DepthwiseConv2DOptions.cpp
new file mode 100644
index 000000000..2aa35abc6
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/DepthwiseConv2DOptions.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "DepthwiseConv2DOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::DepthwiseConv2DOptions>
+build_circle_DepthwiseConv2DOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_DepthwiseConv2DOptions();
+ assert(tflite_builtin_options);
+ circle::DepthwiseConv2DOptionsBuilder builtin_options_builder{fb};
+ 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_depth_multiplier(tflite_builtin_options->depth_multiplier());
+ builtin_options_builder.add_fused_activation_function(
+ get_circle_activation_function_type(tflite_builtin_options->fused_activation_function()));
+ builtin_options_builder.add_dilation_w_factor(tflite_builtin_options->dilation_w_factor());
+ builtin_options_builder.add_dilation_h_factor(tflite_builtin_options->dilation_h_factor());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/DepthwiseConv2DOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/DepthwiseConv2DOptions.h
new file mode 100644
index 000000000..2d92993df
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/DepthwiseConv2DOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_DEPTHWISECONV2D_OPTIONS_H__
+#define __BBO_DEPTHWISECONV2D_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::DepthwiseConv2DOptions>
+build_circle_DepthwiseConv2DOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_DEPTHWISECONV2D_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/DivOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/DivOptions.cpp
new file mode 100644
index 000000000..4272fe144
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/DivOptions.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DivOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::DivOptions> build_circle_DivOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_DivOptions();
+ assert(tflite_builtin_options);
+ circle::DivOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_fused_activation_function(
+ get_circle_activation_function_type(tflite_builtin_options->fused_activation_function()));
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/DivOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/DivOptions.h
new file mode 100644
index 000000000..2b2bc57dc
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/DivOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_DIV_OPTIONS_H__
+#define __BBO_DIV_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::DivOptions> build_circle_DivOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_DIV_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/EqualOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/EqualOptions.cpp
new file mode 100644
index 000000000..cc2efa4fa
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/EqualOptions.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "EqualOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::EqualOptions>
+build_circle_EqualOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::EqualOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/EqualOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/EqualOptions.h
new file mode 100644
index 000000000..8572157f2
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/EqualOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_EQUAL_OPTIONS_H__
+#define __BBO_EQUAL_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::EqualOptions>
+build_circle_EqualOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_EQUAL_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ExpOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/ExpOptions.cpp
new file mode 100644
index 000000000..20a64c714
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ExpOptions.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ExpOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ExpOptions> build_circle_ExpOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *)
+{
+ circle::ExpOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ExpOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/ExpOptions.h
new file mode 100644
index 000000000..2279083fb
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ExpOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_EXP_OPTIONS_H__
+#define __BBO_EXP_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ExpOptions> build_circle_ExpOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_EXP_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ExpandDimsOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/ExpandDimsOptions.cpp
new file mode 100644
index 000000000..ee2af2c09
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ExpandDimsOptions.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "ExpandDimsOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ExpandDimsOptions>
+build_circle_ExpandDimsOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::ExpandDimsOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ExpandDimsOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/ExpandDimsOptions.h
new file mode 100644
index 000000000..814e50374
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ExpandDimsOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_EXPANDDIMS_OPTIONS_H__
+#define __BBO_EXPANDDIMS_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ExpandDimsOptions>
+build_circle_ExpandDimsOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_EXPANDDIMS_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/FillOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/FillOptions.cpp
new file mode 100644
index 000000000..919c57cdc
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/FillOptions.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "FillOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::FillOptions>
+build_circle_FillOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::FillOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/FillOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/FillOptions.h
new file mode 100644
index 000000000..81eb5e3a2
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/FillOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_FILL_OPTIONS_H__
+#define __BBO_FILL_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::FillOptions>
+build_circle_FillOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_FILL_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/FloorDivOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/FloorDivOptions.cpp
new file mode 100644
index 000000000..aa08cfdca
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/FloorDivOptions.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "FloorDivOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::FloorDivOptions>
+build_circle_FloorDivOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *)
+{
+ circle::FloorDivOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/FloorDivOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/FloorDivOptions.h
new file mode 100644
index 000000000..0f65591f4
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/FloorDivOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_FLOOR_DIV_OPTIONS_H__
+#define __BBO_FLOOR_DIV_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::FloorDivOptions>
+build_circle_FloorDivOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_FLOOR_DIV_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/FloorModOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/FloorModOptions.cpp
new file mode 100644
index 000000000..770351e8a
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/FloorModOptions.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "FloorModOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::FloorModOptions>
+build_circle_FloorModOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::FloorModOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/FloorModOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/FloorModOptions.h
new file mode 100644
index 000000000..e53d5f7e7
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/FloorModOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_FLOOR_MOD_OPTIONS_H__
+#define __BBO_FLOOR_MOD_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::FloorModOptions>
+build_circle_FloorModOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_FLOOR_MOD_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/FullyConnectedOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/FullyConnectedOptions.cpp
new file mode 100644
index 000000000..098a96a40
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/FullyConnectedOptions.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FullyConnectedOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::FullyConnectedOptions>
+build_circle_FullyConnectedOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_FullyConnectedOptions();
+ assert(tflite_builtin_options);
+ circle::FullyConnectedOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_fused_activation_function(
+ get_circle_activation_function_type(tflite_builtin_options->fused_activation_function()));
+ // Get FullyConnectedOptionsWeightsFormat
+ auto tflite_weight_format = tflite_builtin_options->weights_format();
+ if (tflite_weight_format == tflite::FullyConnectedOptionsWeightsFormat_DEFAULT)
+ builtin_options_builder.add_weights_format(circle::FullyConnectedOptionsWeightsFormat_DEFAULT);
+ else if (tflite_weight_format == tflite::FullyConnectedOptionsWeightsFormat_SHUFFLED4x16INT8)
+ builtin_options_builder.add_weights_format(
+ circle::FullyConnectedOptionsWeightsFormat_SHUFFLED4x16INT8);
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/FullyConnectedOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/FullyConnectedOptions.h
new file mode 100644
index 000000000..8f4ac43cf
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/FullyConnectedOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_FULLYCONNECTED_OPTIONS_H__
+#define __BBO_FULLYCONNECTED_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::FullyConnectedOptions>
+build_circle_FullyConnectedOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_FULLYCONNECTED_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/GatherNdOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/GatherNdOptions.cpp
new file mode 100644
index 000000000..487cce1f1
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/GatherNdOptions.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GatherNdOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::GatherNdOptions>
+build_circle_GatherNdOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *)
+{
+ circle::GatherNdOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/GatherNdOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/GatherNdOptions.h
new file mode 100644
index 000000000..55e4f7b34
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/GatherNdOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_GATHER_ND_OPTIONS_H__
+#define __BBO_GATHER_ND_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::GatherNdOptions>
+build_circle_GatherNdOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_GATHER_ND_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/GatherOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/GatherOptions.cpp
new file mode 100644
index 000000000..ecd5dc1e4
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/GatherOptions.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "GatherOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::GatherOptions>
+build_circle_GatherOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_GatherOptions();
+ assert(tflite_builtin_options);
+ circle::GatherOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_axis(tflite_builtin_options->axis());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/GatherOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/GatherOptions.h
new file mode 100644
index 000000000..300a7f72e
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/GatherOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_GATHER_OPTIONS_H__
+#define __BBO_GATHER_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::GatherOptions>
+build_circle_GatherOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_GATHER_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/GreaterEqualOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/GreaterEqualOptions.cpp
new file mode 100644
index 000000000..6c890e579
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/GreaterEqualOptions.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "GreaterEqualOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::GreaterEqualOptions>
+build_circle_GreaterEqualOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::GreaterEqualOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/GreaterEqualOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/GreaterEqualOptions.h
new file mode 100644
index 000000000..bddb55371
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/GreaterEqualOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_GREATEREQUAL_OPTIONS_H__
+#define __BBO_GREATEREQUAL_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::GreaterEqualOptions>
+build_circle_GreaterEqualOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_GREATEREQUAL_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/GreaterOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/GreaterOptions.cpp
new file mode 100644
index 000000000..14ca0cdcd
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/GreaterOptions.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "GreaterOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::GreaterOptions>
+build_circle_GreaterOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::GreaterOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/GreaterOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/GreaterOptions.h
new file mode 100644
index 000000000..46e4d9957
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/GreaterOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_GREATER_OPTIONS_H__
+#define __BBO_GREATER_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::GreaterOptions>
+build_circle_GreaterOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_GREATER_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/IfOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/IfOptions.cpp
new file mode 100644
index 000000000..cc5be4901
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/IfOptions.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "IfOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::IfOptions> build_circle_IfOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_IfOptions();
+ assert(tflite_builtin_options);
+ circle::IfOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_then_subgraph_index(tflite_builtin_options->then_subgraph_index());
+ builtin_options_builder.add_else_subgraph_index(tflite_builtin_options->else_subgraph_index());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/IfOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/IfOptions.h
new file mode 100644
index 000000000..6483d7bd6
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/IfOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_IF_OPTIONS_H__
+#define __BBO_IF_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::IfOptions> build_circle_IfOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_IF_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/L2NormalizeOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/L2NormalizeOptions.cpp
new file mode 100644
index 000000000..d58aed83d
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/L2NormalizeOptions.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "L2NormalizeOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::L2NormOptions>
+build_circle_L2NormOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_L2NormOptions();
+ assert(tflite_builtin_options);
+ circle::L2NormOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_fused_activation_function(
+ get_circle_activation_function_type(tflite_builtin_options->fused_activation_function()));
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/L2NormalizeOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/L2NormalizeOptions.h
new file mode 100644
index 000000000..5568c4e08
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/L2NormalizeOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_L2NORMALIZE_OPTIONS_H__
+#define __BBO_L2NORMALIZE_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::L2NormOptions>
+build_circle_L2NormOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_L2NORMALIZE_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/LeakyReluOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/LeakyReluOptions.cpp
new file mode 100644
index 000000000..793b1d7df
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/LeakyReluOptions.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LeakyReluOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::LeakyReluOptions>
+build_circle_LeakyReluOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto *tflite_builtin_options = op->builtin_options_as_LeakyReluOptions();
+ assert(tflite_builtin_options);
+ circle::LeakyReluOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_alpha(tflite_builtin_options->alpha());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/LeakyReluOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/LeakyReluOptions.h
new file mode 100644
index 000000000..a2168f29f
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/LeakyReluOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_LEAKY_RELU_OPTIONS_H__
+#define __BBO_LEAKY_RELU_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::LeakyReluOptions>
+build_circle_LeakyReluOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_LEAKY_RELU_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/LessEqualOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/LessEqualOptions.cpp
new file mode 100644
index 000000000..09a77535e
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/LessEqualOptions.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "LessEqualOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::LessEqualOptions>
+build_circle_LessEqualOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::LessEqualOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/LessEqualOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/LessEqualOptions.h
new file mode 100644
index 000000000..3477c026d
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/LessEqualOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_LESSEQUAL_OPTIONS_H__
+#define __BBO_LESSEQUAL_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::LessEqualOptions>
+build_circle_LessEqualOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_LESSEQUAL_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/LessOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/LessOptions.cpp
new file mode 100644
index 000000000..0009dd6f4
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/LessOptions.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "LessOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::LessOptions>
+build_circle_LessOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::LessOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/LessOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/LessOptions.h
new file mode 100644
index 000000000..932809abd
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/LessOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_LESS_OPTIONS_H__
+#define __BBO_LESS_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::LessOptions>
+build_circle_LessOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_LESS_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/LocalResponseNormalizationOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/LocalResponseNormalizationOptions.cpp
new file mode 100644
index 000000000..342f6a891
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/LocalResponseNormalizationOptions.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LocalResponseNormalizationOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::LocalResponseNormalizationOptions>
+build_circle_LocalResponseNormalizationOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_LocalResponseNormalizationOptions();
+ assert(tflite_builtin_options);
+ circle::LocalResponseNormalizationOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_radius(tflite_builtin_options->radius());
+ builtin_options_builder.add_bias(tflite_builtin_options->bias());
+ builtin_options_builder.add_alpha(tflite_builtin_options->alpha());
+ builtin_options_builder.add_beta(tflite_builtin_options->beta());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/LocalResponseNormalizationOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/LocalResponseNormalizationOptions.h
new file mode 100644
index 000000000..0b43fee94
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/LocalResponseNormalizationOptions.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_LOCAL_RESPONSE_NORMALIZATION_OPTIONS_H__
+#define __BBO_LOCAL_RESPONSE_NORMALIZATION_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::LocalResponseNormalizationOptions>
+build_circle_LocalResponseNormalizationOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_LOCAL_RESPONSE_NORMALIZATION_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/LogSoftmaxOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/LogSoftmaxOptions.cpp
new file mode 100644
index 000000000..271028838
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/LogSoftmaxOptions.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LogSoftmaxOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::LogSoftmaxOptions>
+build_circle_LogSoftmaxOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::LogSoftmaxOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/LogSoftmaxOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/LogSoftmaxOptions.h
new file mode 100644
index 000000000..920d1bd60
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/LogSoftmaxOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_LOG_SOFTMAX_OPTIONS_H__
+#define __BBO_LOG_SOFTMAX_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::LogSoftmaxOptions>
+build_circle_LogSoftmaxOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_LOG_SOFTMAX_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/LogicalAndOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/LogicalAndOptions.cpp
new file mode 100644
index 000000000..52bfe0be9
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/LogicalAndOptions.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LogicalAndOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::LogicalAndOptions>
+build_circle_LogicalAndOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::LogicalAndOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/LogicalAndOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/LogicalAndOptions.h
new file mode 100644
index 000000000..90243d8b8
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/LogicalAndOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_LOGICALAND_OPTIONS_H__
+#define __BBO_LOGICALAND_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::LogicalAndOptions>
+build_circle_LogicalAndOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_LOGICALAND_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/LogicalNotOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/LogicalNotOptions.cpp
new file mode 100644
index 000000000..fc17fd5b1
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/LogicalNotOptions.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LogicalNotOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::LogicalNotOptions>
+build_circle_LogicalNotOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::LogicalNotOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/LogicalNotOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/LogicalNotOptions.h
new file mode 100644
index 000000000..da830b781
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/LogicalNotOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_LOGICALNOT_OPTIONS_H__
+#define __BBO_LOGICALNOT_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::LogicalNotOptions>
+build_circle_LogicalNotOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_LOGICALNOT_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/LogicalOrOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/LogicalOrOptions.cpp
new file mode 100644
index 000000000..6c3a6b3a6
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/LogicalOrOptions.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LogicalOrOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::LogicalOrOptions>
+build_circle_LogicalOrOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::LogicalOrOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/LogicalOrOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/LogicalOrOptions.h
new file mode 100644
index 000000000..59ba914c2
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/LogicalOrOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_LOGICALOR_OPTIONS_H__
+#define __BBO_LOGICALOR_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::LogicalOrOptions>
+build_circle_LogicalOrOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_LOGICALOR_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/MatrixDiagOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/MatrixDiagOptions.cpp
new file mode 100644
index 000000000..87a550451
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/MatrixDiagOptions.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "MatrixDiagOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::MatrixDiagOptions>
+build_circle_MatrixDiagOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::MatrixDiagOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/MatrixDiagOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/MatrixDiagOptions.h
new file mode 100644
index 000000000..0fddaee40
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/MatrixDiagOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_MATRIX_DIAG_OPTIONS_H__
+#define __BBO_MATRIX_DIAG_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::MatrixDiagOptions>
+build_circle_MatrixDiagOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_MATRIX_DIAG_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/MatrixSetDiagOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/MatrixSetDiagOptions.cpp
new file mode 100644
index 000000000..c2e6890e6
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/MatrixSetDiagOptions.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "MatrixSetDiagOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::MatrixSetDiagOptions>
+build_circle_MatrixSetDiagOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::MatrixSetDiagOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/MatrixSetDiagOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/MatrixSetDiagOptions.h
new file mode 100644
index 000000000..2da7426e4
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/MatrixSetDiagOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_MATRIX_SET_DIAG_OPTIONS_H__
+#define __BBO_MATRIX_SET_DIAG_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::MatrixSetDiagOptions>
+build_circle_MatrixSetDiagOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_MATRIX_SET_DIAG_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/MaximumMinimumOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/MaximumMinimumOptions.cpp
new file mode 100644
index 000000000..d2d2888f2
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/MaximumMinimumOptions.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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 "MaximumMinimumOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::MaximumMinimumOptions>
+build_circle_MaximumMinimumOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_MaximumMinimumOptions();
+ assert(tflite_builtin_options);
+ circle::MaximumMinimumOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/MaximumMinimumOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/MaximumMinimumOptions.h
new file mode 100644
index 000000000..fdaa7b8af
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/MaximumMinimumOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_MAXIMUMMINIMUM_OPTIONS_H__
+#define __BBO_MAXIMUMMINIMUM_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::MaximumMinimumOptions>
+build_circle_MaximumMinimumOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_MAXIMUMMINIMUM_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/MirrorPadOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/MirrorPadOptions.cpp
new file mode 100644
index 000000000..5a0f7aa3e
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/MirrorPadOptions.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "PadOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::MirrorPadOptions>
+build_circle_MirrorPadOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_MirrorPadOptions();
+ assert(tflite_builtin_options);
+ circle::MirrorPadOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_mode(get_circle_mirrorpad_mode(tflite_builtin_options->mode()));
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/MirrorPadOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/MirrorPadOptions.h
new file mode 100644
index 000000000..6459dc3eb
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/MirrorPadOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_MIRROR_PAD_OPTIONS_H__
+#define __BBO_MIRROR_PAD_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::MirrorPadOptions>
+build_circle_MirrorPadOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_MIRROR_PAD_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/MulOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/MulOptions.cpp
new file mode 100644
index 000000000..009daea8b
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/MulOptions.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AddOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::MulOptions> build_circle_MulOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_MulOptions();
+ assert(tflite_builtin_options);
+ circle::MulOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_fused_activation_function(
+ get_circle_activation_function_type(tflite_builtin_options->fused_activation_function()));
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/MulOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/MulOptions.h
new file mode 100644
index 000000000..18f1e4967
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/MulOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_MUL_OPTIONS_H__
+#define __BBO_MUL_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::MulOptions> build_circle_MulOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_MUL_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/NegOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/NegOptions.cpp
new file mode 100644
index 000000000..9bc62bba0
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/NegOptions.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NegOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::NegOptions> build_circle_NegOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *)
+{
+ circle::NegOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/NegOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/NegOptions.h
new file mode 100644
index 000000000..8cdba0dea
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/NegOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_NEG_OPTIONS_H__
+#define __BBO_NEG_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::NegOptions> build_circle_NegOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_NEG_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/NonMaxSuppressionV4Options.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/NonMaxSuppressionV4Options.cpp
new file mode 100644
index 000000000..1a39f503b
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/NonMaxSuppressionV4Options.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 "NonMaxSuppressionV4Options.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::NonMaxSuppressionV4Options>
+build_circle_NonMaxSuppressionV4Options(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *)
+{
+ circle::NonMaxSuppressionV4OptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/NonMaxSuppressionV4Options.h b/compiler/tflite2circle/src/BuildBuiltinOptions/NonMaxSuppressionV4Options.h
new file mode 100644
index 000000000..6073142a8
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/NonMaxSuppressionV4Options.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_NON_MAX_SUPPRESSION_V4_OPTIONS_H__
+#define __BBO_NON_MAX_SUPPRESSION_V4_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::NonMaxSuppressionV4Options>
+build_circle_NonMaxSuppressionV4Options(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_NON_MAX_SUPPRESSION_V4_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/NonMaxSuppressionV5Options.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/NonMaxSuppressionV5Options.cpp
new file mode 100644
index 000000000..637c544ff
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/NonMaxSuppressionV5Options.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 "NonMaxSuppressionV5Options.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::NonMaxSuppressionV5Options>
+build_circle_NonMaxSuppressionV5Options(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *)
+{
+ circle::NonMaxSuppressionV5OptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/NonMaxSuppressionV5Options.h b/compiler/tflite2circle/src/BuildBuiltinOptions/NonMaxSuppressionV5Options.h
new file mode 100644
index 000000000..faf989acc
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/NonMaxSuppressionV5Options.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_NON_MAX_SUPPRESSION_V5_OPTIONS_H__
+#define __BBO_NON_MAX_SUPPRESSION_V5_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::NonMaxSuppressionV5Options>
+build_circle_NonMaxSuppressionV5Options(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_NON_MAX_SUPPRESSION_V5_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/NotEqualOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/NotEqualOptions.cpp
new file mode 100644
index 000000000..8ed3bb0bb
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/NotEqualOptions.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "NotEqualOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::NotEqualOptions>
+build_circle_NotEqualOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::NotEqualOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/NotEqualOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/NotEqualOptions.h
new file mode 100644
index 000000000..f7533ec06
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/NotEqualOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_NOTEQUAL_OPTIONS_H__
+#define __BBO_NOTEQUAL_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::NotEqualOptions>
+build_circle_NotEqualOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_NOTEQUAL_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/OneHotOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/OneHotOptions.cpp
new file mode 100644
index 000000000..d4ca0b898
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/OneHotOptions.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "OneHotOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::OneHotOptions>
+build_circle_OneHotOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_OneHotOptions();
+ assert(tflite_builtin_options);
+ circle::OneHotOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_axis(tflite_builtin_options->axis());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/OneHotOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/OneHotOptions.h
new file mode 100644
index 000000000..fa24be981
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/OneHotOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_ONEHOT_OPTIONS_H__
+#define __BBO_ONEHOT_OPTIONS_H__
+
+#include <mio/circle/schema_generated.h>
+#include <mio/tflite/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::OneHotOptions>
+build_circle_OneHotOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_ONEHOT_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/PackOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/PackOptions.cpp
new file mode 100644
index 000000000..7950f62e9
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/PackOptions.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "PackOptions.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::PackOptions>
+build_circle_PackOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_PackOptions();
+ assert(tflite_builtin_options);
+ circle::PackOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_values_count(tflite_builtin_options->values_count());
+ builtin_options_builder.add_axis(tflite_builtin_options->axis());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/PackOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/PackOptions.h
new file mode 100644
index 000000000..7bf7643ef
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/PackOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_PACK_OPTIONS_H__
+#define __BBO_PACK_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::PackOptions>
+build_circle_PackOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_PACK_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/PadOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/PadOptions.cpp
new file mode 100644
index 000000000..1214390c1
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/PadOptions.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "PadOptions.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::PadOptions> build_circle_PadOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op)
+{
+ circle::PadOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/PadOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/PadOptions.h
new file mode 100644
index 000000000..a22b0a126
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/PadOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_PAD_OPTIONS_H__
+#define __BBO_PAD_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::PadOptions> build_circle_PadOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_PAD_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/PadV2Options.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/PadV2Options.cpp
new file mode 100644
index 000000000..6636634a3
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/PadV2Options.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "PadV2Options.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::PadV2Options>
+build_circle_PadV2Options(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::PadV2OptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/PadV2Options.h b/compiler/tflite2circle/src/BuildBuiltinOptions/PadV2Options.h
new file mode 100644
index 000000000..36a2c82e8
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/PadV2Options.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_PADV2_OPTIONS_H__
+#define __BBO_PADV2_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::PadV2Options>
+build_circle_PadV2Options(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_PADV2_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/Pool2DOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/Pool2DOptions.cpp
new file mode 100644
index 000000000..6b0bd1288
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/Pool2DOptions.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Pool2DOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::Pool2DOptions>
+build_circle_Pool2DOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_Pool2DOptions();
+ assert(tflite_builtin_options);
+ circle::Pool2DOptionsBuilder builtin_options_builder{fb};
+ 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_filter_width(tflite_builtin_options->filter_width());
+ builtin_options_builder.add_filter_height(tflite_builtin_options->filter_height());
+ builtin_options_builder.add_fused_activation_function(
+ get_circle_activation_function_type(tflite_builtin_options->fused_activation_function()));
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/Pool2DOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/Pool2DOptions.h
new file mode 100644
index 000000000..69119f377
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/Pool2DOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_POOL2D_OPTIONS_H__
+#define __BBO_POOL2D_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::Pool2DOptions>
+build_circle_Pool2DOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_POOL2D_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/PowOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/PowOptions.cpp
new file mode 100644
index 000000000..1be39d709
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/PowOptions.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PowOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::PowOptions> build_circle_PowOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op)
+{
+ circle::PowOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/PowOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/PowOptions.h
new file mode 100644
index 000000000..9bacd46ec
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/PowOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_POW_OPTIONS_H__
+#define __BBO_POW_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::PowOptions> build_circle_PowOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_POW_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/RangeOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/RangeOptions.cpp
new file mode 100644
index 000000000..493761322
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/RangeOptions.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "RangeOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::RangeOptions>
+build_circle_RangeOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *)
+{
+ circle::RangeOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/RangeOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/RangeOptions.h
new file mode 100644
index 000000000..3ee40043f
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/RangeOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_RANGE_OPTIONS_H__
+#define __BBO_RANGE_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::RangeOptions>
+build_circle_RangeOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_RANGE_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/RankOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/RankOptions.cpp
new file mode 100644
index 000000000..a5cbed572
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/RankOptions.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "RankOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::RankOptions>
+build_circle_RankOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *)
+{
+ circle::RankOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/RankOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/RankOptions.h
new file mode 100644
index 000000000..4f70bb374
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/RankOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_RANK_OPTIONS_H__
+#define __BBO_RANK_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::RankOptions>
+build_circle_RankOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_RANK_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ReducerOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/ReducerOptions.cpp
new file mode 100644
index 000000000..4bcb04e5b
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ReducerOptions.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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 "ReducerOptions.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ReducerOptions>
+build_circle_ReducerOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_ReducerOptions();
+ assert(tflite_builtin_options);
+ circle::ReducerOptionsBuilder reducer_options_builder{fb};
+ reducer_options_builder.add_keep_dims(tflite_builtin_options->keep_dims());
+ return reducer_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ReducerOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/ReducerOptions.h
new file mode 100644
index 000000000..b9b39fcb6
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ReducerOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_REDUCER_OPTIONS_H__
+#define __BBO_REDUCER_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ReducerOptions>
+build_circle_ReducerOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_REDUCER_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ReshapeOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/ReshapeOptions.cpp
new file mode 100644
index 000000000..265f20d58
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ReshapeOptions.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReshapeOptions.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ReshapeOptions>
+build_circle_ReshapeOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_ReshapeOptions();
+ if (tflite_builtin_options == nullptr)
+ return 0;
+
+ std::vector<int32_t> new_shape_vec{tflite_builtin_options->new_shape()->begin(),
+ tflite_builtin_options->new_shape()->end()};
+ auto new_shape = fb.CreateVector(new_shape_vec);
+ circle::ReshapeOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_new_shape(new_shape);
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ReshapeOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/ReshapeOptions.h
new file mode 100644
index 000000000..55546ba3e
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ReshapeOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_RESHAPE_OPTIONS_H__
+#define __BBO_RESHAPE_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ReshapeOptions>
+build_circle_ReshapeOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_RESHAPE_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ResizeBilinearOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/ResizeBilinearOptions.cpp
new file mode 100644
index 000000000..e02ca836c
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ResizeBilinearOptions.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ResizeBilinearOptions.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ResizeBilinearOptions>
+build_circle_ResizeBilinearOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_ResizeBilinearOptions();
+ assert(tflite_builtin_options);
+
+ circle::ResizeBilinearOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_align_corners(tflite_builtin_options->align_corners());
+ builtin_options_builder.add_half_pixel_centers(tflite_builtin_options->half_pixel_centers());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ResizeBilinearOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/ResizeBilinearOptions.h
new file mode 100644
index 000000000..d645eff7e
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ResizeBilinearOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_RESIZE_BILINEAR_OPTIONS_H__
+#define __BBO_RESIZE_BILINEAR_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ResizeBilinearOptions>
+build_circle_ResizeBilinearOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_RESIZE_BILINEAR_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ResizeNearestNeighborOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/ResizeNearestNeighborOptions.cpp
new file mode 100644
index 000000000..572bd3d7c
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ResizeNearestNeighborOptions.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ResizeNearestNeighborOptions.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ResizeNearestNeighborOptions>
+build_circle_ResizeNearestNeighborOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_ResizeNearestNeighborOptions();
+ assert(tflite_builtin_options);
+
+ circle::ResizeNearestNeighborOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_align_corners(tflite_builtin_options->align_corners());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ResizeNearestNeighborOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/ResizeNearestNeighborOptions.h
new file mode 100644
index 000000000..f87a43325
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ResizeNearestNeighborOptions.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_RESIZE_NEAREST_NEIGHBOR_OPTIONS_H__
+#define __BBO_RESIZE_NEAREST_NEIGHBOR_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ResizeNearestNeighborOptions>
+build_circle_ResizeNearestNeighborOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_RESIZE_NEAREST_NEIGHBOR_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ReverseSequenceOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/ReverseSequenceOptions.cpp
new file mode 100644
index 000000000..57f7d6893
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ReverseSequenceOptions.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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 "ReverseSequenceOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ReverseSequenceOptions>
+build_circle_ReverseSequenceOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_ReverseSequenceOptions();
+ assert(tflite_builtin_options);
+ circle::ReverseSequenceOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_seq_dim(tflite_builtin_options->seq_dim());
+ builtin_options_builder.add_batch_dim(tflite_builtin_options->batch_dim());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ReverseSequenceOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/ReverseSequenceOptions.h
new file mode 100644
index 000000000..b0567903e
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ReverseSequenceOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_REVERSE_SEQUENCE_OPTIONS_H__
+#define __BBO_REVERSE_SEQUENCE_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ReverseSequenceOptions>
+build_circle_ReverseSequenceOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_REVERSE_SEQUENCE_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ReverseV2Options.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/ReverseV2Options.cpp
new file mode 100644
index 000000000..b6771d3dc
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ReverseV2Options.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "ReverseV2Options.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ReverseV2Options>
+build_circle_ReverseV2Options(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *)
+{
+ circle::ReverseV2OptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ReverseV2Options.h b/compiler/tflite2circle/src/BuildBuiltinOptions/ReverseV2Options.h
new file mode 100644
index 000000000..a92dd9e75
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ReverseV2Options.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_REVERSE_V2_OPTIONS_H__
+#define __BBO_REVERSE_V2_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ReverseV2Options>
+build_circle_ReverseV2Options(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_REVERSE_V2_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ScatterNdOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/ScatterNdOptions.cpp
new file mode 100644
index 000000000..e79480529
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ScatterNdOptions.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ScatterNdOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ScatterNdOptions>
+build_circle_ScatterNdOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::ScatterNdOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ScatterNdOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/ScatterNdOptions.h
new file mode 100644
index 000000000..de17ebcad
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ScatterNdOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_SCATTER_ND_OPTIONS_H__
+#define __BBO_SCATTER_ND_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ScatterNdOptions>
+build_circle_ScatterNdOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_SCATTER_ND_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SegmentSumOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/SegmentSumOptions.cpp
new file mode 100644
index 000000000..efd80c15d
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SegmentSumOptions.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "SegmentSumOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SegmentSumOptions>
+build_circle_SegmentSumOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::SegmentSumOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SegmentSumOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/SegmentSumOptions.h
new file mode 100644
index 000000000..e6163849a
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SegmentSumOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_SEGMENT_SUM_OPTIONS_H__
+#define __BBO_SEGMENT_SUM_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SegmentSumOptions>
+build_circle_SegmentSumOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_SEGMENT_SUM_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SelectOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/SelectOptions.cpp
new file mode 100644
index 000000000..c584fbfe6
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SelectOptions.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "SelectOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SelectOptions>
+build_circle_SelectOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::SelectOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SelectOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/SelectOptions.h
new file mode 100644
index 000000000..4d4e6fe3c
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SelectOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_SELECT_OPTIONS_H__
+#define __BBO_SELECT_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SelectOptions>
+build_circle_SelectOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_SELECT_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SelectV2Options.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/SelectV2Options.cpp
new file mode 100644
index 000000000..9032d5e82
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SelectV2Options.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "SelectV2Options.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SelectV2Options>
+build_circle_SelectV2Options(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::SelectV2OptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SelectV2Options.h b/compiler/tflite2circle/src/BuildBuiltinOptions/SelectV2Options.h
new file mode 100644
index 000000000..14e91d2a4
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SelectV2Options.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_SELECT_V2_OPTIONS_H__
+#define __BBO_SELECT_V2_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SelectV2Options>
+build_circle_SelectV2Options(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_SELECT_V2_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ShapeOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/ShapeOptions.cpp
new file mode 100644
index 000000000..b1cbaba05
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ShapeOptions.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "ShapeOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ShapeOptions>
+build_circle_ShapeOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_ShapeOptions();
+ assert(tflite_builtin_options);
+ circle::ShapeOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_out_type(get_circle_tensortype(tflite_builtin_options->out_type()));
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ShapeOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/ShapeOptions.h
new file mode 100644
index 000000000..2c6e72074
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ShapeOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_SHAPE_OPTIONS_H__
+#define __BBO_SHAPE_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ShapeOptions>
+build_circle_ShapeOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_SHAPE_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SliceOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/SliceOptions.cpp
new file mode 100644
index 000000000..d1dba1b43
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SliceOptions.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "SliceOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SliceOptions>
+build_circle_SliceOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::SliceOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SliceOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/SliceOptions.h
new file mode 100644
index 000000000..0d7ca606c
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SliceOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_SLICE_OPTIONS_H__
+#define __BBO_SLICE_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SliceOptions>
+build_circle_SliceOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_SLICE_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SoftmaxOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/SoftmaxOptions.cpp
new file mode 100644
index 000000000..a1cf9abe7
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SoftmaxOptions.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "SoftmaxOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SoftmaxOptions>
+build_circle_SoftmaxOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_SoftmaxOptions();
+ assert(tflite_builtin_options);
+ circle::SoftmaxOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_beta(tflite_builtin_options->beta());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SoftmaxOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/SoftmaxOptions.h
new file mode 100644
index 000000000..f4a876aaa
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SoftmaxOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_SOFTMAX_OPTIONS_H__
+#define __BBO_SOFTMAX_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SoftmaxOptions>
+build_circle_SoftmaxOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_SOFTMAX_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SpaceToBatchNDOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/SpaceToBatchNDOptions.cpp
new file mode 100644
index 000000000..0cb4dba9e
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SpaceToBatchNDOptions.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SpaceToBatchNDOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SpaceToBatchNDOptions>
+build_circle_SpaceToBatchNDOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *)
+{
+ circle::SpaceToBatchNDOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SpaceToBatchNDOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/SpaceToBatchNDOptions.h
new file mode 100644
index 000000000..dd3d98305
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SpaceToBatchNDOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_SPACE_TO_BATCH_ND_OPTIONS_H__
+#define __BBO_SPACE_TO_BATCH_ND_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SpaceToBatchNDOptions>
+build_circle_SpaceToBatchNDOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_SPACE_TO_BATCH_ND_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SpaceToDepthOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/SpaceToDepthOptions.cpp
new file mode 100644
index 000000000..d9d6a8fc0
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SpaceToDepthOptions.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "SpaceToDepthOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SpaceToDepthOptions>
+build_circle_SpaceToDepthOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_SpaceToDepthOptions();
+ assert(tflite_builtin_options);
+ circle::SpaceToDepthOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_block_size(tflite_builtin_options->block_size());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SpaceToDepthOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/SpaceToDepthOptions.h
new file mode 100644
index 000000000..e53875b13
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SpaceToDepthOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_SPACE_TO_DEPTH_OPTIONS_H__
+#define __BBO_SPACE_TO_DEPTH_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SpaceToDepthOptions>
+build_circle_SpaceToDepthOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_SPACE_TO_DEPTH_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SparseToDenseOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/SparseToDenseOptions.cpp
new file mode 100644
index 000000000..48abaac95
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SparseToDenseOptions.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "SparseToDenseOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SparseToDenseOptions>
+build_circle_SparseToDenseOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_SparseToDenseOptions();
+ assert(tflite_builtin_options);
+ circle::SparseToDenseOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_validate_indices(tflite_builtin_options->validate_indices());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SparseToDenseOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/SparseToDenseOptions.h
new file mode 100644
index 000000000..c2058f072
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SparseToDenseOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_SPARSETODENSE_OPTIONS_H__
+#define __BBO_SPARSETODENSE_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SparseToDenseOptions>
+build_circle_SparseToDenseOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_SPARSETODENSE_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SplitOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/SplitOptions.cpp
new file mode 100644
index 000000000..fc6c036bf
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SplitOptions.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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 "SplitOptions.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SplitOptions>
+build_circle_SplitOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_SplitOptions();
+ assert(tflite_builtin_options);
+ circle::SplitOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_num_splits(tflite_builtin_options->num_splits());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SplitOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/SplitOptions.h
new file mode 100644
index 000000000..1bbcc397e
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SplitOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_SPLIT_OPTIONS_H__
+#define __BBO_SPLIT_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SplitOptions>
+build_circle_SplitOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_SPLIT_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SplitVOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/SplitVOptions.cpp
new file mode 100644
index 000000000..da309f383
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SplitVOptions.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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 "SplitVOptions.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SplitVOptions>
+build_circle_SplitVOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_SplitVOptions();
+ assert(tflite_builtin_options);
+ circle::SplitVOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_num_splits(tflite_builtin_options->num_splits());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SplitVOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/SplitVOptions.h
new file mode 100644
index 000000000..0550c00f8
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SplitVOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_SPLIT_V_OPTIONS_H__
+#define __BBO_SPLIT_V_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SplitVOptions>
+build_circle_SplitVOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_SPLIT_V_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SquareOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/SquareOptions.cpp
new file mode 100644
index 000000000..4656f9167
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SquareOptions.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "SquareOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SquareOptions>
+build_circle_SquareOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *)
+{
+ circle::SquareOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SquareOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/SquareOptions.h
new file mode 100644
index 000000000..647686e92
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SquareOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_SQUARE_OPTIONS_H__
+#define __BBO_SQUARE_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SquareOptions>
+build_circle_SquareOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_SQUARE_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SquaredDifferenceOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/SquaredDifferenceOptions.cpp
new file mode 100644
index 000000000..0c2dbde83
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SquaredDifferenceOptions.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 "SquaredDifferenceOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SquaredDifferenceOptions>
+build_circle_SquaredDifferenceOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op)
+{
+ circle::SquaredDifferenceOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SquaredDifferenceOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/SquaredDifferenceOptions.h
new file mode 100644
index 000000000..76b45c0b9
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SquaredDifferenceOptions.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_SQUAREDDIFFERENCE_OPTIONS_H__
+#define __BBO_SQUAREDDIFFERENCE_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SquaredDifferenceOptions>
+build_circle_SquaredDifferenceOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_SQUAREDDIFFERENCE_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SqueezeOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/SqueezeOptions.cpp
new file mode 100644
index 000000000..ec29b9bf5
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SqueezeOptions.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SqueezeOptions.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SqueezeOptions>
+build_circle_SqueezeOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_SqueezeOptions();
+ assert(tflite_builtin_options);
+ std::vector<int32_t> squeeze_dims_vec{tflite_builtin_options->squeeze_dims()->begin(),
+ tflite_builtin_options->squeeze_dims()->end()};
+ auto squeeze_dims = fb.CreateVector(squeeze_dims_vec);
+ circle::SqueezeOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_squeeze_dims(squeeze_dims);
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SqueezeOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/SqueezeOptions.h
new file mode 100644
index 000000000..f6237c977
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SqueezeOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_SQUEEZE_OPTIONS_H__
+#define __BBO_SQUEEZE_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SqueezeOptions>
+build_circle_SqueezeOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_SQUEEZE_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/StridedSliceOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/StridedSliceOptions.cpp
new file mode 100644
index 000000000..68cf89795
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/StridedSliceOptions.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "StridedSliceOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::StridedSliceOptions>
+build_circle_StridedSliceOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_StridedSliceOptions();
+ assert(tflite_builtin_options);
+ circle::StridedSliceOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_begin_mask(tflite_builtin_options->begin_mask());
+ builtin_options_builder.add_end_mask(tflite_builtin_options->end_mask());
+ builtin_options_builder.add_ellipsis_mask(tflite_builtin_options->ellipsis_mask());
+ builtin_options_builder.add_new_axis_mask(tflite_builtin_options->new_axis_mask());
+ builtin_options_builder.add_shrink_axis_mask(tflite_builtin_options->shrink_axis_mask());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/StridedSliceOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/StridedSliceOptions.h
new file mode 100644
index 000000000..e6782a053
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/StridedSliceOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_STRIDEDSLICE_OPTIONS_H__
+#define __BBO_STRIDEDSLICE_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::StridedSliceOptions>
+build_circle_StridedSliceOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_STRIDEDSLICE_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SubOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/SubOptions.cpp
new file mode 100644
index 000000000..2e55f4dab
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SubOptions.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SubOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SubOptions> build_circle_SubOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_SubOptions();
+ assert(tflite_builtin_options);
+ circle::SubOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_fused_activation_function(
+ get_circle_activation_function_type(tflite_builtin_options->fused_activation_function()));
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SubOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/SubOptions.h
new file mode 100644
index 000000000..a655548d1
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SubOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_SUB_OPTIONS_H__
+#define __BBO_SUB_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::SubOptions> build_circle_SubOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_SUB_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/TileOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/TileOptions.cpp
new file mode 100644
index 000000000..47316bf67
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/TileOptions.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "TileOptions.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::TileOptions>
+build_circle_TileOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::TileOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/TileOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/TileOptions.h
new file mode 100644
index 000000000..e3e9a7187
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/TileOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_TILE_OPTIONS_H__
+#define __BBO_TILE_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::TileOptions>
+build_circle_TileOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_TILE_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/TopKV2Options.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/TopKV2Options.cpp
new file mode 100644
index 000000000..a2bc76f9f
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/TopKV2Options.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "TopKV2Options.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::TopKV2Options>
+build_circle_TopKV2Options(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::TopKV2OptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/TopKV2Options.h b/compiler/tflite2circle/src/BuildBuiltinOptions/TopKV2Options.h
new file mode 100644
index 000000000..89b5b995c
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/TopKV2Options.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_TOPK_V2_OPTIONS_H__
+#define __BBO_TOPK_V2_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::TopKV2Options>
+build_circle_TopKV2Options(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_TOPK_V2_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/TransposeConvOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/TransposeConvOptions.cpp
new file mode 100644
index 000000000..301f2c421
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/TransposeConvOptions.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TransposeConvOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::TransposeConvOptions>
+build_circle_TransposeConvOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_TransposeConvOptions();
+ assert(tflite_builtin_options);
+ circle::TransposeConvOptionsBuilder builtin_options_builder{fb};
+ 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());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/TransposeConvOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/TransposeConvOptions.h
new file mode 100644
index 000000000..dd0bec296
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/TransposeConvOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_TRANSPOSE_CONV_OPTIONS_H__
+#define __BBO_TRANSPOSE_CONV_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::TransposeConvOptions>
+build_circle_TransposeConvOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_TRANSPOSE_CONV_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/TransposeOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/TransposeOptions.cpp
new file mode 100644
index 000000000..427322949
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/TransposeOptions.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "TransposeOptions.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::TransposeOptions>
+build_circle_TransposeOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ circle::TransposeOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/TransposeOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/TransposeOptions.h
new file mode 100644
index 000000000..bc5e5317a
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/TransposeOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_TRANSPOSE_OPTIONS_H__
+#define __BBO_TRANSPOSE_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::TransposeOptions>
+build_circle_TransposeOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_TRANSPOSE_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.cpp
new file mode 100644
index 000000000..64ceb5a74
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "UnidirectionalSequenceLSTMOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::UnidirectionalSequenceLSTMOptions>
+build_circle_UnidirectionalSequenceLSTMOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_UnidirectionalSequenceLSTMOptions();
+ circle::UnidirectionalSequenceLSTMOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_fused_activation_function(
+ get_circle_activation_function_type(tflite_builtin_options->fused_activation_function()));
+ builtin_options_builder.add_cell_clip(tflite_builtin_options->cell_clip());
+ builtin_options_builder.add_proj_clip(tflite_builtin_options->proj_clip());
+ builtin_options_builder.add_time_major(tflite_builtin_options->time_major());
+ builtin_options_builder.add_asymmetric_quantize_inputs(
+ tflite_builtin_options->asymmetric_quantize_inputs());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.h
new file mode 100644
index 000000000..2be0efbc2
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_UNIDIRECTIONALSEQUENCELSTM_OPTIONS_H__
+#define __BBO_UNIDIRECTIONALSEQUENCELSTM_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::UnidirectionalSequenceLSTMOptions>
+build_circle_UnidirectionalSequenceLSTMOptions(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_UNIDIRECTIONALSEQUENCELSTM_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/UniqueOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/UniqueOptions.cpp
new file mode 100644
index 000000000..96ddc15ad
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/UniqueOptions.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "UniqueOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::UniqueOptions>
+build_circle_UniqueOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_UniqueOptions();
+ assert(tflite_builtin_options);
+ circle::UniqueOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_idx_out_type(
+ get_circle_tensortype(tflite_builtin_options->idx_out_type()));
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/UniqueOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/UniqueOptions.h
new file mode 100644
index 000000000..35736e91e
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/UniqueOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_UNIQUE_OPTIONS_H__
+#define __BBO_UNIQUE_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::UniqueOptions>
+build_circle_UniqueOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_UNIQUE_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/UnpackOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/UnpackOptions.cpp
new file mode 100644
index 000000000..c9a332a4b
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/UnpackOptions.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "UnpackOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::UnpackOptions>
+build_circle_UnpackOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_UnpackOptions();
+ assert(tflite_builtin_options);
+ circle::UnpackOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_num(tflite_builtin_options->num());
+ builtin_options_builder.add_axis(tflite_builtin_options->axis());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/UnpackOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/UnpackOptions.h
new file mode 100644
index 000000000..6dfed87f0
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/UnpackOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_UNPACK_OPTIONS_H__
+#define __BBO_UNPACK_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::UnpackOptions>
+build_circle_UnpackOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_UNPACK_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/WhereOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/WhereOptions.cpp
new file mode 100644
index 000000000..d591a5419
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/WhereOptions.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "WhereOptions.h"
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::WhereOptions>
+build_circle_WhereOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *)
+{
+ circle::WhereOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/WhereOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/WhereOptions.h
new file mode 100644
index 000000000..a113f6923
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/WhereOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_WHERE_OPTIONS_H__
+#define __BBO_WHERE_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::WhereOptions>
+build_circle_WhereOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_WHERE_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/WhileOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/WhileOptions.cpp
new file mode 100644
index 000000000..1ad1af75d
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/WhileOptions.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "WhileOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::WhileOptions>
+build_circle_WhileOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op)
+{
+ auto tflite_builtin_options = op->builtin_options_as_WhileOptions();
+ assert(tflite_builtin_options);
+ circle::WhileOptionsBuilder builtin_options_builder{fb};
+ builtin_options_builder.add_cond_subgraph_index(tflite_builtin_options->cond_subgraph_index());
+ builtin_options_builder.add_body_subgraph_index(tflite_builtin_options->body_subgraph_index());
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/WhileOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/WhileOptions.h
new file mode 100644
index 000000000..000e1a241
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/WhileOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_WHILE_OPTIONS_H__
+#define __BBO_WHILE_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::WhileOptions>
+build_circle_WhileOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_WHILE_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ZerosLikeOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/ZerosLikeOptions.cpp
new file mode 100644
index 000000000..e64e4213c
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ZerosLikeOptions.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ZerosLikeOptions.h"
+#include "DataLookup.h"
+
+#include <cassert>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ZerosLikeOptions>
+build_circle_ZerosLikeOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *)
+{
+ circle::ZerosLikeOptionsBuilder builtin_options_builder{fb};
+ return builtin_options_builder.Finish();
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ZerosLikeOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/ZerosLikeOptions.h
new file mode 100644
index 000000000..5113eccb8
--- /dev/null
+++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ZerosLikeOptions.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BBO_ZEROS_LIKE_OPTIONS_H__
+#define __BBO_ZEROS_LIKE_OPTIONS_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+flatbuffers::Offset<circle::ZerosLikeOptions>
+build_circle_ZerosLikeOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op);
+
+} // namespace tflite2circle
+
+#endif // __BBO_ZEROS_LIKE_OPTIONS_H__
diff --git a/compiler/tflite2circle/src/CircleModel.cpp b/compiler/tflite2circle/src/CircleModel.cpp
new file mode 100644
index 000000000..a95c37089
--- /dev/null
+++ b/compiler/tflite2circle/src/CircleModel.cpp
@@ -0,0 +1,342 @@
+/*
+ * 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 <iostream>
+#include <memory>
+
+#include "CircleModel.h"
+#include "DataLookup.h"
+
+namespace tflite2circle
+{
+
+template <>
+Offset<MetaDataBufferLink>::Offset(FlatBufBuilder &fb, const TFLFlatBufVec *tflite_flatbuffer_vec)
+{
+ if (tflite_flatbuffer_vec == nullptr)
+ return;
+ std::vector<int32_t> metadata_buffer_vec{tflite_flatbuffer_vec->begin(),
+ tflite_flatbuffer_vec->end()};
+ _circle_flatbuffer_vec_offset = fb->CreateVector(metadata_buffer_vec);
+}
+
+template <>
+Offset<BufferLink>::Offset(FlatBufBuilder &fb, const TFLFlatBufVec *tflite_flatbuffer_vec)
+{
+ std::vector<flatbuffers::Offset<circle::Buffer>> buffers_vec;
+
+ for (auto it : *tflite_flatbuffer_vec)
+ {
+ flatbuffers::Offset<flatbuffers::Vector<uint8_t>> buffer_data;
+ if (it->data())
+ {
+ std::vector<uint8_t> data_vec{it->data()->begin(), it->data()->end()};
+ buffer_data = fb->CreateVector(data_vec);
+ }
+ circle::BufferBuilder circle_buffer_builder{*fb};
+ circle_buffer_builder.add_data(buffer_data);
+ auto circle_buffers = circle_buffer_builder.Finish();
+ buffers_vec.emplace_back(circle_buffers);
+ }
+ _circle_flatbuffer_vec_offset = fb->CreateVector(buffers_vec);
+}
+
+template <>
+Offset<SubGraphLink>::Offset(FlatBufBuilder &fb, const TFLFlatBufVec *tflite_flatbuffer_vec)
+{
+ std::vector<flatbuffers::Offset<circle::SubGraph>> subgprahs_vec;
+
+ for (auto it_sg : *tflite_flatbuffer_vec)
+ {
+ // tensors of subgraph
+ std::vector<flatbuffers::Offset<circle::Tensor>> tensor_vec;
+
+ auto tflite_tensors = it_sg->tensors();
+ for (auto it : *tflite_tensors)
+ {
+ // shape
+ flatbuffers::Offset<flatbuffers::Vector<int32_t>> shape;
+ if (it->shape())
+ {
+ auto shape_vec = std::vector<int32_t>({it->shape()->begin(), it->shape()->end()});
+ shape = fb->CreateVector(shape_vec);
+ }
+ // name
+ flatbuffers::Offset<flatbuffers::String> name;
+ if (it->name())
+ name = fb->CreateString(it->name()->str());
+ // quantization
+ flatbuffers::Offset<circle::QuantizationParameters> quantization;
+ if (it->quantization())
+ {
+ std::vector<float> tfmin;
+ std::vector<float> tfmax;
+ std::vector<float> tfscale;
+ std::vector<int64_t> tfzerop;
+ flatbuffers::Offset<flatbuffers::Vector<float>> min;
+ flatbuffers::Offset<flatbuffers::Vector<float>> max;
+ flatbuffers::Offset<flatbuffers::Vector<float>> scale;
+ flatbuffers::Offset<flatbuffers::Vector<int64_t>> zero_point;
+ int32_t quantized_dimension = it->quantization()->quantized_dimension();
+
+ if (it->quantization()->min() && it->quantization()->max())
+ {
+ auto rmin = it->quantization()->min();
+ auto rmax = it->quantization()->max();
+ tfmin = std::vector<float>{rmin->begin(), rmin->end()};
+ tfmax = std::vector<float>{rmax->begin(), rmax->end()};
+ min = fb->CreateVector(tfmin);
+ max = fb->CreateVector(tfmax);
+ }
+
+ if (it->quantization()->scale() && it->quantization()->zero_point())
+ {
+ auto rs = it->quantization()->scale();
+ auto rz = it->quantization()->zero_point();
+ tfscale = std::vector<float>{rs->begin(), rs->end()};
+ tfzerop = std::vector<int64_t>{rz->begin(), rz->end()};
+ scale = fb->CreateVector(tfscale);
+ zero_point = fb->CreateVector(tfzerop);
+ }
+
+ quantization = circle::CreateQuantizationParameters(*fb, min, max, scale, zero_point,
+ circle::QuantizationDetails_NONE, 0,
+ quantized_dimension);
+ }
+ // is_variable
+ bool is_variable = it->is_variable();
+
+ flatbuffers::Offset<circle::SparsityParameters> sparsity;
+ // sparsity
+ if (it->sparsity())
+ {
+ flatbuffers::Offset<flatbuffers::Vector<int32_t>> traversal_order;
+ flatbuffers::Offset<flatbuffers::Vector<int32_t>> block_map;
+ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<circle::DimensionMetadata>>>
+ dim_metadata;
+
+ // traversal_order
+ if (it->sparsity()->traversal_order())
+ {
+ auto traversal_order_vec = std::vector<int32_t>{
+ it->sparsity()->traversal_order()->begin(), it->sparsity()->traversal_order()->end()};
+ traversal_order = fb->CreateVector(traversal_order_vec);
+ }
+
+ // block_map
+ if (it->sparsity()->block_map())
+ {
+ auto block_map_vec = std::vector<int32_t>{it->sparsity()->block_map()->begin(),
+ it->sparsity()->block_map()->end()};
+ block_map = fb->CreateVector(block_map_vec);
+ }
+
+ // dim_metadata
+ std::vector<flatbuffers::Offset<circle::DimensionMetadata>> dim_metadata_vec;
+ auto tflite_dim_metadata = it->sparsity()->dim_metadata();
+ for (auto it : *tflite_dim_metadata)
+ {
+ // array_segments
+ auto tflite_array_segments_type = it->array_segments_type();
+ auto circle_array_segments =
+ get_circle_sparse_index_vector(*fb, it->array_segments(), tflite_array_segments_type);
+ auto circle_array_segments_type =
+ get_circle_sparse_index_vector_type(tflite_array_segments_type);
+
+ // array_indices
+ auto tflite_array_indices_type = it->array_indices_type();
+ auto circle_array_indices =
+ get_circle_sparse_index_vector(*fb, it->array_indices(), tflite_array_indices_type);
+ auto circle_array_indices_type =
+ get_circle_sparse_index_vector_type(tflite_array_indices_type);
+
+ auto circle_dim_metadata_builder = circle::DimensionMetadataBuilder{*fb};
+
+ circle_dim_metadata_builder.add_format(get_circle_dimension_type(it->format()));
+ circle_dim_metadata_builder.add_dense_size(it->dense_size());
+ circle_dim_metadata_builder.add_array_segments(circle_array_segments);
+ circle_dim_metadata_builder.add_array_segments_type(circle_array_segments_type);
+ circle_dim_metadata_builder.add_array_indices(circle_array_indices);
+ circle_dim_metadata_builder.add_array_indices_type(circle_array_indices_type);
+ auto dim_metadata = circle_dim_metadata_builder.Finish();
+ dim_metadata_vec.emplace_back(dim_metadata);
+ }
+ dim_metadata = fb->CreateVector(dim_metadata_vec);
+
+ sparsity = circle::CreateSparsityParameters(*fb, traversal_order, block_map, dim_metadata);
+ }
+
+ // shape signature
+ flatbuffers::Offset<flatbuffers::Vector<int32_t>> shape_signature;
+ if (it->shape_signature())
+ {
+ auto shape_signature_vec =
+ std::vector<int32_t>({it->shape_signature()->begin(), it->shape_signature()->end()});
+ shape_signature = fb->CreateVector(shape_signature_vec);
+ }
+
+ circle::TensorBuilder tensor_builder{*fb};
+ tensor_builder.add_shape(shape);
+ tensor_builder.add_type(get_circle_tensortype(it->type()));
+ tensor_builder.add_buffer(it->buffer());
+ tensor_builder.add_name(name);
+ tensor_builder.add_quantization(quantization);
+ tensor_builder.add_is_variable(is_variable);
+ tensor_builder.add_sparsity(sparsity);
+ tensor_builder.add_shape_signature(shape_signature);
+ auto tensor = tensor_builder.Finish();
+ tensor_vec.emplace_back(tensor);
+ }
+ auto circle_tensors = fb->CreateVector(tensor_vec);
+
+ // inputs of subgraph
+ auto tflite_inputs = it_sg->inputs();
+ std::vector<int32_t> input_vec{tflite_inputs->begin(), tflite_inputs->end()};
+
+ auto circle_inputs = fb->CreateVector(input_vec);
+
+ // outputs of subgraph
+ auto tflite_outputs = it_sg->outputs();
+ std::vector<int32_t> output_vec{tflite_outputs->begin(), tflite_outputs->end()};
+
+ auto circle_outputs = fb->CreateVector(output_vec);
+
+ // operators of subgraph
+ std::vector<flatbuffers::Offset<circle::Operator>> operator_vec;
+
+ auto tflite_operators = it_sg->operators();
+ if (tflite_operators != nullptr)
+ {
+ for (auto it : *tflite_operators)
+ {
+ // inputs
+ std::vector<int32_t> input_vec{it->inputs()->begin(), it->inputs()->end()};
+ auto circle_inputs = fb->CreateVector(input_vec);
+ // outputs
+ std::vector<int32_t> output_vec{it->outputs()->begin(), it->outputs()->end()};
+ auto circle_outputs = fb->CreateVector(output_vec);
+ // builtin options
+ auto circle_builtin_options = get_circle_builtin_options(*fb, it);
+ auto circle_builtin_options_type = get_circle_builtin_options_type(it);
+ // custom options
+ flatbuffers::Offset<flatbuffers::Vector<uint8_t>> circle_custom_options;
+ if (it->custom_options())
+ {
+ std::vector<uint8_t> custom_options_vec{it->custom_options()->begin(),
+ it->custom_options()->end()};
+ circle_custom_options = fb->CreateVector(custom_options_vec);
+ }
+ // custom options format
+ // TODO Make get_circle_custom_options_format
+ assert(it->custom_options_format() == tflite::CustomOptionsFormat_FLEXBUFFERS);
+ auto circle_custom_options_format = circle::CustomOptionsFormat_FLEXBUFFERS;
+
+ circle::OperatorBuilder operator_builder{*fb};
+ operator_builder.add_opcode_index(it->opcode_index());
+ operator_builder.add_inputs(circle_inputs);
+ operator_builder.add_outputs(circle_outputs);
+ operator_builder.add_builtin_options(circle_builtin_options);
+ operator_builder.add_builtin_options_type(circle_builtin_options_type);
+ operator_builder.add_custom_options(circle_custom_options);
+ operator_builder.add_custom_options_format(circle_custom_options_format);
+ // TODO mutating_variable_inputs
+ auto opeartor = operator_builder.Finish();
+ operator_vec.emplace_back(opeartor);
+ }
+ }
+ auto circle_operators = fb->CreateVector(operator_vec);
+
+ // name of subgraph
+ auto subgraphs_name = fb->CreateString(it_sg->name());
+
+ // subgraphs
+ auto circle_subgraph_builder = circle::SubGraphBuilder{*fb};
+
+ circle_subgraph_builder.add_tensors(circle_tensors);
+ circle_subgraph_builder.add_inputs(circle_inputs);
+ circle_subgraph_builder.add_outputs(circle_outputs);
+ circle_subgraph_builder.add_operators(circle_operators);
+ circle_subgraph_builder.add_name(subgraphs_name);
+ circle_subgraph_builder.add_data_format(circle::DataFormat_CHANNELS_LAST);
+
+ auto circle_subgraph = circle_subgraph_builder.Finish();
+ subgprahs_vec.emplace_back(circle_subgraph);
+ }
+ _circle_flatbuffer_vec_offset = fb->CreateVector(subgprahs_vec);
+}
+
+template <>
+Offset<OperatorCodeLink>::Offset(FlatBufBuilder &fb, const TFLFlatBufVec *tflite_flatbuffer_vec)
+{
+ std::vector<flatbuffers::Offset<circle::OperatorCode>> operator_code_vec;
+
+ for (auto it : *tflite_flatbuffer_vec)
+ {
+ auto custom_code = fb->CreateString(it->custom_code());
+ circle::OperatorCodeBuilder operator_code_builder{*fb};
+ operator_code_builder.add_builtin_code(get_circle_builtin_code(it->builtin_code()));
+ operator_code_builder.add_custom_code(custom_code);
+ operator_code_builder.add_version(it->version());
+ auto code = operator_code_builder.Finish();
+ operator_code_vec.emplace_back(code);
+ }
+ _circle_flatbuffer_vec_offset = fb->CreateVector(operator_code_vec);
+}
+
+CircleModel::CircleModel(FlatBufBuilder &fb, TFLModel &model)
+ : _version{0}, _description{fb->CreateString("nnpackage")}, _fb{fb}
+{
+ const tflite::Model *tfl_model = model.load_model();
+ // verify flatbuffers
+ flatbuffers::Verifier verifier{reinterpret_cast<const uint8_t *>(model._data.data()),
+ model._data.size()};
+ if (!tflite::VerifyModelBuffer(verifier))
+ {
+ throw std::runtime_error("ERROR: Failed to verify tflite");
+ }
+
+ _operator_codes_offset =
+ std::make_unique<Offset<OperatorCodeLink>>(fb, tfl_model->operator_codes());
+ _subGraphs_offset = std::make_unique<Offset<SubGraphLink>>(fb, tfl_model->subgraphs());
+ _buffers_offset = std::make_unique<Offset<BufferLink>>(fb, tfl_model->buffers());
+ _metadata_buffer_offset =
+ std::make_unique<Offset<MetaDataBufferLink>>(fb, tfl_model->metadata_buffer());
+ model_build();
+}
+
+void CircleModel::model_build(void) const
+{
+ circle::ModelBuilder model_builder{*_fb};
+
+ model_builder.add_version(_version);
+ model_builder.add_description(_description);
+ model_builder.add_operator_codes(_operator_codes_offset->offset());
+ model_builder.add_subgraphs(_subGraphs_offset->offset());
+ model_builder.add_buffers(_buffers_offset->offset());
+ model_builder.add_metadata_buffer(_metadata_buffer_offset->offset());
+
+ auto model = model_builder.Finish();
+ circle::FinishModelBuffer(*_fb, model);
+}
+
+const char *CircleModel::base(void) const
+{
+ return reinterpret_cast<const char *>(_fb->GetBufferPointer());
+}
+
+size_t CircleModel::size(void) const { return _fb->GetSize(); }
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/DataLookup.cpp b/compiler/tflite2circle/src/DataLookup.cpp
new file mode 100644
index 000000000..f8dd75f4c
--- /dev/null
+++ b/compiler/tflite2circle/src/DataLookup.cpp
@@ -0,0 +1,200 @@
+/*
+ * 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 "DataLookup.h"
+#include "BuildBuiltinOptions.h"
+
+namespace tflite2circle
+{
+
+circle::BuiltinOperator get_circle_builtin_code(tflite::BuiltinOperator tfl_bop)
+{
+ switch (tfl_bop)
+ {
+#define TFL_OPERATOR(OP) \
+ case tflite::BuiltinOperator_##OP: \
+ return circle::BuiltinOperator_##OP;
+#include "TFLOperator.lst"
+#undef TFL_OPERATOR
+ default:
+ throw std::runtime_error("tflite2circle: wrong op");
+ }
+}
+
+circle::TensorType get_circle_tensortype(tflite::TensorType tfl_tt)
+{
+ switch (tfl_tt)
+ {
+#define TFL_TENSORTYPE(TENSORTYPE) \
+ case tflite::TensorType_##TENSORTYPE: \
+ return circle::TensorType_##TENSORTYPE;
+#include "TFLTensorType.lst"
+#undef TFL_TENSORTYPE
+ default:
+ throw std::runtime_error("tflite2circle: wrong tensor type");
+ }
+}
+
+circle::Padding get_circle_padding(tflite::Padding tfl_p)
+{
+ switch (tfl_p)
+ {
+ case tflite::Padding_SAME:
+ return circle::Padding_SAME;
+ case tflite::Padding_VALID:
+ return circle::Padding_VALID;
+ default:
+ throw std::runtime_error("tflite2circle: wrong padding");
+ }
+}
+
+circle::ActivationFunctionType
+get_circle_activation_function_type(tflite::ActivationFunctionType tfl_aft)
+{
+ switch (tfl_aft)
+ {
+#define TFL_ACTIVATION_FUNCTION(TYPE) \
+ case tflite::ActivationFunctionType_##TYPE: \
+ return circle::ActivationFunctionType_##TYPE;
+#include "TFLActivationFunctionType.lst"
+#undef TFL_ACTIVATION_FUNCTION
+ default:
+ throw std::runtime_error("tflite2circle: wrong activation function type.");
+ }
+}
+
+flatbuffers::Offset<void> get_circle_builtin_options(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op)
+{
+ auto tflite_builtin_options_type = op->builtin_options_type();
+ switch (tflite_builtin_options_type)
+ {
+ case tflite::BuiltinOptions_NONE:
+ return flatbuffers::Offset<void>();
+#define TFL_BUILTIN_OPTIONS(TYPE) \
+ case tflite::BuiltinOptions_##TYPE: \
+ return build_circle_##TYPE(fb, op).Union();
+#include "TFLBuiltinOptions.lst"
+#undef TFL_BUILTIN_OPTIONS
+ default:
+ throw std::runtime_error("tflite2circle: wrong builtin options type.");
+ }
+}
+
+circle::BuiltinOptions get_circle_builtin_options_type(const tflite::Operator *op)
+{
+ switch (op->builtin_options_type())
+ {
+ case tflite::BuiltinOptions_NONE:
+ return circle::BuiltinOptions_NONE;
+#define TFL_BUILTIN_OPTIONS(TYPE) \
+ case tflite::BuiltinOptions_##TYPE: \
+ return circle::BuiltinOptions_##TYPE;
+#include "TFLBuiltinOptions.lst"
+#undef TFL_BUILTIN_OPTIONS
+ default:
+ throw std::runtime_error("tflite2circle: wrong builtin options type.");
+ }
+}
+
+circle::MirrorPadMode get_circle_mirrorpad_mode(tflite::MirrorPadMode tfl_mode)
+{
+ switch (tfl_mode)
+ {
+ case tflite::MirrorPadMode_REFLECT:
+ return circle::MirrorPadMode_REFLECT;
+ case tflite::MirrorPadMode_SYMMETRIC:
+ return circle::MirrorPadMode_SYMMETRIC;
+ default:
+ throw std::runtime_error("tflite2circle: wrong mirrorpad mode.");
+ }
+}
+
+circle::DimensionType get_circle_dimension_type(tflite::DimensionType tfl_dim_type)
+{
+ switch (tfl_dim_type)
+ {
+ case tflite::DimensionType_DENSE:
+ return circle::DimensionType_DENSE;
+ case tflite::DimensionType_SPARSE_CSR:
+ return circle::DimensionType_SPARSE_CSR;
+ default:
+ throw std::runtime_error("tflite2circle: wrong dimension type.");
+ }
+}
+
+flatbuffers::Offset<void>
+get_circle_sparse_index_vector(flatbuffers::FlatBufferBuilder &fb, const void *v_array,
+ const tflite::SparseIndexVector &tfl_sparse_index_vector_type)
+{
+ switch (tfl_sparse_index_vector_type)
+ {
+ case tflite::SparseIndexVector_NONE:
+ return flatbuffers::Offset<void>();
+ case tflite::SparseIndexVector_Int32Vector:
+ {
+ const tflite::Int32Vector *i32_array = static_cast<const tflite::Int32Vector *>(v_array);
+ auto values_vec_int32 =
+ std::vector<int32_t>{i32_array->values()->begin(), i32_array->values()->end()};
+ auto values_int32 = fb.CreateVector(values_vec_int32);
+ circle::Int32VectorBuilder int32_vector_builder{fb};
+ int32_vector_builder.add_values(values_int32);
+ return int32_vector_builder.Finish().Union();
+ }
+ case tflite::SparseIndexVector_Uint16Vector:
+ {
+ const tflite::Uint16Vector *u16_array = static_cast<const tflite::Uint16Vector *>(v_array);
+ auto values_vec_uint16 =
+ std::vector<uint16_t>{u16_array->values()->begin(), u16_array->values()->end()};
+ auto values_uint16 = fb.CreateVector(values_vec_uint16);
+ circle::Uint16VectorBuilder uint16_vector_builder{fb};
+ uint16_vector_builder.add_values(values_uint16);
+ return uint16_vector_builder.Finish().Union();
+ }
+ case tflite::SparseIndexVector_Uint8Vector:
+ {
+ const tflite::Uint8Vector *u8_array = static_cast<const tflite::Uint8Vector *>(v_array);
+ auto values_vec_uint8 =
+ std::vector<uint8_t>{u8_array->values()->begin(), u8_array->values()->end()};
+ auto values_uint8 = fb.CreateVector(values_vec_uint8);
+ circle::Uint8VectorBuilder uint8_vector_builder{fb};
+ uint8_vector_builder.add_values(values_uint8);
+ return uint8_vector_builder.Finish().Union();
+ }
+ default:
+ throw std::runtime_error("tflite2circle: wrong SparseIndexVector type.");
+ }
+}
+
+circle::SparseIndexVector
+get_circle_sparse_index_vector_type(const tflite::SparseIndexVector &tfl_sparse_index_vector_type)
+{
+ switch (tfl_sparse_index_vector_type)
+ {
+ case tflite::SparseIndexVector_NONE:
+ return circle::SparseIndexVector_NONE;
+ case tflite::SparseIndexVector_Int32Vector:
+ return circle::SparseIndexVector_Int32Vector;
+ case tflite::SparseIndexVector_Uint16Vector:
+ return circle::SparseIndexVector_Uint16Vector;
+ case tflite::SparseIndexVector_Uint8Vector:
+ return circle::SparseIndexVector_Uint8Vector;
+ default:
+ throw std::runtime_error("tflite2circle: wrong SparseIndexVector type.");
+ }
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/DataLookup.h b/compiler/tflite2circle/src/DataLookup.h
new file mode 100644
index 000000000..58a357703
--- /dev/null
+++ b/compiler/tflite2circle/src/DataLookup.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __DATA_LOOKUP_H__
+#define __DATA_LOOKUP_H__
+
+#include <mio/tflite/schema_generated.h>
+#include <mio/circle/schema_generated.h>
+
+namespace tflite2circle
+{
+
+/**
+ * @brief Returns circle builtin_code according to tflite.
+ *
+ * @note You can see a list of currently supported BuiltinOperator in TFLOperator.lst file.
+*/
+circle::BuiltinOperator get_circle_builtin_code(tflite::BuiltinOperator tfl_bop);
+
+/**
+ * @brief Returns circle TensorType according to tflite.
+ *
+ * @note You can see a list of currently supported TensorType in TFLTensorType.lst file.
+*/
+circle::TensorType get_circle_tensortype(tflite::TensorType tfl_tt);
+
+/**
+ * @brief Returns circle Padding enum according to tflite.
+*/
+circle::Padding get_circle_padding(tflite::Padding tfl_p);
+
+/**
+ * @brief Returns circle ActivationFunctionType according to tflite.
+ *
+ * @note You can see a list of currently supported ActivationFunctionType in
+ * TFLActivationFunctionType.lst file.
+*/
+circle::ActivationFunctionType
+get_circle_activation_function_type(tflite::ActivationFunctionType tfl_aft);
+
+/**
+ * @brief Returns circle builtin_options according to tflite.
+ *
+ * @note You can see a list of currently supported BuiltinOptions in
+ * TFLBuiltinOptions.lst file.
+ *
+ * This function calls the build_circle_##BuiltinOptions internally(e.g.
+ * build_circle_AbsOptions, build_circle_AddOptions, etc.), so refer to it for a more
+ * detailed implementation.
+*/
+flatbuffers::Offset<void> get_circle_builtin_options(flatbuffers::FlatBufferBuilder &fb,
+ const tflite::Operator *op);
+
+/**
+ * @brief Returns circle builtin_options_type according to tflite.
+ *
+ * @note You can see a list of currently supported BuiltinOptions in TFLBuiltinOptions.lst file.
+*/
+circle::BuiltinOptions get_circle_builtin_options_type(const tflite::Operator *op);
+
+/**
+ * @brief Returns circle MirrorPadMode according to tflite.
+*/
+circle::MirrorPadMode get_circle_mirrorpad_mode(tflite::MirrorPadMode tfl_mode);
+
+/**
+ * @brief Returns circle DimensionType according to tflite.
+*/
+circle::DimensionType get_circle_dimension_type(tflite::DimensionType tfl_dim_type);
+
+/**
+ * @brief Returns circle SparseIndexVector according to tflite.
+*/
+flatbuffers::Offset<void>
+get_circle_sparse_index_vector(flatbuffers::FlatBufferBuilder &fb, const void *values,
+ const tflite::SparseIndexVector &tfl_sparse_index_vector_type);
+
+/**
+ * @brief Returns circle SparseIndexVector type according to tflite.
+*/
+circle::SparseIndexVector
+get_circle_sparse_index_vector_type(const tflite::SparseIndexVector &tfl_sparse_index_vector_type);
+
+} // namespace tflite2circle
+
+#endif // __DATA_LOOKUP_H__
diff --git a/compiler/tflite2circle/src/TFLActivationFunctionType.lst b/compiler/tflite2circle/src/TFLActivationFunctionType.lst
new file mode 100644
index 000000000..a378bdc67
--- /dev/null
+++ b/compiler/tflite2circle/src/TFLActivationFunctionType.lst
@@ -0,0 +1,12 @@
+#ifndef TFL_ACTIVATION_FUNCTION
+#error "Activation function type is not defined."
+#endif
+
+// NOTE order of function follows TensorFlow lite schema.fbs 'enum ActivationFunctionType'
+
+TFL_ACTIVATION_FUNCTION(NONE)
+TFL_ACTIVATION_FUNCTION(RELU)
+TFL_ACTIVATION_FUNCTION(RELU_N1_TO_1)
+TFL_ACTIVATION_FUNCTION(RELU6)
+TFL_ACTIVATION_FUNCTION(TANH)
+TFL_ACTIVATION_FUNCTION(SIGN_BIT)
diff --git a/compiler/tflite2circle/src/TFLBuiltinOptions.lst b/compiler/tflite2circle/src/TFLBuiltinOptions.lst
new file mode 100644
index 000000000..4bc101f8e
--- /dev/null
+++ b/compiler/tflite2circle/src/TFLBuiltinOptions.lst
@@ -0,0 +1,108 @@
+#ifndef TFL_BUILTIN_OPTIONS
+#error "TFL_BUILTIN_OPTIONS is not defined"
+#endif
+
+// NOTE order of option follows TensorFlow lite schema.fbs 'union BuiltinOptions'
+
+TFL_BUILTIN_OPTIONS(Conv2DOptions)
+TFL_BUILTIN_OPTIONS(DepthwiseConv2DOptions)
+//TFL_BUILTIN_OPTIONS(ConcatEmbeddingsOptions)
+//TFL_BUILTIN_OPTIONS(LSHProjectionOptions)
+TFL_BUILTIN_OPTIONS(Pool2DOptions)
+//TFL_BUILTIN_OPTIONS(SVDFOptions)
+//TFL_BUILTIN_OPTIONS(RNNOptions)
+TFL_BUILTIN_OPTIONS(FullyConnectedOptions)
+TFL_BUILTIN_OPTIONS(SoftmaxOptions)
+TFL_BUILTIN_OPTIONS(ConcatenationOptions)
+TFL_BUILTIN_OPTIONS(AddOptions)
+TFL_BUILTIN_OPTIONS(L2NormOptions)
+TFL_BUILTIN_OPTIONS(LocalResponseNormalizationOptions)
+//TFL_BUILTIN_OPTIONS(LSTMOptions)
+TFL_BUILTIN_OPTIONS(ResizeBilinearOptions)
+//TFL_BUILTIN_OPTIONS(CallOptions)
+TFL_BUILTIN_OPTIONS(ReshapeOptions)
+//TFL_BUILTIN_OPTIONS(SkipGramOptions)
+TFL_BUILTIN_OPTIONS(SpaceToDepthOptions)
+//TFL_BUILTIN_OPTIONS(EmbeddingLookupSparseOptions)
+TFL_BUILTIN_OPTIONS(MulOptions)
+TFL_BUILTIN_OPTIONS(PadOptions)
+TFL_BUILTIN_OPTIONS(PadV2Options)
+TFL_BUILTIN_OPTIONS(GatherOptions)
+TFL_BUILTIN_OPTIONS(BatchToSpaceNDOptions)
+TFL_BUILTIN_OPTIONS(SpaceToBatchNDOptions)
+TFL_BUILTIN_OPTIONS(TransposeOptions)
+TFL_BUILTIN_OPTIONS(ReducerOptions)
+TFL_BUILTIN_OPTIONS(SubOptions)
+TFL_BUILTIN_OPTIONS(DivOptions)
+TFL_BUILTIN_OPTIONS(SqueezeOptions)
+//TFL_BUILTIN_OPTIONS(SequenceRNNOptions)
+TFL_BUILTIN_OPTIONS(StridedSliceOptions)
+TFL_BUILTIN_OPTIONS(ExpOptions)
+TFL_BUILTIN_OPTIONS(TopKV2Options)
+TFL_BUILTIN_OPTIONS(SplitOptions)
+TFL_BUILTIN_OPTIONS(LogSoftmaxOptions)
+TFL_BUILTIN_OPTIONS(CastOptions)
+//TFL_BUILTIN_OPTIONS(DequantizeOptions)
+TFL_BUILTIN_OPTIONS(MaximumMinimumOptions)
+TFL_BUILTIN_OPTIONS(ArgMaxOptions)
+TFL_BUILTIN_OPTIONS(LessOptions)
+TFL_BUILTIN_OPTIONS(NegOptions)
+//TFL_BUILTIN_OPTIONS(PadV2Options)
+TFL_BUILTIN_OPTIONS(GreaterOptions)
+TFL_BUILTIN_OPTIONS(GreaterEqualOptions)
+TFL_BUILTIN_OPTIONS(LessEqualOptions)
+TFL_BUILTIN_OPTIONS(SelectOptions)
+TFL_BUILTIN_OPTIONS(SelectV2Options)
+TFL_BUILTIN_OPTIONS(SliceOptions)
+TFL_BUILTIN_OPTIONS(TransposeConvOptions)
+TFL_BUILTIN_OPTIONS(SparseToDenseOptions)
+TFL_BUILTIN_OPTIONS(TileOptions)
+TFL_BUILTIN_OPTIONS(ExpandDimsOptions)
+TFL_BUILTIN_OPTIONS(EqualOptions)
+TFL_BUILTIN_OPTIONS(NotEqualOptions)
+TFL_BUILTIN_OPTIONS(ShapeOptions)
+TFL_BUILTIN_OPTIONS(PowOptions)
+TFL_BUILTIN_OPTIONS(ArgMinOptions)
+//TFL_BUILTIN_OPTIONS(FakeQuantOptions)
+TFL_BUILTIN_OPTIONS(PackOptions)
+TFL_BUILTIN_OPTIONS(LogicalOrOptions)
+TFL_BUILTIN_OPTIONS(OneHotOptions)
+TFL_BUILTIN_OPTIONS(LogicalAndOptions)
+TFL_BUILTIN_OPTIONS(LogicalNotOptions)
+TFL_BUILTIN_OPTIONS(UnpackOptions)
+TFL_BUILTIN_OPTIONS(FloorDivOptions)
+TFL_BUILTIN_OPTIONS(SquareOptions)
+TFL_BUILTIN_OPTIONS(ZerosLikeOptions)
+TFL_BUILTIN_OPTIONS(FillOptions)
+//TFL_BUILTIN_OPTIONS(BidirectionalSequenceLSTMOptions)
+//TFL_BUILTIN_OPTIONS(BidirectionalSequenceRNNOptions)
+TFL_BUILTIN_OPTIONS(UnidirectionalSequenceLSTMOptions)
+TFL_BUILTIN_OPTIONS(FloorModOptions)
+TFL_BUILTIN_OPTIONS(RangeOptions)
+TFL_BUILTIN_OPTIONS(ResizeNearestNeighborOptions)
+TFL_BUILTIN_OPTIONS(LeakyReluOptions)
+TFL_BUILTIN_OPTIONS(SquaredDifferenceOptions)
+TFL_BUILTIN_OPTIONS(MirrorPadOptions)
+TFL_BUILTIN_OPTIONS(AbsOptions)
+TFL_BUILTIN_OPTIONS(SplitVOptions)
+TFL_BUILTIN_OPTIONS(UniqueOptions)
+TFL_BUILTIN_OPTIONS(ReverseV2Options)
+TFL_BUILTIN_OPTIONS(AddNOptions)
+TFL_BUILTIN_OPTIONS(GatherNdOptions)
+TFL_BUILTIN_OPTIONS(CosOptions)
+TFL_BUILTIN_OPTIONS(WhereOptions)
+//TFL_BUILTIN_OPTIONS(RankOptions)
+TFL_BUILTIN_OPTIONS(ReverseSequenceOptions)
+TFL_BUILTIN_OPTIONS(MatrixDiagOptions)
+//TFL_BUILTIN_OPTIONS(QuantizeOptions)
+TFL_BUILTIN_OPTIONS(MatrixSetDiagOptions)
+//TFL_BUILTIN_OPTIONS(HardSwishOptions)
+TFL_BUILTIN_OPTIONS(IfOptions)
+TFL_BUILTIN_OPTIONS(WhileOptions)
+TFL_BUILTIN_OPTIONS(DepthToSpaceOptions)
+TFL_BUILTIN_OPTIONS(NonMaxSuppressionV4Options)
+TFL_BUILTIN_OPTIONS(NonMaxSuppressionV5Options)
+TFL_BUILTIN_OPTIONS(RankOptions)
+TFL_BUILTIN_OPTIONS(ScatterNdOptions)
+TFL_BUILTIN_OPTIONS(SegmentSumOptions)
+TFL_BUILTIN_OPTIONS(BatchMatMulOptions)
diff --git a/compiler/tflite2circle/src/TFLModel.cpp b/compiler/tflite2circle/src/TFLModel.cpp
new file mode 100644
index 000000000..33f11fb83
--- /dev/null
+++ b/compiler/tflite2circle/src/TFLModel.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+
+#include "TFLModel.h"
+
+namespace tflite2circle
+{
+
+TFLModel::TFLModel(const std::string &path)
+{
+ _infile.open(path, std::ios::binary | std::ios::in);
+ _valid = _infile.good();
+}
+
+const tflite::Model *TFLModel::load_model(void)
+{
+ assert(_valid == true);
+ _infile.seekg(0, std::ios::end);
+ auto fileSize = _infile.tellg();
+ _infile.seekg(0, std::ios::beg);
+ _data.resize(fileSize);
+ _infile.read(_data.data(), fileSize);
+ _infile.close();
+
+ return tflite::GetModel(_data.data());
+}
+
+} // namespace tflite2circle
diff --git a/compiler/tflite2circle/src/TFLOperator.lst b/compiler/tflite2circle/src/TFLOperator.lst
new file mode 100644
index 000000000..942c846c7
--- /dev/null
+++ b/compiler/tflite2circle/src/TFLOperator.lst
@@ -0,0 +1,133 @@
+#ifndef TFL_OPERATOR
+#error "TFL_OPERATOR is not defined."
+#endif
+
+// NOTE order of operator follows TensorFlow lite schema.fbs 'enum BuiltinOperator'
+
+TFL_OPERATOR(ADD)
+TFL_OPERATOR(AVERAGE_POOL_2D)
+TFL_OPERATOR(CONCATENATION)
+TFL_OPERATOR(CONV_2D)
+TFL_OPERATOR(DEPTH_TO_SPACE)
+TFL_OPERATOR(DEPTHWISE_CONV_2D)
+TFL_OPERATOR(DEQUANTIZE)
+TFL_OPERATOR(EMBEDDING_LOOKUP)
+TFL_OPERATOR(FLOOR)
+TFL_OPERATOR(FULLY_CONNECTED)
+TFL_OPERATOR(HASHTABLE_LOOKUP)
+TFL_OPERATOR(L2_NORMALIZATION)
+TFL_OPERATOR(L2_POOL_2D)
+TFL_OPERATOR(LOCAL_RESPONSE_NORMALIZATION)
+TFL_OPERATOR(LOGISTIC)
+TFL_OPERATOR(LSH_PROJECTION)
+TFL_OPERATOR(LSTM)
+TFL_OPERATOR(MAX_POOL_2D)
+TFL_OPERATOR(MUL)
+TFL_OPERATOR(RELU)
+TFL_OPERATOR(RELU_N1_TO_1)
+TFL_OPERATOR(RELU6)
+TFL_OPERATOR(RESHAPE)
+TFL_OPERATOR(RESIZE_BILINEAR)
+TFL_OPERATOR(RNN)
+TFL_OPERATOR(SOFTMAX)
+TFL_OPERATOR(SPACE_TO_DEPTH)
+TFL_OPERATOR(SVDF)
+TFL_OPERATOR(TANH)
+TFL_OPERATOR(CONCAT_EMBEDDINGS)
+TFL_OPERATOR(SKIP_GRAM)
+TFL_OPERATOR(CALL)
+TFL_OPERATOR(CUSTOM)
+TFL_OPERATOR(EMBEDDING_LOOKUP_SPARSE)
+TFL_OPERATOR(PAD)
+TFL_OPERATOR(UNIDIRECTIONAL_SEQUENCE_RNN)
+TFL_OPERATOR(GATHER)
+TFL_OPERATOR(BATCH_TO_SPACE_ND)
+TFL_OPERATOR(SPACE_TO_BATCH_ND)
+TFL_OPERATOR(TRANSPOSE)
+TFL_OPERATOR(MEAN)
+TFL_OPERATOR(SUB)
+TFL_OPERATOR(DIV)
+TFL_OPERATOR(SQUEEZE)
+TFL_OPERATOR(UNIDIRECTIONAL_SEQUENCE_LSTM)
+TFL_OPERATOR(STRIDED_SLICE)
+TFL_OPERATOR(BIDIRECTIONAL_SEQUENCE_RNN)
+TFL_OPERATOR(EXP)
+TFL_OPERATOR(TOPK_V2)
+TFL_OPERATOR(SPLIT)
+TFL_OPERATOR(LOG_SOFTMAX)
+TFL_OPERATOR(DELEGATE)
+TFL_OPERATOR(BIDIRECTIONAL_SEQUENCE_LSTM)
+TFL_OPERATOR(CAST)
+TFL_OPERATOR(PRELU)
+TFL_OPERATOR(MAXIMUM)
+TFL_OPERATOR(ARG_MAX)
+TFL_OPERATOR(MINIMUM)
+TFL_OPERATOR(LESS)
+TFL_OPERATOR(NEG)
+TFL_OPERATOR(PADV2)
+TFL_OPERATOR(GREATER)
+TFL_OPERATOR(GREATER_EQUAL)
+TFL_OPERATOR(LESS_EQUAL)
+TFL_OPERATOR(SELECT)
+TFL_OPERATOR(SLICE)
+TFL_OPERATOR(SIN)
+TFL_OPERATOR(TRANSPOSE_CONV)
+TFL_OPERATOR(SPARSE_TO_DENSE)
+TFL_OPERATOR(TILE)
+TFL_OPERATOR(EXPAND_DIMS)
+TFL_OPERATOR(EQUAL)
+TFL_OPERATOR(NOT_EQUAL)
+TFL_OPERATOR(LOG)
+TFL_OPERATOR(SUM)
+TFL_OPERATOR(SQRT)
+TFL_OPERATOR(RSQRT)
+TFL_OPERATOR(SHAPE)
+TFL_OPERATOR(POW)
+TFL_OPERATOR(ARG_MIN)
+TFL_OPERATOR(FAKE_QUANT)
+TFL_OPERATOR(REDUCE_PROD)
+TFL_OPERATOR(REDUCE_MAX)
+TFL_OPERATOR(PACK)
+TFL_OPERATOR(LOGICAL_OR)
+TFL_OPERATOR(ONE_HOT)
+TFL_OPERATOR(LOGICAL_AND)
+TFL_OPERATOR(LOGICAL_NOT)
+TFL_OPERATOR(UNPACK)
+TFL_OPERATOR(REDUCE_MIN)
+TFL_OPERATOR(FLOOR_DIV)
+TFL_OPERATOR(REDUCE_ANY)
+TFL_OPERATOR(SQUARE)
+TFL_OPERATOR(ZEROS_LIKE)
+TFL_OPERATOR(FILL)
+TFL_OPERATOR(FLOOR_MOD)
+TFL_OPERATOR(RANGE)
+TFL_OPERATOR(RESIZE_NEAREST_NEIGHBOR)
+TFL_OPERATOR(LEAKY_RELU)
+TFL_OPERATOR(SQUARED_DIFFERENCE)
+TFL_OPERATOR(MIRROR_PAD)
+TFL_OPERATOR(ABS)
+TFL_OPERATOR(SPLIT_V)
+TFL_OPERATOR(UNIQUE)
+TFL_OPERATOR(CEIL)
+TFL_OPERATOR(REVERSE_V2)
+TFL_OPERATOR(ADD_N)
+TFL_OPERATOR(GATHER_ND)
+TFL_OPERATOR(COS)
+TFL_OPERATOR(WHERE)
+TFL_OPERATOR(RANK)
+TFL_OPERATOR(ELU)
+TFL_OPERATOR(REVERSE_SEQUENCE)
+TFL_OPERATOR(MATRIX_DIAG)
+TFL_OPERATOR(QUANTIZE)
+TFL_OPERATOR(MATRIX_SET_DIAG)
+TFL_OPERATOR(ROUND)
+TFL_OPERATOR(HARD_SWISH)
+TFL_OPERATOR(IF)
+TFL_OPERATOR(WHILE)
+TFL_OPERATOR(NON_MAX_SUPPRESSION_V4)
+TFL_OPERATOR(NON_MAX_SUPPRESSION_V5)
+TFL_OPERATOR(SCATTER_ND)
+TFL_OPERATOR(SELECT_V2)
+TFL_OPERATOR(DENSIFY)
+TFL_OPERATOR(SEGMENT_SUM)
+TFL_OPERATOR(BATCH_MATMUL)
diff --git a/compiler/tflite2circle/src/TFLTensorType.lst b/compiler/tflite2circle/src/TFLTensorType.lst
new file mode 100644
index 000000000..968153a5d
--- /dev/null
+++ b/compiler/tflite2circle/src/TFLTensorType.lst
@@ -0,0 +1,16 @@
+#ifndef TFL_TENSORTYPE
+#error "TFL_TENSORTYPE is not defined."
+#endif
+
+// NOTE order of type follows TensorFlow lite schema.fbs 'enum TensorType'
+
+TFL_TENSORTYPE(FLOAT32)
+TFL_TENSORTYPE(FLOAT16)
+TFL_TENSORTYPE(INT32)
+TFL_TENSORTYPE(UINT8)
+TFL_TENSORTYPE(INT64)
+TFL_TENSORTYPE(STRING)
+TFL_TENSORTYPE(BOOL)
+TFL_TENSORTYPE(INT16)
+TFL_TENSORTYPE(COMPLEX64)
+TFL_TENSORTYPE(INT8)
diff --git a/compiler/tfts/CMakeLists.txt b/compiler/tfts/CMakeLists.txt
index 152071049..2dac9445b 100644
--- a/compiler/tfts/CMakeLists.txt
+++ b/compiler/tfts/CMakeLists.txt
@@ -1,9 +1,8 @@
nncc_find_resource(TensorFlowTests)
-nncc_find_package(TensorFlow QUIET)
-if(NOT TensorFlow_FOUND)
+if(NOT TARGET nnkit_tf_backend)
return()
-endif(NOT TensorFlow_FOUND)
+endif(NOT TARGET nnkit_tf_backend)
if(NOT TARGET tfkit)
return()
diff --git a/compiler/v4tf/README.md b/compiler/v4tf/README.md
new file mode 100644
index 000000000..54c8bc889
--- /dev/null
+++ b/compiler/v4tf/README.md
@@ -0,0 +1,16 @@
+# v4tf
+
+## What is this?
+
+*v4tf* is is a wrapper interface to use TensorFlow by its C API.
+The name was originated from the movie, *V for Vendetta*, where the main character *V* hides his face by wearing a mask.
+
+## Why do we need this?
+
+In *nncc*, some tests use TensorFlow, which uses Protocol Buffers.
+For example, TensorFlow 1.12 uses Protocol Buffers 3.5.2.
+
+Some of *nncc* modules use different version Protocol Buffers for internal purpose.
+If such modules also try to use TensorFlow API, errors were thrown due to resolution of wrong symbols of different versions of Protocol Buffers.
+
+To prevent these errors, *v4tf* loads TensorFlow dynamically with all of its symbols resolved.
diff --git a/compiler/vconone/CMakeLists.txt b/compiler/vconone/CMakeLists.txt
new file mode 100644
index 000000000..905515401
--- /dev/null
+++ b/compiler/vconone/CMakeLists.txt
@@ -0,0 +1,31 @@
+if (NOT VCONONE_VERSION)
+ set(VCONONE_VERSION 0x00000000000b0001)
+ # 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
+endif()
+
+configure_file(version_cfg.h.in version_cfg.h @ONLY)
+
+set(DRIVER "driver/driver.cpp")
+
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(vconone STATIC ${SOURCES})
+target_include_directories(vconone PUBLIC include)
+target_include_directories(vconone PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
+
+add_executable(one-version ${DRIVER})
+target_link_libraries(one-version vconone)
+install(TARGETS one-version DESTINATION bin)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(vconone_test ${TESTS})
+target_link_libraries(vconone_test vconone)
diff --git a/compiler/vconone/README.md b/compiler/vconone/README.md
new file mode 100644
index 000000000..c08dd63d3
--- /dev/null
+++ b/compiler/vconone/README.md
@@ -0,0 +1,14 @@
+# vconone
+
+_vconone_ provides version number and strings for one-* commands and command
+line tools
+
+# Revise version number
+
+To revise version number, update `VCONONE_VERSION` in `CmakeLists.txt`
+or give `-DVCONONE_VERSION=0x0000000100080001` at cmake configure step.
+
+Number given is four numbers `build`, `patch`, `minor` and `major` in order for
+each 16bit integers. `build` is not used for now.
+
+`0x0000000100080001` version is interpretered as `1.8.1`
diff --git a/compiler/vconone/driver/driver.cpp b/compiler/vconone/driver/driver.cpp
new file mode 100644
index 000000000..12bd0eef2
--- /dev/null
+++ b/compiler/vconone/driver/driver.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vconone/vconone.h>
+
+#include <string>
+#include <iostream>
+
+int main(int argc, char *argv[])
+{
+ auto str = vconone::get_string();
+ if (argc >= 2)
+ {
+ for (int c = 1; c < argc; ++c)
+ std::cout << argv[c] << " ";
+ std::cout << "version " << str << std::endl;
+ std::cout << vconone::get_copyright() << std::endl;
+ }
+ else
+ std::cout << str;
+
+ return 0;
+}
diff --git a/compiler/vconone/include/vconone/vconone.h b/compiler/vconone/include/vconone/vconone.h
new file mode 100644
index 000000000..a6a1998a5
--- /dev/null
+++ b/compiler/vconone/include/vconone/vconone.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VCON_ONE_H__
+#define __VCON_ONE_H__
+
+#include <cstdint>
+#include <string>
+
+namespace vconone
+{
+
+struct four
+{
+ uint16_t major;
+ uint16_t minor;
+ uint16_t patch;
+ uint16_t build; // build is not used for now
+};
+
+union version {
+ uint64_t v;
+ four f;
+};
+
+/**
+ * @brief get_number will return version union structure
+ */
+version get_number(void);
+
+/**
+ * @brief get_string will return string of major.minor.patch (without build)
+ */
+std::string get_string(void);
+
+/**
+ * @brief get_string4 will return string of major.minor.patch.build
+ */
+std::string get_string4(void);
+
+/**
+ * @brief get_copyright will return copyright string
+ */
+std::string get_copyright(void);
+
+} // namespace vconone
+
+#endif // __VCON_ONE_H__
diff --git a/compiler/vconone/src/version.cpp b/compiler/vconone/src/version.cpp
new file mode 100644
index 000000000..9b693c621
--- /dev/null
+++ b/compiler/vconone/src/version.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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 "vconone/vconone.h"
+
+#include "version_cfg.h"
+
+#include <sstream>
+
+namespace vconone
+{
+
+version get_number(void)
+{
+ version v;
+ v.v = VCONONE_VERSION;
+ return v;
+}
+
+std::string get_string4(void)
+{
+ std::ostringstream ss;
+
+ auto v = get_number();
+ ss << unsigned(v.f.major) << "." << unsigned(v.f.minor) << "." << unsigned(v.f.patch) << "."
+ << unsigned(v.f.build);
+
+ return ss.str();
+}
+
+std::string get_string(void)
+{
+ std::ostringstream ss;
+
+ auto v = get_number();
+ ss << unsigned(v.f.major) << "." << unsigned(v.f.minor) << "." << unsigned(v.f.patch);
+
+ return ss.str();
+}
+
+std::string get_copyright(void)
+{
+ std::string str;
+ str = "Copyright (c) 2020 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;
+}
+
+} // namespace vconone
diff --git a/compiler/vconone/src/version.test.cpp b/compiler/vconone/src/version.test.cpp
new file mode 100644
index 000000000..35a0647c1
--- /dev/null
+++ b/compiler/vconone/src/version.test.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vconone/vconone.h>
+
+#include <gtest/gtest.h>
+
+TEST(vconone, version_number)
+{
+ auto v = vconone::get_number();
+
+ ASSERT_NE(0x0000000000000000ULL, v.v);
+}
+
+TEST(vconone, version_string)
+{
+ auto str = vconone::get_string();
+
+ ASSERT_NE("..", str);
+ ASSERT_NE("", str);
+}
+
+TEST(vconone, version_string4)
+{
+ auto str = vconone::get_string4();
+
+ ASSERT_NE("...", str);
+ ASSERT_NE("", str);
+}
+
+TEST(vconone, copyright)
+{
+ auto str = vconone::get_copyright();
+
+ ASSERT_NE("", str);
+}
diff --git a/compiler/vconone/version_cfg.h.in b/compiler/vconone/version_cfg.h.in
new file mode 100644
index 000000000..aa3ad9e70
--- /dev/null
+++ b/compiler/vconone/version_cfg.h.in
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VCON_ONE_VERSION_CFG_H__
+#define __VCON_ONE_VERSION_CFG_H__
+
+#define VCONONE_VERSION @VCONONE_VERSION@ULL
+
+#endif // __VCON_ONE_VERSION_CFG_H__